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