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