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
23 #if defined OVS_USE_NL_INTERFACE && OVS_USE_NL_INTERFACE == 1
40 #define OVS_DBG_MOD OVS_DBG_DATAPATH
43 #define NETLINK_FAMILY_NAME_LEN 48
47 * Netlink messages are grouped by family (aka type), and each family supports
48 * a set of commands, and can be passed both from kernel -> userspace or
49 * vice-versa. To call into the kernel, userspace uses a device operation which
50 * is outside of a netlink message.
52 * Each command results in the invocation of a handler function to implement the
53 * request functionality.
55 * Expectedly, only certain combinations of (device operation, netlink family,
58 * Here, we implement the basic infrastructure to perform validation on the
59 * incoming message, version checking, and also to invoke the corresponding
60 * handler to do the heavy-lifting.
64 * Handler for a given netlink command. Not all the parameters are used by all
67 typedef NTSTATUS(NetlinkCmdHandler)(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
70 typedef struct _NETLINK_CMD {
72 NetlinkCmdHandler *handler;
73 UINT32 supportedDevOp; /* Supported device operations. */
74 BOOLEAN validateDpIndex; /* Does command require a valid DP argument. */
75 } NETLINK_CMD, *PNETLINK_CMD;
77 /* A netlink family is a group of commands. */
78 typedef struct _NETLINK_FAMILY {
84 NETLINK_CMD *cmds; /* Array of netlink commands and handlers. */
86 } NETLINK_FAMILY, *PNETLINK_FAMILY;
89 * Device operations to tag netlink commands with. This is a bitmask since it is
90 * possible that a particular command can be invoked via different device
93 #define OVS_READ_DEV_OP (1 << 0)
94 #define OVS_WRITE_DEV_OP (1 << 1)
95 #define OVS_TRANSACTION_DEV_OP (1 << 2)
96 #define OVS_READ_EVENT_DEV_OP (1 << 3)
98 /* Handlers for the various netlink commands. */
99 static NetlinkCmdHandler OvsGetPidCmdHandler,
101 OvsPendEventCmdHandler,
102 OvsSubscribeEventCmdHandler,
104 OvsReadEventCmdHandler,
105 OvsGetVportCmdHandler;
107 static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
109 static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
111 static NTSTATUS HandleDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
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_MC_SUBSCRIBE_REQ,
134 .handler = OvsSubscribeEventCmdHandler,
135 .supportedDevOp = OVS_WRITE_DEV_OP,
136 .validateDpIndex = TRUE,
138 { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY,
139 .handler = OvsReadEventCmdHandler,
140 .supportedDevOp = OVS_READ_EVENT_DEV_OP,
141 .validateDpIndex = FALSE,
145 NETLINK_FAMILY nlControlFamilyOps = {
146 .name = OVS_WIN_CONTROL_FAMILY,
147 .id = OVS_WIN_NL_CTRL_FAMILY_ID,
148 .version = OVS_WIN_CONTROL_VERSION,
149 .maxAttr = OVS_WIN_CONTROL_ATTR_MAX,
150 .cmds = nlControlFamilyCmdOps,
151 .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
154 /* Netlink datapath family. */
155 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
156 { .cmd = OVS_DP_CMD_GET,
157 .handler = OvsGetDpCmdHandler,
158 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
159 OVS_TRANSACTION_DEV_OP,
160 .validateDpIndex = FALSE
162 { .cmd = OVS_DP_CMD_SET,
163 .handler = OvsSetDpCmdHandler,
164 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
165 OVS_TRANSACTION_DEV_OP,
166 .validateDpIndex = TRUE
170 NETLINK_FAMILY nlDatapathFamilyOps = {
171 .name = OVS_DATAPATH_FAMILY,
172 .id = OVS_WIN_NL_DATAPATH_FAMILY_ID,
173 .version = OVS_DATAPATH_VERSION,
174 .maxAttr = OVS_DP_ATTR_MAX,
175 .cmds = nlDatapathFamilyCmdOps,
176 .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
179 /* Netlink packet family. */
180 /* XXX: Add commands here. */
181 NETLINK_FAMILY nlPacketFamilyOps = {
182 .name = OVS_PACKET_FAMILY,
183 .id = OVS_WIN_NL_PACKET_FAMILY_ID,
184 .version = OVS_PACKET_VERSION,
185 .maxAttr = OVS_PACKET_ATTR_MAX,
186 .cmds = NULL, /* XXX: placeholder. */
190 /* Netlink vport family. */
191 NETLINK_CMD nlVportFamilyCmdOps[] = {
192 { .cmd = OVS_VPORT_CMD_GET,
193 .handler = OvsGetVportCmdHandler,
194 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
195 OVS_TRANSACTION_DEV_OP,
196 .validateDpIndex = TRUE
200 NETLINK_FAMILY nlVportFamilyOps = {
201 .name = OVS_VPORT_FAMILY,
202 .id = OVS_WIN_NL_VPORT_FAMILY_ID,
203 .version = OVS_VPORT_VERSION,
204 .maxAttr = OVS_VPORT_ATTR_MAX,
205 .cmds = nlVportFamilyCmdOps,
206 .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps)
209 /* Netlink flow family. */
211 NETLINK_CMD nlFlowFamilyCmdOps[] = {
212 { .cmd = OVS_FLOW_CMD_NEW,
213 .handler = OvsFlowNlCmdHandler,
214 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
215 .validateDpIndex = TRUE
217 { .cmd = OVS_FLOW_CMD_SET,
218 .handler = OvsFlowNlCmdHandler,
219 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
220 .validateDpIndex = TRUE
222 { .cmd = OVS_FLOW_CMD_DEL,
223 .handler = OvsFlowNlCmdHandler,
224 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
225 .validateDpIndex = TRUE
229 NETLINK_FAMILY nlFLowFamilyOps = {
230 .name = OVS_FLOW_FAMILY,
231 .id = OVS_WIN_NL_FLOW_FAMILY_ID,
232 .version = OVS_FLOW_VERSION,
233 .maxAttr = OVS_FLOW_ATTR_MAX,
234 .cmds = nlFlowFamilyCmdOps,
235 .opsCount = ARRAY_SIZE(nlFlowFamilyCmdOps)
238 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
240 UINT32 requiredLength,
242 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
243 POVS_OPEN_INSTANCE instance,
245 NETLINK_FAMILY *nlFamilyOps);
246 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
247 NETLINK_FAMILY *nlFamilyOps,
249 static NTSTATUS OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx);
252 /* Handles to the device object for communication with userspace. */
253 NDIS_HANDLE gOvsDeviceHandle;
254 PDEVICE_OBJECT gOvsDeviceObject;
256 _Dispatch_type_(IRP_MJ_CREATE)
257 _Dispatch_type_(IRP_MJ_CLOSE)
258 DRIVER_DISPATCH OvsOpenCloseDevice;
260 _Dispatch_type_(IRP_MJ_CLEANUP)
261 DRIVER_DISPATCH OvsCleanupDevice;
263 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
264 DRIVER_DISPATCH OvsDeviceControl;
267 #pragma alloc_text(INIT, OvsCreateDeviceObject)
268 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
269 #pragma alloc_text(PAGE, OvsCleanupDevice)
270 #pragma alloc_text(PAGE, OvsDeviceControl)
271 #endif // ALLOC_PRAGMA
274 * We might hit this limit easily since userspace opens a netlink descriptor for
275 * each thread, and at least one descriptor per vport. Revisit this later.
277 #define OVS_MAX_OPEN_INSTANCES 512
278 #define OVS_SYSTEM_DP_NAME "ovs-system"
280 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
281 UINT32 ovsNumberOfOpenInstances;
282 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
284 NDIS_SPIN_LOCK ovsCtrlLockObj;
285 PNDIS_SPIN_LOCK gOvsCtrlLock;
291 gOvsCtrlLock = &ovsCtrlLockObj;
292 NdisAllocateSpinLock(gOvsCtrlLock);
300 OvsCleanupEventQueue();
302 NdisFreeSpinLock(gOvsCtrlLock);
311 NdisAcquireSpinLock(gOvsCtrlLock);
317 NdisReleaseSpinLock(gOvsCtrlLock);
322 * --------------------------------------------------------------------------
323 * Creates the communication device between user and kernel, and also
324 * initializes the data associated data structures.
325 * --------------------------------------------------------------------------
328 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
330 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
331 UNICODE_STRING deviceName;
332 UNICODE_STRING symbolicDeviceName;
333 PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
334 NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
335 OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
337 RtlZeroMemory(dispatchTable,
338 (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
339 dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
340 dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
341 dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
342 dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
344 NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
345 NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
347 RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
349 OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
350 NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
351 NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
352 sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
354 deviceAttributes.DeviceName = &deviceName;
355 deviceAttributes.SymbolicName = &symbolicDeviceName;
356 deviceAttributes.MajorFunctions = dispatchTable;
357 deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
359 status = NdisRegisterDeviceEx(ovsExtDriverHandle,
363 if (status != NDIS_STATUS_SUCCESS) {
364 POVS_DEVICE_EXTENSION ovsExt =
365 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
366 ASSERT(gOvsDeviceObject != NULL);
367 ASSERT(gOvsDeviceHandle != NULL);
370 ovsExt->numberOpenInstance = 0;
373 /* Initialize the associated data structures. */
376 OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
382 OvsDeleteDeviceObject()
384 if (gOvsDeviceHandle) {
386 POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
387 NdisGetDeviceReservedExtension(gOvsDeviceObject);
389 ASSERT(ovsExt->numberOpenInstance == 0);
393 ASSERT(gOvsDeviceObject);
394 NdisDeregisterDeviceEx(gOvsDeviceHandle);
395 gOvsDeviceHandle = NULL;
396 gOvsDeviceObject = NULL;
402 OvsGetOpenInstance(PFILE_OBJECT fileObject,
405 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
407 ASSERT(instance->fileObject == fileObject);
408 if (gOvsSwitchContext == NULL ||
409 gOvsSwitchContext->dpNo != dpNo) {
417 OvsFindOpenInstance(PFILE_OBJECT fileObject)
420 for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
421 j < ovsNumberOfOpenInstances; i++) {
422 if (ovsOpenInstanceArray[i]) {
423 if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
424 return ovsOpenInstanceArray[i];
433 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
434 PFILE_OBJECT fileObject)
436 POVS_OPEN_INSTANCE instance =
437 (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
440 if (instance == NULL) {
441 return STATUS_NO_MEMORY;
443 OvsAcquireCtrlLock();
444 ASSERT(OvsFindOpenInstance(fileObject) == NULL);
446 if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
447 OvsReleaseCtrlLock();
448 OvsFreeMemory(instance);
449 return STATUS_INSUFFICIENT_RESOURCES;
451 RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
453 for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
454 if (ovsOpenInstanceArray[i] == NULL) {
455 ovsOpenInstanceArray[i] = instance;
456 instance->cookie = i;
460 ASSERT(i < OVS_MAX_OPEN_INSTANCES);
461 instance->fileObject = fileObject;
462 ASSERT(fileObject->FsContext == NULL);
463 instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
464 if (instance->pid == 0) {
465 /* XXX: check for rollover. */
467 fileObject->FsContext = instance;
468 OvsReleaseCtrlLock();
469 return STATUS_SUCCESS;
473 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
475 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
477 ASSERT(fileObject == instance->fileObject);
478 OvsCleanupEvent(instance);
479 OvsCleanupPacketQueue(instance);
483 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
485 POVS_OPEN_INSTANCE instance;
486 ASSERT(fileObject->FsContext);
487 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
488 ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
490 OvsAcquireCtrlLock();
491 fileObject->FsContext = NULL;
492 ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
493 ovsOpenInstanceArray[instance->cookie] = NULL;
494 OvsReleaseCtrlLock();
495 ASSERT(instance->eventQueue == NULL);
496 ASSERT (instance->packetQueue == NULL);
497 OvsFreeMemory(instance);
501 OvsCompleteIrpRequest(PIRP irp,
505 irp->IoStatus.Information = infoPtr;
506 irp->IoStatus.Status = status;
507 IoCompleteRequest(irp, IO_NO_INCREMENT);
513 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
516 PIO_STACK_LOCATION irpSp;
517 NTSTATUS status = STATUS_SUCCESS;
518 PFILE_OBJECT fileObject;
519 POVS_DEVICE_EXTENSION ovsExt =
520 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
522 ASSERT(deviceObject == gOvsDeviceObject);
523 ASSERT(ovsExt != NULL);
525 irpSp = IoGetCurrentIrpStackLocation(irp);
526 fileObject = irpSp->FileObject;
527 OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
528 deviceObject, fileObject,
529 ovsExt->numberOpenInstance);
531 switch (irpSp->MajorFunction) {
533 status = OvsAddOpenInstance(ovsExt, fileObject);
534 if (STATUS_SUCCESS == status) {
535 InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
539 ASSERT(ovsExt->numberOpenInstance > 0);
540 OvsRemoveOpenInstance(fileObject);
541 InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
546 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
549 _Use_decl_annotations_
551 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
555 PIO_STACK_LOCATION irpSp;
556 PFILE_OBJECT fileObject;
558 NTSTATUS status = STATUS_SUCCESS;
560 POVS_DEVICE_EXTENSION ovsExt =
561 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
563 ASSERT(ovsExt->numberOpenInstance > 0);
566 UNREFERENCED_PARAMETER(deviceObject);
568 ASSERT(deviceObject == gOvsDeviceObject);
569 irpSp = IoGetCurrentIrpStackLocation(irp);
570 fileObject = irpSp->FileObject;
572 ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
574 OvsCleanupOpenInstance(fileObject);
576 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
581 * --------------------------------------------------------------------------
582 * IOCTL function handler for the device.
583 * --------------------------------------------------------------------------
586 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
590 PIO_STACK_LOCATION irpSp;
591 NTSTATUS status = STATUS_SUCCESS;
592 PFILE_OBJECT fileObject;
593 PVOID inputBuffer = NULL;
594 PVOID outputBuffer = NULL;
595 UINT32 inputBufferLen, outputBufferLen;
596 UINT32 code, replyLen = 0;
597 POVS_OPEN_INSTANCE instance;
599 OVS_MESSAGE ovsMsgReadOp;
601 NETLINK_FAMILY *nlFamilyOps;
602 OVS_USER_PARAMS_CONTEXT usrParamsCtx;
605 POVS_DEVICE_EXTENSION ovsExt =
606 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
607 ASSERT(deviceObject == gOvsDeviceObject);
609 ASSERT(ovsExt->numberOpenInstance > 0);
611 UNREFERENCED_PARAMETER(deviceObject);
614 irpSp = IoGetCurrentIrpStackLocation(irp);
616 ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
617 ASSERT(irpSp->FileObject != NULL);
619 fileObject = irpSp->FileObject;
620 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
621 code = irpSp->Parameters.DeviceIoControl.IoControlCode;
622 inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
623 outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
624 inputBuffer = irp->AssociatedIrp.SystemBuffer;
626 /* Concurrent netlink operations are not supported. */
627 if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
628 status = STATUS_RESOURCE_IN_USE;
633 * Validate the input/output buffer arguments depending on the type of the
637 case OVS_IOCTL_TRANSACT:
638 /* Input buffer is mandatory, output buffer is optional. */
639 if (outputBufferLen != 0) {
640 status = MapIrpOutputBuffer(irp, outputBufferLen,
641 sizeof *ovsMsg, &outputBuffer);
642 if (status != STATUS_SUCCESS) {
645 ASSERT(outputBuffer);
648 if (inputBufferLen < sizeof (*ovsMsg)) {
649 status = STATUS_NDIS_INVALID_LENGTH;
653 ovsMsg = inputBuffer;
654 devOp = OVS_TRANSACTION_DEV_OP;
657 case OVS_IOCTL_READ_EVENT:
658 /* This IOCTL is used to read events */
659 if (outputBufferLen != 0) {
660 status = MapIrpOutputBuffer(irp, outputBufferLen,
661 sizeof *ovsMsg, &outputBuffer);
662 if (status != STATUS_SUCCESS) {
665 ASSERT(outputBuffer);
667 status = STATUS_NDIS_INVALID_LENGTH;
673 ovsMsg = &ovsMsgReadOp;
674 ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
675 /* An "artificial" command so we can use NL family function table*/
676 ovsMsg->genlMsg.cmd = OVS_CTRL_CMD_EVENT_NOTIFY;
677 devOp = OVS_READ_DEV_OP;
681 /* Output buffer is mandatory. */
682 if (outputBufferLen != 0) {
683 status = MapIrpOutputBuffer(irp, outputBufferLen,
684 sizeof *ovsMsg, &outputBuffer);
685 if (status != STATUS_SUCCESS) {
688 ASSERT(outputBuffer);
690 status = STATUS_NDIS_INVALID_LENGTH;
695 * Operate in the mode that read ioctl is similar to ReadFile(). This
696 * might change as the userspace code gets implemented.
702 * For implementing read (ioctl or otherwise), we need to store some
703 * state in the instance to indicate the command that started the dump
704 * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
705 * that 'ovsMsgReadOp' is needed only in this function to call into the
706 * appropraite handler. The handler itself can access the state in the
709 * In the absence of a dump start, return 0 bytes.
711 if (instance->dumpState.ovsMsg == NULL) {
713 status = STATUS_SUCCESS;
716 RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
717 sizeof (ovsMsgReadOp));
719 /* Create an NL message for consumption. */
720 ovsMsg = &ovsMsgReadOp;
721 devOp = OVS_READ_DEV_OP;
725 case OVS_IOCTL_WRITE:
726 /* Input buffer is mandatory. */
727 if (inputBufferLen < sizeof (*ovsMsg)) {
728 status = STATUS_NDIS_INVALID_LENGTH;
732 ovsMsg = inputBuffer;
733 devOp = OVS_WRITE_DEV_OP;
737 status = STATUS_INVALID_DEVICE_REQUEST;
742 switch (ovsMsg->nlMsg.nlmsgType) {
743 case OVS_WIN_NL_CTRL_FAMILY_ID:
744 nlFamilyOps = &nlControlFamilyOps;
746 case OVS_WIN_NL_DATAPATH_FAMILY_ID:
747 nlFamilyOps = &nlDatapathFamilyOps;
749 case OVS_WIN_NL_FLOW_FAMILY_ID:
750 nlFamilyOps = &nlFLowFamilyOps;
752 case OVS_WIN_NL_PACKET_FAMILY_ID:
753 status = STATUS_NOT_IMPLEMENTED;
755 case OVS_WIN_NL_VPORT_FAMILY_ID:
756 nlFamilyOps = &nlVportFamilyOps;
759 status = STATUS_INVALID_PARAMETER;
764 * For read operation, the netlink command has already been validated
767 if (devOp != OVS_READ_DEV_OP) {
768 status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
769 if (status != STATUS_SUCCESS) {
774 InitUserParamsCtx(irp, instance, devOp, ovsMsg,
775 inputBuffer, inputBufferLen,
776 outputBuffer, outputBufferLen,
779 status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
784 return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
789 * --------------------------------------------------------------------------
790 * Function to validate a netlink command. Only certain combinations of
791 * (device operation, netlink family, command) are valid.
792 * --------------------------------------------------------------------------
795 ValidateNetlinkCmd(UINT32 devOp,
796 POVS_OPEN_INSTANCE instance,
798 NETLINK_FAMILY *nlFamilyOps)
800 NTSTATUS status = STATUS_INVALID_PARAMETER;
803 for (i = 0; i < nlFamilyOps->opsCount; i++) {
804 if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
805 /* Validate if the command is valid for the device operation. */
806 if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
807 status = STATUS_INVALID_PARAMETER;
811 /* Validate the version. */
812 if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
813 status = STATUS_INVALID_PARAMETER;
817 /* Validate the DP for commands that require a DP. */
818 if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
819 OvsAcquireCtrlLock();
820 if (ovsMsg->ovsHdr.dp_ifindex !=
821 (INT)gOvsSwitchContext->dpNo) {
822 status = STATUS_INVALID_PARAMETER;
823 OvsReleaseCtrlLock();
826 OvsReleaseCtrlLock();
829 /* Validate the PID. */
830 if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
831 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
832 status = STATUS_INVALID_PARAMETER;
837 status = STATUS_SUCCESS;
847 * --------------------------------------------------------------------------
848 * Function to invoke the netlink command handler.
849 * --------------------------------------------------------------------------
852 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
853 NETLINK_FAMILY *nlFamilyOps,
856 NTSTATUS status = STATUS_INVALID_PARAMETER;
859 for (i = 0; i < nlFamilyOps->opsCount; i++) {
860 if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
861 NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
864 status = handler(usrParamsCtx, replyLen);
874 * --------------------------------------------------------------------------
875 * Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
877 * Each handle on the device is assigned a unique PID when the handle is
878 * created. On platforms that support netlink natively, the PID is available
879 * to userspace when the netlink socket is created. However, without native
880 * netlink support on Windows, OVS datapath generates the PID and lets the
881 * userspace query it.
883 * This function implements the query.
884 * --------------------------------------------------------------------------
887 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
890 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
891 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
893 if (usrParamsCtx->outputLength >= sizeof *msgOut) {
894 POVS_OPEN_INSTANCE instance =
895 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
897 RtlZeroMemory(msgOut, sizeof *msgOut);
898 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
899 msgOut->nlMsg.nlmsgPid = instance->pid;
900 *replyLen = sizeof *msgOut;
901 /* XXX: We might need to return the DP index as well. */
903 return STATUS_NDIS_INVALID_LENGTH;
906 return STATUS_SUCCESS;
910 * --------------------------------------------------------------------------
911 * Utility function to fill up information about the datapath in a reply to
913 * Assumes that 'gOvsCtrlLock' lock is acquired.
914 * --------------------------------------------------------------------------
917 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
922 OVS_MESSAGE msgOutTmp;
923 OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
926 /* XXX: Add API for nlBuf->bufRemLen. */
927 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof *msgIn);
929 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
930 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
931 msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
932 msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
934 msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
935 msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
936 msgOutTmp.genlMsg.reserved = 0;
938 msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
940 writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
942 writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
946 OVS_DP_STATS dpStats;
948 dpStats.n_hit = datapath->hits;
949 dpStats.n_missed = datapath->misses;
950 dpStats.n_lost = datapath->lost;
951 dpStats.n_flows = datapath->nFlows;
952 writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
953 (PCHAR)&dpStats, sizeof dpStats);
955 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
956 nlMsg->nlmsgLen = NlBufSize(nlBuf);
958 return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
962 * --------------------------------------------------------------------------
963 * Handler for queueing an IRP used for event notification. The IRP is
964 * completed when a port state changes. STATUS_PENDING is returned on
965 * success. User mode keep a pending IRP at all times.
966 * --------------------------------------------------------------------------
969 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
974 UNREFERENCED_PARAMETER(replyLen);
976 POVS_OPEN_INSTANCE instance =
977 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
978 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
981 poll.dpNo = msgIn->ovsHdr.dp_ifindex;
982 status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
989 * --------------------------------------------------------------------------
990 * Handler for the subscription for the event queue
991 * --------------------------------------------------------------------------
994 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
998 OVS_EVENT_SUBSCRIBE request;
1002 const NL_POLICY policy[] = {
1003 [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
1004 [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
1007 UNREFERENCED_PARAMETER(replyLen);
1009 POVS_OPEN_INSTANCE instance =
1010 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1011 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1013 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1014 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, 2);
1016 status = STATUS_INVALID_PARAMETER;
1020 /* XXX Ignore the MC group for now */
1021 join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1022 request.dpNo = msgIn->ovsHdr.dp_ifindex;
1023 request.subscribe = join;
1024 request.mask = OVS_EVENT_MASK_ALL;
1026 status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1034 * --------------------------------------------------------------------------
1035 * Command Handler for 'OVS_DP_CMD_GET'.
1037 * The function handles both the dump based as well as the transaction based
1038 * 'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1039 * call to setup dump state, as well as subsequent calls to continue dumping
1041 * --------------------------------------------------------------------------
1044 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1047 if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1048 return HandleGetDpTransaction(usrParamsCtx, replyLen);
1050 return HandleGetDpDump(usrParamsCtx, replyLen);
1055 * --------------------------------------------------------------------------
1056 * Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1057 * --------------------------------------------------------------------------
1060 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1063 return HandleDpTransaction(usrParamsCtx, replyLen);
1068 * --------------------------------------------------------------------------
1069 * Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1070 * --------------------------------------------------------------------------
1073 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1076 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1077 POVS_OPEN_INSTANCE instance =
1078 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1080 if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1082 OvsSetupDumpStart(usrParamsCtx);
1086 POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1088 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1090 if (instance->dumpState.ovsMsg == NULL) {
1092 return STATUS_INVALID_DEVICE_STATE;
1095 /* Dump state must have been deleted after previous dump operation. */
1096 ASSERT(instance->dumpState.index[0] == 0);
1097 /* Output buffer has been validated while validating read dev op. */
1098 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1100 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1101 usrParamsCtx->outputLength);
1103 OvsAcquireCtrlLock();
1104 if (!gOvsSwitchContext) {
1105 /* Treat this as a dump done. */
1106 OvsReleaseCtrlLock();
1108 FreeUserDumpState(instance);
1109 return STATUS_SUCCESS;
1111 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1112 OvsReleaseCtrlLock();
1114 if (status != STATUS_SUCCESS) {
1116 FreeUserDumpState(instance);
1120 /* Increment the dump index. */
1121 instance->dumpState.index[0] = 1;
1122 *replyLen = msgOut->nlMsg.nlmsgLen;
1124 /* Free up the dump state, since there's no more data to continue. */
1125 FreeUserDumpState(instance);
1128 return STATUS_SUCCESS;
1133 * --------------------------------------------------------------------------
1134 * Command Handler for 'OVS_DP_CMD_SET'.
1135 * --------------------------------------------------------------------------
1138 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1141 return HandleDpTransaction(usrParamsCtx, replyLen);
1145 * --------------------------------------------------------------------------
1146 * Function for handling transaction based 'OVS_DP_CMD_GET' and
1147 * 'OVS_DP_CMD_SET' commands.
1148 * --------------------------------------------------------------------------
1151 HandleDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1154 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1155 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1156 NTSTATUS status = STATUS_SUCCESS;
1158 static const NL_POLICY ovsDatapathSetPolicy[] = {
1159 [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1160 [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1161 [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1163 PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1165 /* input buffer has been validated while validating write dev op. */
1166 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1168 /* Parse any attributes in the request. */
1169 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1170 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1171 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1172 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1173 ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1174 return STATUS_INVALID_PARAMETER;
1178 * XXX: Not clear at this stage if there's any role for the
1179 * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1184 RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1187 /* Output buffer is optional for OVS_TRANSACTION_DEV_OP. */
1188 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1189 return STATUS_NDIS_INVALID_LENGTH;
1191 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1193 OvsAcquireCtrlLock();
1194 if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1195 if (!gOvsSwitchContext &&
1196 !OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1197 OVS_SYSTEM_DP_NAME)) {
1198 OvsReleaseCtrlLock();
1199 status = STATUS_NOT_FOUND;
1202 } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1203 OvsReleaseCtrlLock();
1204 status = STATUS_NOT_FOUND;
1208 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1209 OvsReleaseCtrlLock();
1211 *replyLen = NlBufSize(&nlBuf);
1219 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1221 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1222 POVS_OPEN_INSTANCE instance =
1223 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1225 /* input buffer has been validated while validating write dev op. */
1226 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1228 /* A write operation that does not indicate dump start is invalid. */
1229 if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1230 return STATUS_INVALID_PARAMETER;
1232 /* XXX: Handle other NLM_F_* flags in the future. */
1235 * This operation should be setting up the dump state. If there's any
1236 * previous state, clear it up so as to set it up afresh.
1238 if (instance->dumpState.ovsMsg != NULL) {
1239 FreeUserDumpState(instance);
1242 return InitUserDumpState(instance, msgIn);
1246 BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
1247 UINT32 length, UINT16 flags)
1249 msgOut->nlMsg.nlmsgType = type;
1250 msgOut->nlMsg.nlmsgFlags = flags;
1251 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1252 msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1253 msgOut->nlMsg.nlmsgLen = length;
1255 msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
1256 msgOut->genlMsg.version = nlDatapathFamilyOps.version;
1257 msgOut->genlMsg.reserved = 0;
1261 BuildReplyMsgFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
1263 BuildMsgOut(msgIn, msgOut, msgIn->nlMsg.nlmsgType, sizeof(OVS_MESSAGE),
1268 BuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode)
1270 BuildMsgOut(msgIn, (POVS_MESSAGE)msgOut, NLMSG_ERROR,
1271 sizeof(OVS_MESSAGE_ERROR), 0);
1273 msgOut->errorMsg.error = errorCode;
1274 msgOut->errorMsg.nlMsg = msgIn->nlMsg;
1278 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1285 OVS_VPORT_FULL_STATS vportStats;
1290 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1292 BuildReplyMsgFromMsgIn(msgIn, &msgOut, NLM_F_MULTI);
1293 msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1295 ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1297 return STATUS_INSUFFICIENT_RESOURCES;
1300 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1302 return STATUS_INSUFFICIENT_RESOURCES;
1305 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1307 return STATUS_INSUFFICIENT_RESOURCES;
1310 ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1312 return STATUS_INSUFFICIENT_RESOURCES;
1316 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1317 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1318 * it means we have an array of pids, instead of a single pid.
1319 * ATM we assume we have one pid only.
1322 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1325 return STATUS_INSUFFICIENT_RESOURCES;
1329 vportStats.rxPackets = vport->stats.rxPackets;
1330 vportStats.rxBytes = vport->stats.rxBytes;
1331 vportStats.txPackets = vport->stats.txPackets;
1332 vportStats.txBytes = vport->stats.txBytes;
1333 vportStats.rxErrors = vport->errStats.rxErrors;
1334 vportStats.txErrors = vport->errStats.txErrors;
1335 vportStats.rxDropped = vport->errStats.rxDropped;
1336 vportStats.txDropped = vport->errStats.txDropped;
1338 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1340 sizeof(OVS_VPORT_FULL_STATS));
1342 return STATUS_INSUFFICIENT_RESOURCES;
1346 * XXX: when vxlan udp dest port becomes configurable, we will also need
1347 * to add vport options
1350 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1351 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1353 return STATUS_SUCCESS;
1357 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1361 POVS_OPEN_INSTANCE instance =
1362 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1363 LOCK_STATE_EX lockState;
1364 UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1367 * XXX: this function shares some code with other dump command(s).
1368 * In the future, we will need to refactor the dump functions
1371 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1373 if (instance->dumpState.ovsMsg == NULL) {
1375 return STATUS_INVALID_DEVICE_STATE;
1378 /* Output buffer has been validated while validating read dev op. */
1379 ASSERT(usrParamsCtx->outputBuffer != NULL);
1381 msgIn = instance->dumpState.ovsMsg;
1383 OvsAcquireCtrlLock();
1384 if (!gOvsSwitchContext) {
1385 /* Treat this as a dump done. */
1386 OvsReleaseCtrlLock();
1388 FreeUserDumpState(instance);
1389 return STATUS_SUCCESS;
1393 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1394 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1395 * it means we have an array of pids, instead of a single pid.
1396 * ATM we assume we have one pid only.
1399 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1401 if (gOvsSwitchContext->numVports > 0) {
1402 /* inBucket: the bucket, used for lookup */
1403 UINT32 inBucket = instance->dumpState.index[0];
1404 /* inIndex: index within the given bucket, used for lookup */
1405 UINT32 inIndex = instance->dumpState.index[1];
1406 /* the bucket to be used for the next dump operation */
1407 UINT32 outBucket = 0;
1408 /* the index within the outBucket to be used for the next dump */
1409 UINT32 outIndex = 0;
1411 for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1412 PLIST_ENTRY head, link;
1413 head = &(gOvsSwitchContext->portHashArray[i]);
1414 POVS_VPORT_ENTRY vport = NULL;
1417 LIST_FORALL(head, link) {
1420 * if one or more dumps were previously done on this same bucket,
1421 * inIndex will be > 0, so we'll need to reply with the
1422 * inIndex + 1 vport from the bucket.
1424 if (outIndex >= inIndex) {
1425 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portLink);
1427 if (vport->portNo != 0) {
1428 OvsCreateMsgFromVport(vport, msgIn,
1429 usrParamsCtx->outputBuffer,
1430 usrParamsCtx->outputLength,
1431 gOvsSwitchContext->dpNo);
1447 * if no vport was found above, check the next bucket, beginning
1448 * with the first (i.e. index 0) elem from within that bucket
1455 /* XXX: what about NLMSG_DONE (as msg type)? */
1456 instance->dumpState.index[0] = outBucket;
1457 instance->dumpState.index[1] = outIndex;
1460 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1462 OvsReleaseCtrlLock();
1464 /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1465 if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1466 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1467 *replyLen = msgOut->nlMsg.nlmsgLen;
1470 * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1474 /* Free up the dump state, since there's no more data to continue. */
1475 FreeUserDumpState(instance);
1478 return STATUS_SUCCESS;
1482 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1485 NTSTATUS status = STATUS_SUCCESS;
1486 LOCK_STATE_EX lockState;
1488 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1489 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1490 POVS_VPORT_ENTRY vport = NULL;
1491 NL_ERROR nlError = NL_ERROR_SUCCESS;
1493 static const NL_POLICY ovsVportPolicy[] = {
1494 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1495 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1500 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1502 /* input buffer has been validated while validating write dev op. */
1503 ASSERT(usrParamsCtx->inputBuffer != NULL);
1505 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1506 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1507 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1508 return STATUS_INVALID_PARAMETER;
1511 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1512 return STATUS_INVALID_BUFFER_SIZE;
1515 OvsAcquireCtrlLock();
1516 if (!gOvsSwitchContext) {
1517 OvsReleaseCtrlLock();
1518 return STATUS_INVALID_PARAMETER;
1520 OvsReleaseCtrlLock();
1522 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1523 vport = OvsFindVportByOvsName(gOvsSwitchContext,
1524 NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]),
1525 NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]) - 1);
1526 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1527 vport = OvsFindVportByPortNo(gOvsSwitchContext,
1528 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
1530 nlError = NL_ERROR_INVAL;
1535 nlError = NL_ERROR_NODEV;
1539 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1540 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1541 usrParamsCtx->outputLength,
1542 gOvsSwitchContext->dpNo);
1543 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1545 *replyLen = msgOut->nlMsg.nlmsgLen;
1548 if (nlError != NL_ERROR_SUCCESS) {
1549 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1550 usrParamsCtx->outputBuffer;
1552 BuildErrorMsg(msgIn, msgError, nlError);
1553 *replyLen = msgError->nlMsg.nlmsgLen;
1556 return STATUS_SUCCESS;
1560 * --------------------------------------------------------------------------
1561 * Handler for the get vport command. The function handles the initial call to
1562 * setup the dump state, as well as subsequent calls to continue dumping data.
1563 * --------------------------------------------------------------------------
1566 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1571 switch (usrParamsCtx->devOp)
1573 case OVS_WRITE_DEV_OP:
1574 return OvsSetupDumpStart(usrParamsCtx);
1576 case OVS_READ_DEV_OP:
1577 return OvsGetVportDumpNext(usrParamsCtx, replyLen);
1579 case OVS_TRANSACTION_DEV_OP:
1580 return OvsGetVport(usrParamsCtx, replyLen);
1583 return STATUS_INVALID_DEVICE_REQUEST;
1589 * --------------------------------------------------------------------------
1590 * Utility function to map the output buffer in an IRP. The buffer is assumed
1591 * to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
1592 * --------------------------------------------------------------------------
1595 MapIrpOutputBuffer(PIRP irp,
1596 UINT32 bufferLength,
1597 UINT32 requiredLength,
1602 ASSERT(bufferLength);
1603 ASSERT(requiredLength);
1604 if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
1605 return STATUS_INVALID_PARAMETER;
1608 if (bufferLength < requiredLength) {
1609 return STATUS_NDIS_INVALID_LENGTH;
1611 if (irp->MdlAddress == NULL) {
1612 return STATUS_INVALID_PARAMETER;
1614 *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
1615 NormalPagePriority);
1616 if (*buffer == NULL) {
1617 return STATUS_INSUFFICIENT_RESOURCES;
1620 return STATUS_SUCCESS;
1624 * --------------------------------------------------------------------------
1625 * Utility function to fill up information about the state of a port in a reply
1627 * Assumes that 'gOvsCtrlLock' lock is acquired.
1628 * --------------------------------------------------------------------------
1631 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1632 POVS_EVENT_ENTRY eventEntry,
1637 OVS_MESSAGE msgOutTmp;
1639 POVS_VPORT_ENTRY vport;
1641 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
1643 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
1644 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
1646 /* driver intiated messages should have zerp seq number*/
1647 msgOutTmp.nlMsg.nlmsgSeq = 0;
1648 msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
1650 msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
1651 msgOutTmp.genlMsg.reserved = 0;
1653 /* we don't have netdev yet, treat link up/down a adding/removing a port*/
1654 if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
1655 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
1656 } else if (eventEntry->status &
1657 (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
1658 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
1661 return STATUS_UNSUCCESSFUL;
1663 msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
1665 rc = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
1667 status = STATUS_INVALID_BUFFER_SIZE;
1671 vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
1673 status = STATUS_DEVICE_DOES_NOT_EXIST;
1677 rc = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) ||
1678 NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) ||
1679 NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
1681 status = STATUS_INVALID_BUFFER_SIZE;
1685 /* XXXX Should we add the port stats attributes?*/
1686 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1687 nlMsg->nlmsgLen = NlBufSize(nlBuf);
1688 status = STATUS_SUCCESS;
1696 * --------------------------------------------------------------------------
1697 * Handler for reading events from the driver event queue. This handler is
1698 * executed when user modes issues a socket receive on a socket assocaited
1699 * with the MC group for events.
1700 * XXX user mode should read multiple events in one system call
1701 * --------------------------------------------------------------------------
1704 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1708 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1709 POVS_OPEN_INSTANCE instance =
1710 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1714 OVS_EVENT_ENTRY eventEntry;
1716 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1718 /* Should never read events with a dump socket */
1719 ASSERT(instance->dumpState.ovsMsg == NULL);
1721 /* Must have an event queue */
1722 ASSERT(instance->eventQueue != NULL);
1724 /* Output buffer has been validated while validating read dev op. */
1725 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1727 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1729 OvsAcquireCtrlLock();
1730 if (!gOvsSwitchContext) {
1731 status = STATUS_SUCCESS;
1735 /* remove an event entry from the event queue */
1736 status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
1737 if (status != STATUS_SUCCESS) {
1741 status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
1742 if (status == NDIS_STATUS_SUCCESS) {
1743 *replyLen = NlBufSize(&nlBuf);
1747 OvsReleaseCtrlLock();
1750 #endif /* OVS_USE_NL_INTERFACE */