datapath-windows: Fixes in OvsSetVportCmdHandler()
[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 #include "Vxlan.h"
37
38 #ifdef OVS_DBG_MOD
39 #undef OVS_DBG_MOD
40 #endif
41 #define OVS_DBG_MOD OVS_DBG_DATAPATH
42 #include "Debug.h"
43
44 #define NETLINK_FAMILY_NAME_LEN 48
45
46
47 /*
48  * Netlink messages are grouped by family (aka type), and each family supports
49  * a set of commands, and can be passed both from kernel -> userspace or
50  * vice-versa. To call into the kernel, userspace uses a device operation which
51  * is outside of a netlink message.
52  *
53  * Each command results in the invocation of a handler function to implement the
54  * request functionality.
55  *
56  * Expectedly, only certain combinations of (device operation, netlink family,
57  * command) are valid.
58  *
59  * Here, we implement the basic infrastructure to perform validation on the
60  * incoming message, version checking, and also to invoke the corresponding
61  * handler to do the heavy-lifting.
62  */
63
64 /*
65  * Handler for a given netlink command. Not all the parameters are used by all
66  * the handlers.
67  */
68 typedef NTSTATUS(NetlinkCmdHandler)(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
69                                     UINT32 *replyLen);
70
71 typedef struct _NETLINK_CMD {
72     UINT16 cmd;
73     NetlinkCmdHandler *handler;
74     UINT32 supportedDevOp;      /* Supported device operations. */
75     BOOLEAN validateDpIndex;    /* Does command require a valid DP argument. */
76 } NETLINK_CMD, *PNETLINK_CMD;
77
78 /* A netlink family is a group of commands. */
79 typedef struct _NETLINK_FAMILY {
80     CHAR *name;
81     UINT32 id;
82     UINT8 version;
83     UINT8 pad;
84     UINT16 maxAttr;
85     NETLINK_CMD *cmds;          /* Array of netlink commands and handlers. */
86     UINT16 opsCount;
87 } NETLINK_FAMILY, *PNETLINK_FAMILY;
88
89 /* Handlers for the various netlink commands. */
90 static NetlinkCmdHandler OvsGetPidCmdHandler,
91                          OvsPendEventCmdHandler,
92                          OvsSubscribeEventCmdHandler,
93                          OvsReadEventCmdHandler,
94                          OvsNewDpCmdHandler,
95                          OvsGetDpCmdHandler,
96                          OvsSetDpCmdHandler,
97                          OvsGetVportCmdHandler,
98                          OvsSetVportCmdHandler,
99                          OvsNewVportCmdHandler,
100                          OvsDeleteVportCmdHandler;
101
102 NetlinkCmdHandler        OvsGetNetdevCmdHandler;
103
104 static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
105                                        UINT32 *replyLen);
106 static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
107                                 UINT32 *replyLen);
108 static NTSTATUS HandleDpTransactionCommon(
109                     POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen);
110
111 /*
112  * The various netlink families, along with the supported commands. Most of
113  * these families and commands are part of the openvswitch specification for a
114  * netlink datapath. In addition, each platform can implement a few families
115  * and commands as extensions.
116  */
117
118 /* Netlink control family: this is a Windows specific family. */
119 NETLINK_CMD nlControlFamilyCmdOps[] = {
120     { .cmd             = OVS_CTRL_CMD_WIN_GET_PID,
121       .handler         = OvsGetPidCmdHandler,
122       .supportedDevOp  = OVS_TRANSACTION_DEV_OP,
123       .validateDpIndex = FALSE,
124     },
125     { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ,
126       .handler = OvsPendEventCmdHandler,
127       .supportedDevOp = OVS_WRITE_DEV_OP,
128       .validateDpIndex = TRUE,
129     },
130     { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ,
131       .handler = OvsSubscribeEventCmdHandler,
132       .supportedDevOp = OVS_WRITE_DEV_OP,
133       .validateDpIndex = TRUE,
134     },
135     { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY,
136       .handler = OvsReadEventCmdHandler,
137       .supportedDevOp = OVS_READ_EVENT_DEV_OP,
138       .validateDpIndex = FALSE,
139     }
140 };
141
142 NETLINK_FAMILY nlControlFamilyOps = {
143     .name     = OVS_WIN_CONTROL_FAMILY,
144     .id       = OVS_WIN_NL_CTRL_FAMILY_ID,
145     .version  = OVS_WIN_CONTROL_VERSION,
146     .maxAttr  = OVS_WIN_CONTROL_ATTR_MAX,
147     .cmds     = nlControlFamilyCmdOps,
148     .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
149 };
150
151 /* Netlink datapath family. */
152 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
153     { .cmd             = OVS_DP_CMD_NEW,
154       .handler         = OvsNewDpCmdHandler,
155       .supportedDevOp  = OVS_TRANSACTION_DEV_OP,
156       .validateDpIndex = FALSE
157     },
158     { .cmd             = OVS_DP_CMD_GET,
159       .handler         = OvsGetDpCmdHandler,
160       .supportedDevOp  = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
161                          OVS_TRANSACTION_DEV_OP,
162       .validateDpIndex = FALSE
163     },
164     { .cmd             = OVS_DP_CMD_SET,
165       .handler         = OvsSetDpCmdHandler,
166       .supportedDevOp  = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
167                          OVS_TRANSACTION_DEV_OP,
168       .validateDpIndex = TRUE
169     }
170 };
171
172 NETLINK_FAMILY nlDatapathFamilyOps = {
173     .name     = OVS_DATAPATH_FAMILY,
174     .id       = OVS_WIN_NL_DATAPATH_FAMILY_ID,
175     .version  = OVS_DATAPATH_VERSION,
176     .maxAttr  = OVS_DP_ATTR_MAX,
177     .cmds     = nlDatapathFamilyCmdOps,
178     .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
179 };
180
181 /* Netlink packet family. */
182
183 NETLINK_CMD nlPacketFamilyCmdOps[] = {
184     { .cmd             = OVS_PACKET_CMD_EXECUTE,
185       .handler         = OvsNlExecuteCmdHandler,
186       .supportedDevOp  = OVS_TRANSACTION_DEV_OP,
187       .validateDpIndex = TRUE
188     }
189 };
190
191 NETLINK_FAMILY nlPacketFamilyOps = {
192     .name     = OVS_PACKET_FAMILY,
193     .id       = OVS_WIN_NL_PACKET_FAMILY_ID,
194     .version  = OVS_PACKET_VERSION,
195     .maxAttr  = OVS_PACKET_ATTR_MAX,
196     .cmds     = nlPacketFamilyCmdOps,
197     .opsCount = ARRAY_SIZE(nlPacketFamilyCmdOps)
198 };
199
200 /* Netlink vport family. */
201 NETLINK_CMD nlVportFamilyCmdOps[] = {
202     { .cmd = OVS_VPORT_CMD_GET,
203       .handler = OvsGetVportCmdHandler,
204       .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
205                         OVS_TRANSACTION_DEV_OP,
206       .validateDpIndex = TRUE
207     },
208     { .cmd = OVS_VPORT_CMD_NEW,
209       .handler = OvsNewVportCmdHandler,
210       .supportedDevOp = OVS_TRANSACTION_DEV_OP,
211       .validateDpIndex = TRUE
212     },
213     { .cmd = OVS_VPORT_CMD_SET,
214       .handler = OvsSetVportCmdHandler,
215       .supportedDevOp = OVS_TRANSACTION_DEV_OP,
216       .validateDpIndex = TRUE
217     },
218     { .cmd = OVS_VPORT_CMD_DEL,
219       .handler = OvsDeleteVportCmdHandler,
220       .supportedDevOp = OVS_TRANSACTION_DEV_OP,
221       .validateDpIndex = TRUE
222     },
223 };
224
225 NETLINK_FAMILY nlVportFamilyOps = {
226     .name     = OVS_VPORT_FAMILY,
227     .id       = OVS_WIN_NL_VPORT_FAMILY_ID,
228     .version  = OVS_VPORT_VERSION,
229     .maxAttr  = OVS_VPORT_ATTR_MAX,
230     .cmds     = nlVportFamilyCmdOps,
231     .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps)
232 };
233
234 /* Netlink flow family. */
235
236 NETLINK_CMD nlFlowFamilyCmdOps[] = {
237     { .cmd              = OVS_FLOW_CMD_NEW,
238       .handler          = OvsFlowNlCmdHandler,
239       .supportedDevOp   = OVS_TRANSACTION_DEV_OP,
240       .validateDpIndex  = TRUE
241     },
242     { .cmd              = OVS_FLOW_CMD_SET,
243       .handler          = OvsFlowNlCmdHandler,
244       .supportedDevOp   = OVS_TRANSACTION_DEV_OP,
245       .validateDpIndex  = TRUE
246     },
247     { .cmd              = OVS_FLOW_CMD_DEL,
248       .handler          = OvsFlowNlCmdHandler,
249       .supportedDevOp   = OVS_TRANSACTION_DEV_OP,
250       .validateDpIndex  = TRUE
251     },
252     { .cmd              = OVS_FLOW_CMD_GET,
253       .handler          = OvsFlowNlGetCmdHandler,
254       .supportedDevOp   = OVS_TRANSACTION_DEV_OP |
255                           OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
256       .validateDpIndex  = TRUE
257     },
258 };
259
260 NETLINK_FAMILY nlFLowFamilyOps = {
261     .name     = OVS_FLOW_FAMILY,
262     .id       = OVS_WIN_NL_FLOW_FAMILY_ID,
263     .version  = OVS_FLOW_VERSION,
264     .maxAttr  = OVS_FLOW_ATTR_MAX,
265     .cmds     = nlFlowFamilyCmdOps,
266     .opsCount = ARRAY_SIZE(nlFlowFamilyCmdOps)
267 };
268
269 /* Netlink netdev family. */
270 NETLINK_CMD nlNetdevFamilyCmdOps[] = {
271     { .cmd = OVS_WIN_NETDEV_CMD_GET,
272       .handler = OvsGetNetdevCmdHandler,
273       .supportedDevOp = OVS_TRANSACTION_DEV_OP,
274       .validateDpIndex = FALSE
275     },
276 };
277
278 NETLINK_FAMILY nlNetdevFamilyOps = {
279     .name     = OVS_WIN_NETDEV_FAMILY,
280     .id       = OVS_WIN_NL_NETDEV_FAMILY_ID,
281     .version  = OVS_WIN_NETDEV_VERSION,
282     .maxAttr  = OVS_WIN_NETDEV_ATTR_MAX,
283     .cmds     = nlNetdevFamilyCmdOps,
284     .opsCount = ARRAY_SIZE(nlNetdevFamilyCmdOps)
285 };
286
287 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
288                                    UINT32 bufferLength,
289                                    UINT32 requiredLength,
290                                    PVOID *buffer);
291 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
292                                    POVS_OPEN_INSTANCE instance,
293                                    POVS_MESSAGE ovsMsg,
294                                    NETLINK_FAMILY *nlFamilyOps);
295 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
296                                         NETLINK_FAMILY *nlFamilyOps,
297                                         UINT32 *replyLen);
298
299 /* Handles to the device object for communication with userspace. */
300 NDIS_HANDLE gOvsDeviceHandle;
301 PDEVICE_OBJECT gOvsDeviceObject;
302
303 _Dispatch_type_(IRP_MJ_CREATE)
304 _Dispatch_type_(IRP_MJ_CLOSE)
305 DRIVER_DISPATCH OvsOpenCloseDevice;
306
307 _Dispatch_type_(IRP_MJ_CLEANUP)
308 DRIVER_DISPATCH OvsCleanupDevice;
309
310 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
311 DRIVER_DISPATCH OvsDeviceControl;
312
313 #ifdef ALLOC_PRAGMA
314 #pragma alloc_text(INIT, OvsCreateDeviceObject)
315 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
316 #pragma alloc_text(PAGE, OvsCleanupDevice)
317 #pragma alloc_text(PAGE, OvsDeviceControl)
318 #endif // ALLOC_PRAGMA
319
320 /*
321  * We might hit this limit easily since userspace opens a netlink descriptor for
322  * each thread, and at least one descriptor per vport. Revisit this later.
323  */
324 #define OVS_MAX_OPEN_INSTANCES 512
325 #define OVS_SYSTEM_DP_NAME     "ovs-system"
326
327 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
328 UINT32 ovsNumberOfOpenInstances;
329 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
330
331 NDIS_SPIN_LOCK ovsCtrlLockObj;
332 PNDIS_SPIN_LOCK gOvsCtrlLock;
333
334
335 VOID
336 OvsInit()
337 {
338     gOvsCtrlLock = &ovsCtrlLockObj;
339     NdisAllocateSpinLock(gOvsCtrlLock);
340     OvsInitEventQueue();
341     OvsUserInit();
342 }
343
344 VOID
345 OvsCleanup()
346 {
347     OvsCleanupEventQueue();
348     if (gOvsCtrlLock) {
349         NdisFreeSpinLock(gOvsCtrlLock);
350         gOvsCtrlLock = NULL;
351     }
352     OvsUserCleanup();
353 }
354
355 VOID
356 OvsAcquireCtrlLock()
357 {
358     NdisAcquireSpinLock(gOvsCtrlLock);
359 }
360
361 VOID
362 OvsReleaseCtrlLock()
363 {
364     NdisReleaseSpinLock(gOvsCtrlLock);
365 }
366
367
368 /*
369  * --------------------------------------------------------------------------
370  * Creates the communication device between user and kernel, and also
371  * initializes the data associated data structures.
372  * --------------------------------------------------------------------------
373  */
374 NDIS_STATUS
375 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
376 {
377     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
378     UNICODE_STRING deviceName;
379     UNICODE_STRING symbolicDeviceName;
380     PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
381     NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
382     OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
383
384     RtlZeroMemory(dispatchTable,
385                   (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
386     dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
387     dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
388     dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
389     dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
390
391     NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
392     NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
393
394     RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
395
396     OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
397                            NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
398                            NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
399                            sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
400
401     deviceAttributes.DeviceName = &deviceName;
402     deviceAttributes.SymbolicName = &symbolicDeviceName;
403     deviceAttributes.MajorFunctions = dispatchTable;
404     deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
405
406     status = NdisRegisterDeviceEx(ovsExtDriverHandle,
407                                   &deviceAttributes,
408                                   &gOvsDeviceObject,
409                                   &gOvsDeviceHandle);
410     if (status != NDIS_STATUS_SUCCESS) {
411         POVS_DEVICE_EXTENSION ovsExt =
412             (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
413         ASSERT(gOvsDeviceObject != NULL);
414         ASSERT(gOvsDeviceHandle != NULL);
415
416         if (ovsExt) {
417             ovsExt->numberOpenInstance = 0;
418         }
419     } else {
420         /* Initialize the associated data structures. */
421         OvsInit();
422     }
423     OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
424     return status;
425 }
426
427
428 VOID
429 OvsDeleteDeviceObject()
430 {
431     if (gOvsDeviceHandle) {
432 #ifdef DBG
433         POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
434                     NdisGetDeviceReservedExtension(gOvsDeviceObject);
435         if (ovsExt) {
436             ASSERT(ovsExt->numberOpenInstance == 0);
437         }
438 #endif
439
440         ASSERT(gOvsDeviceObject);
441         NdisDeregisterDeviceEx(gOvsDeviceHandle);
442         gOvsDeviceHandle = NULL;
443         gOvsDeviceObject = NULL;
444     }
445     OvsCleanup();
446 }
447
448 POVS_OPEN_INSTANCE
449 OvsGetOpenInstance(PFILE_OBJECT fileObject,
450                    UINT32 dpNo)
451 {
452     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
453     ASSERT(instance);
454     ASSERT(instance->fileObject == fileObject);
455     if (gOvsSwitchContext == NULL ||
456         gOvsSwitchContext->dpNo != dpNo) {
457         return NULL;
458     }
459     return instance;
460 }
461
462
463 POVS_OPEN_INSTANCE
464 OvsFindOpenInstance(PFILE_OBJECT fileObject)
465 {
466     UINT32 i, j;
467     for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
468                        j < ovsNumberOfOpenInstances; i++) {
469         if (ovsOpenInstanceArray[i]) {
470             if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
471                 return ovsOpenInstanceArray[i];
472             }
473             j++;
474         }
475     }
476     return NULL;
477 }
478
479 NTSTATUS
480 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
481                    PFILE_OBJECT fileObject)
482 {
483     POVS_OPEN_INSTANCE instance =
484         (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
485     UINT32 i;
486
487     if (instance == NULL) {
488         return STATUS_NO_MEMORY;
489     }
490     OvsAcquireCtrlLock();
491     ASSERT(OvsFindOpenInstance(fileObject) == NULL);
492
493     if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
494         OvsReleaseCtrlLock();
495         OvsFreeMemory(instance);
496         return STATUS_INSUFFICIENT_RESOURCES;
497     }
498     RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
499
500     for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
501         if (ovsOpenInstanceArray[i] == NULL) {
502             ovsOpenInstanceArray[i] = instance;
503             ovsNumberOfOpenInstances++;
504             instance->cookie = i;
505             break;
506         }
507     }
508     ASSERT(i < OVS_MAX_OPEN_INSTANCES);
509     instance->fileObject = fileObject;
510     ASSERT(fileObject->FsContext == NULL);
511     instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
512     if (instance->pid == 0) {
513         /* XXX: check for rollover. */
514     }
515     fileObject->FsContext = instance;
516     OvsReleaseCtrlLock();
517     return STATUS_SUCCESS;
518 }
519
520 static VOID
521 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
522 {
523     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
524     ASSERT(instance);
525     ASSERT(fileObject == instance->fileObject);
526     OvsCleanupEvent(instance);
527     OvsCleanupPacketQueue(instance);
528 }
529
530 VOID
531 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
532 {
533     POVS_OPEN_INSTANCE instance;
534     ASSERT(fileObject->FsContext);
535     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
536     ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
537
538     OvsAcquireCtrlLock();
539     fileObject->FsContext = NULL;
540     ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
541     ovsOpenInstanceArray[instance->cookie] = NULL;
542     ovsNumberOfOpenInstances--;
543     OvsReleaseCtrlLock();
544     ASSERT(instance->eventQueue == NULL);
545     ASSERT (instance->packetQueue == NULL);
546     OvsFreeMemory(instance);
547 }
548
549 NTSTATUS
550 OvsCompleteIrpRequest(PIRP irp,
551                       ULONG_PTR infoPtr,
552                       NTSTATUS status)
553 {
554     irp->IoStatus.Information = infoPtr;
555     irp->IoStatus.Status = status;
556     IoCompleteRequest(irp, IO_NO_INCREMENT);
557     return status;
558 }
559
560
561 NTSTATUS
562 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
563                    PIRP irp)
564 {
565     PIO_STACK_LOCATION irpSp;
566     NTSTATUS status = STATUS_SUCCESS;
567     PFILE_OBJECT fileObject;
568     POVS_DEVICE_EXTENSION ovsExt =
569         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
570
571     ASSERT(deviceObject == gOvsDeviceObject);
572     ASSERT(ovsExt != NULL);
573
574     irpSp = IoGetCurrentIrpStackLocation(irp);
575     fileObject = irpSp->FileObject;
576     OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
577                   deviceObject, fileObject,
578                   ovsExt->numberOpenInstance);
579
580     switch (irpSp->MajorFunction) {
581     case IRP_MJ_CREATE:
582         status = OvsAddOpenInstance(ovsExt, fileObject);
583         if (STATUS_SUCCESS == status) {
584             InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
585         }
586         break;
587     case IRP_MJ_CLOSE:
588         ASSERT(ovsExt->numberOpenInstance > 0);
589         OvsRemoveOpenInstance(fileObject);
590         InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
591         break;
592     default:
593         ASSERT(0);
594     }
595     return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
596 }
597
598 _Use_decl_annotations_
599 NTSTATUS
600 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
601                  PIRP irp)
602 {
603
604     PIO_STACK_LOCATION irpSp;
605     PFILE_OBJECT fileObject;
606
607     NTSTATUS status = STATUS_SUCCESS;
608 #ifdef DBG
609     POVS_DEVICE_EXTENSION ovsExt =
610         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
611     if (ovsExt) {
612         ASSERT(ovsExt->numberOpenInstance > 0);
613     }
614 #else
615     UNREFERENCED_PARAMETER(deviceObject);
616 #endif
617     ASSERT(deviceObject == gOvsDeviceObject);
618     irpSp = IoGetCurrentIrpStackLocation(irp);
619     fileObject = irpSp->FileObject;
620
621     ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
622
623     OvsCleanupOpenInstance(fileObject);
624
625     return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
626 }
627
628
629 /*
630  * --------------------------------------------------------------------------
631  * IOCTL function handler for the device.
632  * --------------------------------------------------------------------------
633  */
634 NTSTATUS
635 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
636                  PIRP irp)
637 {
638
639     PIO_STACK_LOCATION irpSp;
640     NTSTATUS status = STATUS_SUCCESS;
641     PFILE_OBJECT fileObject;
642     PVOID inputBuffer = NULL;
643     PVOID outputBuffer = NULL;
644     UINT32 inputBufferLen, outputBufferLen;
645     UINT32 code, replyLen = 0;
646     POVS_OPEN_INSTANCE instance;
647     UINT32 devOp;
648     OVS_MESSAGE ovsMsgReadOp;
649     POVS_MESSAGE ovsMsg;
650     NETLINK_FAMILY *nlFamilyOps;
651     OVS_USER_PARAMS_CONTEXT usrParamsCtx;
652
653 #ifdef DBG
654     POVS_DEVICE_EXTENSION ovsExt =
655         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
656     ASSERT(deviceObject == gOvsDeviceObject);
657     ASSERT(ovsExt);
658     ASSERT(ovsExt->numberOpenInstance > 0);
659 #else
660     UNREFERENCED_PARAMETER(deviceObject);
661 #endif
662
663     irpSp = IoGetCurrentIrpStackLocation(irp);
664
665     ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
666     ASSERT(irpSp->FileObject != NULL);
667
668     fileObject = irpSp->FileObject;
669     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
670     code = irpSp->Parameters.DeviceIoControl.IoControlCode;
671     inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
672     outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
673     inputBuffer = irp->AssociatedIrp.SystemBuffer;
674
675     /* Concurrent netlink operations are not supported. */
676     if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
677         status = STATUS_RESOURCE_IN_USE;
678         goto done;
679     }
680
681     /*
682      * Validate the input/output buffer arguments depending on the type of the
683      * operation.
684      */
685     switch (code) {
686     case OVS_IOCTL_TRANSACT:
687         /* Input buffer is mandatory, output buffer is optional. */
688         if (outputBufferLen != 0) {
689             status = MapIrpOutputBuffer(irp, outputBufferLen,
690                                         sizeof *ovsMsg, &outputBuffer);
691             if (status != STATUS_SUCCESS) {
692                 goto done;
693             }
694             ASSERT(outputBuffer);
695         }
696
697         if (inputBufferLen < sizeof (*ovsMsg)) {
698             status = STATUS_NDIS_INVALID_LENGTH;
699             goto done;
700         }
701
702         ovsMsg = inputBuffer;
703         devOp = OVS_TRANSACTION_DEV_OP;
704         break;
705
706     case OVS_IOCTL_READ_EVENT:
707         /* This IOCTL is used to read events */
708         if (outputBufferLen != 0) {
709             status = MapIrpOutputBuffer(irp, outputBufferLen,
710                                         sizeof *ovsMsg, &outputBuffer);
711             if (status != STATUS_SUCCESS) {
712                 goto done;
713             }
714             ASSERT(outputBuffer);
715         } else {
716             status = STATUS_NDIS_INVALID_LENGTH;
717             goto done;
718         }
719         inputBuffer = NULL;
720         inputBufferLen = 0;
721
722         ovsMsg = &ovsMsgReadOp;
723         ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
724         /* An "artificial" command so we can use NL family function table*/
725         ovsMsg->genlMsg.cmd = OVS_CTRL_CMD_EVENT_NOTIFY;
726         devOp = OVS_READ_DEV_OP;
727         break;
728
729     case OVS_IOCTL_READ:
730         /* Output buffer is mandatory. */
731         if (outputBufferLen != 0) {
732             status = MapIrpOutputBuffer(irp, outputBufferLen,
733                                         sizeof *ovsMsg, &outputBuffer);
734             if (status != STATUS_SUCCESS) {
735                 goto done;
736             }
737             ASSERT(outputBuffer);
738         } else {
739             status = STATUS_NDIS_INVALID_LENGTH;
740             goto done;
741         }
742
743         /*
744          * Operate in the mode that read ioctl is similar to ReadFile(). This
745          * might change as the userspace code gets implemented.
746          */
747         inputBuffer = NULL;
748         inputBufferLen = 0;
749
750         /*
751          * For implementing read (ioctl or otherwise), we need to store some
752          * state in the instance to indicate the command that started the dump
753          * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
754          * that 'ovsMsgReadOp' is needed only in this function to call into the
755          * appropraite handler. The handler itself can access the state in the
756          * instance.
757          *
758          * In the absence of a dump start, return 0 bytes.
759          */
760         if (instance->dumpState.ovsMsg == NULL) {
761             replyLen = 0;
762             status = STATUS_SUCCESS;
763             goto done;
764         }
765         RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
766                       sizeof (ovsMsgReadOp));
767
768         /* Create an NL message for consumption. */
769         ovsMsg = &ovsMsgReadOp;
770         devOp = OVS_READ_DEV_OP;
771
772         break;
773
774     case OVS_IOCTL_WRITE:
775         /* Input buffer is mandatory. */
776         if (inputBufferLen < sizeof (*ovsMsg)) {
777             status = STATUS_NDIS_INVALID_LENGTH;
778             goto done;
779         }
780
781         ovsMsg = inputBuffer;
782         devOp = OVS_WRITE_DEV_OP;
783         break;
784
785     default:
786         status = STATUS_INVALID_DEVICE_REQUEST;
787         goto done;
788     }
789
790     ASSERT(ovsMsg);
791     switch (ovsMsg->nlMsg.nlmsgType) {
792     case OVS_WIN_NL_CTRL_FAMILY_ID:
793         nlFamilyOps = &nlControlFamilyOps;
794         break;
795     case OVS_WIN_NL_DATAPATH_FAMILY_ID:
796         nlFamilyOps = &nlDatapathFamilyOps;
797         break;
798     case OVS_WIN_NL_FLOW_FAMILY_ID:
799          nlFamilyOps = &nlFLowFamilyOps;
800          break;
801     case OVS_WIN_NL_PACKET_FAMILY_ID:
802          nlFamilyOps = &nlPacketFamilyOps;
803          break;
804     case OVS_WIN_NL_VPORT_FAMILY_ID:
805         nlFamilyOps = &nlVportFamilyOps;
806         break;
807     case OVS_WIN_NL_NETDEV_FAMILY_ID:
808         nlFamilyOps = &nlNetdevFamilyOps;
809         break;
810     default:
811         status = STATUS_INVALID_PARAMETER;
812         goto done;
813     }
814
815     /*
816      * For read operation, the netlink command has already been validated
817      * previously.
818      */
819     if (devOp != OVS_READ_DEV_OP) {
820         status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
821         if (status != STATUS_SUCCESS) {
822             goto done;
823         }
824     }
825
826     InitUserParamsCtx(irp, instance, devOp, ovsMsg,
827                       inputBuffer, inputBufferLen,
828                       outputBuffer, outputBufferLen,
829                       &usrParamsCtx);
830
831     status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
832
833 done:
834     KeMemoryBarrier();
835     instance->inUse = 0;
836     return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
837 }
838
839
840 /*
841  * --------------------------------------------------------------------------
842  * Function to validate a netlink command. Only certain combinations of
843  * (device operation, netlink family, command) are valid.
844  * --------------------------------------------------------------------------
845  */
846 static NTSTATUS
847 ValidateNetlinkCmd(UINT32 devOp,
848                    POVS_OPEN_INSTANCE instance,
849                    POVS_MESSAGE ovsMsg,
850                    NETLINK_FAMILY *nlFamilyOps)
851 {
852     NTSTATUS status = STATUS_INVALID_PARAMETER;
853     UINT16 i;
854
855     for (i = 0; i < nlFamilyOps->opsCount; i++) {
856         if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
857             /* Validate if the command is valid for the device operation. */
858             if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
859                 status = STATUS_INVALID_PARAMETER;
860                 goto done;
861             }
862
863             /* Validate the version. */
864             if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
865                 status = STATUS_INVALID_PARAMETER;
866                 goto done;
867             }
868
869             /* Validate the DP for commands that require a DP. */
870             if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
871                 OvsAcquireCtrlLock();
872                 if (ovsMsg->ovsHdr.dp_ifindex !=
873                     (INT)gOvsSwitchContext->dpNo) {
874                     status = STATUS_INVALID_PARAMETER;
875                     OvsReleaseCtrlLock();
876                     goto done;
877                 }
878                 OvsReleaseCtrlLock();
879             }
880
881             /* Validate the PID. */
882             if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
883                 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
884                     status = STATUS_INVALID_PARAMETER;
885                     goto done;
886                 }
887             }
888
889             status = STATUS_SUCCESS;
890             break;
891         }
892     }
893
894 done:
895     return status;
896 }
897
898 /*
899  * --------------------------------------------------------------------------
900  * Function to invoke the netlink command handler.
901  * --------------------------------------------------------------------------
902  */
903 static NTSTATUS
904 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
905                         NETLINK_FAMILY *nlFamilyOps,
906                         UINT32 *replyLen)
907 {
908     NTSTATUS status = STATUS_INVALID_PARAMETER;
909     UINT16 i;
910
911     for (i = 0; i < nlFamilyOps->opsCount; i++) {
912         if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
913             NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
914             ASSERT(handler);
915             if (handler) {
916                 status = handler(usrParamsCtx, replyLen);
917             }
918             break;
919         }
920     }
921
922     return status;
923 }
924
925 /*
926  * --------------------------------------------------------------------------
927  *  Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
928  *
929  *  Each handle on the device is assigned a unique PID when the handle is
930  *  created. On platforms that support netlink natively, the PID is available
931  *  to userspace when the netlink socket is created. However, without native
932  *  netlink support on Windows, OVS datapath generates the PID and lets the
933  *  userspace query it.
934  *
935  *  This function implements the query.
936  * --------------------------------------------------------------------------
937  */
938 static NTSTATUS
939 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
940                     UINT32 *replyLen)
941 {
942     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
943     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
944
945     if (usrParamsCtx->outputLength >= sizeof *msgOut) {
946         POVS_OPEN_INSTANCE instance =
947             (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
948
949         RtlZeroMemory(msgOut, sizeof *msgOut);
950         msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
951         msgOut->nlMsg.nlmsgPid = instance->pid;
952         *replyLen = sizeof *msgOut;
953         /* XXX: We might need to return the DP index as well. */
954     } else {
955         return STATUS_NDIS_INVALID_LENGTH;
956     }
957
958     return STATUS_SUCCESS;
959 }
960
961 /*
962  * --------------------------------------------------------------------------
963  * Utility function to fill up information about the datapath in a reply to
964  * userspace.
965  * Assumes that 'gOvsCtrlLock' lock is acquired.
966  * --------------------------------------------------------------------------
967  */
968 static NTSTATUS
969 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
970               POVS_MESSAGE msgIn,
971               PNL_BUFFER nlBuf)
972 {
973     BOOLEAN writeOk;
974     OVS_MESSAGE msgOutTmp;
975     OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
976     PNL_MSG_HDR nlMsg;
977
978     ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && NlBufRemLen(nlBuf) >= sizeof *msgIn);
979
980     msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
981     msgOutTmp.nlMsg.nlmsgFlags = 0;  /* XXX: ? */
982     msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
983     msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
984
985     msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
986     msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
987     msgOutTmp.genlMsg.reserved = 0;
988
989     msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
990
991     writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
992     if (writeOk) {
993         writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
994                                      OVS_SYSTEM_DP_NAME);
995     }
996     if (writeOk) {
997         OVS_DP_STATS dpStats;
998
999         dpStats.n_hit = datapath->hits;
1000         dpStats.n_missed = datapath->misses;
1001         dpStats.n_lost = datapath->lost;
1002         dpStats.n_flows = datapath->nFlows;
1003         writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
1004                                      (PCHAR)&dpStats, sizeof dpStats);
1005     }
1006     nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1007     nlMsg->nlmsgLen = NlBufSize(nlBuf);
1008
1009     return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
1010 }
1011
1012 /*
1013  * --------------------------------------------------------------------------
1014  * Handler for queueing an IRP used for event notification. The IRP is
1015  * completed when a port state changes. STATUS_PENDING is returned on
1016  * success. User mode keep a pending IRP at all times.
1017  * --------------------------------------------------------------------------
1018  */
1019 static NTSTATUS
1020 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1021                        UINT32 *replyLen)
1022 {
1023     NDIS_STATUS status;
1024
1025     UNREFERENCED_PARAMETER(replyLen);
1026
1027     POVS_OPEN_INSTANCE instance =
1028         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1029     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1030     OVS_EVENT_POLL poll;
1031
1032     poll.dpNo = msgIn->ovsHdr.dp_ifindex;
1033     status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
1034                                &poll, sizeof poll);
1035     return status;
1036 }
1037
1038 /*
1039  * --------------------------------------------------------------------------
1040  *  Handler for the subscription for the event queue
1041  * --------------------------------------------------------------------------
1042  */
1043 static NTSTATUS
1044 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1045                             UINT32 *replyLen)
1046 {
1047     NDIS_STATUS status;
1048     OVS_EVENT_SUBSCRIBE request;
1049     BOOLEAN rc;
1050     UINT8 join;
1051     PNL_ATTR attrs[2];
1052     const NL_POLICY policy[] =  {
1053         [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
1054         [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
1055         };
1056
1057     UNREFERENCED_PARAMETER(replyLen);
1058
1059     POVS_OPEN_INSTANCE instance =
1060         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1061     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1062
1063     rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1064          NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, 2);
1065     if (!rc) {
1066         status = STATUS_INVALID_PARAMETER;
1067         goto done;
1068     }
1069
1070     /* XXX Ignore the MC group for now */
1071     join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1072     request.dpNo = msgIn->ovsHdr.dp_ifindex;
1073     request.subscribe = join;
1074     request.mask = OVS_EVENT_MASK_ALL;
1075
1076     status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1077                                     sizeof request);
1078 done:
1079     return status;
1080 }
1081
1082 /*
1083  * --------------------------------------------------------------------------
1084  *  Command Handler for 'OVS_DP_CMD_NEW'.
1085  * --------------------------------------------------------------------------
1086  */
1087 static NTSTATUS
1088 OvsNewDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1089                    UINT32 *replyLen)
1090 {
1091     return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1092 }
1093
1094 /*
1095  * --------------------------------------------------------------------------
1096  *  Command Handler for 'OVS_DP_CMD_GET'.
1097  *
1098  *  The function handles both the dump based as well as the transaction based
1099  *  'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1100  *  call to setup dump state, as well as subsequent calls to continue dumping
1101  *  data.
1102  * --------------------------------------------------------------------------
1103  */
1104 static NTSTATUS
1105 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1106                    UINT32 *replyLen)
1107 {
1108     if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1109         return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1110     } else {
1111         return HandleGetDpDump(usrParamsCtx, replyLen);
1112     }
1113 }
1114
1115 /*
1116  * --------------------------------------------------------------------------
1117  *  Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1118  * --------------------------------------------------------------------------
1119  */
1120 static NTSTATUS
1121 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1122                        UINT32 *replyLen)
1123 {
1124     return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1125 }
1126
1127
1128 /*
1129  * --------------------------------------------------------------------------
1130  *  Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1131  * --------------------------------------------------------------------------
1132  */
1133 static NTSTATUS
1134 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1135                 UINT32 *replyLen)
1136 {
1137     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1138     POVS_OPEN_INSTANCE instance =
1139         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1140
1141     if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1142         *replyLen = 0;
1143         OvsSetupDumpStart(usrParamsCtx);
1144     } else {
1145         NL_BUFFER nlBuf;
1146         NTSTATUS status;
1147         POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1148
1149         ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1150
1151         if (instance->dumpState.ovsMsg == NULL) {
1152             ASSERT(FALSE);
1153             return STATUS_INVALID_DEVICE_STATE;
1154         }
1155
1156         /* Dump state must have been deleted after previous dump operation. */
1157         ASSERT(instance->dumpState.index[0] == 0);
1158         /* Output buffer has been validated while validating read dev op. */
1159         ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1160
1161         NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1162                   usrParamsCtx->outputLength);
1163
1164         OvsAcquireCtrlLock();
1165         if (!gOvsSwitchContext) {
1166             /* Treat this as a dump done. */
1167             OvsReleaseCtrlLock();
1168             *replyLen = 0;
1169             FreeUserDumpState(instance);
1170             return STATUS_SUCCESS;
1171         }
1172         status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1173         OvsReleaseCtrlLock();
1174
1175         if (status != STATUS_SUCCESS) {
1176             *replyLen = 0;
1177             FreeUserDumpState(instance);
1178             return status;
1179         }
1180
1181         /* Increment the dump index. */
1182         instance->dumpState.index[0] = 1;
1183         *replyLen = msgOut->nlMsg.nlmsgLen;
1184
1185         /* Free up the dump state, since there's no more data to continue. */
1186         FreeUserDumpState(instance);
1187     }
1188
1189     return STATUS_SUCCESS;
1190 }
1191
1192
1193 /*
1194  * --------------------------------------------------------------------------
1195  *  Command Handler for 'OVS_DP_CMD_SET'.
1196  * --------------------------------------------------------------------------
1197  */
1198 static NTSTATUS
1199 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1200                    UINT32 *replyLen)
1201 {
1202     return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1203 }
1204
1205 /*
1206  * --------------------------------------------------------------------------
1207  *  Function for handling transaction based 'OVS_DP_CMD_NEW', 'OVS_DP_CMD_GET'
1208  *  and 'OVS_DP_CMD_SET' commands.
1209  *
1210  * 'OVS_DP_CMD_NEW' is implemented to keep userspace code happy. Creation of a
1211  * new datapath is not supported currently.
1212  * --------------------------------------------------------------------------
1213  */
1214 static NTSTATUS
1215 HandleDpTransactionCommon(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1216                           UINT32 *replyLen)
1217 {
1218     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1219     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1220     NTSTATUS status = STATUS_SUCCESS;
1221     NL_BUFFER nlBuf;
1222     NL_ERROR nlError = NL_ERROR_SUCCESS;
1223     static const NL_POLICY ovsDatapathSetPolicy[] = {
1224         [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1225         [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1226         [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1227     };
1228     PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1229
1230     /* input buffer has been validated while validating write dev op. */
1231     ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1232
1233     /* Parse any attributes in the request. */
1234     if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET ||
1235         usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1236         if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1237                         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1238                         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1239                         ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1240             return STATUS_INVALID_PARAMETER;
1241         }
1242
1243         /*
1244         * XXX: Not clear at this stage if there's any role for the
1245         * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1246         * from userspace.
1247         */
1248
1249     } else {
1250         RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1251     }
1252
1253     /* Output buffer is optional for OVS_TRANSACTION_DEV_OP. */
1254     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1255         return STATUS_NDIS_INVALID_LENGTH;
1256     }
1257     NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1258
1259     OvsAcquireCtrlLock();
1260     if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1261         if (!gOvsSwitchContext &&
1262             !OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1263                               OVS_SYSTEM_DP_NAME)) {
1264             OvsReleaseCtrlLock();
1265
1266             /* Creation of new datapaths is not supported. */
1267             if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1268                 nlError = NL_ERROR_NOTSUPP;
1269                 goto cleanup;
1270             }
1271
1272             nlError = NL_ERROR_NODEV;
1273             goto cleanup;
1274         }
1275     } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1276         OvsReleaseCtrlLock();
1277         nlError = NL_ERROR_NODEV;
1278         goto cleanup;
1279     }
1280
1281     if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1282         OvsReleaseCtrlLock();
1283         nlError = NL_ERROR_EXIST;
1284         goto cleanup;
1285     }
1286
1287     status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1288     OvsReleaseCtrlLock();
1289
1290     *replyLen = NlBufSize(&nlBuf);
1291
1292 cleanup:
1293     if (nlError != NL_ERROR_SUCCESS) {
1294         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1295             usrParamsCtx->outputBuffer;
1296
1297         BuildErrorMsg(msgIn, msgError, nlError);
1298         *replyLen = msgError->nlMsg.nlmsgLen;
1299     }
1300
1301     return STATUS_SUCCESS;
1302 }
1303
1304
1305 NTSTATUS
1306 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1307 {
1308     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1309     POVS_OPEN_INSTANCE instance =
1310         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1311
1312     /* input buffer has been validated while validating write dev op. */
1313     ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1314
1315     /* A write operation that does not indicate dump start is invalid. */
1316     if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1317         return STATUS_INVALID_PARAMETER;
1318     }
1319     /* XXX: Handle other NLM_F_* flags in the future. */
1320
1321     /*
1322      * This operation should be setting up the dump state. If there's any
1323      * previous state, clear it up so as to set it up afresh.
1324      */
1325     if (instance->dumpState.ovsMsg != NULL) {
1326         FreeUserDumpState(instance);
1327     }
1328
1329     return InitUserDumpState(instance, msgIn);
1330 }
1331
1332 static VOID
1333 BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
1334             UINT32 length, UINT16 flags)
1335 {
1336     msgOut->nlMsg.nlmsgType = type;
1337     msgOut->nlMsg.nlmsgFlags = flags;
1338     msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1339     msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1340     msgOut->nlMsg.nlmsgLen = length;
1341
1342     msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
1343     msgOut->genlMsg.version = msgIn->genlMsg.version;
1344     msgOut->genlMsg.reserved = 0;
1345 }
1346
1347 /*
1348  * XXX: should move out these functions to a Netlink.c or to a OvsMessage.c
1349  * or even make them inlined functions in Datapath.h. Can be done after the
1350  * first sprint once we have more code to refactor.
1351  */
1352 VOID
1353 BuildReplyMsgFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
1354 {
1355     BuildMsgOut(msgIn, msgOut, msgIn->nlMsg.nlmsgType, sizeof(OVS_MESSAGE),
1356                 flags);
1357 }
1358
1359 VOID
1360 BuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode)
1361 {
1362     BuildMsgOut(msgIn, (POVS_MESSAGE)msgOut, NLMSG_ERROR,
1363                 sizeof(OVS_MESSAGE_ERROR), 0);
1364
1365     msgOut->errorMsg.error = errorCode;
1366     msgOut->errorMsg.nlMsg = msgIn->nlMsg;
1367 }
1368
1369 static NTSTATUS
1370 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1371                       POVS_MESSAGE msgIn,
1372                       PVOID outBuffer,
1373                       UINT32 outBufLen,
1374                       int dpIfIndex)
1375 {
1376     NL_BUFFER nlBuffer;
1377     OVS_VPORT_FULL_STATS vportStats;
1378     BOOLEAN ok;
1379     OVS_MESSAGE msgOut;
1380     PNL_MSG_HDR nlMsg;
1381
1382     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1383
1384     BuildReplyMsgFromMsgIn(msgIn, &msgOut, NLM_F_MULTI);
1385     msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1386
1387     ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1388     if (!ok) {
1389         return STATUS_INSUFFICIENT_RESOURCES;
1390     }
1391
1392     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1393     if (!ok) {
1394         return STATUS_INSUFFICIENT_RESOURCES;
1395     }
1396
1397     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1398     if (!ok) {
1399         return STATUS_INSUFFICIENT_RESOURCES;
1400     }
1401
1402     ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1403     if (!ok) {
1404         return STATUS_INSUFFICIENT_RESOURCES;
1405     }
1406
1407     /*
1408      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1409      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1410      * it means we have an array of pids, instead of a single pid.
1411      * ATM we assume we have one pid only.
1412     */
1413
1414     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1415                          vport->upcallPid);
1416     if (!ok) {
1417         return STATUS_INSUFFICIENT_RESOURCES;
1418     }
1419
1420     /*stats*/
1421     vportStats.rxPackets = vport->stats.rxPackets;
1422     vportStats.rxBytes = vport->stats.rxBytes;
1423     vportStats.txPackets = vport->stats.txPackets;
1424     vportStats.txBytes = vport->stats.txBytes;
1425     vportStats.rxErrors = vport->errStats.rxErrors;
1426     vportStats.txErrors = vport->errStats.txErrors;
1427     vportStats.rxDropped = vport->errStats.rxDropped;
1428     vportStats.txDropped = vport->errStats.txDropped;
1429
1430     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1431                             (PCHAR)&vportStats,
1432                             sizeof(OVS_VPORT_FULL_STATS));
1433     if (!ok) {
1434         return STATUS_INSUFFICIENT_RESOURCES;
1435     }
1436
1437     /*
1438      * XXX: when vxlan udp dest port becomes configurable, we will also need
1439      * to add vport options
1440     */
1441
1442     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1443     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1444
1445     return STATUS_SUCCESS;
1446 }
1447
1448 static NTSTATUS
1449 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1450                     UINT32 *replyLen)
1451 {
1452     POVS_MESSAGE msgIn;
1453     POVS_OPEN_INSTANCE instance =
1454         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1455     LOCK_STATE_EX lockState;
1456     UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1457
1458     /*
1459      * XXX: this function shares some code with other dump command(s).
1460      * In the future, we will need to refactor the dump functions
1461     */
1462
1463     ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1464
1465     if (instance->dumpState.ovsMsg == NULL) {
1466         ASSERT(FALSE);
1467         return STATUS_INVALID_DEVICE_STATE;
1468     }
1469
1470     /* Output buffer has been validated while validating read dev op. */
1471     ASSERT(usrParamsCtx->outputBuffer != NULL);
1472
1473     msgIn = instance->dumpState.ovsMsg;
1474
1475     OvsAcquireCtrlLock();
1476     if (!gOvsSwitchContext) {
1477         /* Treat this as a dump done. */
1478         OvsReleaseCtrlLock();
1479         *replyLen = 0;
1480         FreeUserDumpState(instance);
1481         return STATUS_SUCCESS;
1482     }
1483
1484     /*
1485      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1486      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1487      * it means we have an array of pids, instead of a single pid.
1488      * ATM we assume we have one pid only.
1489     */
1490     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1491     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1492                           NDIS_RWL_AT_DISPATCH_LEVEL);
1493
1494     if (gOvsSwitchContext->numVports > 0) {
1495         /* inBucket: the bucket, used for lookup */
1496         UINT32 inBucket = instance->dumpState.index[0];
1497         /* inIndex: index within the given bucket, used for lookup */
1498         UINT32 inIndex = instance->dumpState.index[1];
1499         /* the bucket to be used for the next dump operation */
1500         UINT32 outBucket = 0;
1501         /* the index within the outBucket to be used for the next dump */
1502         UINT32 outIndex = 0;
1503
1504         for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1505             PLIST_ENTRY head, link;
1506             head = &(gOvsSwitchContext->portIdHashArray[i]);
1507             POVS_VPORT_ENTRY vport = NULL;
1508
1509             outIndex = 0;
1510             LIST_FORALL(head, link) {
1511
1512                 /*
1513                  * if one or more dumps were previously done on this same bucket,
1514                  * inIndex will be > 0, so we'll need to reply with the
1515                  * inIndex + 1 vport from the bucket.
1516                 */
1517                 if (outIndex >= inIndex) {
1518                     vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1519
1520                     if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
1521                         OvsCreateMsgFromVport(vport, msgIn,
1522                                               usrParamsCtx->outputBuffer,
1523                                               usrParamsCtx->outputLength,
1524                                               gOvsSwitchContext->dpNo);
1525                         ++outIndex;
1526                         break;
1527                     } else {
1528                         vport = NULL;
1529                     }
1530                 }
1531
1532                 ++outIndex;
1533             }
1534
1535             if (vport) {
1536                 break;
1537             }
1538
1539             /*
1540              * if no vport was found above, check the next bucket, beginning
1541              * with the first (i.e. index 0) elem from within that bucket
1542             */
1543             inIndex = 0;
1544         }
1545
1546         outBucket = i;
1547
1548         /* XXX: what about NLMSG_DONE (as msg type)? */
1549         instance->dumpState.index[0] = outBucket;
1550         instance->dumpState.index[1] = outIndex;
1551     }
1552
1553     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1554
1555     OvsReleaseCtrlLock();
1556
1557     /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1558     if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1559         POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1560         *replyLen = msgOut->nlMsg.nlmsgLen;
1561     } else {
1562         /*
1563          * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1564          * it's dump done
1565          */
1566         *replyLen = 0;
1567         /* Free up the dump state, since there's no more data to continue. */
1568         FreeUserDumpState(instance);
1569     }
1570
1571     return STATUS_SUCCESS;
1572 }
1573
1574 static NTSTATUS
1575 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1576             UINT32 *replyLen)
1577 {
1578     NTSTATUS status = STATUS_SUCCESS;
1579     LOCK_STATE_EX lockState;
1580
1581     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1582     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1583     POVS_VPORT_ENTRY vport = NULL;
1584     NL_ERROR nlError = NL_ERROR_SUCCESS;
1585     PCHAR portName = NULL;
1586     UINT32 portNameLen = 0;
1587     UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
1588
1589     static const NL_POLICY ovsVportPolicy[] = {
1590         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1591         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1592                                   .minLen = 2,
1593                                   .maxLen = IFNAMSIZ,
1594                                   .optional = TRUE},
1595     };
1596     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1597
1598     /* input buffer has been validated while validating write dev op. */
1599     ASSERT(usrParamsCtx->inputBuffer != NULL);
1600
1601     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1602         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1603         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1604         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1605         return STATUS_INVALID_PARAMETER;
1606     }
1607
1608     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1609         return STATUS_INVALID_BUFFER_SIZE;
1610     }
1611
1612     OvsAcquireCtrlLock();
1613     if (!gOvsSwitchContext) {
1614         OvsReleaseCtrlLock();
1615         return STATUS_INVALID_PARAMETER;
1616     }
1617     OvsReleaseCtrlLock();
1618
1619     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1620     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1621         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1622         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1623
1624         /* the port name is expected to be null-terminated */
1625         ASSERT(portName[portNameLen - 1] == '\0');
1626
1627         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1628     } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1629         portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1630
1631         vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
1632     } else {
1633         nlError = NL_ERROR_INVAL;
1634         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1635         goto Cleanup;
1636     }
1637
1638     if (!vport) {
1639         nlError = NL_ERROR_NODEV;
1640         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1641         goto Cleanup;
1642     }
1643
1644     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1645                                    usrParamsCtx->outputLength,
1646                                    gOvsSwitchContext->dpNo);
1647     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1648
1649     *replyLen = msgOut->nlMsg.nlmsgLen;
1650
1651 Cleanup:
1652     if (nlError != NL_ERROR_SUCCESS) {
1653         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1654             usrParamsCtx->outputBuffer;
1655
1656         BuildErrorMsg(msgIn, msgError, nlError);
1657         *replyLen = msgError->nlMsg.nlmsgLen;
1658     }
1659
1660     return STATUS_SUCCESS;
1661 }
1662
1663 /*
1664  * --------------------------------------------------------------------------
1665  *  Handler for the get vport command. The function handles the initial call to
1666  *  setup the dump state, as well as subsequent calls to continue dumping data.
1667  * --------------------------------------------------------------------------
1668 */
1669 static NTSTATUS
1670 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1671                       UINT32 *replyLen)
1672 {
1673     *replyLen = 0;
1674
1675     switch (usrParamsCtx->devOp)
1676     {
1677     case OVS_WRITE_DEV_OP:
1678         return OvsSetupDumpStart(usrParamsCtx);
1679
1680     case OVS_READ_DEV_OP:
1681         return OvsGetVportDumpNext(usrParamsCtx, replyLen);
1682
1683     case OVS_TRANSACTION_DEV_OP:
1684         return OvsGetVport(usrParamsCtx, replyLen);
1685
1686     default:
1687         return STATUS_INVALID_DEVICE_REQUEST;
1688     }
1689
1690 }
1691
1692
1693
1694 static UINT32
1695 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
1696 {
1697     /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
1698     for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
1699         POVS_VPORT_ENTRY vport;
1700
1701         vport = OvsFindVportByPortNo(switchContext, i);
1702         if (!vport) {
1703             return i;
1704         }
1705     }
1706
1707     return OVS_DPPORT_NUMBER_INVALID;
1708 }
1709
1710 /*
1711  * --------------------------------------------------------------------------
1712  *  Command Handler for 'OVS_VPORT_CMD_NEW'.
1713  * --------------------------------------------------------------------------
1714  */
1715 static NTSTATUS
1716 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1717                       UINT32 *replyLen)
1718 {
1719     NDIS_STATUS status = STATUS_SUCCESS;
1720     LOCK_STATE_EX lockState;
1721
1722     NL_ERROR nlError = NL_ERROR_SUCCESS;
1723     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1724     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1725     POVS_VPORT_ENTRY vport = NULL;
1726     PCHAR portName;
1727     ULONG portNameLen;
1728     UINT32 portType;
1729     UINT32 hash;
1730
1731     static const NL_POLICY ovsVportPolicy[] = {
1732         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1733         [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
1734         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
1735                                   .optional = FALSE},
1736         [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
1737                                         .optional = FALSE },
1738         [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
1739     };
1740
1741     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1742
1743     /* input buffer has been validated while validating write dev op. */
1744     ASSERT(usrParamsCtx->inputBuffer != NULL);
1745
1746     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1747         return STATUS_INVALID_BUFFER_SIZE;
1748     }
1749
1750     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1751         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1752         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1753         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1754         return STATUS_INVALID_PARAMETER;
1755     }
1756
1757     OvsAcquireCtrlLock();
1758     if (!gOvsSwitchContext) {
1759         OvsReleaseCtrlLock();
1760         return STATUS_INVALID_PARAMETER;
1761     }
1762     OvsReleaseCtrlLock();
1763
1764     portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1765     portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1766     portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
1767
1768     /* we are expecting null terminated strings to be passed */
1769     ASSERT(portName[portNameLen - 1] == '\0');
1770
1771     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
1772
1773     vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1774     if (vport) {
1775         nlError = NL_ERROR_EXIST;
1776         goto Cleanup;
1777     }
1778
1779     if (portType == OVS_VPORT_TYPE_INTERNAL) {
1780         vport = gOvsSwitchContext->internalVport;
1781     } else if (portType == OVS_VPORT_TYPE_NETDEV) {
1782         if (!strcmp(portName, "external")) {
1783             vport = gOvsSwitchContext->externalVport;
1784         } else {
1785             vport = OvsFindVportByHvName(gOvsSwitchContext, portName);
1786         }
1787     } else {
1788         /* XXX: change when other tunneling ports are added */
1789         ASSERT(portType == OVS_VPORT_TYPE_VXLAN);
1790
1791         if (gOvsSwitchContext->vxlanVport) {
1792             nlError = NL_ERROR_EXIST;
1793             goto Cleanup;
1794         }
1795
1796         vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
1797         if (vport == NULL) {
1798             nlError = NL_ERROR_NOMEM;
1799             goto Cleanup;
1800         }
1801
1802         vport->ovsState = OVS_STATE_PORT_CREATED;
1803
1804         /*
1805          * XXX: when we allow configuring the vxlan udp port, we should read
1806          * this from vport->options instead!
1807         */
1808         nlError = OvsInitVxlanTunnel(vport, VXLAN_UDP_PORT);
1809         if (nlError != NL_ERROR_SUCCESS) {
1810             goto Cleanup;
1811         }
1812     }
1813
1814     if (!vport) {
1815         nlError = NL_ERROR_INVAL;
1816         goto Cleanup;
1817     }
1818
1819     if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
1820         nlError = NL_ERROR_EXIST;
1821         goto Cleanup;
1822     }
1823
1824     /* Fill the data in vport */
1825     vport->ovsType = portType;
1826
1827     if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1828         /*
1829         * XXX: when we implement the limit for ovs port number to be
1830         * MAXUINT16, we'll need to check the port number received from the
1831         * userspace.
1832         */
1833         vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1834     } else {
1835         vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
1836         if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1837             nlError = NL_ERROR_NOMEM;
1838             goto Cleanup;
1839         }
1840     }
1841
1842     /* The ovs port name must be uninitialized. */
1843     ASSERT(vport->ovsName[0] == '\0');
1844     ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
1845
1846     RtlCopyMemory(vport->ovsName, portName, portNameLen);
1847
1848     /* if we don't have options, then vport->portOptions will be NULL */
1849     vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
1850
1851     /*
1852     * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1853     * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1854     * it means we have an array of pids, instead of a single pid.
1855     * ATM we assume we have one pid only.
1856     */
1857     vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
1858
1859     if (vport->ovsType == OVS_VPORT_TYPE_VXLAN) {
1860         gOvsSwitchContext->vxlanVport = vport;
1861     } else if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
1862         gOvsSwitchContext->internalVport = vport;
1863         gOvsSwitchContext->internalPortId = vport->portId;
1864     } else if (vport->ovsType == OVS_VPORT_TYPE_NETDEV &&
1865                vport->isExternal) {
1866         gOvsSwitchContext->externalVport = vport;
1867         gOvsSwitchContext->externalPortId = vport->portId;
1868     }
1869
1870     /*
1871      * insert the port into the hash array of ports: by port number and ovs
1872      * and ovs (datapath) port name.
1873      * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1874      * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1875     */
1876     hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
1877     InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
1878                    &vport->portNoLink);
1879
1880     hash = OvsJhashBytes(vport->ovsName, portNameLen, OVS_HASH_BASIS);
1881     InsertHeadList(&gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
1882                    &vport->ovsNameLink);
1883
1884     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1885                                    usrParamsCtx->outputLength,
1886                                    gOvsSwitchContext->dpNo);
1887
1888     *replyLen = msgOut->nlMsg.nlmsgLen;
1889
1890 Cleanup:
1891     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1892
1893     if (nlError != NL_ERROR_SUCCESS) {
1894         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1895             usrParamsCtx->outputBuffer;
1896
1897         if (vport && vport->ovsType == OVS_VPORT_TYPE_VXLAN) {
1898             OvsRemoveAndDeleteVport(gOvsSwitchContext, vport);
1899         }
1900
1901         BuildErrorMsg(msgIn, msgError, nlError);
1902         *replyLen = msgError->nlMsg.nlmsgLen;
1903     }
1904
1905     return STATUS_SUCCESS;
1906 }
1907
1908
1909 /*
1910  * --------------------------------------------------------------------------
1911  *  Command Handler for 'OVS_VPORT_CMD_SET'.
1912  * --------------------------------------------------------------------------
1913  */
1914 static NTSTATUS
1915 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1916                       UINT32 *replyLen)
1917 {
1918     NDIS_STATUS status = STATUS_SUCCESS;
1919     LOCK_STATE_EX lockState;
1920
1921     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1922     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1923     POVS_VPORT_ENTRY vport = NULL;
1924     NL_ERROR nlError = NL_ERROR_SUCCESS;
1925
1926     static const NL_POLICY ovsVportPolicy[] = {
1927         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1928         [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
1929         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
1930                                   .optional = TRUE },
1931         [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
1932                                         .optional = TRUE },
1933         [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
1934                                    .minLen = sizeof(OVS_VPORT_FULL_STATS),
1935                                    .maxLen = sizeof(OVS_VPORT_FULL_STATS),
1936                                    .optional = TRUE },
1937         [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
1938     };
1939     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1940
1941     ASSERT(usrParamsCtx->inputBuffer != NULL);
1942
1943     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1944         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1945         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1946         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1947         return STATUS_INVALID_PARAMETER;
1948     }
1949
1950     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1951         return STATUS_NDIS_INVALID_LENGTH;
1952     }
1953
1954     OvsAcquireCtrlLock();
1955     if (!gOvsSwitchContext) {
1956         OvsReleaseCtrlLock();
1957         return STATUS_INVALID_PARAMETER;
1958     }
1959
1960     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
1961     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1962         PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1963         UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1964
1965         /* the port name is expected to be null-terminated */
1966         ASSERT(portName[portNameLen - 1] == '\0');
1967
1968         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1969     } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1970         vport = OvsFindVportByPortNo(gOvsSwitchContext,
1971                     NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
1972     }
1973
1974     if (!vport) {
1975         nlError = NL_ERROR_NODEV;
1976         goto Cleanup;
1977     }
1978
1979     /*
1980      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1981      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1982      * it means we have an array of pids, instead of a single pid.
1983      * Currently, we support only one pid.
1984      */
1985     if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
1986         vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
1987     }
1988
1989     if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
1990         OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
1991         if (type != vport->ovsType) {
1992             nlError = NL_ERROR_INVAL;
1993             goto Cleanup;
1994         }
1995     }
1996
1997     if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
1998         OVS_LOG_ERROR("Vport options not supported");
1999         nlError = NL_ERROR_NOTSUPP;
2000         goto Cleanup;
2001     }
2002
2003     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2004                                    usrParamsCtx->outputLength,
2005                                    gOvsSwitchContext->dpNo);
2006
2007     *replyLen = msgOut->nlMsg.nlmsgLen;
2008
2009 Cleanup:
2010     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2011     OvsReleaseCtrlLock();
2012
2013     if (nlError != NL_ERROR_SUCCESS) {
2014         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2015             usrParamsCtx->outputBuffer;
2016
2017         BuildErrorMsg(msgIn, msgError, nlError);
2018         *replyLen = msgError->nlMsg.nlmsgLen;
2019     }
2020
2021     return STATUS_SUCCESS;
2022 }
2023
2024 /*
2025  * --------------------------------------------------------------------------
2026  *  Command Handler for 'OVS_VPORT_CMD_DEL'.
2027  * --------------------------------------------------------------------------
2028  */
2029 static NTSTATUS
2030 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2031                          UINT32 *replyLen)
2032 {
2033     NDIS_STATUS status = STATUS_SUCCESS;
2034     LOCK_STATE_EX lockState;
2035
2036     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2037     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2038     POVS_VPORT_ENTRY vport = NULL;
2039     NL_ERROR nlError = NL_ERROR_SUCCESS;
2040     PSTR portName = NULL;
2041     UINT32 portNameLen = 0;
2042
2043     static const NL_POLICY ovsVportPolicy[] = {
2044         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2045         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ, .optional = TRUE },
2046     };
2047     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2048
2049     ASSERT(usrParamsCtx->inputBuffer != NULL);
2050
2051     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2052         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2053         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2054         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2055         return STATUS_INVALID_PARAMETER;
2056     }
2057
2058     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
2059         return STATUS_NDIS_INVALID_LENGTH;
2060     }
2061
2062     OvsAcquireCtrlLock();
2063     if (!gOvsSwitchContext) {
2064         OvsReleaseCtrlLock();
2065         return STATUS_INVALID_PARAMETER;
2066     }
2067     OvsReleaseCtrlLock();
2068
2069     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2070     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2071         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2072         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2073
2074         /* the port name is expected to be null-terminated */
2075         ASSERT(portName[portNameLen - 1] == '\0');
2076
2077         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2078     }
2079     else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2080         vport = OvsFindVportByPortNo(gOvsSwitchContext,
2081             NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2082     }
2083
2084     if (!vport) {
2085         nlError = NL_ERROR_NODEV;
2086         goto Cleanup;
2087     }
2088
2089     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2090                                    usrParamsCtx->outputLength,
2091                                    gOvsSwitchContext->dpNo);
2092
2093     if (vport->hvDeleted || OvsIsTunnelVportType(vport->ovsType)) {
2094         /*
2095          * The associated hyper-v switch port is not in created state, or,
2096          * there is no hyper-v switch port counterpart (for logical ports).
2097          * This means that this datapath port is not mapped to a living
2098          * hyper-v switc hport. We can destroy and remove the vport from the
2099          * list.
2100         */
2101         OvsRemoveAndDeleteVport(gOvsSwitchContext, vport);
2102     } else {
2103         /* The associated hyper-v switch port is in the created state, and the
2104          * datapath port is mapped to a living hyper-v switch port. We cannot
2105          * destroy the vport and cannot remove it from the list of vports.
2106          * Instead, we mark the datapath (ovs) part of the vport as
2107          * "not created", i.e. we set vport->portNo = OVS_PORT_NUMBER_INVALID.
2108         */
2109         RemoveEntryList(&vport->ovsNameLink);
2110         RemoveEntryList(&vport->portNoLink);
2111         vport->portNo = OVS_DPPORT_NUMBER_INVALID;
2112         vport->ovsName[0] = '\0';
2113     }
2114
2115     *replyLen = msgOut->nlMsg.nlmsgLen;
2116
2117 Cleanup:
2118     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2119
2120     if (nlError != NL_ERROR_SUCCESS) {
2121         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2122             usrParamsCtx->outputBuffer;
2123
2124         BuildErrorMsg(msgIn, msgError, nlError);
2125         *replyLen = msgError->nlMsg.nlmsgLen;
2126     }
2127
2128     return STATUS_SUCCESS;
2129 }
2130
2131
2132 /*
2133  * --------------------------------------------------------------------------
2134  *  Utility function to map the output buffer in an IRP. The buffer is assumed
2135  *  to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
2136  * --------------------------------------------------------------------------
2137  */
2138 static NTSTATUS
2139 MapIrpOutputBuffer(PIRP irp,
2140                    UINT32 bufferLength,
2141                    UINT32 requiredLength,
2142                    PVOID *buffer)
2143 {
2144     ASSERT(irp);
2145     ASSERT(buffer);
2146     ASSERT(bufferLength);
2147     ASSERT(requiredLength);
2148     if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
2149         return STATUS_INVALID_PARAMETER;
2150     }
2151
2152     if (bufferLength < requiredLength) {
2153         return STATUS_NDIS_INVALID_LENGTH;
2154     }
2155     if (irp->MdlAddress == NULL) {
2156         return STATUS_INVALID_PARAMETER;
2157     }
2158     *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
2159                                            NormalPagePriority);
2160     if (*buffer == NULL) {
2161         return STATUS_INSUFFICIENT_RESOURCES;
2162     }
2163
2164     return STATUS_SUCCESS;
2165 }
2166
2167 /*
2168  * --------------------------------------------------------------------------
2169  * Utility function to fill up information about the state of a port in a reply
2170  * to* userspace.
2171  * Assumes that 'gOvsCtrlLock' lock is acquired.
2172  * --------------------------------------------------------------------------
2173  */
2174 static NTSTATUS
2175 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2176                 POVS_EVENT_ENTRY eventEntry,
2177                 PNL_BUFFER nlBuf)
2178 {
2179     NTSTATUS status;
2180     BOOLEAN rc;
2181     OVS_MESSAGE msgOutTmp;
2182     PNL_MSG_HDR nlMsg;
2183     POVS_VPORT_ENTRY vport;
2184
2185     ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
2186
2187     msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
2188     msgOutTmp.nlMsg.nlmsgFlags = 0;  /* XXX: ? */
2189
2190     /* driver intiated messages should have zerp seq number*/
2191     msgOutTmp.nlMsg.nlmsgSeq = 0;
2192     msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
2193
2194     msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
2195     msgOutTmp.genlMsg.reserved = 0;
2196
2197     /* we don't have netdev yet, treat link up/down a adding/removing a port*/
2198     if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
2199         msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
2200     } else if (eventEntry->status &
2201              (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
2202         msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
2203     } else {
2204         ASSERT(FALSE);
2205         return STATUS_UNSUCCESSFUL;
2206     }
2207     msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
2208
2209     rc = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
2210     if (!rc) {
2211         status = STATUS_INVALID_BUFFER_SIZE;
2212         goto cleanup;
2213     }
2214
2215     vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
2216     if (!vport) {
2217         status = STATUS_DEVICE_DOES_NOT_EXIST;
2218         goto cleanup;
2219     }
2220
2221     rc = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) ||
2222          NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) ||
2223          NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
2224     if (!rc) {
2225         status = STATUS_INVALID_BUFFER_SIZE;
2226         goto cleanup;
2227     }
2228
2229     /* XXXX Should we add the port stats attributes?*/
2230     nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
2231     nlMsg->nlmsgLen = NlBufSize(nlBuf);
2232     status = STATUS_SUCCESS;
2233
2234 cleanup:
2235     return status;
2236 }
2237
2238
2239 /*
2240  * --------------------------------------------------------------------------
2241  * Handler for reading events from the driver event queue. This handler is
2242  * executed when user modes issues a socket receive on a socket assocaited
2243  * with the MC group for events.
2244  * XXX user mode should read multiple events in one system call
2245  * --------------------------------------------------------------------------
2246  */
2247 static NTSTATUS
2248 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2249                        UINT32 *replyLen)
2250 {
2251 #ifdef DBG
2252     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2253     POVS_OPEN_INSTANCE instance =
2254         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
2255 #endif
2256     NL_BUFFER nlBuf;
2257     NTSTATUS status;
2258     OVS_EVENT_ENTRY eventEntry;
2259
2260     ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
2261
2262     /* Should never read events with a dump socket */
2263     ASSERT(instance->dumpState.ovsMsg == NULL);
2264
2265     /* Must have an event queue */
2266     ASSERT(instance->eventQueue != NULL);
2267
2268     /* Output buffer has been validated while validating read dev op. */
2269     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2270
2271     NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
2272
2273     OvsAcquireCtrlLock();
2274     if (!gOvsSwitchContext) {
2275         status = STATUS_SUCCESS;
2276         goto cleanup;
2277     }
2278
2279     /* remove an event entry from the event queue */
2280     status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
2281     if (status != STATUS_SUCCESS) {
2282         goto cleanup;
2283     }
2284
2285     status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
2286     if (status == NDIS_STATUS_SUCCESS) {
2287         *replyLen = NlBufSize(&nlBuf);
2288     }
2289
2290 cleanup:
2291     OvsReleaseCtrlLock();
2292     return status;
2293 }
2294 #endif /* OVS_USE_NL_INTERFACE */