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