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