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