datapath-windows: refactor code to setup dump start state
[cascardo/ovs.git] / datapath-windows / ovsext / Datapath.c
1 /*
2  * Copyright (c) 2014 VMware, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * XXX: OVS_USE_NL_INTERFACE is being used to keep the legacy DPIF interface
19  * alive while we transition over to the netlink based interface.
20  * OVS_USE_NL_INTERFACE = 0 => legacy inteface to use with dpif-windows.c
21  * OVS_USE_NL_INTERFACE = 1 => netlink inteface to use with ported dpif-linux.c
22  */
23 #if defined OVS_USE_NL_INTERFACE && OVS_USE_NL_INTERFACE == 1
24
25 #include "precomp.h"
26 #include "Datapath.h"
27 #include "Jhash.h"
28 #include "Switch.h"
29 #include "Vport.h"
30 #include "Event.h"
31 #include "User.h"
32 #include "PacketIO.h"
33 #include "NetProto.h"
34 #include "Flow.h"
35 #include "User.h"
36
37 #ifdef OVS_DBG_MOD
38 #undef OVS_DBG_MOD
39 #endif
40 #define OVS_DBG_MOD OVS_DBG_DATAPATH
41 #include "Debug.h"
42
43 #define NETLINK_FAMILY_NAME_LEN 48
44
45
46 /*
47  * Netlink messages are grouped by family (aka type), and each family supports
48  * a set of commands, and can be passed both from kernel -> userspace or
49  * vice-versa. To call into the kernel, userspace uses a device operation which
50  * is outside of a netlink message.
51  *
52  * Each command results in the invocation of a handler function to implement the
53  * request functionality.
54  *
55  * Expectedly, only certain combinations of (device operation, netlink family,
56  * command) are valid.
57  *
58  * Here, we implement the basic infrastructure to perform validation on the
59  * incoming message, version checking, and also to invoke the corresponding
60  * handler to do the heavy-lifting.
61  */
62
63 /*
64  * Handler for a given netlink command. Not all the parameters are used by all
65  * the handlers.
66  */
67 typedef NTSTATUS (*NetlinkCmdHandler)(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
68                                       UINT32 *replyLen);
69
70 typedef struct _NETLINK_CMD {
71     UINT16 cmd;
72     NetlinkCmdHandler handler;
73     UINT32 supportedDevOp;      /* Supported device operations. */
74     BOOLEAN validateDpIndex;    /* Does command require a valid DP argument. */
75 } NETLINK_CMD, *PNETLINK_CMD;
76
77 /* A netlink family is a group of commands. */
78 typedef struct _NETLINK_FAMILY {
79     CHAR *name;
80     UINT32 id;
81     UINT8 version;
82     UINT8 pad;
83     UINT16 maxAttr;
84     NETLINK_CMD *cmds;          /* Array of netlink commands and handlers. */
85     UINT16 opsCount;
86 } NETLINK_FAMILY, *PNETLINK_FAMILY;
87
88 /*
89  * Device operations to tag netlink commands with. This is a bitmask since it is
90  * possible that a particular command can be invoked via different device
91  * operations.
92  */
93 #define OVS_READ_DEV_OP          (1 << 0)
94 #define OVS_WRITE_DEV_OP         (1 << 1)
95 #define OVS_TRANSACTION_DEV_OP   (1 << 2)
96
97 /* Handlers for the various netlink commands. */
98 static NTSTATUS OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
99                                     UINT32 *replyLen);
100
101 static NTSTATUS OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
102                                    UINT32 *replyLen);
103
104 /*
105  * The various netlink families, along with the supported commands. Most of
106  * these families and commands are part of the openvswitch specification for a
107  * netlink datapath. In addition, each platform can implement a few families
108  * and commands as extensions.
109  */
110
111 /* Netlink control family: this is a Windows specific family. */
112 NETLINK_CMD nlControlFamilyCmdOps[] = {
113     { .cmd             = OVS_CTRL_CMD_WIN_GET_PID,
114       .handler         = OvsGetPidCmdHandler,
115       .supportedDevOp  = OVS_TRANSACTION_DEV_OP,
116       .validateDpIndex = FALSE
117     }
118 };
119
120 NETLINK_FAMILY nlControlFamilyOps = {
121     .name     = OVS_WIN_CONTROL_FAMILY,
122     .id       = OVS_WIN_NL_CTRL_FAMILY_ID,
123     .version  = OVS_WIN_CONTROL_VERSION,
124     .maxAttr  = OVS_WIN_CONTROL_ATTR_MAX,
125     .cmds     = nlControlFamilyCmdOps,
126     .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
127 };
128
129 /* Netlink datapath family. */
130 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
131     { .cmd             = OVS_DP_CMD_GET,
132       .handler         = OvsGetDpCmdHandler,
133       .supportedDevOp  = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
134       .validateDpIndex = FALSE
135     }
136 };
137
138 NETLINK_FAMILY nlDatapathFamilyOps = {
139     .name     = OVS_DATAPATH_FAMILY,
140     .id       = OVS_WIN_NL_DATAPATH_FAMILY_ID,
141     .version  = OVS_DATAPATH_VERSION,
142     .maxAttr  = OVS_DP_ATTR_MAX,
143     .cmds     = nlDatapathFamilyCmdOps,
144     .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
145 };
146
147 /* Netlink packet family. */
148 /* XXX: Add commands here. */
149 NETLINK_FAMILY nlPacketFamilyOps = {
150     .name     = OVS_PACKET_FAMILY,
151     .id       = OVS_WIN_NL_PACKET_FAMILY_ID,
152     .version  = OVS_PACKET_VERSION,
153     .maxAttr  = OVS_PACKET_ATTR_MAX,
154     .cmds     = NULL, /* XXX: placeholder. */
155     .opsCount = 0
156 };
157
158 /* Netlink vport family. */
159 /* XXX: Add commands here. */
160 NETLINK_FAMILY nlVportFamilyOps = {
161     .name     = OVS_VPORT_FAMILY,
162     .id       = OVS_WIN_NL_VPORT_FAMILY_ID,
163     .version  = OVS_VPORT_VERSION,
164     .maxAttr  = OVS_VPORT_ATTR_MAX,
165     .cmds     = NULL, /* XXX: placeholder. */
166     .opsCount = 0
167 };
168
169 /* Netlink flow family. */
170 /* XXX: Add commands here. */
171 NETLINK_FAMILY nlFLowFamilyOps = {
172     .name     = OVS_FLOW_FAMILY,
173     .id       = OVS_WIN_NL_FLOW_FAMILY_ID,
174     .version  = OVS_FLOW_VERSION,
175     .maxAttr  = OVS_FLOW_ATTR_MAX,
176     .cmds     = NULL, /* XXX: placeholder. */
177     .opsCount = 0
178 };
179
180 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
181                                    UINT32 bufferLength,
182                                    UINT32 requiredLength,
183                                    PVOID *buffer);
184 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
185                                    POVS_OPEN_INSTANCE instance,
186                                    POVS_MESSAGE ovsMsg,
187                                    NETLINK_FAMILY *nlFamilyOps);
188 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
189                                         NETLINK_FAMILY *nlFamilyOps,
190                                         UINT32 *replyLen);
191 static NTSTATUS OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx);
192
193
194 /* Handles to the device object for communication with userspace. */
195 NDIS_HANDLE gOvsDeviceHandle;
196 PDEVICE_OBJECT gOvsDeviceObject;
197
198 _Dispatch_type_(IRP_MJ_CREATE)
199 _Dispatch_type_(IRP_MJ_CLOSE)
200 DRIVER_DISPATCH OvsOpenCloseDevice;
201
202 _Dispatch_type_(IRP_MJ_CLEANUP)
203 DRIVER_DISPATCH OvsCleanupDevice;
204
205 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
206 DRIVER_DISPATCH OvsDeviceControl;
207
208 #ifdef ALLOC_PRAGMA
209 #pragma alloc_text(INIT, OvsCreateDeviceObject)
210 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
211 #pragma alloc_text(PAGE, OvsCleanupDevice)
212 #pragma alloc_text(PAGE, OvsDeviceControl)
213 #endif // ALLOC_PRAGMA
214
215 /*
216  * We might hit this limit easily since userspace opens a netlink descriptor for
217  * each thread, and at least one descriptor per vport. Revisit this later.
218  */
219 #define OVS_MAX_OPEN_INSTANCES 512
220 #define OVS_SYSTEM_DP_NAME     "ovs-system"
221
222 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
223 UINT32 ovsNumberOfOpenInstances;
224 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
225
226 NDIS_SPIN_LOCK ovsCtrlLockObj;
227 PNDIS_SPIN_LOCK gOvsCtrlLock;
228
229
230 VOID
231 OvsInit()
232 {
233     gOvsCtrlLock = &ovsCtrlLockObj;
234     NdisAllocateSpinLock(gOvsCtrlLock);
235     OvsInitEventQueue();
236     OvsUserInit();
237 }
238
239 VOID
240 OvsCleanup()
241 {
242     OvsCleanupEventQueue();
243     if (gOvsCtrlLock) {
244         NdisFreeSpinLock(gOvsCtrlLock);
245         gOvsCtrlLock = NULL;
246     }
247     OvsUserCleanup();
248 }
249
250 VOID
251 OvsAcquireCtrlLock()
252 {
253     NdisAcquireSpinLock(gOvsCtrlLock);
254 }
255
256 VOID
257 OvsReleaseCtrlLock()
258 {
259     NdisReleaseSpinLock(gOvsCtrlLock);
260 }
261
262
263 /*
264  * --------------------------------------------------------------------------
265  * Creates the communication device between user and kernel, and also
266  * initializes the data associated data structures.
267  * --------------------------------------------------------------------------
268  */
269 NDIS_STATUS
270 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
271 {
272     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
273     UNICODE_STRING deviceName;
274     UNICODE_STRING symbolicDeviceName;
275     PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
276     NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
277     OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
278
279     RtlZeroMemory(dispatchTable,
280                   (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
281     dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
282     dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
283     dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
284     dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
285
286     NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
287     NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
288
289     RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
290
291     OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
292                            NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
293                            NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
294                            sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
295
296     deviceAttributes.DeviceName = &deviceName;
297     deviceAttributes.SymbolicName = &symbolicDeviceName;
298     deviceAttributes.MajorFunctions = dispatchTable;
299     deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
300
301     status = NdisRegisterDeviceEx(ovsExtDriverHandle,
302                                   &deviceAttributes,
303                                   &gOvsDeviceObject,
304                                   &gOvsDeviceHandle);
305     if (status != NDIS_STATUS_SUCCESS) {
306         POVS_DEVICE_EXTENSION ovsExt =
307             (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
308         ASSERT(gOvsDeviceObject != NULL);
309         ASSERT(gOvsDeviceHandle != NULL);
310
311         if (ovsExt) {
312             ovsExt->numberOpenInstance = 0;
313         }
314     } else {
315         /* Initialize the associated data structures. */
316         OvsInit();
317     }
318     OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
319     return status;
320 }
321
322
323 VOID
324 OvsDeleteDeviceObject()
325 {
326     if (gOvsDeviceHandle) {
327 #ifdef DBG
328         POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
329                     NdisGetDeviceReservedExtension(gOvsDeviceObject);
330         if (ovsExt) {
331             ASSERT(ovsExt->numberOpenInstance == 0);
332         }
333 #endif
334
335         ASSERT(gOvsDeviceObject);
336         NdisDeregisterDeviceEx(gOvsDeviceHandle);
337         gOvsDeviceHandle = NULL;
338         gOvsDeviceObject = NULL;
339     }
340     OvsCleanup();
341 }
342
343 POVS_OPEN_INSTANCE
344 OvsGetOpenInstance(PFILE_OBJECT fileObject,
345                    UINT32 dpNo)
346 {
347     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
348     ASSERT(instance);
349     ASSERT(instance->fileObject == fileObject);
350     if (gOvsSwitchContext == NULL ||
351         gOvsSwitchContext->dpNo != dpNo) {
352         return NULL;
353     }
354     return instance;
355 }
356
357
358 POVS_OPEN_INSTANCE
359 OvsFindOpenInstance(PFILE_OBJECT fileObject)
360 {
361     UINT32 i, j;
362     for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
363                        j < ovsNumberOfOpenInstances; i++) {
364         if (ovsOpenInstanceArray[i]) {
365             if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
366                 return ovsOpenInstanceArray[i];
367             }
368             j++;
369         }
370     }
371     return NULL;
372 }
373
374 NTSTATUS
375 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
376                    PFILE_OBJECT fileObject)
377 {
378     POVS_OPEN_INSTANCE instance =
379         (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
380     UINT32 i;
381
382     if (instance == NULL) {
383         return STATUS_NO_MEMORY;
384     }
385     OvsAcquireCtrlLock();
386     ASSERT(OvsFindOpenInstance(fileObject) == NULL);
387
388     if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
389         OvsReleaseCtrlLock();
390         OvsFreeMemory(instance);
391         return STATUS_INSUFFICIENT_RESOURCES;
392     }
393     RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
394
395     for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
396         if (ovsOpenInstanceArray[i] == NULL) {
397             ovsOpenInstanceArray[i] = instance;
398             instance->cookie = i;
399             break;
400         }
401     }
402     ASSERT(i < OVS_MAX_OPEN_INSTANCES);
403     instance->fileObject = fileObject;
404     ASSERT(fileObject->FsContext == NULL);
405     instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
406     if (instance->pid == 0) {
407         /* XXX: check for rollover. */
408     }
409     fileObject->FsContext = instance;
410     OvsReleaseCtrlLock();
411     return STATUS_SUCCESS;
412 }
413
414 static VOID
415 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
416 {
417     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
418     ASSERT(instance);
419     ASSERT(fileObject == instance->fileObject);
420     OvsCleanupEvent(instance);
421     OvsCleanupPacketQueue(instance);
422 }
423
424 VOID
425 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
426 {
427     POVS_OPEN_INSTANCE instance;
428     ASSERT(fileObject->FsContext);
429     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
430     ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
431
432     OvsAcquireCtrlLock();
433     fileObject->FsContext = NULL;
434     ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
435     ovsOpenInstanceArray[instance->cookie] = NULL;
436     OvsReleaseCtrlLock();
437     ASSERT(instance->eventQueue == NULL);
438     ASSERT (instance->packetQueue == NULL);
439     OvsFreeMemory(instance);
440 }
441
442 NTSTATUS
443 OvsCompleteIrpRequest(PIRP irp,
444                       ULONG_PTR infoPtr,
445                       NTSTATUS status)
446 {
447     irp->IoStatus.Information = infoPtr;
448     irp->IoStatus.Status = status;
449     IoCompleteRequest(irp, IO_NO_INCREMENT);
450     return status;
451 }
452
453
454 NTSTATUS
455 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
456                    PIRP irp)
457 {
458     PIO_STACK_LOCATION irpSp;
459     NTSTATUS status = STATUS_SUCCESS;
460     PFILE_OBJECT fileObject;
461     POVS_DEVICE_EXTENSION ovsExt =
462         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
463
464     ASSERT(deviceObject == gOvsDeviceObject);
465     ASSERT(ovsExt != NULL);
466
467     irpSp = IoGetCurrentIrpStackLocation(irp);
468     fileObject = irpSp->FileObject;
469     OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
470                   deviceObject, fileObject,
471                   ovsExt->numberOpenInstance);
472
473     switch (irpSp->MajorFunction) {
474     case IRP_MJ_CREATE:
475         status = OvsAddOpenInstance(ovsExt, fileObject);
476         if (STATUS_SUCCESS == status) {
477             InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
478         }
479         break;
480     case IRP_MJ_CLOSE:
481         ASSERT(ovsExt->numberOpenInstance > 0);
482         OvsRemoveOpenInstance(fileObject);
483         InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
484         break;
485     default:
486         ASSERT(0);
487     }
488     return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
489 }
490
491 _Use_decl_annotations_
492 NTSTATUS
493 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
494                  PIRP irp)
495 {
496
497     PIO_STACK_LOCATION irpSp;
498     PFILE_OBJECT fileObject;
499
500     NTSTATUS status = STATUS_SUCCESS;
501 #ifdef DBG
502     POVS_DEVICE_EXTENSION ovsExt =
503         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
504     if (ovsExt) {
505         ASSERT(ovsExt->numberOpenInstance > 0);
506     }
507 #else
508     UNREFERENCED_PARAMETER(deviceObject);
509 #endif
510     ASSERT(deviceObject == gOvsDeviceObject);
511     irpSp = IoGetCurrentIrpStackLocation(irp);
512     fileObject = irpSp->FileObject;
513
514     ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
515
516     OvsCleanupOpenInstance(fileObject);
517
518     return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
519 }
520
521
522 /*
523  * --------------------------------------------------------------------------
524  * IOCTL function handler for the device.
525  * --------------------------------------------------------------------------
526  */
527 NTSTATUS
528 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
529                  PIRP irp)
530 {
531
532     PIO_STACK_LOCATION irpSp;
533     NTSTATUS status = STATUS_SUCCESS;
534     PFILE_OBJECT fileObject;
535     PVOID inputBuffer = NULL;
536     PVOID outputBuffer = NULL;
537     UINT32 inputBufferLen, outputBufferLen;
538     UINT32 code, replyLen = 0;
539     POVS_OPEN_INSTANCE instance;
540     UINT32 devOp;
541     OVS_MESSAGE ovsMsgReadOp;
542     POVS_MESSAGE ovsMsg;
543     NETLINK_FAMILY *nlFamilyOps;
544     OVS_USER_PARAMS_CONTEXT usrParamsCtx;
545
546 #ifdef DBG
547     POVS_DEVICE_EXTENSION ovsExt =
548         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
549     ASSERT(deviceObject == gOvsDeviceObject);
550     ASSERT(ovsExt);
551     ASSERT(ovsExt->numberOpenInstance > 0);
552 #else
553     UNREFERENCED_PARAMETER(deviceObject);
554 #endif
555
556     irpSp = IoGetCurrentIrpStackLocation(irp);
557
558     ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
559     ASSERT(irpSp->FileObject != NULL);
560
561     fileObject = irpSp->FileObject;
562     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
563     code = irpSp->Parameters.DeviceIoControl.IoControlCode;
564     inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
565     outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
566     inputBuffer = irp->AssociatedIrp.SystemBuffer;
567
568     /* Concurrent netlink operations are not supported. */
569     if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
570         status = STATUS_RESOURCE_IN_USE;
571         goto done;
572     }
573
574     /*
575      * Validate the input/output buffer arguments depending on the type of the
576      * operation.
577      */
578     switch (code) {
579     case OVS_IOCTL_TRANSACT:
580         /* Input buffer is mandatory, output buffer is optional. */
581         if (outputBufferLen != 0) {
582             status = MapIrpOutputBuffer(irp, outputBufferLen,
583                                         sizeof *ovsMsg, &outputBuffer);
584             if (status != STATUS_SUCCESS) {
585                 goto done;
586             }
587             ASSERT(outputBuffer);
588         }
589
590         if (inputBufferLen < sizeof (*ovsMsg)) {
591             status = STATUS_NDIS_INVALID_LENGTH;
592             goto done;
593         }
594
595         ovsMsg = inputBuffer;
596         devOp = OVS_TRANSACTION_DEV_OP;
597         break;
598
599     case OVS_IOCTL_READ:
600         /* Output buffer is mandatory. */
601         if (outputBufferLen != 0) {
602             status = MapIrpOutputBuffer(irp, outputBufferLen,
603                                         sizeof *ovsMsg, &outputBuffer);
604             if (status != STATUS_SUCCESS) {
605                 goto done;
606             }
607             ASSERT(outputBuffer);
608         } else {
609             status = STATUS_NDIS_INVALID_LENGTH;
610             goto done;
611         }
612
613         /*
614          * Operate in the mode that read ioctl is similar to ReadFile(). This
615          * might change as the userspace code gets implemented.
616          */
617         inputBuffer = NULL;
618         inputBufferLen = 0;
619
620         /*
621          * For implementing read (ioctl or otherwise), we need to store some
622          * state in the instance to indicate the command that started the dump
623          * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
624          * that 'ovsMsgReadOp' is needed only in this function to call into the
625          * appropraite handler. The handler itself can access the state in the
626          * instance.
627          *
628          * In the absence of a dump start, return 0 bytes.
629          */
630         if (instance->dumpState.ovsMsg == NULL) {
631             replyLen = 0;
632             status = STATUS_SUCCESS;
633             goto done;
634         }
635         RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
636                       sizeof (ovsMsgReadOp));
637
638         /* Create an NL message for consumption. */
639         ovsMsg = &ovsMsgReadOp;
640         devOp = OVS_READ_DEV_OP;
641
642         break;
643
644     case OVS_IOCTL_WRITE:
645         /* Input buffer is mandatory. */
646         if (inputBufferLen < sizeof (*ovsMsg)) {
647             status = STATUS_NDIS_INVALID_LENGTH;
648             goto done;
649         }
650
651         ovsMsg = inputBuffer;
652         devOp = OVS_WRITE_DEV_OP;
653         break;
654
655     default:
656         status = STATUS_INVALID_DEVICE_REQUEST;
657         goto done;
658     }
659
660     ASSERT(ovsMsg);
661     switch (ovsMsg->nlMsg.nlmsgType) {
662     case OVS_WIN_NL_CTRL_FAMILY_ID:
663         nlFamilyOps = &nlControlFamilyOps;
664         break;
665     case OVS_WIN_NL_DATAPATH_FAMILY_ID:
666         nlFamilyOps = &nlDatapathFamilyOps;
667         break;
668     case OVS_WIN_NL_PACKET_FAMILY_ID:
669     case OVS_WIN_NL_FLOW_FAMILY_ID:
670     case OVS_WIN_NL_VPORT_FAMILY_ID:
671         status = STATUS_NOT_IMPLEMENTED;
672         goto done;
673
674     default:
675         status = STATUS_INVALID_PARAMETER;
676         goto done;
677     }
678
679     /*
680      * For read operation, the netlink command has already been validated
681      * previously.
682      */
683     if (devOp != OVS_READ_DEV_OP) {
684         status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
685         if (status != STATUS_SUCCESS) {
686             goto done;
687         }
688     }
689
690     InitUserParamsCtx(irp, instance, devOp, ovsMsg,
691                       inputBuffer, inputBufferLen,
692                       outputBuffer, outputBufferLen,
693                       &usrParamsCtx);
694
695     status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
696
697 done:
698     KeMemoryBarrier();
699     instance->inUse = 0;
700     return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
701 }
702
703
704 /*
705  * --------------------------------------------------------------------------
706  * Function to validate a netlink command. Only certain combinations of
707  * (device operation, netlink family, command) are valid.
708  *
709  * XXX: Take policy into consideration.
710  * --------------------------------------------------------------------------
711  */
712 static NTSTATUS
713 ValidateNetlinkCmd(UINT32 devOp,
714                    POVS_OPEN_INSTANCE instance,
715                    POVS_MESSAGE ovsMsg,
716                    NETLINK_FAMILY *nlFamilyOps)
717 {
718     NTSTATUS status = STATUS_INVALID_PARAMETER;
719     UINT16 i;
720
721     for (i = 0; i < nlFamilyOps->opsCount; i++) {
722         if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
723             /* Validate if the command is valid for the device operation. */
724             if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
725                 status = STATUS_INVALID_PARAMETER;
726                 goto done;
727             }
728
729             /* Validate the version. */
730             if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
731                 status = STATUS_INVALID_PARAMETER;
732                 goto done;
733             }
734
735             /* Validate the DP for commands that require a DP. */
736             if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
737                 OvsAcquireCtrlLock();
738                 if (ovsMsg->ovsHdr.dp_ifindex !=
739                     (INT)gOvsSwitchContext->dpNo) {
740                     status = STATUS_INVALID_PARAMETER;
741                     OvsReleaseCtrlLock();
742                     goto done;
743                 }
744                 OvsReleaseCtrlLock();
745             }
746
747             /* Validate the PID. */
748             if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
749                 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
750                     status = STATUS_INVALID_PARAMETER;
751                     goto done;
752                 }
753             }
754
755             status = STATUS_SUCCESS;
756             break;
757         }
758     }
759
760 done:
761     return status;
762 }
763
764 /*
765  * --------------------------------------------------------------------------
766  * Function to invoke the netlink command handler.
767  * --------------------------------------------------------------------------
768  */
769 static NTSTATUS
770 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
771                         NETLINK_FAMILY *nlFamilyOps,
772                         UINT32 *replyLen)
773 {
774     NTSTATUS status = STATUS_INVALID_PARAMETER;
775     UINT16 i;
776
777     for (i = 0; i < nlFamilyOps->opsCount; i++) {
778         if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
779             status = nlFamilyOps->cmds[i].handler(usrParamsCtx, replyLen);
780             break;
781         }
782     }
783
784     return status;
785 }
786
787
788 /*
789  * --------------------------------------------------------------------------
790  *  Each handle on the device is assigned a unique PID when the handle is
791  *  created. On platforms that support netlink natively, the PID is available
792  *  to userspace when the netlink socket is created. However, without native
793  *  netlink support on Windows, OVS datapath generates the PID and lets the
794  *  userspace query it.
795  *
796  *  This function implements the query.
797  * --------------------------------------------------------------------------
798  */
799 static NTSTATUS
800 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
801                     UINT32 *replyLen)
802 {
803     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
804     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
805
806     if (usrParamsCtx->outputLength >= sizeof *msgOut) {
807         POVS_OPEN_INSTANCE instance =
808             (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
809
810         RtlZeroMemory(msgOut, sizeof *msgOut);
811         msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
812         msgOut->nlMsg.nlmsgPid = instance->pid;
813         *replyLen = sizeof *msgOut;
814         /* XXX: We might need to return the DP index as well. */
815     } else {
816         return STATUS_NDIS_INVALID_LENGTH;
817     }
818
819     return STATUS_SUCCESS;
820 }
821
822 /*
823  * --------------------------------------------------------------------------
824  *  Handler for the get dp command. The function handles the initial call to
825  *  setup the dump state, as well as subsequent calls to continue dumping data.
826  * --------------------------------------------------------------------------
827  */
828 static NTSTATUS
829 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
830                    UINT32 *replyLen)
831 {
832     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
833     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
834     POVS_OPEN_INSTANCE instance =
835         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
836
837     if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
838         *replyLen = 0;
839         OvsSetupDumpStart(usrParamsCtx);
840     } else {
841         ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
842
843         if (instance->dumpState.ovsMsg == NULL) {
844             ASSERT(FALSE);
845             return STATUS_INVALID_DEVICE_STATE;
846         }
847
848         /* Dump state must have been deleted after previous dump operation. */
849         ASSERT(instance->dumpState.index[0] == 0);
850         /* Output buffer has been validated while validating read dev op. */
851         ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
852
853         /* XXX: Replace this with the netlink set API. */
854         msgIn = instance->dumpState.ovsMsg;
855         msgOut->nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
856         msgOut->nlMsg.nlmsgFlags = 0;  /* XXX: ? */
857         msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
858         msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
859         msgOut->nlMsg.nlmsgLen = sizeof *msgOut;
860
861         msgOut->genlMsg.cmd = OVS_DP_CMD_GET;
862         msgOut->genlMsg.version = nlDatapathFamilyOps.version;
863         msgOut->genlMsg.reserved = 0;
864
865         OvsAcquireCtrlLock();
866         if (gOvsSwitchContext) {
867             msgOut->ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
868         } else {
869             /* Treat this as a dump done. */
870             OvsReleaseCtrlLock();
871             *replyLen = 0;
872             FreeUserDumpState(instance);
873             return STATUS_SUCCESS;
874         }
875
876         /* XXX: Replace this with the netlink set API. */
877         {
878             /*
879              * Assume that the output buffer is at least 512 bytes. Once the
880              * netlink set API is implemented, the netlink buffer manipulation
881              * API should provide for checking the remaining number of bytes as
882              * we write out the message.
883              */
884             if (usrParamsCtx->outputLength <= 512) {
885                 OvsReleaseCtrlLock();
886                 return STATUS_NDIS_INVALID_LENGTH;
887             }
888             UINT16 attrTotalSize = 0;
889             UINT16 attrSize = 0;
890             PNL_ATTR nlAttr = (PNL_ATTR) ((PCHAR)msgOut + sizeof (*msgOut));
891             struct ovs_dp_stats *dpStats;
892             OVS_DATAPATH *datapath = &gOvsSwitchContext->datapath;
893
894             nlAttr->nlaType = OVS_DP_ATTR_NAME;
895             /* 'nlaLen' must not include the pad. */
896             nlAttr->nlaLen = sizeof (*nlAttr) + sizeof (OVS_SYSTEM_DP_NAME);
897             attrSize = sizeof (*nlAttr) +
898                        NLA_ALIGN(sizeof (OVS_SYSTEM_DP_NAME));
899             attrTotalSize += attrSize;
900
901             PNL_ATTR nlAttrNext = NlAttrGet(nlAttr);
902             RtlCopyMemory((PCHAR)nlAttrNext, OVS_SYSTEM_DP_NAME,
903                           sizeof (OVS_SYSTEM_DP_NAME));
904
905             nlAttr = (PNL_ATTR)((PCHAR)nlAttr + attrSize);
906             nlAttr->nlaType = OVS_DP_ATTR_STATS;
907             nlAttr->nlaLen = sizeof (*nlAttr) + sizeof (*dpStats);
908             attrSize = sizeof (*nlAttr) + NLA_ALIGN(sizeof (*dpStats));
909             attrTotalSize += attrSize;
910
911             dpStats = NlAttrGet(nlAttr);
912             dpStats->n_hit = datapath->hits;
913             dpStats->n_missed = datapath->misses;
914             dpStats->n_lost = datapath->lost;
915             dpStats->n_flows = datapath->nFlows;
916
917             msgOut->nlMsg.nlmsgLen += attrTotalSize;
918         }
919         OvsReleaseCtrlLock();
920
921         /* Increment the dump index. */
922         instance->dumpState.index[0] = 1;
923         *replyLen = msgOut->nlMsg.nlmsgLen;
924         ASSERT(*replyLen <= 512);
925
926         /* Free up the dump state, since there's no more data to continue. */
927         FreeUserDumpState(instance);
928     }
929
930     return STATUS_SUCCESS;
931 }
932
933 static NTSTATUS
934 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
935 {
936     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
937     POVS_OPEN_INSTANCE instance =
938         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
939
940     /* input buffer has been validated while validating write dev op. */
941     ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
942
943     /* A write operation that does not indicate dump start is invalid. */
944     if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
945         return STATUS_INVALID_PARAMETER;
946     }
947     /* XXX: Handle other NLM_F_* flags in the future. */
948
949     /*
950      * This operation should be setting up the dump state. If there's any
951      * previous state, clear it up so as to set it up afresh.
952      */
953     if (instance->dumpState.ovsMsg != NULL) {
954         FreeUserDumpState(instance);
955     }
956
957     return InitUserDumpState(instance, msgIn);
958 }
959
960 /*
961  * --------------------------------------------------------------------------
962  *  Utility function to map the output buffer in an IRP. The buffer is assumed
963  *  to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
964  * --------------------------------------------------------------------------
965  */
966 static NTSTATUS
967 MapIrpOutputBuffer(PIRP irp,
968                    UINT32 bufferLength,
969                    UINT32 requiredLength,
970                    PVOID *buffer)
971 {
972     ASSERT(irp);
973     ASSERT(buffer);
974     ASSERT(bufferLength);
975     ASSERT(requiredLength);
976     if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
977         return STATUS_INVALID_PARAMETER;
978     }
979
980     if (bufferLength < requiredLength) {
981         return STATUS_NDIS_INVALID_LENGTH;
982     }
983     if (irp->MdlAddress == NULL) {
984         return STATUS_INVALID_PARAMETER;
985     }
986     *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
987                                            NormalPagePriority);
988     if (*buffer == NULL) {
989         return STATUS_INSUFFICIENT_RESOURCES;
990     }
991
992     return STATUS_SUCCESS;
993 }
994
995 #endif /* OVS_USE_NL_INTERFACE */