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