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