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