19c08343b4208d2240bf53b40b76c177ac62b0bf
[cascardo/ovs.git] / datapath-windows / ovsext / Datapath.c
1 /*
2  * Copyright (c) 2014 VMware, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * XXX: OVS_USE_NL_INTERFACE is being used to keep the legacy DPIF interface
19  * alive while we transition over to the netlink based interface.
20  * OVS_USE_NL_INTERFACE = 0 => legacy inteface to use with dpif-windows.c
21  * OVS_USE_NL_INTERFACE = 1 => netlink inteface to use with ported dpif-linux.c
22  */
23 #if defined OVS_USE_NL_INTERFACE && OVS_USE_NL_INTERFACE == 1
24
25 #include "precomp.h"
26 #include "Datapath.h"
27 #include "Jhash.h"
28 #include "Switch.h"
29 #include "Vport.h"
30 #include "Event.h"
31 #include "User.h"
32 #include "PacketIO.h"
33 #include "NetProto.h"
34 #include "Flow.h"
35 #include "User.h"
36 #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                          OvsSubscribeEventCmdHandler,
93                          OvsReadEventCmdHandler,
94                          OvsNewDpCmdHandler,
95                          OvsGetDpCmdHandler,
96                          OvsSetDpCmdHandler,
97                          OvsGetVportCmdHandler,
98                          OvsNewVportCmdHandler,
99                          OvsDeleteVportCmdHandler;
100
101 NetlinkCmdHandler        OvsGetNetdevCmdHandler;
102
103 static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
104                                        UINT32 *replyLen);
105 static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
106                                 UINT32 *replyLen);
107 static NTSTATUS HandleDpTransactionCommon(
108                     POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen);
109
110 /*
111  * The various netlink families, along with the supported commands. Most of
112  * these families and commands are part of the openvswitch specification for a
113  * netlink datapath. In addition, each platform can implement a few families
114  * and commands as extensions.
115  */
116
117 /* Netlink control family: this is a Windows specific family. */
118 NETLINK_CMD nlControlFamilyCmdOps[] = {
119     { .cmd             = OVS_CTRL_CMD_WIN_GET_PID,
120       .handler         = OvsGetPidCmdHandler,
121       .supportedDevOp  = OVS_TRANSACTION_DEV_OP,
122       .validateDpIndex = FALSE,
123     },
124     { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ,
125       .handler = OvsPendEventCmdHandler,
126       .supportedDevOp = OVS_WRITE_DEV_OP,
127       .validateDpIndex = TRUE,
128     },
129     { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ,
130       .handler = OvsSubscribeEventCmdHandler,
131       .supportedDevOp = OVS_WRITE_DEV_OP,
132       .validateDpIndex = TRUE,
133     },
134     { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY,
135       .handler = OvsReadEventCmdHandler,
136       .supportedDevOp = OVS_READ_EVENT_DEV_OP,
137       .validateDpIndex = FALSE,
138     }
139 };
140
141 NETLINK_FAMILY nlControlFamilyOps = {
142     .name     = OVS_WIN_CONTROL_FAMILY,
143     .id       = OVS_WIN_NL_CTRL_FAMILY_ID,
144     .version  = OVS_WIN_CONTROL_VERSION,
145     .maxAttr  = OVS_WIN_CONTROL_ATTR_MAX,
146     .cmds     = nlControlFamilyCmdOps,
147     .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
148 };
149
150 /* Netlink datapath family. */
151 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
152     { .cmd             = OVS_DP_CMD_NEW,
153       .handler         = OvsNewDpCmdHandler,
154       .supportedDevOp  = OVS_TRANSACTION_DEV_OP,
155       .validateDpIndex = FALSE
156     },
157     { .cmd             = OVS_DP_CMD_GET,
158       .handler         = OvsGetDpCmdHandler,
159       .supportedDevOp  = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
160                          OVS_TRANSACTION_DEV_OP,
161       .validateDpIndex = FALSE
162     },
163     { .cmd             = OVS_DP_CMD_SET,
164       .handler         = OvsSetDpCmdHandler,
165       .supportedDevOp  = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
166                          OVS_TRANSACTION_DEV_OP,
167       .validateDpIndex = TRUE
168     }
169 };
170
171 NETLINK_FAMILY nlDatapathFamilyOps = {
172     .name     = OVS_DATAPATH_FAMILY,
173     .id       = OVS_WIN_NL_DATAPATH_FAMILY_ID,
174     .version  = OVS_DATAPATH_VERSION,
175     .maxAttr  = OVS_DP_ATTR_MAX,
176     .cmds     = nlDatapathFamilyCmdOps,
177     .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
178 };
179
180 /* Netlink packet family. */
181 /* XXX: Add commands here. */
182 NETLINK_FAMILY nlPacketFamilyOps = {
183     .name     = OVS_PACKET_FAMILY,
184     .id       = OVS_WIN_NL_PACKET_FAMILY_ID,
185     .version  = OVS_PACKET_VERSION,
186     .maxAttr  = OVS_PACKET_ATTR_MAX,
187     .cmds     = NULL, /* XXX: placeholder. */
188     .opsCount = 0
189 };
190
191 /* Netlink vport family. */
192 NETLINK_CMD nlVportFamilyCmdOps[] = {
193     { .cmd = OVS_VPORT_CMD_GET,
194       .handler = OvsGetVportCmdHandler,
195       .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
196                         OVS_TRANSACTION_DEV_OP,
197       .validateDpIndex = TRUE
198     },
199     { .cmd = OVS_VPORT_CMD_NEW,
200       .handler = OvsNewVportCmdHandler,
201       .supportedDevOp = OVS_TRANSACTION_DEV_OP,
202       .validateDpIndex = TRUE
203     },
204     { .cmd = OVS_VPORT_CMD_DEL,
205       .handler = OvsDeleteVportCmdHandler,
206       .supportedDevOp = OVS_TRANSACTION_DEV_OP,
207       .validateDpIndex = TRUE
208     },
209 };
210
211 NETLINK_FAMILY nlVportFamilyOps = {
212     .name     = OVS_VPORT_FAMILY,
213     .id       = OVS_WIN_NL_VPORT_FAMILY_ID,
214     .version  = OVS_VPORT_VERSION,
215     .maxAttr  = OVS_VPORT_ATTR_MAX,
216     .cmds     = nlVportFamilyCmdOps,
217     .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps)
218 };
219
220 /* Netlink flow family. */
221
222 NETLINK_CMD nlFlowFamilyCmdOps[] = {
223     { .cmd              = OVS_FLOW_CMD_NEW,
224       .handler          = OvsFlowNlCmdHandler,
225       .supportedDevOp   = OVS_TRANSACTION_DEV_OP,
226       .validateDpIndex  = TRUE
227     },
228     { .cmd              = OVS_FLOW_CMD_SET,
229       .handler          = OvsFlowNlCmdHandler,
230       .supportedDevOp   = OVS_TRANSACTION_DEV_OP,
231       .validateDpIndex  = TRUE
232     },
233     { .cmd              = OVS_FLOW_CMD_DEL,
234       .handler          = OvsFlowNlCmdHandler,
235       .supportedDevOp   = OVS_TRANSACTION_DEV_OP,
236       .validateDpIndex  = TRUE
237     },
238     { .cmd              = OVS_FLOW_CMD_GET,
239       .handler          = OvsFlowNlGetCmdHandler,
240       .supportedDevOp   = OVS_TRANSACTION_DEV_OP |
241                           OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
242       .validateDpIndex  = TRUE
243     },
244 };
245
246 NETLINK_FAMILY nlFLowFamilyOps = {
247     .name     = OVS_FLOW_FAMILY,
248     .id       = OVS_WIN_NL_FLOW_FAMILY_ID,
249     .version  = OVS_FLOW_VERSION,
250     .maxAttr  = OVS_FLOW_ATTR_MAX,
251     .cmds     = nlFlowFamilyCmdOps,
252     .opsCount = ARRAY_SIZE(nlFlowFamilyCmdOps)
253 };
254
255 /* Netlink netdev family. */
256 NETLINK_CMD nlNetdevFamilyCmdOps[] = {
257     { .cmd = OVS_WIN_NETDEV_CMD_GET,
258       .handler = OvsGetNetdevCmdHandler,
259       .supportedDevOp = OVS_TRANSACTION_DEV_OP,
260       .validateDpIndex = FALSE
261     },
262 };
263
264 NETLINK_FAMILY nlNetdevFamilyOps = {
265     .name     = OVS_WIN_NETDEV_FAMILY,
266     .id       = OVS_WIN_NL_NETDEV_FAMILY_ID,
267     .version  = OVS_WIN_NETDEV_VERSION,
268     .maxAttr  = OVS_WIN_NETDEV_ATTR_MAX,
269     .cmds     = nlNetdevFamilyCmdOps,
270     .opsCount = ARRAY_SIZE(nlNetdevFamilyCmdOps)
271 };
272
273 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
274                                    UINT32 bufferLength,
275                                    UINT32 requiredLength,
276                                    PVOID *buffer);
277 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
278                                    POVS_OPEN_INSTANCE instance,
279                                    POVS_MESSAGE ovsMsg,
280                                    NETLINK_FAMILY *nlFamilyOps);
281 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
282                                         NETLINK_FAMILY *nlFamilyOps,
283                                         UINT32 *replyLen);
284
285 /* Handles to the device object for communication with userspace. */
286 NDIS_HANDLE gOvsDeviceHandle;
287 PDEVICE_OBJECT gOvsDeviceObject;
288
289 _Dispatch_type_(IRP_MJ_CREATE)
290 _Dispatch_type_(IRP_MJ_CLOSE)
291 DRIVER_DISPATCH OvsOpenCloseDevice;
292
293 _Dispatch_type_(IRP_MJ_CLEANUP)
294 DRIVER_DISPATCH OvsCleanupDevice;
295
296 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
297 DRIVER_DISPATCH OvsDeviceControl;
298
299 #ifdef ALLOC_PRAGMA
300 #pragma alloc_text(INIT, OvsCreateDeviceObject)
301 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
302 #pragma alloc_text(PAGE, OvsCleanupDevice)
303 #pragma alloc_text(PAGE, OvsDeviceControl)
304 #endif // ALLOC_PRAGMA
305
306 /*
307  * We might hit this limit easily since userspace opens a netlink descriptor for
308  * each thread, and at least one descriptor per vport. Revisit this later.
309  */
310 #define OVS_MAX_OPEN_INSTANCES 512
311 #define OVS_SYSTEM_DP_NAME     "ovs-system"
312
313 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
314 UINT32 ovsNumberOfOpenInstances;
315 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
316
317 NDIS_SPIN_LOCK ovsCtrlLockObj;
318 PNDIS_SPIN_LOCK gOvsCtrlLock;
319
320
321 VOID
322 OvsInit()
323 {
324     gOvsCtrlLock = &ovsCtrlLockObj;
325     NdisAllocateSpinLock(gOvsCtrlLock);
326     OvsInitEventQueue();
327     OvsUserInit();
328 }
329
330 VOID
331 OvsCleanup()
332 {
333     OvsCleanupEventQueue();
334     if (gOvsCtrlLock) {
335         NdisFreeSpinLock(gOvsCtrlLock);
336         gOvsCtrlLock = NULL;
337     }
338     OvsUserCleanup();
339 }
340
341 VOID
342 OvsAcquireCtrlLock()
343 {
344     NdisAcquireSpinLock(gOvsCtrlLock);
345 }
346
347 VOID
348 OvsReleaseCtrlLock()
349 {
350     NdisReleaseSpinLock(gOvsCtrlLock);
351 }
352
353
354 /*
355  * --------------------------------------------------------------------------
356  * Creates the communication device between user and kernel, and also
357  * initializes the data associated data structures.
358  * --------------------------------------------------------------------------
359  */
360 NDIS_STATUS
361 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
362 {
363     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
364     UNICODE_STRING deviceName;
365     UNICODE_STRING symbolicDeviceName;
366     PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
367     NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
368     OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
369
370     RtlZeroMemory(dispatchTable,
371                   (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
372     dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
373     dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
374     dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
375     dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
376
377     NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
378     NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
379
380     RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
381
382     OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
383                            NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
384                            NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
385                            sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
386
387     deviceAttributes.DeviceName = &deviceName;
388     deviceAttributes.SymbolicName = &symbolicDeviceName;
389     deviceAttributes.MajorFunctions = dispatchTable;
390     deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
391
392     status = NdisRegisterDeviceEx(ovsExtDriverHandle,
393                                   &deviceAttributes,
394                                   &gOvsDeviceObject,
395                                   &gOvsDeviceHandle);
396     if (status != NDIS_STATUS_SUCCESS) {
397         POVS_DEVICE_EXTENSION ovsExt =
398             (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
399         ASSERT(gOvsDeviceObject != NULL);
400         ASSERT(gOvsDeviceHandle != NULL);
401
402         if (ovsExt) {
403             ovsExt->numberOpenInstance = 0;
404         }
405     } else {
406         /* Initialize the associated data structures. */
407         OvsInit();
408     }
409     OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
410     return status;
411 }
412
413
414 VOID
415 OvsDeleteDeviceObject()
416 {
417     if (gOvsDeviceHandle) {
418 #ifdef DBG
419         POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
420                     NdisGetDeviceReservedExtension(gOvsDeviceObject);
421         if (ovsExt) {
422             ASSERT(ovsExt->numberOpenInstance == 0);
423         }
424 #endif
425
426         ASSERT(gOvsDeviceObject);
427         NdisDeregisterDeviceEx(gOvsDeviceHandle);
428         gOvsDeviceHandle = NULL;
429         gOvsDeviceObject = NULL;
430     }
431     OvsCleanup();
432 }
433
434 POVS_OPEN_INSTANCE
435 OvsGetOpenInstance(PFILE_OBJECT fileObject,
436                    UINT32 dpNo)
437 {
438     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
439     ASSERT(instance);
440     ASSERT(instance->fileObject == fileObject);
441     if (gOvsSwitchContext == NULL ||
442         gOvsSwitchContext->dpNo != dpNo) {
443         return NULL;
444     }
445     return instance;
446 }
447
448
449 POVS_OPEN_INSTANCE
450 OvsFindOpenInstance(PFILE_OBJECT fileObject)
451 {
452     UINT32 i, j;
453     for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
454                        j < ovsNumberOfOpenInstances; i++) {
455         if (ovsOpenInstanceArray[i]) {
456             if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
457                 return ovsOpenInstanceArray[i];
458             }
459             j++;
460         }
461     }
462     return NULL;
463 }
464
465 NTSTATUS
466 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
467                    PFILE_OBJECT fileObject)
468 {
469     POVS_OPEN_INSTANCE instance =
470         (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
471     UINT32 i;
472
473     if (instance == NULL) {
474         return STATUS_NO_MEMORY;
475     }
476     OvsAcquireCtrlLock();
477     ASSERT(OvsFindOpenInstance(fileObject) == NULL);
478
479     if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
480         OvsReleaseCtrlLock();
481         OvsFreeMemory(instance);
482         return STATUS_INSUFFICIENT_RESOURCES;
483     }
484     RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
485
486     for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
487         if (ovsOpenInstanceArray[i] == NULL) {
488             ovsOpenInstanceArray[i] = instance;
489             ovsNumberOfOpenInstances++;
490             instance->cookie = i;
491             break;
492         }
493     }
494     ASSERT(i < OVS_MAX_OPEN_INSTANCES);
495     instance->fileObject = fileObject;
496     ASSERT(fileObject->FsContext == NULL);
497     instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
498     if (instance->pid == 0) {
499         /* XXX: check for rollover. */
500     }
501     fileObject->FsContext = instance;
502     OvsReleaseCtrlLock();
503     return STATUS_SUCCESS;
504 }
505
506 static VOID
507 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
508 {
509     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
510     ASSERT(instance);
511     ASSERT(fileObject == instance->fileObject);
512     OvsCleanupEvent(instance);
513     OvsCleanupPacketQueue(instance);
514 }
515
516 VOID
517 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
518 {
519     POVS_OPEN_INSTANCE instance;
520     ASSERT(fileObject->FsContext);
521     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
522     ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
523
524     OvsAcquireCtrlLock();
525     fileObject->FsContext = NULL;
526     ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
527     ovsOpenInstanceArray[instance->cookie] = NULL;
528     ovsNumberOfOpenInstances--;
529     OvsReleaseCtrlLock();
530     ASSERT(instance->eventQueue == NULL);
531     ASSERT (instance->packetQueue == NULL);
532     OvsFreeMemory(instance);
533 }
534
535 NTSTATUS
536 OvsCompleteIrpRequest(PIRP irp,
537                       ULONG_PTR infoPtr,
538                       NTSTATUS status)
539 {
540     irp->IoStatus.Information = infoPtr;
541     irp->IoStatus.Status = status;
542     IoCompleteRequest(irp, IO_NO_INCREMENT);
543     return status;
544 }
545
546
547 NTSTATUS
548 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
549                    PIRP irp)
550 {
551     PIO_STACK_LOCATION irpSp;
552     NTSTATUS status = STATUS_SUCCESS;
553     PFILE_OBJECT fileObject;
554     POVS_DEVICE_EXTENSION ovsExt =
555         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
556
557     ASSERT(deviceObject == gOvsDeviceObject);
558     ASSERT(ovsExt != NULL);
559
560     irpSp = IoGetCurrentIrpStackLocation(irp);
561     fileObject = irpSp->FileObject;
562     OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
563                   deviceObject, fileObject,
564                   ovsExt->numberOpenInstance);
565
566     switch (irpSp->MajorFunction) {
567     case IRP_MJ_CREATE:
568         status = OvsAddOpenInstance(ovsExt, fileObject);
569         if (STATUS_SUCCESS == status) {
570             InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
571         }
572         break;
573     case IRP_MJ_CLOSE:
574         ASSERT(ovsExt->numberOpenInstance > 0);
575         OvsRemoveOpenInstance(fileObject);
576         InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
577         break;
578     default:
579         ASSERT(0);
580     }
581     return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
582 }
583
584 _Use_decl_annotations_
585 NTSTATUS
586 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
587                  PIRP irp)
588 {
589
590     PIO_STACK_LOCATION irpSp;
591     PFILE_OBJECT fileObject;
592
593     NTSTATUS status = STATUS_SUCCESS;
594 #ifdef DBG
595     POVS_DEVICE_EXTENSION ovsExt =
596         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
597     if (ovsExt) {
598         ASSERT(ovsExt->numberOpenInstance > 0);
599     }
600 #else
601     UNREFERENCED_PARAMETER(deviceObject);
602 #endif
603     ASSERT(deviceObject == gOvsDeviceObject);
604     irpSp = IoGetCurrentIrpStackLocation(irp);
605     fileObject = irpSp->FileObject;
606
607     ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
608
609     OvsCleanupOpenInstance(fileObject);
610
611     return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
612 }
613
614
615 /*
616  * --------------------------------------------------------------------------
617  * IOCTL function handler for the device.
618  * --------------------------------------------------------------------------
619  */
620 NTSTATUS
621 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
622                  PIRP irp)
623 {
624
625     PIO_STACK_LOCATION irpSp;
626     NTSTATUS status = STATUS_SUCCESS;
627     PFILE_OBJECT fileObject;
628     PVOID inputBuffer = NULL;
629     PVOID outputBuffer = NULL;
630     UINT32 inputBufferLen, outputBufferLen;
631     UINT32 code, replyLen = 0;
632     POVS_OPEN_INSTANCE instance;
633     UINT32 devOp;
634     OVS_MESSAGE ovsMsgReadOp;
635     POVS_MESSAGE ovsMsg;
636     NETLINK_FAMILY *nlFamilyOps;
637     OVS_USER_PARAMS_CONTEXT usrParamsCtx;
638
639 #ifdef DBG
640     POVS_DEVICE_EXTENSION ovsExt =
641         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
642     ASSERT(deviceObject == gOvsDeviceObject);
643     ASSERT(ovsExt);
644     ASSERT(ovsExt->numberOpenInstance > 0);
645 #else
646     UNREFERENCED_PARAMETER(deviceObject);
647 #endif
648
649     irpSp = IoGetCurrentIrpStackLocation(irp);
650
651     ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
652     ASSERT(irpSp->FileObject != NULL);
653
654     fileObject = irpSp->FileObject;
655     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
656     code = irpSp->Parameters.DeviceIoControl.IoControlCode;
657     inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
658     outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
659     inputBuffer = irp->AssociatedIrp.SystemBuffer;
660
661     /* Concurrent netlink operations are not supported. */
662     if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
663         status = STATUS_RESOURCE_IN_USE;
664         goto done;
665     }
666
667     /*
668      * Validate the input/output buffer arguments depending on the type of the
669      * operation.
670      */
671     switch (code) {
672     case OVS_IOCTL_TRANSACT:
673         /* Input buffer is mandatory, output buffer is optional. */
674         if (outputBufferLen != 0) {
675             status = MapIrpOutputBuffer(irp, outputBufferLen,
676                                         sizeof *ovsMsg, &outputBuffer);
677             if (status != STATUS_SUCCESS) {
678                 goto done;
679             }
680             ASSERT(outputBuffer);
681         }
682
683         if (inputBufferLen < sizeof (*ovsMsg)) {
684             status = STATUS_NDIS_INVALID_LENGTH;
685             goto done;
686         }
687
688         ovsMsg = inputBuffer;
689         devOp = OVS_TRANSACTION_DEV_OP;
690         break;
691
692     case OVS_IOCTL_READ_EVENT:
693         /* This IOCTL is used to read events */
694         if (outputBufferLen != 0) {
695             status = MapIrpOutputBuffer(irp, outputBufferLen,
696                                         sizeof *ovsMsg, &outputBuffer);
697             if (status != STATUS_SUCCESS) {
698                 goto done;
699             }
700             ASSERT(outputBuffer);
701         } else {
702             status = STATUS_NDIS_INVALID_LENGTH;
703             goto done;
704         }
705         inputBuffer = NULL;
706         inputBufferLen = 0;
707
708         ovsMsg = &ovsMsgReadOp;
709         ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
710         /* An "artificial" command so we can use NL family function table*/
711         ovsMsg->genlMsg.cmd = OVS_CTRL_CMD_EVENT_NOTIFY;
712         devOp = OVS_READ_DEV_OP;
713         break;
714
715     case OVS_IOCTL_READ:
716         /* Output buffer is mandatory. */
717         if (outputBufferLen != 0) {
718             status = MapIrpOutputBuffer(irp, outputBufferLen,
719                                         sizeof *ovsMsg, &outputBuffer);
720             if (status != STATUS_SUCCESS) {
721                 goto done;
722             }
723             ASSERT(outputBuffer);
724         } else {
725             status = STATUS_NDIS_INVALID_LENGTH;
726             goto done;
727         }
728
729         /*
730          * Operate in the mode that read ioctl is similar to ReadFile(). This
731          * might change as the userspace code gets implemented.
732          */
733         inputBuffer = NULL;
734         inputBufferLen = 0;
735
736         /*
737          * For implementing read (ioctl or otherwise), we need to store some
738          * state in the instance to indicate the command that started the dump
739          * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
740          * that 'ovsMsgReadOp' is needed only in this function to call into the
741          * appropraite handler. The handler itself can access the state in the
742          * instance.
743          *
744          * In the absence of a dump start, return 0 bytes.
745          */
746         if (instance->dumpState.ovsMsg == NULL) {
747             replyLen = 0;
748             status = STATUS_SUCCESS;
749             goto done;
750         }
751         RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
752                       sizeof (ovsMsgReadOp));
753
754         /* Create an NL message for consumption. */
755         ovsMsg = &ovsMsgReadOp;
756         devOp = OVS_READ_DEV_OP;
757
758         break;
759
760     case OVS_IOCTL_WRITE:
761         /* Input buffer is mandatory. */
762         if (inputBufferLen < sizeof (*ovsMsg)) {
763             status = STATUS_NDIS_INVALID_LENGTH;
764             goto done;
765         }
766
767         ovsMsg = inputBuffer;
768         devOp = OVS_WRITE_DEV_OP;
769         break;
770
771     default:
772         status = STATUS_INVALID_DEVICE_REQUEST;
773         goto done;
774     }
775
776     ASSERT(ovsMsg);
777     switch (ovsMsg->nlMsg.nlmsgType) {
778     case OVS_WIN_NL_CTRL_FAMILY_ID:
779         nlFamilyOps = &nlControlFamilyOps;
780         break;
781     case OVS_WIN_NL_DATAPATH_FAMILY_ID:
782         nlFamilyOps = &nlDatapathFamilyOps;
783         break;
784     case OVS_WIN_NL_FLOW_FAMILY_ID:
785          nlFamilyOps = &nlFLowFamilyOps;
786          break;
787     case OVS_WIN_NL_PACKET_FAMILY_ID:
788         status = STATUS_NOT_IMPLEMENTED;
789         goto done;
790     case OVS_WIN_NL_VPORT_FAMILY_ID:
791         nlFamilyOps = &nlVportFamilyOps;
792         break;
793     case OVS_WIN_NL_NETDEV_FAMILY_ID:
794         nlFamilyOps = &nlNetdevFamilyOps;
795         break;
796     default:
797         status = STATUS_INVALID_PARAMETER;
798         goto done;
799     }
800
801     /*
802      * For read operation, the netlink command has already been validated
803      * previously.
804      */
805     if (devOp != OVS_READ_DEV_OP) {
806         status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
807         if (status != STATUS_SUCCESS) {
808             goto done;
809         }
810     }
811
812     InitUserParamsCtx(irp, instance, devOp, ovsMsg,
813                       inputBuffer, inputBufferLen,
814                       outputBuffer, outputBufferLen,
815                       &usrParamsCtx);
816
817     status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
818
819 done:
820     KeMemoryBarrier();
821     instance->inUse = 0;
822     return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
823 }
824
825
826 /*
827  * --------------------------------------------------------------------------
828  * Function to validate a netlink command. Only certain combinations of
829  * (device operation, netlink family, command) are valid.
830  * --------------------------------------------------------------------------
831  */
832 static NTSTATUS
833 ValidateNetlinkCmd(UINT32 devOp,
834                    POVS_OPEN_INSTANCE instance,
835                    POVS_MESSAGE ovsMsg,
836                    NETLINK_FAMILY *nlFamilyOps)
837 {
838     NTSTATUS status = STATUS_INVALID_PARAMETER;
839     UINT16 i;
840
841     for (i = 0; i < nlFamilyOps->opsCount; i++) {
842         if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
843             /* Validate if the command is valid for the device operation. */
844             if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
845                 status = STATUS_INVALID_PARAMETER;
846                 goto done;
847             }
848
849             /* Validate the version. */
850             if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
851                 status = STATUS_INVALID_PARAMETER;
852                 goto done;
853             }
854
855             /* Validate the DP for commands that require a DP. */
856             if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
857                 OvsAcquireCtrlLock();
858                 if (ovsMsg->ovsHdr.dp_ifindex !=
859                     (INT)gOvsSwitchContext->dpNo) {
860                     status = STATUS_INVALID_PARAMETER;
861                     OvsReleaseCtrlLock();
862                     goto done;
863                 }
864                 OvsReleaseCtrlLock();
865             }
866
867             /* Validate the PID. */
868             if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
869                 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
870                     status = STATUS_INVALID_PARAMETER;
871                     goto done;
872                 }
873             }
874
875             status = STATUS_SUCCESS;
876             break;
877         }
878     }
879
880 done:
881     return status;
882 }
883
884 /*
885  * --------------------------------------------------------------------------
886  * Function to invoke the netlink command handler.
887  * --------------------------------------------------------------------------
888  */
889 static NTSTATUS
890 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
891                         NETLINK_FAMILY *nlFamilyOps,
892                         UINT32 *replyLen)
893 {
894     NTSTATUS status = STATUS_INVALID_PARAMETER;
895     UINT16 i;
896
897     for (i = 0; i < nlFamilyOps->opsCount; i++) {
898         if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
899             NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
900             ASSERT(handler);
901             if (handler) {
902                 status = handler(usrParamsCtx, replyLen);
903             }
904             break;
905         }
906     }
907
908     return status;
909 }
910
911 /*
912  * --------------------------------------------------------------------------
913  *  Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
914  *
915  *  Each handle on the device is assigned a unique PID when the handle is
916  *  created. On platforms that support netlink natively, the PID is available
917  *  to userspace when the netlink socket is created. However, without native
918  *  netlink support on Windows, OVS datapath generates the PID and lets the
919  *  userspace query it.
920  *
921  *  This function implements the query.
922  * --------------------------------------------------------------------------
923  */
924 static NTSTATUS
925 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
926                     UINT32 *replyLen)
927 {
928     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
929     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
930
931     if (usrParamsCtx->outputLength >= sizeof *msgOut) {
932         POVS_OPEN_INSTANCE instance =
933             (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
934
935         RtlZeroMemory(msgOut, sizeof *msgOut);
936         msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
937         msgOut->nlMsg.nlmsgPid = instance->pid;
938         *replyLen = sizeof *msgOut;
939         /* XXX: We might need to return the DP index as well. */
940     } else {
941         return STATUS_NDIS_INVALID_LENGTH;
942     }
943
944     return STATUS_SUCCESS;
945 }
946
947 /*
948  * --------------------------------------------------------------------------
949  * Utility function to fill up information about the datapath in a reply to
950  * userspace.
951  * Assumes that 'gOvsCtrlLock' lock is acquired.
952  * --------------------------------------------------------------------------
953  */
954 static NTSTATUS
955 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
956               POVS_MESSAGE msgIn,
957               PNL_BUFFER nlBuf)
958 {
959     BOOLEAN writeOk;
960     OVS_MESSAGE msgOutTmp;
961     OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
962     PNL_MSG_HDR nlMsg;
963
964     ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && NlBufRemLen(nlBuf) >= sizeof *msgIn);
965
966     msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
967     msgOutTmp.nlMsg.nlmsgFlags = 0;  /* XXX: ? */
968     msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
969     msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
970
971     msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
972     msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
973     msgOutTmp.genlMsg.reserved = 0;
974
975     msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
976
977     writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
978     if (writeOk) {
979         writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
980                                      OVS_SYSTEM_DP_NAME);
981     }
982     if (writeOk) {
983         OVS_DP_STATS dpStats;
984
985         dpStats.n_hit = datapath->hits;
986         dpStats.n_missed = datapath->misses;
987         dpStats.n_lost = datapath->lost;
988         dpStats.n_flows = datapath->nFlows;
989         writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
990                                      (PCHAR)&dpStats, sizeof dpStats);
991     }
992     nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
993     nlMsg->nlmsgLen = NlBufSize(nlBuf);
994
995     return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
996 }
997
998 /*
999  * --------------------------------------------------------------------------
1000  * Handler for queueing an IRP used for event notification. The IRP is
1001  * completed when a port state changes. STATUS_PENDING is returned on
1002  * success. User mode keep a pending IRP at all times.
1003  * --------------------------------------------------------------------------
1004  */
1005 static NTSTATUS
1006 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1007                        UINT32 *replyLen)
1008 {
1009     NDIS_STATUS status;
1010
1011     UNREFERENCED_PARAMETER(replyLen);
1012
1013     POVS_OPEN_INSTANCE instance =
1014         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1015     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1016     OVS_EVENT_POLL poll;
1017
1018     poll.dpNo = msgIn->ovsHdr.dp_ifindex;
1019     status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
1020                                &poll, sizeof poll);
1021     return status;
1022 }
1023
1024 /*
1025  * --------------------------------------------------------------------------
1026  *  Handler for the subscription for the event queue
1027  * --------------------------------------------------------------------------
1028  */
1029 static NTSTATUS
1030 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1031                             UINT32 *replyLen)
1032 {
1033     NDIS_STATUS status;
1034     OVS_EVENT_SUBSCRIBE request;
1035     BOOLEAN rc;
1036     UINT8 join;
1037     PNL_ATTR attrs[2];
1038     const NL_POLICY policy[] =  {
1039         [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
1040         [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
1041         };
1042
1043     UNREFERENCED_PARAMETER(replyLen);
1044
1045     POVS_OPEN_INSTANCE instance =
1046         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1047     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1048
1049     rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1050          NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, 2);
1051     if (!rc) {
1052         status = STATUS_INVALID_PARAMETER;
1053         goto done;
1054     }
1055
1056     /* XXX Ignore the MC group for now */
1057     join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1058     request.dpNo = msgIn->ovsHdr.dp_ifindex;
1059     request.subscribe = join;
1060     request.mask = OVS_EVENT_MASK_ALL;
1061
1062     status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1063                                     sizeof request);
1064 done:
1065     return status;
1066 }
1067
1068 /*
1069  * --------------------------------------------------------------------------
1070  *  Command Handler for 'OVS_DP_CMD_NEW'.
1071  * --------------------------------------------------------------------------
1072  */
1073 static NTSTATUS
1074 OvsNewDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1075                    UINT32 *replyLen)
1076 {
1077     return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1078 }
1079
1080 /*
1081  * --------------------------------------------------------------------------
1082  *  Command Handler for 'OVS_DP_CMD_GET'.
1083  *
1084  *  The function handles both the dump based as well as the transaction based
1085  *  'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1086  *  call to setup dump state, as well as subsequent calls to continue dumping
1087  *  data.
1088  * --------------------------------------------------------------------------
1089  */
1090 static NTSTATUS
1091 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1092                    UINT32 *replyLen)
1093 {
1094     if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1095         return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1096     } else {
1097         return HandleGetDpDump(usrParamsCtx, replyLen);
1098     }
1099 }
1100
1101 /*
1102  * --------------------------------------------------------------------------
1103  *  Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1104  * --------------------------------------------------------------------------
1105  */
1106 static NTSTATUS
1107 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1108                        UINT32 *replyLen)
1109 {
1110     return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1111 }
1112
1113
1114 /*
1115  * --------------------------------------------------------------------------
1116  *  Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1117  * --------------------------------------------------------------------------
1118  */
1119 static NTSTATUS
1120 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1121                 UINT32 *replyLen)
1122 {
1123     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1124     POVS_OPEN_INSTANCE instance =
1125         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1126
1127     if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1128         *replyLen = 0;
1129         OvsSetupDumpStart(usrParamsCtx);
1130     } else {
1131         NL_BUFFER nlBuf;
1132         NTSTATUS status;
1133         POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1134
1135         ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1136
1137         if (instance->dumpState.ovsMsg == NULL) {
1138             ASSERT(FALSE);
1139             return STATUS_INVALID_DEVICE_STATE;
1140         }
1141
1142         /* Dump state must have been deleted after previous dump operation. */
1143         ASSERT(instance->dumpState.index[0] == 0);
1144         /* Output buffer has been validated while validating read dev op. */
1145         ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1146
1147         NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1148                   usrParamsCtx->outputLength);
1149
1150         OvsAcquireCtrlLock();
1151         if (!gOvsSwitchContext) {
1152             /* Treat this as a dump done. */
1153             OvsReleaseCtrlLock();
1154             *replyLen = 0;
1155             FreeUserDumpState(instance);
1156             return STATUS_SUCCESS;
1157         }
1158         status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1159         OvsReleaseCtrlLock();
1160
1161         if (status != STATUS_SUCCESS) {
1162             *replyLen = 0;
1163             FreeUserDumpState(instance);
1164             return status;
1165         }
1166
1167         /* Increment the dump index. */
1168         instance->dumpState.index[0] = 1;
1169         *replyLen = msgOut->nlMsg.nlmsgLen;
1170
1171         /* Free up the dump state, since there's no more data to continue. */
1172         FreeUserDumpState(instance);
1173     }
1174
1175     return STATUS_SUCCESS;
1176 }
1177
1178
1179 /*
1180  * --------------------------------------------------------------------------
1181  *  Command Handler for 'OVS_DP_CMD_SET'.
1182  * --------------------------------------------------------------------------
1183  */
1184 static NTSTATUS
1185 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1186                    UINT32 *replyLen)
1187 {
1188     return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1189 }
1190
1191 /*
1192  * --------------------------------------------------------------------------
1193  *  Function for handling transaction based 'OVS_DP_CMD_NEW', 'OVS_DP_CMD_GET'
1194  *  and 'OVS_DP_CMD_SET' commands.
1195  *
1196  * 'OVS_DP_CMD_NEW' is implemented to keep userspace code happy. Creation of a
1197  * new datapath is not supported currently.
1198  * --------------------------------------------------------------------------
1199  */
1200 static NTSTATUS
1201 HandleDpTransactionCommon(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1202                           UINT32 *replyLen)
1203 {
1204     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1205     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1206     NTSTATUS status = STATUS_SUCCESS;
1207     NL_BUFFER nlBuf;
1208     NL_ERROR nlError = NL_ERROR_SUCCESS;
1209     static const NL_POLICY ovsDatapathSetPolicy[] = {
1210         [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1211         [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1212         [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1213     };
1214     PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1215
1216     /* input buffer has been validated while validating write dev op. */
1217     ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1218
1219     /* Parse any attributes in the request. */
1220     if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET ||
1221         usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1222         if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1223                         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1224                         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1225                         ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1226             return STATUS_INVALID_PARAMETER;
1227         }
1228
1229         /*
1230         * XXX: Not clear at this stage if there's any role for the
1231         * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1232         * from userspace.
1233         */
1234
1235     } else {
1236         RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1237     }
1238
1239     /* Output buffer is optional for OVS_TRANSACTION_DEV_OP. */
1240     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1241         return STATUS_NDIS_INVALID_LENGTH;
1242     }
1243     NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1244
1245     OvsAcquireCtrlLock();
1246     if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1247         if (!gOvsSwitchContext &&
1248             !OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1249                               OVS_SYSTEM_DP_NAME)) {
1250             OvsReleaseCtrlLock();
1251
1252             /* Creation of new datapaths is not supported. */
1253             if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1254                 nlError = NL_ERROR_NOTSUPP;
1255                 goto cleanup;
1256             }
1257
1258             nlError = NL_ERROR_NODEV;
1259             goto cleanup;
1260         }
1261     } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1262         OvsReleaseCtrlLock();
1263         nlError = NL_ERROR_NODEV;
1264         goto cleanup;
1265     }
1266
1267     if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1268         OvsReleaseCtrlLock();
1269         nlError = NL_ERROR_EXIST;
1270         goto cleanup;
1271     }
1272
1273     status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1274     OvsReleaseCtrlLock();
1275
1276     *replyLen = NlBufSize(&nlBuf);
1277
1278 cleanup:
1279     if (nlError != NL_ERROR_SUCCESS) {
1280         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1281             usrParamsCtx->outputBuffer;
1282
1283         BuildErrorMsg(msgIn, msgError, nlError);
1284         *replyLen = msgError->nlMsg.nlmsgLen;
1285     }
1286
1287     return STATUS_SUCCESS;
1288 }
1289
1290
1291 NTSTATUS
1292 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1293 {
1294     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1295     POVS_OPEN_INSTANCE instance =
1296         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1297
1298     /* input buffer has been validated while validating write dev op. */
1299     ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1300
1301     /* A write operation that does not indicate dump start is invalid. */
1302     if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1303         return STATUS_INVALID_PARAMETER;
1304     }
1305     /* XXX: Handle other NLM_F_* flags in the future. */
1306
1307     /*
1308      * This operation should be setting up the dump state. If there's any
1309      * previous state, clear it up so as to set it up afresh.
1310      */
1311     if (instance->dumpState.ovsMsg != NULL) {
1312         FreeUserDumpState(instance);
1313     }
1314
1315     return InitUserDumpState(instance, msgIn);
1316 }
1317
1318 static VOID
1319 BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
1320             UINT32 length, UINT16 flags)
1321 {
1322     msgOut->nlMsg.nlmsgType = type;
1323     msgOut->nlMsg.nlmsgFlags = flags;
1324     msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1325     msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1326     msgOut->nlMsg.nlmsgLen = length;
1327
1328     msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
1329     msgOut->genlMsg.version = msgIn->genlMsg.version;
1330     msgOut->genlMsg.reserved = 0;
1331 }
1332
1333 /*
1334  * XXX: should move out these functions to a Netlink.c or to a OvsMessage.c
1335  * or even make them inlined functions in Datapath.h. Can be done after the
1336  * first sprint once we have more code to refactor.
1337  */
1338 VOID
1339 BuildReplyMsgFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
1340 {
1341     BuildMsgOut(msgIn, msgOut, msgIn->nlMsg.nlmsgType, sizeof(OVS_MESSAGE),
1342                 flags);
1343 }
1344
1345 VOID
1346 BuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode)
1347 {
1348     BuildMsgOut(msgIn, (POVS_MESSAGE)msgOut, NLMSG_ERROR,
1349                 sizeof(OVS_MESSAGE_ERROR), 0);
1350
1351     msgOut->errorMsg.error = errorCode;
1352     msgOut->errorMsg.nlMsg = msgIn->nlMsg;
1353 }
1354
1355 static NTSTATUS
1356 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1357                       POVS_MESSAGE msgIn,
1358                       PVOID outBuffer,
1359                       UINT32 outBufLen,
1360                       int dpIfIndex)
1361 {
1362     NL_BUFFER nlBuffer;
1363     OVS_VPORT_FULL_STATS vportStats;
1364     BOOLEAN ok;
1365     OVS_MESSAGE msgOut;
1366     PNL_MSG_HDR nlMsg;
1367
1368     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1369
1370     BuildReplyMsgFromMsgIn(msgIn, &msgOut, NLM_F_MULTI);
1371     msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1372
1373     ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1374     if (!ok) {
1375         return STATUS_INSUFFICIENT_RESOURCES;
1376     }
1377
1378     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1379     if (!ok) {
1380         return STATUS_INSUFFICIENT_RESOURCES;
1381     }
1382
1383     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1384     if (!ok) {
1385         return STATUS_INSUFFICIENT_RESOURCES;
1386     }
1387
1388     ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1389     if (!ok) {
1390         return STATUS_INSUFFICIENT_RESOURCES;
1391     }
1392
1393     /*
1394      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1395      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1396      * it means we have an array of pids, instead of a single pid.
1397      * ATM we assume we have one pid only.
1398     */
1399
1400     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1401                          vport->upcallPid);
1402     if (!ok) {
1403         return STATUS_INSUFFICIENT_RESOURCES;
1404     }
1405
1406     /*stats*/
1407     vportStats.rxPackets = vport->stats.rxPackets;
1408     vportStats.rxBytes = vport->stats.rxBytes;
1409     vportStats.txPackets = vport->stats.txPackets;
1410     vportStats.txBytes = vport->stats.txBytes;
1411     vportStats.rxErrors = vport->errStats.rxErrors;
1412     vportStats.txErrors = vport->errStats.txErrors;
1413     vportStats.rxDropped = vport->errStats.rxDropped;
1414     vportStats.txDropped = vport->errStats.txDropped;
1415
1416     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1417                             (PCHAR)&vportStats,
1418                             sizeof(OVS_VPORT_FULL_STATS));
1419     if (!ok) {
1420         return STATUS_INSUFFICIENT_RESOURCES;
1421     }
1422
1423     /*
1424      * XXX: when vxlan udp dest port becomes configurable, we will also need
1425      * to add vport options
1426     */
1427
1428     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1429     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1430
1431     return STATUS_SUCCESS;
1432 }
1433
1434 static NTSTATUS
1435 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1436                     UINT32 *replyLen)
1437 {
1438     POVS_MESSAGE msgIn;
1439     POVS_OPEN_INSTANCE instance =
1440         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1441     LOCK_STATE_EX lockState;
1442     UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1443
1444     /*
1445      * XXX: this function shares some code with other dump command(s).
1446      * In the future, we will need to refactor the dump functions
1447     */
1448
1449     ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1450
1451     if (instance->dumpState.ovsMsg == NULL) {
1452         ASSERT(FALSE);
1453         return STATUS_INVALID_DEVICE_STATE;
1454     }
1455
1456     /* Output buffer has been validated while validating read dev op. */
1457     ASSERT(usrParamsCtx->outputBuffer != NULL);
1458
1459     msgIn = instance->dumpState.ovsMsg;
1460
1461     OvsAcquireCtrlLock();
1462     if (!gOvsSwitchContext) {
1463         /* Treat this as a dump done. */
1464         OvsReleaseCtrlLock();
1465         *replyLen = 0;
1466         FreeUserDumpState(instance);
1467         return STATUS_SUCCESS;
1468     }
1469
1470     /*
1471      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1472      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1473      * it means we have an array of pids, instead of a single pid.
1474      * ATM we assume we have one pid only.
1475     */
1476     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1477     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1478                           NDIS_RWL_AT_DISPATCH_LEVEL);
1479
1480     if (gOvsSwitchContext->numVports > 0) {
1481         /* inBucket: the bucket, used for lookup */
1482         UINT32 inBucket = instance->dumpState.index[0];
1483         /* inIndex: index within the given bucket, used for lookup */
1484         UINT32 inIndex = instance->dumpState.index[1];
1485         /* the bucket to be used for the next dump operation */
1486         UINT32 outBucket = 0;
1487         /* the index within the outBucket to be used for the next dump */
1488         UINT32 outIndex = 0;
1489
1490         for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1491             PLIST_ENTRY head, link;
1492             head = &(gOvsSwitchContext->portIdHashArray[i]);
1493             POVS_VPORT_ENTRY vport = NULL;
1494
1495             outIndex = 0;
1496             LIST_FORALL(head, link) {
1497
1498                 /*
1499                  * if one or more dumps were previously done on this same bucket,
1500                  * inIndex will be > 0, so we'll need to reply with the
1501                  * inIndex + 1 vport from the bucket.
1502                 */
1503                 if (outIndex >= inIndex) {
1504                     vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1505
1506                     if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
1507                         OvsCreateMsgFromVport(vport, msgIn,
1508                                               usrParamsCtx->outputBuffer,
1509                                               usrParamsCtx->outputLength,
1510                                               gOvsSwitchContext->dpNo);
1511                         ++outIndex;
1512                         break;
1513                     } else {
1514                         vport = NULL;
1515                     }
1516                 }
1517
1518                 ++outIndex;
1519             }
1520
1521             if (vport) {
1522                 break;
1523             }
1524
1525             /*
1526              * if no vport was found above, check the next bucket, beginning
1527              * with the first (i.e. index 0) elem from within that bucket
1528             */
1529             inIndex = 0;
1530         }
1531
1532         outBucket = i;
1533
1534         /* XXX: what about NLMSG_DONE (as msg type)? */
1535         instance->dumpState.index[0] = outBucket;
1536         instance->dumpState.index[1] = outIndex;
1537     }
1538
1539     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1540
1541     OvsReleaseCtrlLock();
1542
1543     /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1544     if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1545         POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1546         *replyLen = msgOut->nlMsg.nlmsgLen;
1547     } else {
1548         /*
1549          * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1550          * it's dump done
1551          */
1552         *replyLen = 0;
1553         /* Free up the dump state, since there's no more data to continue. */
1554         FreeUserDumpState(instance);
1555     }
1556
1557     return STATUS_SUCCESS;
1558 }
1559
1560 static NTSTATUS
1561 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1562             UINT32 *replyLen)
1563 {
1564     NTSTATUS status = STATUS_SUCCESS;
1565     LOCK_STATE_EX lockState;
1566
1567     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1568     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1569     POVS_VPORT_ENTRY vport = NULL;
1570     NL_ERROR nlError = NL_ERROR_SUCCESS;
1571     PCHAR portName = NULL;
1572     UINT32 portNameLen = 0;
1573     UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
1574
1575     static const NL_POLICY ovsVportPolicy[] = {
1576         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1577         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1578                                   .minLen = 2,
1579                                   .maxLen = IFNAMSIZ,
1580                                   .optional = TRUE},
1581     };
1582     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1583
1584     /* input buffer has been validated while validating write dev op. */
1585     ASSERT(usrParamsCtx->inputBuffer != NULL);
1586
1587     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1588         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1589         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1590         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1591         return STATUS_INVALID_PARAMETER;
1592     }
1593
1594     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1595         return STATUS_INVALID_BUFFER_SIZE;
1596     }
1597
1598     OvsAcquireCtrlLock();
1599     if (!gOvsSwitchContext) {
1600         OvsReleaseCtrlLock();
1601         return STATUS_INVALID_PARAMETER;
1602     }
1603     OvsReleaseCtrlLock();
1604
1605     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1606     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1607         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1608         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1609
1610         /* the port name is expected to be null-terminated */
1611         ASSERT(portName[portNameLen - 1] == '\0');
1612
1613         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1614     } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1615         portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1616
1617         vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
1618     } else {
1619         nlError = NL_ERROR_INVAL;
1620         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1621         goto Cleanup;
1622     }
1623
1624     if (!vport) {
1625         nlError = NL_ERROR_NODEV;
1626         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1627         goto Cleanup;
1628     }
1629
1630     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1631                                    usrParamsCtx->outputLength,
1632                                    gOvsSwitchContext->dpNo);
1633     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1634
1635     *replyLen = msgOut->nlMsg.nlmsgLen;
1636
1637 Cleanup:
1638     if (nlError != NL_ERROR_SUCCESS) {
1639         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1640             usrParamsCtx->outputBuffer;
1641
1642         BuildErrorMsg(msgIn, msgError, nlError);
1643         *replyLen = msgError->nlMsg.nlmsgLen;
1644     }
1645
1646     return STATUS_SUCCESS;
1647 }
1648
1649 /*
1650  * --------------------------------------------------------------------------
1651  *  Handler for the get vport command. The function handles the initial call to
1652  *  setup the dump state, as well as subsequent calls to continue dumping data.
1653  * --------------------------------------------------------------------------
1654 */
1655 static NTSTATUS
1656 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1657                       UINT32 *replyLen)
1658 {
1659     *replyLen = 0;
1660
1661     switch (usrParamsCtx->devOp)
1662     {
1663     case OVS_WRITE_DEV_OP:
1664         return OvsSetupDumpStart(usrParamsCtx);
1665
1666     case OVS_READ_DEV_OP:
1667         return OvsGetVportDumpNext(usrParamsCtx, replyLen);
1668
1669     case OVS_TRANSACTION_DEV_OP:
1670         return OvsGetVport(usrParamsCtx, replyLen);
1671
1672     default:
1673         return STATUS_INVALID_DEVICE_REQUEST;
1674     }
1675
1676 }
1677
1678
1679
1680 static UINT32
1681 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
1682 {
1683     /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
1684     for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
1685         POVS_VPORT_ENTRY vport;
1686
1687         vport = OvsFindVportByPortNo(switchContext, i);
1688         if (!vport) {
1689             return i;
1690         }
1691     }
1692
1693     return OVS_DPPORT_NUMBER_INVALID;
1694 }
1695
1696 static NTSTATUS
1697 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1698                       UINT32 *replyLen)
1699 {
1700     NDIS_STATUS status = STATUS_SUCCESS;
1701     LOCK_STATE_EX lockState;
1702
1703     NL_ERROR nlError = NL_ERROR_SUCCESS;
1704     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1705     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1706     POVS_VPORT_ENTRY vport = NULL;
1707     PCHAR portName;
1708     ULONG portNameLen;
1709     UINT32 portType;
1710     UINT32 hash;
1711
1712     static const NL_POLICY ovsVportPolicy[] = {
1713         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1714         [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
1715         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
1716                                   .optional = FALSE},
1717         [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
1718                                         .optional = FALSE },
1719         [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
1720     };
1721
1722     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1723
1724     /* input buffer has been validated while validating write dev op. */
1725     ASSERT(usrParamsCtx->inputBuffer != NULL);
1726
1727     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1728         return STATUS_INVALID_BUFFER_SIZE;
1729     }
1730
1731     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1732         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1733         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1734         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1735         return STATUS_INVALID_PARAMETER;
1736     }
1737
1738     OvsAcquireCtrlLock();
1739     if (!gOvsSwitchContext) {
1740         OvsReleaseCtrlLock();
1741         return STATUS_INVALID_PARAMETER;
1742     }
1743     OvsReleaseCtrlLock();
1744
1745     portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1746     portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1747     portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
1748
1749     /* we are expecting null terminated strings to be passed */
1750     ASSERT(portName[portNameLen - 1] == '\0');
1751
1752     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
1753
1754     vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1755     if (vport) {
1756         nlError = NL_ERROR_EXIST;
1757         goto Cleanup;
1758     }
1759
1760     if (portType == OVS_VPORT_TYPE_INTERNAL) {
1761         vport = gOvsSwitchContext->internalVport;
1762     } else if (portType == OVS_VPORT_TYPE_NETDEV) {
1763         if (!strcmp(portName, "external")) {
1764             vport = gOvsSwitchContext->externalVport;
1765         } else {
1766             vport = OvsFindVportByHvName(gOvsSwitchContext, portName);
1767         }
1768     } else {
1769         /* XXX: change when other tunneling ports are added */
1770         ASSERT(portType == OVS_VPORT_TYPE_VXLAN);
1771
1772         if (gOvsSwitchContext->vxlanVport) {
1773             nlError = NL_ERROR_EXIST;
1774             goto Cleanup;
1775         }
1776
1777         vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
1778         if (vport == NULL) {
1779             nlError = NL_ERROR_NOMEM;
1780             goto Cleanup;
1781         }
1782
1783         vport->ovsState = OVS_STATE_PORT_CREATED;
1784
1785         /*
1786          * XXX: when we allow configuring the vxlan udp port, we should read
1787          * this from vport->options instead!
1788         */
1789         nlError = OvsInitVxlanTunnel(vport, VXLAN_UDP_PORT);
1790         if (nlError != NL_ERROR_SUCCESS) {
1791             goto Cleanup;
1792         }
1793     }
1794
1795     if (!vport) {
1796         nlError = NL_ERROR_INVAL;
1797         goto Cleanup;
1798     }
1799
1800     if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
1801         nlError = NL_ERROR_EXIST;
1802         goto Cleanup;
1803     }
1804
1805     /* Fill the data in vport */
1806     vport->ovsType = portType;
1807
1808     if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1809         /*
1810         * XXX: when we implement the limit for ovs port number to be
1811         * MAXUINT16, we'll need to check the port number received from the
1812         * userspace.
1813         */
1814         vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1815     } else {
1816         vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
1817         if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1818             nlError = NL_ERROR_NOMEM;
1819             goto Cleanup;
1820         }
1821     }
1822
1823     /* The ovs port name must be uninitialized. */
1824     ASSERT(vport->ovsName[0] == '\0');
1825     ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
1826
1827     RtlCopyMemory(vport->ovsName, portName, portNameLen);
1828
1829     /* if we don't have options, then vport->portOptions will be NULL */
1830     vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
1831
1832     /*
1833     * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1834     * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1835     * it means we have an array of pids, instead of a single pid.
1836     * ATM we assume we have one pid only.
1837     */
1838     vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
1839
1840     if (vport->ovsType == OVS_VPORT_TYPE_VXLAN) {
1841         gOvsSwitchContext->vxlanVport = vport;
1842     } else if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
1843         gOvsSwitchContext->internalVport = vport;
1844         gOvsSwitchContext->internalPortId = vport->portId;
1845     } else if (vport->ovsType == OVS_VPORT_TYPE_NETDEV &&
1846                vport->isExternal) {
1847         gOvsSwitchContext->externalVport = vport;
1848         gOvsSwitchContext->externalPortId = vport->portId;
1849     }
1850
1851     /*
1852      * insert the port into the hash array of ports: by port number and ovs
1853      * and ovs (datapath) port name.
1854      * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1855      * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1856     */
1857     hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
1858     InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
1859                    &vport->portNoLink);
1860
1861     hash = OvsJhashBytes(vport->ovsName, portNameLen, OVS_HASH_BASIS);
1862     InsertHeadList(&gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
1863                    &vport->ovsNameLink);
1864
1865     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1866                                    usrParamsCtx->outputLength,
1867                                    gOvsSwitchContext->dpNo);
1868
1869     *replyLen = msgOut->nlMsg.nlmsgLen;
1870
1871 Cleanup:
1872     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1873
1874     if (nlError != NL_ERROR_SUCCESS) {
1875         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1876             usrParamsCtx->outputBuffer;
1877
1878         if (vport && vport->ovsType == OVS_VPORT_TYPE_VXLAN) {
1879             OvsRemoveAndDeleteVport(gOvsSwitchContext, vport);
1880         }
1881
1882         BuildErrorMsg(msgIn, msgError, nlError);
1883         *replyLen = msgError->nlMsg.nlmsgLen;
1884     }
1885
1886     return STATUS_SUCCESS;
1887 }
1888
1889
1890 static NTSTATUS
1891 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1892                          UINT32 *replyLen)
1893 {
1894     NDIS_STATUS status = STATUS_SUCCESS;
1895     LOCK_STATE_EX lockState;
1896
1897     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1898     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1899     POVS_VPORT_ENTRY vport = NULL;
1900     NL_ERROR nlError = NL_ERROR_SUCCESS;
1901     PSTR portName = NULL;
1902     UINT32 portNameLen = 0;
1903
1904     static const NL_POLICY ovsVportPolicy[] = {
1905         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1906         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ, .optional = TRUE },
1907     };
1908     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1909
1910     ASSERT(usrParamsCtx->inputBuffer != NULL);
1911
1912     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1913         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1914         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1915         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1916         return STATUS_INVALID_PARAMETER;
1917     }
1918
1919     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1920         return STATUS_NDIS_INVALID_LENGTH;
1921     }
1922
1923     OvsAcquireCtrlLock();
1924     if (!gOvsSwitchContext) {
1925         OvsReleaseCtrlLock();
1926         return STATUS_INVALID_PARAMETER;
1927     }
1928     OvsReleaseCtrlLock();
1929
1930     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
1931     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1932         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1933         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1934
1935         /* the port name is expected to be null-terminated */
1936         ASSERT(portName[portNameLen - 1] == '\0');
1937
1938         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1939     }
1940     else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1941         vport = OvsFindVportByPortNo(gOvsSwitchContext,
1942             NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
1943     }
1944
1945     if (!vport) {
1946         nlError = NL_ERROR_NODEV;
1947         goto Cleanup;
1948     }
1949
1950     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1951                                    usrParamsCtx->outputLength,
1952                                    gOvsSwitchContext->dpNo);
1953
1954     if (vport->hvDeleted || OvsIsTunnelVportType(vport->ovsType)) {
1955         /*
1956          * The associated hyper-v switch port is not in created state, or,
1957          * there is no hyper-v switch port counterpart (for logical ports).
1958          * This means that this datapath port is not mapped to a living
1959          * hyper-v switc hport. We can destroy and remove the vport from the
1960          * list.
1961         */
1962         OvsRemoveAndDeleteVport(gOvsSwitchContext, vport);
1963     } else {
1964         /* The associated hyper-v switch port is in the created state, and the
1965          * datapath port is mapped to a living hyper-v switch port. We cannot
1966          * destroy the vport and cannot remove it from the list of vports.
1967          * Instead, we mark the datapath (ovs) part of the vport as
1968          * "not created", i.e. we set vport->portNo = OVS_PORT_NUMBER_INVALID.
1969         */
1970         vport->portNo = OVS_DPPORT_NUMBER_INVALID;
1971         vport->ovsName[0] = '\0';
1972     }
1973
1974     *replyLen = msgOut->nlMsg.nlmsgLen;
1975
1976 Cleanup:
1977     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1978
1979     if (nlError != NL_ERROR_SUCCESS) {
1980         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1981             usrParamsCtx->outputBuffer;
1982
1983         BuildErrorMsg(msgIn, msgError, nlError);
1984         *replyLen = msgError->nlMsg.nlmsgLen;
1985     }
1986
1987     return STATUS_SUCCESS;
1988 }
1989
1990
1991 /*
1992  * --------------------------------------------------------------------------
1993  *  Utility function to map the output buffer in an IRP. The buffer is assumed
1994  *  to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
1995  * --------------------------------------------------------------------------
1996  */
1997 static NTSTATUS
1998 MapIrpOutputBuffer(PIRP irp,
1999                    UINT32 bufferLength,
2000                    UINT32 requiredLength,
2001                    PVOID *buffer)
2002 {
2003     ASSERT(irp);
2004     ASSERT(buffer);
2005     ASSERT(bufferLength);
2006     ASSERT(requiredLength);
2007     if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
2008         return STATUS_INVALID_PARAMETER;
2009     }
2010
2011     if (bufferLength < requiredLength) {
2012         return STATUS_NDIS_INVALID_LENGTH;
2013     }
2014     if (irp->MdlAddress == NULL) {
2015         return STATUS_INVALID_PARAMETER;
2016     }
2017     *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
2018                                            NormalPagePriority);
2019     if (*buffer == NULL) {
2020         return STATUS_INSUFFICIENT_RESOURCES;
2021     }
2022
2023     return STATUS_SUCCESS;
2024 }
2025
2026 /*
2027  * --------------------------------------------------------------------------
2028  * Utility function to fill up information about the state of a port in a reply
2029  * to* userspace.
2030  * Assumes that 'gOvsCtrlLock' lock is acquired.
2031  * --------------------------------------------------------------------------
2032  */
2033 static NTSTATUS
2034 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2035                 POVS_EVENT_ENTRY eventEntry,
2036                 PNL_BUFFER nlBuf)
2037 {
2038     NTSTATUS status;
2039     BOOLEAN rc;
2040     OVS_MESSAGE msgOutTmp;
2041     PNL_MSG_HDR nlMsg;
2042     POVS_VPORT_ENTRY vport;
2043
2044     ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
2045
2046     msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
2047     msgOutTmp.nlMsg.nlmsgFlags = 0;  /* XXX: ? */
2048
2049     /* driver intiated messages should have zerp seq number*/
2050     msgOutTmp.nlMsg.nlmsgSeq = 0;
2051     msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
2052
2053     msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
2054     msgOutTmp.genlMsg.reserved = 0;
2055
2056     /* we don't have netdev yet, treat link up/down a adding/removing a port*/
2057     if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
2058         msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
2059     } else if (eventEntry->status &
2060              (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
2061         msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
2062     } else {
2063         ASSERT(FALSE);
2064         return STATUS_UNSUCCESSFUL;
2065     }
2066     msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
2067
2068     rc = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
2069     if (!rc) {
2070         status = STATUS_INVALID_BUFFER_SIZE;
2071         goto cleanup;
2072     }
2073
2074     vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
2075     if (!vport) {
2076         status = STATUS_DEVICE_DOES_NOT_EXIST;
2077         goto cleanup;
2078     }
2079
2080     rc = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) ||
2081          NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) ||
2082          NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
2083     if (!rc) {
2084         status = STATUS_INVALID_BUFFER_SIZE;
2085         goto cleanup;
2086     }
2087
2088     /* XXXX Should we add the port stats attributes?*/
2089     nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
2090     nlMsg->nlmsgLen = NlBufSize(nlBuf);
2091     status = STATUS_SUCCESS;
2092
2093 cleanup:
2094     return status;
2095 }
2096
2097
2098 /*
2099  * --------------------------------------------------------------------------
2100  * Handler for reading events from the driver event queue. This handler is
2101  * executed when user modes issues a socket receive on a socket assocaited
2102  * with the MC group for events.
2103  * XXX user mode should read multiple events in one system call
2104  * --------------------------------------------------------------------------
2105  */
2106 static NTSTATUS
2107 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2108                        UINT32 *replyLen)
2109 {
2110 #ifdef DBG
2111     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2112     POVS_OPEN_INSTANCE instance =
2113         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
2114 #endif
2115     NL_BUFFER nlBuf;
2116     NTSTATUS status;
2117     OVS_EVENT_ENTRY eventEntry;
2118
2119     ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
2120
2121     /* Should never read events with a dump socket */
2122     ASSERT(instance->dumpState.ovsMsg == NULL);
2123
2124     /* Must have an event queue */
2125     ASSERT(instance->eventQueue != NULL);
2126
2127     /* Output buffer has been validated while validating read dev op. */
2128     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2129
2130     NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
2131
2132     OvsAcquireCtrlLock();
2133     if (!gOvsSwitchContext) {
2134         status = STATUS_SUCCESS;
2135         goto cleanup;
2136     }
2137
2138     /* remove an event entry from the event queue */
2139     status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
2140     if (status != STATUS_SUCCESS) {
2141         goto cleanup;
2142     }
2143
2144     status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
2145     if (status == NDIS_STATUS_SUCCESS) {
2146         *replyLen = NlBufSize(&nlBuf);
2147     }
2148
2149 cleanup:
2150     OvsReleaseCtrlLock();
2151     return status;
2152 }
2153 #endif /* OVS_USE_NL_INTERFACE */