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