datapath-windows: Rename OvsFlowNlNewCmdHandler to OvsFlowNlCmdHandler
[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             instance->cookie = i;
457             break;
458         }
459     }
460     ASSERT(i < OVS_MAX_OPEN_INSTANCES);
461     instance->fileObject = fileObject;
462     ASSERT(fileObject->FsContext == NULL);
463     instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
464     if (instance->pid == 0) {
465         /* XXX: check for rollover. */
466     }
467     fileObject->FsContext = instance;
468     OvsReleaseCtrlLock();
469     return STATUS_SUCCESS;
470 }
471
472 static VOID
473 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
474 {
475     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
476     ASSERT(instance);
477     ASSERT(fileObject == instance->fileObject);
478     OvsCleanupEvent(instance);
479     OvsCleanupPacketQueue(instance);
480 }
481
482 VOID
483 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
484 {
485     POVS_OPEN_INSTANCE instance;
486     ASSERT(fileObject->FsContext);
487     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
488     ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
489
490     OvsAcquireCtrlLock();
491     fileObject->FsContext = NULL;
492     ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
493     ovsOpenInstanceArray[instance->cookie] = NULL;
494     OvsReleaseCtrlLock();
495     ASSERT(instance->eventQueue == NULL);
496     ASSERT (instance->packetQueue == NULL);
497     OvsFreeMemory(instance);
498 }
499
500 NTSTATUS
501 OvsCompleteIrpRequest(PIRP irp,
502                       ULONG_PTR infoPtr,
503                       NTSTATUS status)
504 {
505     irp->IoStatus.Information = infoPtr;
506     irp->IoStatus.Status = status;
507     IoCompleteRequest(irp, IO_NO_INCREMENT);
508     return status;
509 }
510
511
512 NTSTATUS
513 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
514                    PIRP irp)
515 {
516     PIO_STACK_LOCATION irpSp;
517     NTSTATUS status = STATUS_SUCCESS;
518     PFILE_OBJECT fileObject;
519     POVS_DEVICE_EXTENSION ovsExt =
520         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
521
522     ASSERT(deviceObject == gOvsDeviceObject);
523     ASSERT(ovsExt != NULL);
524
525     irpSp = IoGetCurrentIrpStackLocation(irp);
526     fileObject = irpSp->FileObject;
527     OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
528                   deviceObject, fileObject,
529                   ovsExt->numberOpenInstance);
530
531     switch (irpSp->MajorFunction) {
532     case IRP_MJ_CREATE:
533         status = OvsAddOpenInstance(ovsExt, fileObject);
534         if (STATUS_SUCCESS == status) {
535             InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
536         }
537         break;
538     case IRP_MJ_CLOSE:
539         ASSERT(ovsExt->numberOpenInstance > 0);
540         OvsRemoveOpenInstance(fileObject);
541         InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
542         break;
543     default:
544         ASSERT(0);
545     }
546     return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
547 }
548
549 _Use_decl_annotations_
550 NTSTATUS
551 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
552                  PIRP irp)
553 {
554
555     PIO_STACK_LOCATION irpSp;
556     PFILE_OBJECT fileObject;
557
558     NTSTATUS status = STATUS_SUCCESS;
559 #ifdef DBG
560     POVS_DEVICE_EXTENSION ovsExt =
561         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
562     if (ovsExt) {
563         ASSERT(ovsExt->numberOpenInstance > 0);
564     }
565 #else
566     UNREFERENCED_PARAMETER(deviceObject);
567 #endif
568     ASSERT(deviceObject == gOvsDeviceObject);
569     irpSp = IoGetCurrentIrpStackLocation(irp);
570     fileObject = irpSp->FileObject;
571
572     ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
573
574     OvsCleanupOpenInstance(fileObject);
575
576     return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
577 }
578
579
580 /*
581  * --------------------------------------------------------------------------
582  * IOCTL function handler for the device.
583  * --------------------------------------------------------------------------
584  */
585 NTSTATUS
586 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
587                  PIRP irp)
588 {
589
590     PIO_STACK_LOCATION irpSp;
591     NTSTATUS status = STATUS_SUCCESS;
592     PFILE_OBJECT fileObject;
593     PVOID inputBuffer = NULL;
594     PVOID outputBuffer = NULL;
595     UINT32 inputBufferLen, outputBufferLen;
596     UINT32 code, replyLen = 0;
597     POVS_OPEN_INSTANCE instance;
598     UINT32 devOp;
599     OVS_MESSAGE ovsMsgReadOp;
600     POVS_MESSAGE ovsMsg;
601     NETLINK_FAMILY *nlFamilyOps;
602     OVS_USER_PARAMS_CONTEXT usrParamsCtx;
603
604 #ifdef DBG
605     POVS_DEVICE_EXTENSION ovsExt =
606         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
607     ASSERT(deviceObject == gOvsDeviceObject);
608     ASSERT(ovsExt);
609     ASSERT(ovsExt->numberOpenInstance > 0);
610 #else
611     UNREFERENCED_PARAMETER(deviceObject);
612 #endif
613
614     irpSp = IoGetCurrentIrpStackLocation(irp);
615
616     ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
617     ASSERT(irpSp->FileObject != NULL);
618
619     fileObject = irpSp->FileObject;
620     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
621     code = irpSp->Parameters.DeviceIoControl.IoControlCode;
622     inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
623     outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
624     inputBuffer = irp->AssociatedIrp.SystemBuffer;
625
626     /* Concurrent netlink operations are not supported. */
627     if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
628         status = STATUS_RESOURCE_IN_USE;
629         goto done;
630     }
631
632     /*
633      * Validate the input/output buffer arguments depending on the type of the
634      * operation.
635      */
636     switch (code) {
637     case OVS_IOCTL_TRANSACT:
638         /* Input buffer is mandatory, output buffer is optional. */
639         if (outputBufferLen != 0) {
640             status = MapIrpOutputBuffer(irp, outputBufferLen,
641                                         sizeof *ovsMsg, &outputBuffer);
642             if (status != STATUS_SUCCESS) {
643                 goto done;
644             }
645             ASSERT(outputBuffer);
646         }
647
648         if (inputBufferLen < sizeof (*ovsMsg)) {
649             status = STATUS_NDIS_INVALID_LENGTH;
650             goto done;
651         }
652
653         ovsMsg = inputBuffer;
654         devOp = OVS_TRANSACTION_DEV_OP;
655         break;
656
657     case OVS_IOCTL_READ_EVENT:
658         /* This IOCTL is used to read events */
659         if (outputBufferLen != 0) {
660             status = MapIrpOutputBuffer(irp, outputBufferLen,
661                                         sizeof *ovsMsg, &outputBuffer);
662             if (status != STATUS_SUCCESS) {
663                 goto done;
664             }
665             ASSERT(outputBuffer);
666         } else {
667             status = STATUS_NDIS_INVALID_LENGTH;
668             goto done;
669         }
670         inputBuffer = NULL;
671         inputBufferLen = 0;
672
673         ovsMsg = &ovsMsgReadOp;
674         ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
675         /* An "artificial" command so we can use NL family function table*/
676         ovsMsg->genlMsg.cmd = OVS_CTRL_CMD_EVENT_NOTIFY;
677         devOp = OVS_READ_DEV_OP;
678         break;
679
680     case OVS_IOCTL_READ:
681         /* Output buffer is mandatory. */
682         if (outputBufferLen != 0) {
683             status = MapIrpOutputBuffer(irp, outputBufferLen,
684                                         sizeof *ovsMsg, &outputBuffer);
685             if (status != STATUS_SUCCESS) {
686                 goto done;
687             }
688             ASSERT(outputBuffer);
689         } else {
690             status = STATUS_NDIS_INVALID_LENGTH;
691             goto done;
692         }
693
694         /*
695          * Operate in the mode that read ioctl is similar to ReadFile(). This
696          * might change as the userspace code gets implemented.
697          */
698         inputBuffer = NULL;
699         inputBufferLen = 0;
700
701         /*
702          * For implementing read (ioctl or otherwise), we need to store some
703          * state in the instance to indicate the command that started the dump
704          * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
705          * that 'ovsMsgReadOp' is needed only in this function to call into the
706          * appropraite handler. The handler itself can access the state in the
707          * instance.
708          *
709          * In the absence of a dump start, return 0 bytes.
710          */
711         if (instance->dumpState.ovsMsg == NULL) {
712             replyLen = 0;
713             status = STATUS_SUCCESS;
714             goto done;
715         }
716         RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
717                       sizeof (ovsMsgReadOp));
718
719         /* Create an NL message for consumption. */
720         ovsMsg = &ovsMsgReadOp;
721         devOp = OVS_READ_DEV_OP;
722
723         break;
724
725     case OVS_IOCTL_WRITE:
726         /* Input buffer is mandatory. */
727         if (inputBufferLen < sizeof (*ovsMsg)) {
728             status = STATUS_NDIS_INVALID_LENGTH;
729             goto done;
730         }
731
732         ovsMsg = inputBuffer;
733         devOp = OVS_WRITE_DEV_OP;
734         break;
735
736     default:
737         status = STATUS_INVALID_DEVICE_REQUEST;
738         goto done;
739     }
740
741     ASSERT(ovsMsg);
742     switch (ovsMsg->nlMsg.nlmsgType) {
743     case OVS_WIN_NL_CTRL_FAMILY_ID:
744         nlFamilyOps = &nlControlFamilyOps;
745         break;
746     case OVS_WIN_NL_DATAPATH_FAMILY_ID:
747         nlFamilyOps = &nlDatapathFamilyOps;
748         break;
749     case OVS_WIN_NL_FLOW_FAMILY_ID:
750          nlFamilyOps = &nlFLowFamilyOps;
751          break;
752     case OVS_WIN_NL_PACKET_FAMILY_ID:
753         status = STATUS_NOT_IMPLEMENTED;
754         goto done;
755     case OVS_WIN_NL_VPORT_FAMILY_ID:
756         nlFamilyOps = &nlVportFamilyOps;
757         break;
758     default:
759         status = STATUS_INVALID_PARAMETER;
760         goto done;
761     }
762
763     /*
764      * For read operation, the netlink command has already been validated
765      * previously.
766      */
767     if (devOp != OVS_READ_DEV_OP) {
768         status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
769         if (status != STATUS_SUCCESS) {
770             goto done;
771         }
772     }
773
774     InitUserParamsCtx(irp, instance, devOp, ovsMsg,
775                       inputBuffer, inputBufferLen,
776                       outputBuffer, outputBufferLen,
777                       &usrParamsCtx);
778
779     status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
780
781 done:
782     KeMemoryBarrier();
783     instance->inUse = 0;
784     return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
785 }
786
787
788 /*
789  * --------------------------------------------------------------------------
790  * Function to validate a netlink command. Only certain combinations of
791  * (device operation, netlink family, command) are valid.
792  * --------------------------------------------------------------------------
793  */
794 static NTSTATUS
795 ValidateNetlinkCmd(UINT32 devOp,
796                    POVS_OPEN_INSTANCE instance,
797                    POVS_MESSAGE ovsMsg,
798                    NETLINK_FAMILY *nlFamilyOps)
799 {
800     NTSTATUS status = STATUS_INVALID_PARAMETER;
801     UINT16 i;
802
803     for (i = 0; i < nlFamilyOps->opsCount; i++) {
804         if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
805             /* Validate if the command is valid for the device operation. */
806             if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
807                 status = STATUS_INVALID_PARAMETER;
808                 goto done;
809             }
810
811             /* Validate the version. */
812             if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
813                 status = STATUS_INVALID_PARAMETER;
814                 goto done;
815             }
816
817             /* Validate the DP for commands that require a DP. */
818             if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
819                 OvsAcquireCtrlLock();
820                 if (ovsMsg->ovsHdr.dp_ifindex !=
821                     (INT)gOvsSwitchContext->dpNo) {
822                     status = STATUS_INVALID_PARAMETER;
823                     OvsReleaseCtrlLock();
824                     goto done;
825                 }
826                 OvsReleaseCtrlLock();
827             }
828
829             /* Validate the PID. */
830             if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
831                 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
832                     status = STATUS_INVALID_PARAMETER;
833                     goto done;
834                 }
835             }
836
837             status = STATUS_SUCCESS;
838             break;
839         }
840     }
841
842 done:
843     return status;
844 }
845
846 /*
847  * --------------------------------------------------------------------------
848  * Function to invoke the netlink command handler.
849  * --------------------------------------------------------------------------
850  */
851 static NTSTATUS
852 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
853                         NETLINK_FAMILY *nlFamilyOps,
854                         UINT32 *replyLen)
855 {
856     NTSTATUS status = STATUS_INVALID_PARAMETER;
857     UINT16 i;
858
859     for (i = 0; i < nlFamilyOps->opsCount; i++) {
860         if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
861             NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
862             ASSERT(handler);
863             if (handler) {
864                 status = handler(usrParamsCtx, replyLen);
865             }
866             break;
867         }
868     }
869
870     return status;
871 }
872
873 /*
874  * --------------------------------------------------------------------------
875  *  Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
876  *
877  *  Each handle on the device is assigned a unique PID when the handle is
878  *  created. On platforms that support netlink natively, the PID is available
879  *  to userspace when the netlink socket is created. However, without native
880  *  netlink support on Windows, OVS datapath generates the PID and lets the
881  *  userspace query it.
882  *
883  *  This function implements the query.
884  * --------------------------------------------------------------------------
885  */
886 static NTSTATUS
887 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
888                     UINT32 *replyLen)
889 {
890     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
891     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
892
893     if (usrParamsCtx->outputLength >= sizeof *msgOut) {
894         POVS_OPEN_INSTANCE instance =
895             (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
896
897         RtlZeroMemory(msgOut, sizeof *msgOut);
898         msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
899         msgOut->nlMsg.nlmsgPid = instance->pid;
900         *replyLen = sizeof *msgOut;
901         /* XXX: We might need to return the DP index as well. */
902     } else {
903         return STATUS_NDIS_INVALID_LENGTH;
904     }
905
906     return STATUS_SUCCESS;
907 }
908
909 /*
910  * --------------------------------------------------------------------------
911  * Utility function to fill up information about the datapath in a reply to
912  * userspace.
913  * Assumes that 'gOvsCtrlLock' lock is acquired.
914  * --------------------------------------------------------------------------
915  */
916 static NTSTATUS
917 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
918               POVS_MESSAGE msgIn,
919               PNL_BUFFER nlBuf)
920 {
921     BOOLEAN writeOk;
922     OVS_MESSAGE msgOutTmp;
923     OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
924     PNL_MSG_HDR nlMsg;
925
926     /* XXX: Add API for nlBuf->bufRemLen. */
927     ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof *msgIn);
928
929     msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
930     msgOutTmp.nlMsg.nlmsgFlags = 0;  /* XXX: ? */
931     msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
932     msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
933
934     msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
935     msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
936     msgOutTmp.genlMsg.reserved = 0;
937
938     msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
939
940     writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
941     if (writeOk) {
942         writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
943                                      OVS_SYSTEM_DP_NAME);
944     }
945     if (writeOk) {
946         OVS_DP_STATS dpStats;
947
948         dpStats.n_hit = datapath->hits;
949         dpStats.n_missed = datapath->misses;
950         dpStats.n_lost = datapath->lost;
951         dpStats.n_flows = datapath->nFlows;
952         writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
953                                      (PCHAR)&dpStats, sizeof dpStats);
954     }
955     nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
956     nlMsg->nlmsgLen = NlBufSize(nlBuf);
957
958     return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
959 }
960
961 /*
962  * --------------------------------------------------------------------------
963  * Handler for queueing an IRP used for event notification. The IRP is
964  * completed when a port state changes. STATUS_PENDING is returned on
965  * success. User mode keep a pending IRP at all times.
966  * --------------------------------------------------------------------------
967  */
968 static NTSTATUS
969 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
970                        UINT32 *replyLen)
971 {
972     NDIS_STATUS status;
973
974     UNREFERENCED_PARAMETER(replyLen);
975
976     POVS_OPEN_INSTANCE instance =
977         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
978     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
979     OVS_EVENT_POLL poll;
980
981     poll.dpNo = msgIn->ovsHdr.dp_ifindex;
982     status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
983                                &poll, sizeof poll);
984     return status;
985 }
986
987
988 /*
989  * --------------------------------------------------------------------------
990  *  Handler for the subscription for the event queue
991  * --------------------------------------------------------------------------
992  */
993 static NTSTATUS
994 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
995                             UINT32 *replyLen)
996 {
997     NDIS_STATUS status;
998     OVS_EVENT_SUBSCRIBE request;
999     BOOLEAN rc;
1000     UINT8 join;
1001     PNL_ATTR attrs[2];
1002     const NL_POLICY policy[] =  {
1003         [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
1004         [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
1005         };
1006
1007     UNREFERENCED_PARAMETER(replyLen);
1008
1009     POVS_OPEN_INSTANCE instance =
1010         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1011     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1012
1013     rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1014          NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, 2);
1015     if (!rc) {
1016         status = STATUS_INVALID_PARAMETER;
1017         goto done;
1018     }
1019
1020     /* XXX Ignore the MC group for now */
1021     join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1022     request.dpNo = msgIn->ovsHdr.dp_ifindex;
1023     request.subscribe = join;
1024     request.mask = OVS_EVENT_MASK_ALL;
1025
1026     status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1027                                     sizeof request);
1028 done:
1029     return status;
1030 }
1031
1032
1033 /*
1034  * --------------------------------------------------------------------------
1035  *  Command Handler for 'OVS_DP_CMD_GET'.
1036  *
1037  *  The function handles both the dump based as well as the transaction based
1038  *  'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1039  *  call to setup dump state, as well as subsequent calls to continue dumping
1040  *  data.
1041  * --------------------------------------------------------------------------
1042  */
1043 static NTSTATUS
1044 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1045                    UINT32 *replyLen)
1046 {
1047     if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1048         return HandleGetDpTransaction(usrParamsCtx, replyLen);
1049     } else {
1050         return HandleGetDpDump(usrParamsCtx, replyLen);
1051     }
1052 }
1053
1054 /*
1055  * --------------------------------------------------------------------------
1056  *  Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1057  * --------------------------------------------------------------------------
1058  */
1059 static NTSTATUS
1060 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1061                        UINT32 *replyLen)
1062 {
1063     return HandleDpTransaction(usrParamsCtx, replyLen);
1064 }
1065
1066
1067 /*
1068  * --------------------------------------------------------------------------
1069  *  Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1070  * --------------------------------------------------------------------------
1071  */
1072 static NTSTATUS
1073 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1074                 UINT32 *replyLen)
1075 {
1076     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1077     POVS_OPEN_INSTANCE instance =
1078         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1079
1080     if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1081         *replyLen = 0;
1082         OvsSetupDumpStart(usrParamsCtx);
1083     } else {
1084         NL_BUFFER nlBuf;
1085         NTSTATUS status;
1086         POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1087
1088         ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1089
1090         if (instance->dumpState.ovsMsg == NULL) {
1091             ASSERT(FALSE);
1092             return STATUS_INVALID_DEVICE_STATE;
1093         }
1094
1095         /* Dump state must have been deleted after previous dump operation. */
1096         ASSERT(instance->dumpState.index[0] == 0);
1097         /* Output buffer has been validated while validating read dev op. */
1098         ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1099
1100         NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1101                   usrParamsCtx->outputLength);
1102
1103         OvsAcquireCtrlLock();
1104         if (!gOvsSwitchContext) {
1105             /* Treat this as a dump done. */
1106             OvsReleaseCtrlLock();
1107             *replyLen = 0;
1108             FreeUserDumpState(instance);
1109             return STATUS_SUCCESS;
1110         }
1111         status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1112         OvsReleaseCtrlLock();
1113
1114         if (status != STATUS_SUCCESS) {
1115             *replyLen = 0;
1116             FreeUserDumpState(instance);
1117             return status;
1118         }
1119
1120         /* Increment the dump index. */
1121         instance->dumpState.index[0] = 1;
1122         *replyLen = msgOut->nlMsg.nlmsgLen;
1123
1124         /* Free up the dump state, since there's no more data to continue. */
1125         FreeUserDumpState(instance);
1126     }
1127
1128     return STATUS_SUCCESS;
1129 }
1130
1131
1132 /*
1133  * --------------------------------------------------------------------------
1134  *  Command Handler for 'OVS_DP_CMD_SET'.
1135  * --------------------------------------------------------------------------
1136  */
1137 static NTSTATUS
1138 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1139                    UINT32 *replyLen)
1140 {
1141     return HandleDpTransaction(usrParamsCtx, replyLen);
1142 }
1143
1144 /*
1145  * --------------------------------------------------------------------------
1146  *  Function for handling transaction based 'OVS_DP_CMD_GET' and
1147  *  'OVS_DP_CMD_SET' commands.
1148  * --------------------------------------------------------------------------
1149  */
1150 static NTSTATUS
1151 HandleDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1152                     UINT32 *replyLen)
1153 {
1154     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1155     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1156     NTSTATUS status = STATUS_SUCCESS;
1157     NL_BUFFER nlBuf;
1158     static const NL_POLICY ovsDatapathSetPolicy[] = {
1159         [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1160         [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1161         [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1162     };
1163     PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1164
1165     /* input buffer has been validated while validating write dev op. */
1166     ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1167
1168     /* Parse any attributes in the request. */
1169     if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1170         if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1171                         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1172                         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1173                         ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1174             return STATUS_INVALID_PARAMETER;
1175         }
1176
1177         /*
1178         * XXX: Not clear at this stage if there's any role for the
1179         * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1180         * from userspace.
1181         */
1182
1183     } else {
1184         RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1185     }
1186
1187     /* Output buffer is optional for OVS_TRANSACTION_DEV_OP. */
1188     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1189         return STATUS_NDIS_INVALID_LENGTH;
1190     }
1191     NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1192
1193     OvsAcquireCtrlLock();
1194     if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1195         if (!gOvsSwitchContext &&
1196             !OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1197                               OVS_SYSTEM_DP_NAME)) {
1198             OvsReleaseCtrlLock();
1199             status = STATUS_NOT_FOUND;
1200             goto cleanup;
1201         }
1202     } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1203         OvsReleaseCtrlLock();
1204         status = STATUS_NOT_FOUND;
1205         goto cleanup;
1206     }
1207
1208     status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1209     OvsReleaseCtrlLock();
1210
1211     *replyLen = NlBufSize(&nlBuf);
1212
1213 cleanup:
1214     return status;
1215 }
1216
1217
1218 static NTSTATUS
1219 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1220 {
1221     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1222     POVS_OPEN_INSTANCE instance =
1223         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1224
1225     /* input buffer has been validated while validating write dev op. */
1226     ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1227
1228     /* A write operation that does not indicate dump start is invalid. */
1229     if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1230         return STATUS_INVALID_PARAMETER;
1231     }
1232     /* XXX: Handle other NLM_F_* flags in the future. */
1233
1234     /*
1235      * This operation should be setting up the dump state. If there's any
1236      * previous state, clear it up so as to set it up afresh.
1237      */
1238     if (instance->dumpState.ovsMsg != NULL) {
1239         FreeUserDumpState(instance);
1240     }
1241
1242     return InitUserDumpState(instance, msgIn);
1243 }
1244
1245 static VOID
1246 BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
1247                      UINT32 length, UINT16 flags)
1248 {
1249     msgOut->nlMsg.nlmsgType = type;
1250     msgOut->nlMsg.nlmsgFlags = flags;
1251     msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1252     msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1253     msgOut->nlMsg.nlmsgLen = length;
1254
1255     msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
1256     msgOut->genlMsg.version = nlDatapathFamilyOps.version;
1257     msgOut->genlMsg.reserved = 0;
1258 }
1259
1260 static VOID
1261 BuildReplyMsgFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
1262 {
1263     BuildMsgOut(msgIn, msgOut, msgIn->nlMsg.nlmsgType, sizeof(OVS_MESSAGE),
1264                 flags);
1265 }
1266
1267 static VOID
1268 BuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode)
1269 {
1270     BuildMsgOut(msgIn, (POVS_MESSAGE)msgOut, NLMSG_ERROR,
1271                 sizeof(OVS_MESSAGE_ERROR), 0);
1272
1273     msgOut->errorMsg.error = errorCode;
1274     msgOut->errorMsg.nlMsg = msgIn->nlMsg;
1275 }
1276
1277 static NTSTATUS
1278 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1279                       POVS_MESSAGE msgIn,
1280                       PVOID outBuffer,
1281                       UINT32 outBufLen,
1282                       int dpIfIndex)
1283 {
1284     NL_BUFFER nlBuffer;
1285     OVS_VPORT_FULL_STATS vportStats;
1286     BOOLEAN ok;
1287     OVS_MESSAGE msgOut;
1288     PNL_MSG_HDR nlMsg;
1289
1290     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1291
1292     BuildReplyMsgFromMsgIn(msgIn, &msgOut, NLM_F_MULTI);
1293     msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1294
1295     ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1296     if (!ok) {
1297         return STATUS_INSUFFICIENT_RESOURCES;
1298     }
1299
1300     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1301     if (!ok) {
1302         return STATUS_INSUFFICIENT_RESOURCES;
1303     }
1304
1305     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1306     if (!ok) {
1307         return STATUS_INSUFFICIENT_RESOURCES;
1308     }
1309
1310     ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1311     if (!ok) {
1312         return STATUS_INSUFFICIENT_RESOURCES;
1313     }
1314
1315     /*
1316      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1317      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1318      * it means we have an array of pids, instead of a single pid.
1319      * ATM we assume we have one pid only.
1320     */
1321
1322     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1323                          vport->upcallPid);
1324     if (!ok) {
1325         return STATUS_INSUFFICIENT_RESOURCES;
1326     }
1327
1328     /*stats*/
1329     vportStats.rxPackets = vport->stats.rxPackets;
1330     vportStats.rxBytes = vport->stats.rxBytes;
1331     vportStats.txPackets = vport->stats.txPackets;
1332     vportStats.txBytes = vport->stats.txBytes;
1333     vportStats.rxErrors = vport->errStats.rxErrors;
1334     vportStats.txErrors = vport->errStats.txErrors;
1335     vportStats.rxDropped = vport->errStats.rxDropped;
1336     vportStats.txDropped = vport->errStats.txDropped;
1337
1338     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1339                             (PCHAR)&vportStats,
1340                             sizeof(OVS_VPORT_FULL_STATS));
1341     if (!ok) {
1342         return STATUS_INSUFFICIENT_RESOURCES;
1343     }
1344
1345     /*
1346      * XXX: when vxlan udp dest port becomes configurable, we will also need
1347      * to add vport options
1348     */
1349
1350     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1351     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1352
1353     return STATUS_SUCCESS;
1354 }
1355
1356 static NTSTATUS
1357 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1358                     UINT32 *replyLen)
1359 {
1360     POVS_MESSAGE msgIn;
1361     POVS_OPEN_INSTANCE instance =
1362         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1363     LOCK_STATE_EX lockState;
1364     UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1365
1366     /*
1367      * XXX: this function shares some code with other dump command(s).
1368      * In the future, we will need to refactor the dump functions
1369     */
1370
1371     ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1372
1373     if (instance->dumpState.ovsMsg == NULL) {
1374         ASSERT(FALSE);
1375         return STATUS_INVALID_DEVICE_STATE;
1376     }
1377
1378     /* Output buffer has been validated while validating read dev op. */
1379     ASSERT(usrParamsCtx->outputBuffer != NULL);
1380
1381     msgIn = instance->dumpState.ovsMsg;
1382
1383     OvsAcquireCtrlLock();
1384     if (!gOvsSwitchContext) {
1385         /* Treat this as a dump done. */
1386         OvsReleaseCtrlLock();
1387         *replyLen = 0;
1388         FreeUserDumpState(instance);
1389         return STATUS_SUCCESS;
1390     }
1391
1392     /*
1393      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1394      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1395      * it means we have an array of pids, instead of a single pid.
1396      * ATM we assume we have one pid only.
1397     */
1398
1399     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1400
1401     if (gOvsSwitchContext->numVports > 0) {
1402         /* inBucket: the bucket, used for lookup */
1403         UINT32 inBucket = instance->dumpState.index[0];
1404         /* inIndex: index within the given bucket, used for lookup */
1405         UINT32 inIndex = instance->dumpState.index[1];
1406         /* the bucket to be used for the next dump operation */
1407         UINT32 outBucket = 0;
1408         /* the index within the outBucket to be used for the next dump */
1409         UINT32 outIndex = 0;
1410
1411         for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1412             PLIST_ENTRY head, link;
1413             head = &(gOvsSwitchContext->portHashArray[i]);
1414             POVS_VPORT_ENTRY vport = NULL;
1415
1416             outIndex = 0;
1417             LIST_FORALL(head, link) {
1418
1419                 /*
1420                  * if one or more dumps were previously done on this same bucket,
1421                  * inIndex will be > 0, so we'll need to reply with the
1422                  * inIndex + 1 vport from the bucket.
1423                 */
1424                 if (outIndex >= inIndex) {
1425                     vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portLink);
1426
1427                     if (vport->portNo != 0) {
1428                         OvsCreateMsgFromVport(vport, msgIn,
1429                                               usrParamsCtx->outputBuffer,
1430                                               usrParamsCtx->outputLength,
1431                                               gOvsSwitchContext->dpNo);
1432                         ++outIndex;
1433                         break;
1434                     } else {
1435                         vport = NULL;
1436                     }
1437                 }
1438
1439                 ++outIndex;
1440             }
1441
1442             if (vport) {
1443                 break;
1444             }
1445
1446             /*
1447              * if no vport was found above, check the next bucket, beginning
1448              * with the first (i.e. index 0) elem from within that bucket
1449             */
1450             inIndex = 0;
1451         }
1452
1453         outBucket = i;
1454
1455         /* XXX: what about NLMSG_DONE (as msg type)? */
1456         instance->dumpState.index[0] = outBucket;
1457         instance->dumpState.index[1] = outIndex;
1458     }
1459
1460     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1461
1462     OvsReleaseCtrlLock();
1463
1464     /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1465     if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1466         POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1467         *replyLen = msgOut->nlMsg.nlmsgLen;
1468     } else {
1469         /*
1470          * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1471          * it's dump done
1472          */
1473         *replyLen = 0;
1474         /* Free up the dump state, since there's no more data to continue. */
1475         FreeUserDumpState(instance);
1476     }
1477
1478     return STATUS_SUCCESS;
1479 }
1480
1481 static NTSTATUS
1482 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1483             UINT32 *replyLen)
1484 {
1485     NTSTATUS status = STATUS_SUCCESS;
1486     LOCK_STATE_EX lockState;
1487
1488     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1489     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1490     POVS_VPORT_ENTRY vport = NULL;
1491     NL_ERROR nlError = NL_ERROR_SUCCESS;
1492
1493     static const NL_POLICY ovsVportPolicy[] = {
1494         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1495         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1496                                   .minLen = 2,
1497                                   .maxLen = IFNAMSIZ,
1498                                   .optional = TRUE},
1499     };
1500     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1501
1502     /* input buffer has been validated while validating write dev op. */
1503     ASSERT(usrParamsCtx->inputBuffer != NULL);
1504
1505     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1506         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1507         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1508         return STATUS_INVALID_PARAMETER;
1509     }
1510
1511     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1512         return STATUS_INVALID_BUFFER_SIZE;
1513     }
1514
1515     OvsAcquireCtrlLock();
1516     if (!gOvsSwitchContext) {
1517         OvsReleaseCtrlLock();
1518         return STATUS_INVALID_PARAMETER;
1519     }
1520     OvsReleaseCtrlLock();
1521
1522     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1523         vport = OvsFindVportByOvsName(gOvsSwitchContext,
1524             NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]),
1525             NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]) - 1);
1526     } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1527         vport = OvsFindVportByPortNo(gOvsSwitchContext,
1528             NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
1529     } else {
1530         nlError = NL_ERROR_INVAL;
1531         goto Cleanup;
1532     }
1533
1534     if (!vport) {
1535         nlError = NL_ERROR_NODEV;
1536         goto Cleanup;
1537     }
1538
1539     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1540     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1541                                    usrParamsCtx->outputLength,
1542                                    gOvsSwitchContext->dpNo);
1543     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1544
1545     *replyLen = msgOut->nlMsg.nlmsgLen;
1546
1547 Cleanup:
1548     if (nlError != NL_ERROR_SUCCESS) {
1549         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1550             usrParamsCtx->outputBuffer;
1551
1552         BuildErrorMsg(msgIn, msgError, nlError);
1553         *replyLen = msgError->nlMsg.nlmsgLen;
1554     }
1555
1556     return STATUS_SUCCESS;
1557 }
1558
1559 /*
1560  * --------------------------------------------------------------------------
1561  *  Handler for the get vport command. The function handles the initial call to
1562  *  setup the dump state, as well as subsequent calls to continue dumping data.
1563  * --------------------------------------------------------------------------
1564 */
1565 static NTSTATUS
1566 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1567                       UINT32 *replyLen)
1568 {
1569     *replyLen = 0;
1570
1571     switch (usrParamsCtx->devOp)
1572     {
1573     case OVS_WRITE_DEV_OP:
1574         return OvsSetupDumpStart(usrParamsCtx);
1575
1576     case OVS_READ_DEV_OP:
1577         return OvsGetVportDumpNext(usrParamsCtx, replyLen);
1578
1579     case OVS_TRANSACTION_DEV_OP:
1580         return OvsGetVport(usrParamsCtx, replyLen);
1581
1582     default:
1583         return STATUS_INVALID_DEVICE_REQUEST;
1584     }
1585
1586 }
1587
1588 /*
1589  * --------------------------------------------------------------------------
1590  *  Utility function to map the output buffer in an IRP. The buffer is assumed
1591  *  to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
1592  * --------------------------------------------------------------------------
1593  */
1594 static NTSTATUS
1595 MapIrpOutputBuffer(PIRP irp,
1596                    UINT32 bufferLength,
1597                    UINT32 requiredLength,
1598                    PVOID *buffer)
1599 {
1600     ASSERT(irp);
1601     ASSERT(buffer);
1602     ASSERT(bufferLength);
1603     ASSERT(requiredLength);
1604     if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
1605         return STATUS_INVALID_PARAMETER;
1606     }
1607
1608     if (bufferLength < requiredLength) {
1609         return STATUS_NDIS_INVALID_LENGTH;
1610     }
1611     if (irp->MdlAddress == NULL) {
1612         return STATUS_INVALID_PARAMETER;
1613     }
1614     *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
1615                                            NormalPagePriority);
1616     if (*buffer == NULL) {
1617         return STATUS_INSUFFICIENT_RESOURCES;
1618     }
1619
1620     return STATUS_SUCCESS;
1621 }
1622
1623 /*
1624  * --------------------------------------------------------------------------
1625  * Utility function to fill up information about the state of a port in a reply
1626  * to* userspace.
1627  * Assumes that 'gOvsCtrlLock' lock is acquired.
1628  * --------------------------------------------------------------------------
1629  */
1630 static NTSTATUS
1631 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1632                 POVS_EVENT_ENTRY eventEntry,
1633                 PNL_BUFFER nlBuf)
1634 {
1635     NTSTATUS status;
1636     BOOLEAN rc;
1637     OVS_MESSAGE msgOutTmp;
1638     PNL_MSG_HDR nlMsg;
1639     POVS_VPORT_ENTRY vport;
1640
1641     ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
1642
1643     msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
1644     msgOutTmp.nlMsg.nlmsgFlags = 0;  /* XXX: ? */
1645
1646     /* driver intiated messages should have zerp seq number*/
1647     msgOutTmp.nlMsg.nlmsgSeq = 0;
1648     msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
1649
1650     msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
1651     msgOutTmp.genlMsg.reserved = 0;
1652
1653     /* we don't have netdev yet, treat link up/down a adding/removing a port*/
1654     if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
1655         msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
1656     } else if (eventEntry->status &
1657              (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
1658         msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
1659     } else {
1660         ASSERT(FALSE);
1661         return STATUS_UNSUCCESSFUL;
1662     }
1663     msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
1664
1665     rc = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
1666     if (!rc) {
1667         status = STATUS_INVALID_BUFFER_SIZE;
1668         goto cleanup;
1669     }
1670
1671     vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
1672     if (!vport) {
1673         status = STATUS_DEVICE_DOES_NOT_EXIST;
1674         goto cleanup;
1675     }
1676
1677     rc = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) ||
1678          NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) ||
1679          NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
1680     if (!rc) {
1681         status = STATUS_INVALID_BUFFER_SIZE;
1682         goto cleanup;
1683     }
1684
1685     /* XXXX Should we add the port stats attributes?*/
1686     nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1687     nlMsg->nlmsgLen = NlBufSize(nlBuf);
1688     status = STATUS_SUCCESS;
1689
1690 cleanup:
1691     return status;
1692 }
1693
1694
1695 /*
1696  * --------------------------------------------------------------------------
1697  * Handler for reading events from the driver event queue. This handler is
1698  * executed when user modes issues a socket receive on a socket assocaited
1699  * with the MC group for events.
1700  * XXX user mode should read multiple events in one system call
1701  * --------------------------------------------------------------------------
1702  */
1703 static NTSTATUS
1704 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1705                        UINT32 *replyLen)
1706 {
1707 #ifdef DBG
1708     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1709     POVS_OPEN_INSTANCE instance =
1710         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1711 #endif
1712     NL_BUFFER nlBuf;
1713     NTSTATUS status;
1714     OVS_EVENT_ENTRY eventEntry;
1715
1716     ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1717
1718     /* Should never read events with a dump socket */
1719     ASSERT(instance->dumpState.ovsMsg == NULL);
1720
1721     /* Must have an event queue */
1722     ASSERT(instance->eventQueue != NULL);
1723
1724     /* Output buffer has been validated while validating read dev op. */
1725     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1726
1727     NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1728
1729     OvsAcquireCtrlLock();
1730     if (!gOvsSwitchContext) {
1731         status = STATUS_SUCCESS;
1732         goto cleanup;
1733     }
1734
1735     /* remove an event entry from the event queue */
1736     status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
1737     if (status != STATUS_SUCCESS) {
1738         goto cleanup;
1739     }
1740
1741     status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
1742     if (status == NDIS_STATUS_SUCCESS) {
1743         *replyLen = NlBufSize(&nlBuf);
1744     }
1745
1746 cleanup:
1747     OvsReleaseCtrlLock();
1748     return status;
1749 }
1750 #endif /* OVS_USE_NL_INTERFACE */