2 * Copyright (c) 2014 VMware, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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
41 #define OVS_DBG_MOD OVS_DBG_DATAPATH
44 #define NETLINK_FAMILY_NAME_LEN 48
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.
53 * Each command results in the invocation of a handler function to implement the
54 * request functionality.
56 * Expectedly, only certain combinations of (device operation, netlink family,
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.
65 * Handler for a given netlink command. Not all the parameters are used by all
68 typedef NTSTATUS(NetlinkCmdHandler)(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
71 typedef struct _NETLINK_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;
78 /* A netlink family is a group of commands. */
79 typedef struct _NETLINK_FAMILY {
85 NETLINK_CMD *cmds; /* Array of netlink commands and handlers. */
87 } NETLINK_FAMILY, *PNETLINK_FAMILY;
89 /* Handlers for the various netlink commands. */
90 static NetlinkCmdHandler OvsGetPidCmdHandler,
91 OvsPendEventCmdHandler,
92 OvsPendPacketCmdHandler,
93 OvsSubscribeEventCmdHandler,
94 OvsSubscribePacketCmdHandler,
95 OvsReadEventCmdHandler,
96 OvsReadPacketCmdHandler,
101 NetlinkCmdHandler OvsGetNetdevCmdHandler,
102 OvsGetVportCmdHandler,
103 OvsSetVportCmdHandler,
104 OvsNewVportCmdHandler,
105 OvsDeleteVportCmdHandler;
107 static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
109 static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
111 static NTSTATUS HandleDpTransactionCommon(
112 POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen);
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.
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,
128 { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ,
129 .handler = OvsPendEventCmdHandler,
130 .supportedDevOp = OVS_WRITE_DEV_OP,
131 .validateDpIndex = TRUE,
133 { .cmd = OVS_CTRL_CMD_WIN_PEND_PACKET_REQ,
134 .handler = OvsPendPacketCmdHandler,
135 .supportedDevOp = OVS_WRITE_DEV_OP,
136 .validateDpIndex = TRUE,
138 { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ,
139 .handler = OvsSubscribeEventCmdHandler,
140 .supportedDevOp = OVS_WRITE_DEV_OP,
141 .validateDpIndex = TRUE,
143 { .cmd = OVS_CTRL_CMD_PACKET_SUBSCRIBE_REQ,
144 .handler = OvsSubscribePacketCmdHandler,
145 .supportedDevOp = OVS_WRITE_DEV_OP,
146 .validateDpIndex = TRUE,
148 { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY,
149 .handler = OvsReadEventCmdHandler,
150 .supportedDevOp = OVS_READ_EVENT_DEV_OP,
151 .validateDpIndex = FALSE,
153 { .cmd = OVS_CTRL_CMD_READ_NOTIFY,
154 .handler = OvsReadPacketCmdHandler,
155 .supportedDevOp = OVS_READ_PACKET_DEV_OP,
156 .validateDpIndex = FALSE,
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)
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
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
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
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)
199 /* Netlink packet family. */
201 NETLINK_CMD nlPacketFamilyCmdOps[] = {
202 { .cmd = OVS_PACKET_CMD_EXECUTE,
203 .handler = OvsNlExecuteCmdHandler,
204 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
205 .validateDpIndex = TRUE
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)
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
226 { .cmd = OVS_VPORT_CMD_NEW,
227 .handler = OvsNewVportCmdHandler,
228 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
229 .validateDpIndex = TRUE
231 { .cmd = OVS_VPORT_CMD_SET,
232 .handler = OvsSetVportCmdHandler,
233 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
234 .validateDpIndex = TRUE
236 { .cmd = OVS_VPORT_CMD_DEL,
237 .handler = OvsDeleteVportCmdHandler,
238 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
239 .validateDpIndex = TRUE
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)
252 /* Netlink flow family. */
254 NETLINK_CMD nlFlowFamilyCmdOps[] = {
255 { .cmd = OVS_FLOW_CMD_NEW,
256 .handler = OvsFlowNlCmdHandler,
257 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
258 .validateDpIndex = TRUE
260 { .cmd = OVS_FLOW_CMD_SET,
261 .handler = OvsFlowNlCmdHandler,
262 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
263 .validateDpIndex = TRUE
265 { .cmd = OVS_FLOW_CMD_DEL,
266 .handler = OvsFlowNlCmdHandler,
267 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
268 .validateDpIndex = TRUE
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
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)
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
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)
305 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
307 UINT32 requiredLength,
309 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
310 POVS_OPEN_INSTANCE instance,
312 NETLINK_FAMILY *nlFamilyOps);
313 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
314 NETLINK_FAMILY *nlFamilyOps,
317 /* Handles to the device object for communication with userspace. */
318 NDIS_HANDLE gOvsDeviceHandle;
319 PDEVICE_OBJECT gOvsDeviceObject;
321 _Dispatch_type_(IRP_MJ_CREATE)
322 _Dispatch_type_(IRP_MJ_CLOSE)
323 DRIVER_DISPATCH OvsOpenCloseDevice;
325 _Dispatch_type_(IRP_MJ_CLEANUP)
326 DRIVER_DISPATCH OvsCleanupDevice;
328 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
329 DRIVER_DISPATCH OvsDeviceControl;
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
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.
342 #define OVS_MAX_OPEN_INSTANCES 512
343 #define OVS_SYSTEM_DP_NAME "ovs-system"
345 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
346 UINT32 ovsNumberOfOpenInstances;
347 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
349 NDIS_SPIN_LOCK ovsCtrlLockObj;
350 PNDIS_SPIN_LOCK gOvsCtrlLock;
356 gOvsCtrlLock = &ovsCtrlLockObj;
357 NdisAllocateSpinLock(gOvsCtrlLock);
364 OvsCleanupEventQueue();
366 NdisFreeSpinLock(gOvsCtrlLock);
374 NdisAcquireSpinLock(gOvsCtrlLock);
380 NdisReleaseSpinLock(gOvsCtrlLock);
385 * --------------------------------------------------------------------------
386 * Creates the communication device between user and kernel, and also
387 * initializes the data associated data structures.
388 * --------------------------------------------------------------------------
391 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
393 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
394 UNICODE_STRING deviceName;
395 UNICODE_STRING symbolicDeviceName;
396 PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
397 NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
398 OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
400 RtlZeroMemory(dispatchTable,
401 (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
402 dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
403 dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
404 dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
405 dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
407 NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
408 NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
410 RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
412 OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
413 NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
414 NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
415 sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
417 deviceAttributes.DeviceName = &deviceName;
418 deviceAttributes.SymbolicName = &symbolicDeviceName;
419 deviceAttributes.MajorFunctions = dispatchTable;
420 deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
422 status = NdisRegisterDeviceEx(ovsExtDriverHandle,
426 if (status != NDIS_STATUS_SUCCESS) {
427 POVS_DEVICE_EXTENSION ovsExt =
428 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
429 ASSERT(gOvsDeviceObject != NULL);
430 ASSERT(gOvsDeviceHandle != NULL);
433 ovsExt->numberOpenInstance = 0;
436 /* Initialize the associated data structures. */
439 OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
445 OvsDeleteDeviceObject()
447 if (gOvsDeviceHandle) {
449 POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
450 NdisGetDeviceReservedExtension(gOvsDeviceObject);
452 ASSERT(ovsExt->numberOpenInstance == 0);
456 ASSERT(gOvsDeviceObject);
457 NdisDeregisterDeviceEx(gOvsDeviceHandle);
458 gOvsDeviceHandle = NULL;
459 gOvsDeviceObject = NULL;
465 OvsGetOpenInstance(PFILE_OBJECT fileObject,
468 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
470 ASSERT(instance->fileObject == fileObject);
471 if (gOvsSwitchContext->dpNo != dpNo) {
479 OvsFindOpenInstance(PFILE_OBJECT fileObject)
482 for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
483 j < ovsNumberOfOpenInstances; i++) {
484 if (ovsOpenInstanceArray[i]) {
485 if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
486 return ovsOpenInstanceArray[i];
495 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
496 PFILE_OBJECT fileObject)
498 POVS_OPEN_INSTANCE instance =
499 (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
502 if (instance == NULL) {
503 return STATUS_NO_MEMORY;
505 OvsAcquireCtrlLock();
506 ASSERT(OvsFindOpenInstance(fileObject) == NULL);
508 if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
509 OvsReleaseCtrlLock();
510 OvsFreeMemory(instance);
511 return STATUS_INSUFFICIENT_RESOURCES;
513 RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
515 for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
516 if (ovsOpenInstanceArray[i] == NULL) {
517 ovsOpenInstanceArray[i] = instance;
518 ovsNumberOfOpenInstances++;
519 instance->cookie = i;
523 ASSERT(i < OVS_MAX_OPEN_INSTANCES);
524 instance->fileObject = fileObject;
525 ASSERT(fileObject->FsContext == NULL);
526 instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
527 if (instance->pid == 0) {
528 /* XXX: check for rollover. */
530 fileObject->FsContext = instance;
531 OvsReleaseCtrlLock();
532 return STATUS_SUCCESS;
536 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
538 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
540 ASSERT(fileObject == instance->fileObject);
541 OvsCleanupEvent(instance);
542 OvsCleanupPacketQueue(instance);
546 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
548 POVS_OPEN_INSTANCE instance;
549 ASSERT(fileObject->FsContext);
550 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
551 ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
553 OvsAcquireCtrlLock();
554 fileObject->FsContext = NULL;
555 ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
556 ovsOpenInstanceArray[instance->cookie] = NULL;
557 ovsNumberOfOpenInstances--;
558 OvsReleaseCtrlLock();
559 ASSERT(instance->eventQueue == NULL);
560 ASSERT (instance->packetQueue == NULL);
561 OvsFreeMemory(instance);
565 OvsCompleteIrpRequest(PIRP irp,
569 irp->IoStatus.Information = infoPtr;
570 irp->IoStatus.Status = status;
571 IoCompleteRequest(irp, IO_NO_INCREMENT);
577 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
580 PIO_STACK_LOCATION irpSp;
581 NTSTATUS status = STATUS_SUCCESS;
582 PFILE_OBJECT fileObject;
583 POVS_DEVICE_EXTENSION ovsExt =
584 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
586 ASSERT(deviceObject == gOvsDeviceObject);
587 ASSERT(ovsExt != NULL);
589 irpSp = IoGetCurrentIrpStackLocation(irp);
590 fileObject = irpSp->FileObject;
591 OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
592 deviceObject, fileObject,
593 ovsExt->numberOpenInstance);
595 switch (irpSp->MajorFunction) {
597 status = OvsAddOpenInstance(ovsExt, fileObject);
598 if (STATUS_SUCCESS == status) {
599 InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
603 ASSERT(ovsExt->numberOpenInstance > 0);
604 OvsRemoveOpenInstance(fileObject);
605 InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
610 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
613 _Use_decl_annotations_
615 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
619 PIO_STACK_LOCATION irpSp;
620 PFILE_OBJECT fileObject;
622 NTSTATUS status = STATUS_SUCCESS;
624 POVS_DEVICE_EXTENSION ovsExt =
625 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
627 ASSERT(ovsExt->numberOpenInstance > 0);
630 UNREFERENCED_PARAMETER(deviceObject);
632 ASSERT(deviceObject == gOvsDeviceObject);
633 irpSp = IoGetCurrentIrpStackLocation(irp);
634 fileObject = irpSp->FileObject;
636 ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
638 OvsCleanupOpenInstance(fileObject);
640 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
645 * --------------------------------------------------------------------------
646 * IOCTL function handler for the device.
647 * --------------------------------------------------------------------------
650 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
653 PIO_STACK_LOCATION irpSp;
654 NTSTATUS status = STATUS_SUCCESS;
655 PFILE_OBJECT fileObject;
656 PVOID inputBuffer = NULL;
657 PVOID outputBuffer = NULL;
658 UINT32 inputBufferLen, outputBufferLen;
659 UINT32 code, replyLen = 0;
660 POVS_OPEN_INSTANCE instance;
662 OVS_MESSAGE ovsMsgReadOp;
664 NETLINK_FAMILY *nlFamilyOps;
665 OVS_USER_PARAMS_CONTEXT usrParamsCtx;
668 POVS_DEVICE_EXTENSION ovsExt =
669 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
670 ASSERT(deviceObject == gOvsDeviceObject);
672 ASSERT(ovsExt->numberOpenInstance > 0);
674 UNREFERENCED_PARAMETER(deviceObject);
677 irpSp = IoGetCurrentIrpStackLocation(irp);
679 ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
680 ASSERT(irpSp->FileObject != NULL);
682 fileObject = irpSp->FileObject;
683 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
684 code = irpSp->Parameters.DeviceIoControl.IoControlCode;
685 inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
686 outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
687 inputBuffer = irp->AssociatedIrp.SystemBuffer;
689 /* Check if the extension is enabled. */
690 if (NULL == gOvsSwitchContext) {
691 status = STATUS_DEVICE_NOT_READY;
695 /* Concurrent netlink operations are not supported. */
696 if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
697 status = STATUS_RESOURCE_IN_USE;
702 * Validate the input/output buffer arguments depending on the type of the
706 case OVS_IOCTL_TRANSACT:
707 /* Both input buffer and output buffer are mandatory. */
708 if (outputBufferLen != 0) {
709 status = MapIrpOutputBuffer(irp, outputBufferLen,
710 sizeof *ovsMsg, &outputBuffer);
711 if (status != STATUS_SUCCESS) {
714 ASSERT(outputBuffer);
716 status = STATUS_NDIS_INVALID_LENGTH;
720 if (inputBufferLen < sizeof (*ovsMsg)) {
721 status = STATUS_NDIS_INVALID_LENGTH;
725 ovsMsg = inputBuffer;
726 devOp = OVS_TRANSACTION_DEV_OP;
729 case OVS_IOCTL_READ_EVENT:
730 case OVS_IOCTL_READ_PACKET:
732 * Output buffer is mandatory. These IOCTLs are used to read events and
733 * packets respectively. It is convenient to have separate ioctls.
735 if (outputBufferLen != 0) {
736 status = MapIrpOutputBuffer(irp, outputBufferLen,
737 sizeof *ovsMsg, &outputBuffer);
738 if (status != STATUS_SUCCESS) {
741 ASSERT(outputBuffer);
743 status = STATUS_NDIS_INVALID_LENGTH;
749 ovsMsg = &ovsMsgReadOp;
750 ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
751 ovsMsg->nlMsg.nlmsgPid = instance->pid;
752 /* An "artificial" command so we can use NL family function table*/
753 ovsMsg->genlMsg.cmd = (code == OVS_IOCTL_READ_EVENT) ?
754 OVS_CTRL_CMD_EVENT_NOTIFY :
755 OVS_CTRL_CMD_READ_NOTIFY;
756 devOp = OVS_READ_DEV_OP;
760 /* Output buffer is mandatory. */
761 if (outputBufferLen != 0) {
762 status = MapIrpOutputBuffer(irp, outputBufferLen,
763 sizeof *ovsMsg, &outputBuffer);
764 if (status != STATUS_SUCCESS) {
767 ASSERT(outputBuffer);
769 status = STATUS_NDIS_INVALID_LENGTH;
774 * Operate in the mode that read ioctl is similar to ReadFile(). This
775 * might change as the userspace code gets implemented.
781 * For implementing read (ioctl or otherwise), we need to store some
782 * state in the instance to indicate the command that started the dump
783 * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
784 * that 'ovsMsgReadOp' is needed only in this function to call into the
785 * appropriate handler. The handler itself can access the state in the
788 * In the absence of a dump start, return 0 bytes.
790 if (instance->dumpState.ovsMsg == NULL) {
792 status = STATUS_SUCCESS;
795 RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
796 sizeof (ovsMsgReadOp));
798 /* Create an NL message for consumption. */
799 ovsMsg = &ovsMsgReadOp;
800 devOp = OVS_READ_DEV_OP;
804 case OVS_IOCTL_WRITE:
805 /* Input buffer is mandatory. */
806 if (inputBufferLen < sizeof (*ovsMsg)) {
807 status = STATUS_NDIS_INVALID_LENGTH;
811 ovsMsg = inputBuffer;
812 devOp = OVS_WRITE_DEV_OP;
816 status = STATUS_INVALID_DEVICE_REQUEST;
821 switch (ovsMsg->nlMsg.nlmsgType) {
822 case OVS_WIN_NL_CTRL_FAMILY_ID:
823 nlFamilyOps = &nlControlFamilyOps;
825 case OVS_WIN_NL_DATAPATH_FAMILY_ID:
826 nlFamilyOps = &nlDatapathFamilyOps;
828 case OVS_WIN_NL_FLOW_FAMILY_ID:
829 nlFamilyOps = &nlFLowFamilyOps;
831 case OVS_WIN_NL_PACKET_FAMILY_ID:
832 nlFamilyOps = &nlPacketFamilyOps;
834 case OVS_WIN_NL_VPORT_FAMILY_ID:
835 nlFamilyOps = &nlVportFamilyOps;
837 case OVS_WIN_NL_NETDEV_FAMILY_ID:
838 nlFamilyOps = &nlNetdevFamilyOps;
841 status = STATUS_INVALID_PARAMETER;
846 * For read operation, the netlink command has already been validated
849 if (devOp != OVS_READ_DEV_OP) {
850 status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
851 if (status != STATUS_SUCCESS) {
856 InitUserParamsCtx(irp, instance, devOp, ovsMsg,
857 inputBuffer, inputBufferLen,
858 outputBuffer, outputBufferLen,
861 status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
867 /* Should not complete a pending IRP unless proceesing is completed */
868 if (status == STATUS_PENDING) {
871 return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
876 * --------------------------------------------------------------------------
877 * Function to validate a netlink command. Only certain combinations of
878 * (device operation, netlink family, command) are valid.
879 * --------------------------------------------------------------------------
882 ValidateNetlinkCmd(UINT32 devOp,
883 POVS_OPEN_INSTANCE instance,
885 NETLINK_FAMILY *nlFamilyOps)
887 NTSTATUS status = STATUS_INVALID_PARAMETER;
890 for (i = 0; i < nlFamilyOps->opsCount; i++) {
891 if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
892 /* Validate if the command is valid for the device operation. */
893 if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
894 status = STATUS_INVALID_PARAMETER;
898 /* Validate the version. */
899 if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
900 status = STATUS_INVALID_PARAMETER;
904 /* Validate the DP for commands that require a DP. */
905 if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
906 OvsAcquireCtrlLock();
907 if (ovsMsg->ovsHdr.dp_ifindex !=
908 (INT)gOvsSwitchContext->dpNo) {
909 status = STATUS_INVALID_PARAMETER;
910 OvsReleaseCtrlLock();
913 OvsReleaseCtrlLock();
916 /* Validate the PID. */
917 if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
918 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
919 status = STATUS_INVALID_PARAMETER;
924 status = STATUS_SUCCESS;
934 * --------------------------------------------------------------------------
935 * Function to invoke the netlink command handler.
936 * --------------------------------------------------------------------------
939 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
940 NETLINK_FAMILY *nlFamilyOps,
943 NTSTATUS status = STATUS_INVALID_PARAMETER;
946 for (i = 0; i < nlFamilyOps->opsCount; i++) {
947 if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
948 NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
951 status = handler(usrParamsCtx, replyLen);
961 * --------------------------------------------------------------------------
962 * Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
964 * Each handle on the device is assigned a unique PID when the handle is
965 * created. On platforms that support netlink natively, the PID is available
966 * to userspace when the netlink socket is created. However, without native
967 * netlink support on Windows, OVS datapath generates the PID and lets the
968 * userspace query it.
970 * This function implements the query.
971 * --------------------------------------------------------------------------
974 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
977 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
978 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
980 if (usrParamsCtx->outputLength >= sizeof *msgOut) {
981 POVS_OPEN_INSTANCE instance =
982 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
984 RtlZeroMemory(msgOut, sizeof *msgOut);
985 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
986 msgOut->nlMsg.nlmsgPid = instance->pid;
987 *replyLen = sizeof *msgOut;
988 /* XXX: We might need to return the DP index as well. */
990 return STATUS_NDIS_INVALID_LENGTH;
993 return STATUS_SUCCESS;
997 * --------------------------------------------------------------------------
998 * Utility function to fill up information about the datapath in a reply to
1000 * Assumes that 'gOvsCtrlLock' lock is acquired.
1001 * --------------------------------------------------------------------------
1004 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
1009 OVS_MESSAGE msgOutTmp;
1010 OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
1013 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && NlBufRemLen(nlBuf) >= sizeof *msgIn);
1015 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
1016 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
1017 msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1018 msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1020 msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
1021 msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
1022 msgOutTmp.genlMsg.reserved = 0;
1024 msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
1026 writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
1028 writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
1029 OVS_SYSTEM_DP_NAME);
1032 OVS_DP_STATS dpStats;
1034 dpStats.n_hit = datapath->hits;
1035 dpStats.n_missed = datapath->misses;
1036 dpStats.n_lost = datapath->lost;
1037 dpStats.n_flows = datapath->nFlows;
1038 writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
1039 (PCHAR)&dpStats, sizeof dpStats);
1041 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1042 nlMsg->nlmsgLen = NlBufSize(nlBuf);
1044 return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
1048 * --------------------------------------------------------------------------
1049 * Handler for queueing an IRP used for event notification. The IRP is
1050 * completed when a port state changes. STATUS_PENDING is returned on
1051 * success. User mode keep a pending IRP at all times.
1052 * --------------------------------------------------------------------------
1055 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1060 UNREFERENCED_PARAMETER(replyLen);
1062 POVS_OPEN_INSTANCE instance =
1063 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1064 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1065 OVS_EVENT_POLL poll;
1067 poll.dpNo = msgIn->ovsHdr.dp_ifindex;
1068 status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
1069 &poll, sizeof poll);
1074 * --------------------------------------------------------------------------
1075 * Handler for the subscription for the event queue
1076 * --------------------------------------------------------------------------
1079 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1083 OVS_EVENT_SUBSCRIBE request;
1087 const NL_POLICY policy[] = {
1088 [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
1089 [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
1092 UNREFERENCED_PARAMETER(replyLen);
1094 POVS_OPEN_INSTANCE instance =
1095 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1096 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1098 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1099 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, ARRAY_SIZE(attrs));
1101 status = STATUS_INVALID_PARAMETER;
1105 /* XXX Ignore the MC group for now */
1106 join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1107 request.dpNo = msgIn->ovsHdr.dp_ifindex;
1108 request.subscribe = join;
1109 request.mask = OVS_EVENT_MASK_ALL;
1111 status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1118 * --------------------------------------------------------------------------
1119 * Command Handler for 'OVS_DP_CMD_NEW'.
1120 * --------------------------------------------------------------------------
1123 OvsNewDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1126 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1130 * --------------------------------------------------------------------------
1131 * Command Handler for 'OVS_DP_CMD_GET'.
1133 * The function handles both the dump based as well as the transaction based
1134 * 'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1135 * call to setup dump state, as well as subsequent calls to continue dumping
1137 * --------------------------------------------------------------------------
1140 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1143 if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1144 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1146 return HandleGetDpDump(usrParamsCtx, replyLen);
1151 * --------------------------------------------------------------------------
1152 * Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1153 * --------------------------------------------------------------------------
1156 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1159 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1164 * --------------------------------------------------------------------------
1165 * Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1166 * --------------------------------------------------------------------------
1169 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1172 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1173 POVS_OPEN_INSTANCE instance =
1174 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1176 if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1178 OvsSetupDumpStart(usrParamsCtx);
1182 POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1184 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1186 if (instance->dumpState.ovsMsg == NULL) {
1188 return STATUS_INVALID_DEVICE_STATE;
1191 /* Dump state must have been deleted after previous dump operation. */
1192 ASSERT(instance->dumpState.index[0] == 0);
1194 /* Output buffer has been validated while validating read dev op. */
1195 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1197 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1198 usrParamsCtx->outputLength);
1200 OvsAcquireCtrlLock();
1201 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1202 OvsReleaseCtrlLock();
1204 if (status != STATUS_SUCCESS) {
1206 FreeUserDumpState(instance);
1210 /* Increment the dump index. */
1211 instance->dumpState.index[0] = 1;
1212 *replyLen = msgOut->nlMsg.nlmsgLen;
1214 /* Free up the dump state, since there's no more data to continue. */
1215 FreeUserDumpState(instance);
1218 return STATUS_SUCCESS;
1223 * --------------------------------------------------------------------------
1224 * Command Handler for 'OVS_DP_CMD_SET'.
1225 * --------------------------------------------------------------------------
1228 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1231 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1235 * --------------------------------------------------------------------------
1236 * Function for handling transaction based 'OVS_DP_CMD_NEW', 'OVS_DP_CMD_GET'
1237 * and 'OVS_DP_CMD_SET' commands.
1239 * 'OVS_DP_CMD_NEW' is implemented to keep userspace code happy. Creation of a
1240 * new datapath is not supported currently.
1241 * --------------------------------------------------------------------------
1244 HandleDpTransactionCommon(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1247 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1248 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1249 NTSTATUS status = STATUS_SUCCESS;
1251 NL_ERROR nlError = NL_ERROR_SUCCESS;
1252 static const NL_POLICY ovsDatapathSetPolicy[] = {
1253 [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1254 [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1255 [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1257 PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1259 UNREFERENCED_PARAMETER(msgOut);
1261 /* input buffer has been validated while validating write dev op. */
1262 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1264 /* Parse any attributes in the request. */
1265 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET ||
1266 usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1267 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1268 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1269 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1270 ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1271 return STATUS_INVALID_PARAMETER;
1275 * XXX: Not clear at this stage if there's any role for the
1276 * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1281 RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1284 /* Output buffer has been validated while validating transact dev op. */
1285 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1287 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1289 OvsAcquireCtrlLock();
1290 if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1291 if (!OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1292 OVS_SYSTEM_DP_NAME)) {
1293 OvsReleaseCtrlLock();
1295 /* Creation of new datapaths is not supported. */
1296 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1297 nlError = NL_ERROR_NOTSUPP;
1301 nlError = NL_ERROR_NODEV;
1304 } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1305 OvsReleaseCtrlLock();
1306 nlError = NL_ERROR_NODEV;
1310 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1311 OvsReleaseCtrlLock();
1312 nlError = NL_ERROR_EXIST;
1316 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1317 OvsReleaseCtrlLock();
1319 *replyLen = NlBufSize(&nlBuf);
1322 if (nlError != NL_ERROR_SUCCESS) {
1323 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1324 usrParamsCtx->outputBuffer;
1326 NlBuildErrorMsg(msgIn, msgError, nlError);
1327 *replyLen = msgError->nlMsg.nlmsgLen;
1330 return STATUS_SUCCESS;
1335 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1337 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1338 POVS_OPEN_INSTANCE instance =
1339 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1341 /* input buffer has been validated while validating write dev op. */
1342 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1344 /* A write operation that does not indicate dump start is invalid. */
1345 if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1346 return STATUS_INVALID_PARAMETER;
1348 /* XXX: Handle other NLM_F_* flags in the future. */
1351 * This operation should be setting up the dump state. If there's any
1352 * previous state, clear it up so as to set it up afresh.
1354 FreeUserDumpState(instance);
1356 return InitUserDumpState(instance, msgIn);
1361 * --------------------------------------------------------------------------
1362 * Utility function to map the output buffer in an IRP. The buffer is assumed
1363 * to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
1364 * --------------------------------------------------------------------------
1367 MapIrpOutputBuffer(PIRP irp,
1368 UINT32 bufferLength,
1369 UINT32 requiredLength,
1374 ASSERT(bufferLength);
1375 ASSERT(requiredLength);
1376 if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
1377 return STATUS_INVALID_PARAMETER;
1380 if (bufferLength < requiredLength) {
1381 return STATUS_NDIS_INVALID_LENGTH;
1383 if (irp->MdlAddress == NULL) {
1384 return STATUS_INVALID_PARAMETER;
1386 *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
1387 NormalPagePriority);
1388 if (*buffer == NULL) {
1389 return STATUS_INSUFFICIENT_RESOURCES;
1392 return STATUS_SUCCESS;
1396 * --------------------------------------------------------------------------
1397 * Utility function to fill up information about the state of a port in a reply
1399 * Assumes that 'gOvsCtrlLock' lock is acquired.
1400 * --------------------------------------------------------------------------
1403 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1404 POVS_EVENT_ENTRY eventEntry,
1409 OVS_MESSAGE msgOutTmp;
1411 POVS_VPORT_ENTRY vport;
1413 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
1415 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
1416 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
1418 /* driver intiated messages should have zerp seq number*/
1419 msgOutTmp.nlMsg.nlmsgSeq = 0;
1420 msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
1422 msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
1423 msgOutTmp.genlMsg.reserved = 0;
1425 /* we don't have netdev yet, treat link up/down a adding/removing a port*/
1426 if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
1427 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
1428 } else if (eventEntry->status &
1429 (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
1430 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
1433 return STATUS_UNSUCCESSFUL;
1435 msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
1437 ok = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
1439 status = STATUS_INVALID_BUFFER_SIZE;
1443 vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
1445 status = STATUS_DEVICE_DOES_NOT_EXIST;
1449 ok = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) &&
1450 NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) &&
1451 NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_UPCALL_PID,
1452 vport->upcallPid) &&
1453 NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
1455 status = STATUS_INVALID_BUFFER_SIZE;
1459 /* XXXX Should we add the port stats attributes?*/
1460 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1461 nlMsg->nlmsgLen = NlBufSize(nlBuf);
1462 status = STATUS_SUCCESS;
1470 * --------------------------------------------------------------------------
1471 * Handler for reading events from the driver event queue. This handler is
1472 * executed when user modes issues a socket receive on a socket assocaited
1473 * with the MC group for events.
1474 * XXX user mode should read multiple events in one system call
1475 * --------------------------------------------------------------------------
1478 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1482 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1483 POVS_OPEN_INSTANCE instance =
1484 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1488 OVS_EVENT_ENTRY eventEntry;
1490 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1492 /* Should never read events with a dump socket */
1493 ASSERT(instance->dumpState.ovsMsg == NULL);
1495 /* Must have an event queue */
1496 ASSERT(instance->eventQueue != NULL);
1498 /* Output buffer has been validated while validating read dev op. */
1499 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1501 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1503 OvsAcquireCtrlLock();
1505 /* remove an event entry from the event queue */
1506 status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
1507 if (status != STATUS_SUCCESS) {
1508 /* If there were not elements, read should return no data. */
1509 status = STATUS_SUCCESS;
1514 status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
1515 if (status == NDIS_STATUS_SUCCESS) {
1516 *replyLen = NlBufSize(&nlBuf);
1520 OvsReleaseCtrlLock();
1525 * --------------------------------------------------------------------------
1526 * Handler for reading missed pacckets from the driver event queue. This
1527 * handler is executed when user modes issues a socket receive on a socket
1528 * --------------------------------------------------------------------------
1531 OvsReadPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1535 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1537 POVS_OPEN_INSTANCE instance =
1538 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1541 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1543 /* Should never read events with a dump socket */
1544 ASSERT(instance->dumpState.ovsMsg == NULL);
1546 /* Must have an packet queue */
1547 ASSERT(instance->packetQueue != NULL);
1549 /* Output buffer has been validated while validating read dev op. */
1550 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1552 /* Read a packet from the instance queue */
1553 status = OvsReadDpIoctl(instance->fileObject, usrParamsCtx->outputBuffer,
1554 usrParamsCtx->outputLength, replyLen);
1559 * --------------------------------------------------------------------------
1560 * Handler for the subscription for a packet queue
1561 * --------------------------------------------------------------------------
1564 OvsSubscribePacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1571 const NL_POLICY policy[] = {
1572 [OVS_NL_ATTR_PACKET_PID] = {.type = NL_A_U32 },
1573 [OVS_NL_ATTR_PACKET_SUBSCRIBE] = {.type = NL_A_U8 }
1575 PNL_ATTR attrs[ARRAY_SIZE(policy)];
1577 UNREFERENCED_PARAMETER(replyLen);
1579 POVS_OPEN_INSTANCE instance =
1580 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1581 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1583 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1584 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, ARRAY_SIZE(attrs));
1586 status = STATUS_INVALID_PARAMETER;
1590 join = NlAttrGetU8(attrs[OVS_NL_ATTR_PACKET_PID]);
1591 pid = NlAttrGetU32(attrs[OVS_NL_ATTR_PACKET_PID]);
1593 /* The socket subscribed with must be the same socket we perform receive*/
1594 ASSERT(pid == instance->pid);
1596 status = OvsSubscribeDpIoctl(instance, pid, join);
1599 * XXX Need to add this instance to a global data structure
1600 * which hold all packet based instances. The data structure (hash)
1601 * should be searched through the pid field of the instance for
1602 * placing the missed packet into the correct queue
1609 * --------------------------------------------------------------------------
1610 * Handler for queueing an IRP used for missed packet notification. The IRP is
1611 * completed when a packet received and mismatched. STATUS_PENDING is returned
1612 * on success. User mode keep a pending IRP at all times.
1613 * --------------------------------------------------------------------------
1616 OvsPendPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1619 UNREFERENCED_PARAMETER(replyLen);
1621 POVS_OPEN_INSTANCE instance =
1622 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1625 * XXX access to packet queue must be through acquiring a lock as user mode
1626 * could unsubscribe and the instnace will be freed.
1628 return OvsWaitDpIoctl(usrParamsCtx->irp, instance->fileObject);