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 ovsNumberOfOpenInstances++;
457 instance->cookie = i;
461 ASSERT(i < OVS_MAX_OPEN_INSTANCES);
462 instance->fileObject = fileObject;
463 ASSERT(fileObject->FsContext == NULL);
464 instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
465 if (instance->pid == 0) {
466 /* XXX: check for rollover. */
468 fileObject->FsContext = instance;
469 OvsReleaseCtrlLock();
470 return STATUS_SUCCESS;
474 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
476 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
478 ASSERT(fileObject == instance->fileObject);
479 OvsCleanupEvent(instance);
480 OvsCleanupPacketQueue(instance);
484 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
486 POVS_OPEN_INSTANCE instance;
487 ASSERT(fileObject->FsContext);
488 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
489 ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
491 OvsAcquireCtrlLock();
492 fileObject->FsContext = NULL;
493 ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
494 ovsOpenInstanceArray[instance->cookie] = NULL;
495 ovsNumberOfOpenInstances--;
496 OvsReleaseCtrlLock();
497 ASSERT(instance->eventQueue == NULL);
498 ASSERT (instance->packetQueue == NULL);
499 OvsFreeMemory(instance);
503 OvsCompleteIrpRequest(PIRP irp,
507 irp->IoStatus.Information = infoPtr;
508 irp->IoStatus.Status = status;
509 IoCompleteRequest(irp, IO_NO_INCREMENT);
515 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
518 PIO_STACK_LOCATION irpSp;
519 NTSTATUS status = STATUS_SUCCESS;
520 PFILE_OBJECT fileObject;
521 POVS_DEVICE_EXTENSION ovsExt =
522 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
524 ASSERT(deviceObject == gOvsDeviceObject);
525 ASSERT(ovsExt != NULL);
527 irpSp = IoGetCurrentIrpStackLocation(irp);
528 fileObject = irpSp->FileObject;
529 OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
530 deviceObject, fileObject,
531 ovsExt->numberOpenInstance);
533 switch (irpSp->MajorFunction) {
535 status = OvsAddOpenInstance(ovsExt, fileObject);
536 if (STATUS_SUCCESS == status) {
537 InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
541 ASSERT(ovsExt->numberOpenInstance > 0);
542 OvsRemoveOpenInstance(fileObject);
543 InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
548 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
551 _Use_decl_annotations_
553 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
557 PIO_STACK_LOCATION irpSp;
558 PFILE_OBJECT fileObject;
560 NTSTATUS status = STATUS_SUCCESS;
562 POVS_DEVICE_EXTENSION ovsExt =
563 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
565 ASSERT(ovsExt->numberOpenInstance > 0);
568 UNREFERENCED_PARAMETER(deviceObject);
570 ASSERT(deviceObject == gOvsDeviceObject);
571 irpSp = IoGetCurrentIrpStackLocation(irp);
572 fileObject = irpSp->FileObject;
574 ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
576 OvsCleanupOpenInstance(fileObject);
578 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
583 * --------------------------------------------------------------------------
584 * IOCTL function handler for the device.
585 * --------------------------------------------------------------------------
588 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
592 PIO_STACK_LOCATION irpSp;
593 NTSTATUS status = STATUS_SUCCESS;
594 PFILE_OBJECT fileObject;
595 PVOID inputBuffer = NULL;
596 PVOID outputBuffer = NULL;
597 UINT32 inputBufferLen, outputBufferLen;
598 UINT32 code, replyLen = 0;
599 POVS_OPEN_INSTANCE instance;
601 OVS_MESSAGE ovsMsgReadOp;
603 NETLINK_FAMILY *nlFamilyOps;
604 OVS_USER_PARAMS_CONTEXT usrParamsCtx;
607 POVS_DEVICE_EXTENSION ovsExt =
608 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
609 ASSERT(deviceObject == gOvsDeviceObject);
611 ASSERT(ovsExt->numberOpenInstance > 0);
613 UNREFERENCED_PARAMETER(deviceObject);
616 irpSp = IoGetCurrentIrpStackLocation(irp);
618 ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
619 ASSERT(irpSp->FileObject != NULL);
621 fileObject = irpSp->FileObject;
622 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
623 code = irpSp->Parameters.DeviceIoControl.IoControlCode;
624 inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
625 outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
626 inputBuffer = irp->AssociatedIrp.SystemBuffer;
628 /* Concurrent netlink operations are not supported. */
629 if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
630 status = STATUS_RESOURCE_IN_USE;
635 * Validate the input/output buffer arguments depending on the type of the
639 case OVS_IOCTL_TRANSACT:
640 /* Input buffer is mandatory, output buffer is optional. */
641 if (outputBufferLen != 0) {
642 status = MapIrpOutputBuffer(irp, outputBufferLen,
643 sizeof *ovsMsg, &outputBuffer);
644 if (status != STATUS_SUCCESS) {
647 ASSERT(outputBuffer);
650 if (inputBufferLen < sizeof (*ovsMsg)) {
651 status = STATUS_NDIS_INVALID_LENGTH;
655 ovsMsg = inputBuffer;
656 devOp = OVS_TRANSACTION_DEV_OP;
659 case OVS_IOCTL_READ_EVENT:
660 /* This IOCTL is used to read events */
661 if (outputBufferLen != 0) {
662 status = MapIrpOutputBuffer(irp, outputBufferLen,
663 sizeof *ovsMsg, &outputBuffer);
664 if (status != STATUS_SUCCESS) {
667 ASSERT(outputBuffer);
669 status = STATUS_NDIS_INVALID_LENGTH;
675 ovsMsg = &ovsMsgReadOp;
676 ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
677 /* An "artificial" command so we can use NL family function table*/
678 ovsMsg->genlMsg.cmd = OVS_CTRL_CMD_EVENT_NOTIFY;
679 devOp = OVS_READ_DEV_OP;
683 /* Output buffer is mandatory. */
684 if (outputBufferLen != 0) {
685 status = MapIrpOutputBuffer(irp, outputBufferLen,
686 sizeof *ovsMsg, &outputBuffer);
687 if (status != STATUS_SUCCESS) {
690 ASSERT(outputBuffer);
692 status = STATUS_NDIS_INVALID_LENGTH;
697 * Operate in the mode that read ioctl is similar to ReadFile(). This
698 * might change as the userspace code gets implemented.
704 * For implementing read (ioctl or otherwise), we need to store some
705 * state in the instance to indicate the command that started the dump
706 * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
707 * that 'ovsMsgReadOp' is needed only in this function to call into the
708 * appropraite handler. The handler itself can access the state in the
711 * In the absence of a dump start, return 0 bytes.
713 if (instance->dumpState.ovsMsg == NULL) {
715 status = STATUS_SUCCESS;
718 RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
719 sizeof (ovsMsgReadOp));
721 /* Create an NL message for consumption. */
722 ovsMsg = &ovsMsgReadOp;
723 devOp = OVS_READ_DEV_OP;
727 case OVS_IOCTL_WRITE:
728 /* Input buffer is mandatory. */
729 if (inputBufferLen < sizeof (*ovsMsg)) {
730 status = STATUS_NDIS_INVALID_LENGTH;
734 ovsMsg = inputBuffer;
735 devOp = OVS_WRITE_DEV_OP;
739 status = STATUS_INVALID_DEVICE_REQUEST;
744 switch (ovsMsg->nlMsg.nlmsgType) {
745 case OVS_WIN_NL_CTRL_FAMILY_ID:
746 nlFamilyOps = &nlControlFamilyOps;
748 case OVS_WIN_NL_DATAPATH_FAMILY_ID:
749 nlFamilyOps = &nlDatapathFamilyOps;
751 case OVS_WIN_NL_FLOW_FAMILY_ID:
752 nlFamilyOps = &nlFLowFamilyOps;
754 case OVS_WIN_NL_PACKET_FAMILY_ID:
755 status = STATUS_NOT_IMPLEMENTED;
757 case OVS_WIN_NL_VPORT_FAMILY_ID:
758 nlFamilyOps = &nlVportFamilyOps;
761 status = STATUS_INVALID_PARAMETER;
766 * For read operation, the netlink command has already been validated
769 if (devOp != OVS_READ_DEV_OP) {
770 status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
771 if (status != STATUS_SUCCESS) {
776 InitUserParamsCtx(irp, instance, devOp, ovsMsg,
777 inputBuffer, inputBufferLen,
778 outputBuffer, outputBufferLen,
781 status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
786 return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
791 * --------------------------------------------------------------------------
792 * Function to validate a netlink command. Only certain combinations of
793 * (device operation, netlink family, command) are valid.
794 * --------------------------------------------------------------------------
797 ValidateNetlinkCmd(UINT32 devOp,
798 POVS_OPEN_INSTANCE instance,
800 NETLINK_FAMILY *nlFamilyOps)
802 NTSTATUS status = STATUS_INVALID_PARAMETER;
805 for (i = 0; i < nlFamilyOps->opsCount; i++) {
806 if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
807 /* Validate if the command is valid for the device operation. */
808 if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
809 status = STATUS_INVALID_PARAMETER;
813 /* Validate the version. */
814 if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
815 status = STATUS_INVALID_PARAMETER;
819 /* Validate the DP for commands that require a DP. */
820 if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
821 OvsAcquireCtrlLock();
822 if (ovsMsg->ovsHdr.dp_ifindex !=
823 (INT)gOvsSwitchContext->dpNo) {
824 status = STATUS_INVALID_PARAMETER;
825 OvsReleaseCtrlLock();
828 OvsReleaseCtrlLock();
831 /* Validate the PID. */
832 if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
833 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
834 status = STATUS_INVALID_PARAMETER;
839 status = STATUS_SUCCESS;
849 * --------------------------------------------------------------------------
850 * Function to invoke the netlink command handler.
851 * --------------------------------------------------------------------------
854 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
855 NETLINK_FAMILY *nlFamilyOps,
858 NTSTATUS status = STATUS_INVALID_PARAMETER;
861 for (i = 0; i < nlFamilyOps->opsCount; i++) {
862 if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
863 NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
866 status = handler(usrParamsCtx, replyLen);
876 * --------------------------------------------------------------------------
877 * Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
879 * Each handle on the device is assigned a unique PID when the handle is
880 * created. On platforms that support netlink natively, the PID is available
881 * to userspace when the netlink socket is created. However, without native
882 * netlink support on Windows, OVS datapath generates the PID and lets the
883 * userspace query it.
885 * This function implements the query.
886 * --------------------------------------------------------------------------
889 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
892 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
893 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
895 if (usrParamsCtx->outputLength >= sizeof *msgOut) {
896 POVS_OPEN_INSTANCE instance =
897 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
899 RtlZeroMemory(msgOut, sizeof *msgOut);
900 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
901 msgOut->nlMsg.nlmsgPid = instance->pid;
902 *replyLen = sizeof *msgOut;
903 /* XXX: We might need to return the DP index as well. */
905 return STATUS_NDIS_INVALID_LENGTH;
908 return STATUS_SUCCESS;
912 * --------------------------------------------------------------------------
913 * Utility function to fill up information about the datapath in a reply to
915 * Assumes that 'gOvsCtrlLock' lock is acquired.
916 * --------------------------------------------------------------------------
919 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
924 OVS_MESSAGE msgOutTmp;
925 OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
928 /* XXX: Add API for nlBuf->bufRemLen. */
929 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof *msgIn);
931 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
932 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
933 msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
934 msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
936 msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
937 msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
938 msgOutTmp.genlMsg.reserved = 0;
940 msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
942 writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
944 writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
948 OVS_DP_STATS dpStats;
950 dpStats.n_hit = datapath->hits;
951 dpStats.n_missed = datapath->misses;
952 dpStats.n_lost = datapath->lost;
953 dpStats.n_flows = datapath->nFlows;
954 writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
955 (PCHAR)&dpStats, sizeof dpStats);
957 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
958 nlMsg->nlmsgLen = NlBufSize(nlBuf);
960 return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
964 * --------------------------------------------------------------------------
965 * Handler for queueing an IRP used for event notification. The IRP is
966 * completed when a port state changes. STATUS_PENDING is returned on
967 * success. User mode keep a pending IRP at all times.
968 * --------------------------------------------------------------------------
971 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
976 UNREFERENCED_PARAMETER(replyLen);
978 POVS_OPEN_INSTANCE instance =
979 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
980 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
983 poll.dpNo = msgIn->ovsHdr.dp_ifindex;
984 status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
991 * --------------------------------------------------------------------------
992 * Handler for the subscription for the event queue
993 * --------------------------------------------------------------------------
996 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1000 OVS_EVENT_SUBSCRIBE request;
1004 const NL_POLICY policy[] = {
1005 [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
1006 [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
1009 UNREFERENCED_PARAMETER(replyLen);
1011 POVS_OPEN_INSTANCE instance =
1012 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1013 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1015 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1016 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, 2);
1018 status = STATUS_INVALID_PARAMETER;
1022 /* XXX Ignore the MC group for now */
1023 join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1024 request.dpNo = msgIn->ovsHdr.dp_ifindex;
1025 request.subscribe = join;
1026 request.mask = OVS_EVENT_MASK_ALL;
1028 status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1036 * --------------------------------------------------------------------------
1037 * Command Handler for 'OVS_DP_CMD_GET'.
1039 * The function handles both the dump based as well as the transaction based
1040 * 'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1041 * call to setup dump state, as well as subsequent calls to continue dumping
1043 * --------------------------------------------------------------------------
1046 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1049 if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1050 return HandleGetDpTransaction(usrParamsCtx, replyLen);
1052 return HandleGetDpDump(usrParamsCtx, replyLen);
1057 * --------------------------------------------------------------------------
1058 * Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1059 * --------------------------------------------------------------------------
1062 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1065 return HandleDpTransaction(usrParamsCtx, replyLen);
1070 * --------------------------------------------------------------------------
1071 * Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1072 * --------------------------------------------------------------------------
1075 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1078 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1079 POVS_OPEN_INSTANCE instance =
1080 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1082 if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1084 OvsSetupDumpStart(usrParamsCtx);
1088 POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1090 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1092 if (instance->dumpState.ovsMsg == NULL) {
1094 return STATUS_INVALID_DEVICE_STATE;
1097 /* Dump state must have been deleted after previous dump operation. */
1098 ASSERT(instance->dumpState.index[0] == 0);
1099 /* Output buffer has been validated while validating read dev op. */
1100 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1102 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1103 usrParamsCtx->outputLength);
1105 OvsAcquireCtrlLock();
1106 if (!gOvsSwitchContext) {
1107 /* Treat this as a dump done. */
1108 OvsReleaseCtrlLock();
1110 FreeUserDumpState(instance);
1111 return STATUS_SUCCESS;
1113 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1114 OvsReleaseCtrlLock();
1116 if (status != STATUS_SUCCESS) {
1118 FreeUserDumpState(instance);
1122 /* Increment the dump index. */
1123 instance->dumpState.index[0] = 1;
1124 *replyLen = msgOut->nlMsg.nlmsgLen;
1126 /* Free up the dump state, since there's no more data to continue. */
1127 FreeUserDumpState(instance);
1130 return STATUS_SUCCESS;
1135 * --------------------------------------------------------------------------
1136 * Command Handler for 'OVS_DP_CMD_SET'.
1137 * --------------------------------------------------------------------------
1140 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1143 return HandleDpTransaction(usrParamsCtx, replyLen);
1147 * --------------------------------------------------------------------------
1148 * Function for handling transaction based 'OVS_DP_CMD_GET' and
1149 * 'OVS_DP_CMD_SET' commands.
1150 * --------------------------------------------------------------------------
1153 HandleDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1156 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1157 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1158 NTSTATUS status = STATUS_SUCCESS;
1160 static const NL_POLICY ovsDatapathSetPolicy[] = {
1161 [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1162 [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1163 [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1165 PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1167 /* input buffer has been validated while validating write dev op. */
1168 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1170 /* Parse any attributes in the request. */
1171 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1172 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1173 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1174 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1175 ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1176 return STATUS_INVALID_PARAMETER;
1180 * XXX: Not clear at this stage if there's any role for the
1181 * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1186 RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1189 /* Output buffer is optional for OVS_TRANSACTION_DEV_OP. */
1190 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1191 return STATUS_NDIS_INVALID_LENGTH;
1193 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1195 OvsAcquireCtrlLock();
1196 if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1197 if (!gOvsSwitchContext &&
1198 !OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1199 OVS_SYSTEM_DP_NAME)) {
1200 OvsReleaseCtrlLock();
1201 status = STATUS_NOT_FOUND;
1204 } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1205 OvsReleaseCtrlLock();
1206 status = STATUS_NOT_FOUND;
1210 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1211 OvsReleaseCtrlLock();
1213 *replyLen = NlBufSize(&nlBuf);
1221 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1223 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1224 POVS_OPEN_INSTANCE instance =
1225 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1227 /* input buffer has been validated while validating write dev op. */
1228 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1230 /* A write operation that does not indicate dump start is invalid. */
1231 if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1232 return STATUS_INVALID_PARAMETER;
1234 /* XXX: Handle other NLM_F_* flags in the future. */
1237 * This operation should be setting up the dump state. If there's any
1238 * previous state, clear it up so as to set it up afresh.
1240 if (instance->dumpState.ovsMsg != NULL) {
1241 FreeUserDumpState(instance);
1244 return InitUserDumpState(instance, msgIn);
1248 BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
1249 UINT32 length, UINT16 flags)
1251 msgOut->nlMsg.nlmsgType = type;
1252 msgOut->nlMsg.nlmsgFlags = flags;
1253 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1254 msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1255 msgOut->nlMsg.nlmsgLen = length;
1257 msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
1258 msgOut->genlMsg.version = nlDatapathFamilyOps.version;
1259 msgOut->genlMsg.reserved = 0;
1263 BuildReplyMsgFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
1265 BuildMsgOut(msgIn, msgOut, msgIn->nlMsg.nlmsgType, sizeof(OVS_MESSAGE),
1270 BuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode)
1272 BuildMsgOut(msgIn, (POVS_MESSAGE)msgOut, NLMSG_ERROR,
1273 sizeof(OVS_MESSAGE_ERROR), 0);
1275 msgOut->errorMsg.error = errorCode;
1276 msgOut->errorMsg.nlMsg = msgIn->nlMsg;
1280 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1287 OVS_VPORT_FULL_STATS vportStats;
1292 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1294 BuildReplyMsgFromMsgIn(msgIn, &msgOut, NLM_F_MULTI);
1295 msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1297 ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1299 return STATUS_INSUFFICIENT_RESOURCES;
1302 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1304 return STATUS_INSUFFICIENT_RESOURCES;
1307 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1309 return STATUS_INSUFFICIENT_RESOURCES;
1312 ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1314 return STATUS_INSUFFICIENT_RESOURCES;
1318 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1319 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1320 * it means we have an array of pids, instead of a single pid.
1321 * ATM we assume we have one pid only.
1324 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1327 return STATUS_INSUFFICIENT_RESOURCES;
1331 vportStats.rxPackets = vport->stats.rxPackets;
1332 vportStats.rxBytes = vport->stats.rxBytes;
1333 vportStats.txPackets = vport->stats.txPackets;
1334 vportStats.txBytes = vport->stats.txBytes;
1335 vportStats.rxErrors = vport->errStats.rxErrors;
1336 vportStats.txErrors = vport->errStats.txErrors;
1337 vportStats.rxDropped = vport->errStats.rxDropped;
1338 vportStats.txDropped = vport->errStats.txDropped;
1340 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1342 sizeof(OVS_VPORT_FULL_STATS));
1344 return STATUS_INSUFFICIENT_RESOURCES;
1348 * XXX: when vxlan udp dest port becomes configurable, we will also need
1349 * to add vport options
1352 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1353 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1355 return STATUS_SUCCESS;
1359 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1363 POVS_OPEN_INSTANCE instance =
1364 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1365 LOCK_STATE_EX lockState;
1366 UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1369 * XXX: this function shares some code with other dump command(s).
1370 * In the future, we will need to refactor the dump functions
1373 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1375 if (instance->dumpState.ovsMsg == NULL) {
1377 return STATUS_INVALID_DEVICE_STATE;
1380 /* Output buffer has been validated while validating read dev op. */
1381 ASSERT(usrParamsCtx->outputBuffer != NULL);
1383 msgIn = instance->dumpState.ovsMsg;
1385 OvsAcquireCtrlLock();
1386 if (!gOvsSwitchContext) {
1387 /* Treat this as a dump done. */
1388 OvsReleaseCtrlLock();
1390 FreeUserDumpState(instance);
1391 return STATUS_SUCCESS;
1395 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1396 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1397 * it means we have an array of pids, instead of a single pid.
1398 * ATM we assume we have one pid only.
1401 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1403 if (gOvsSwitchContext->numVports > 0) {
1404 /* inBucket: the bucket, used for lookup */
1405 UINT32 inBucket = instance->dumpState.index[0];
1406 /* inIndex: index within the given bucket, used for lookup */
1407 UINT32 inIndex = instance->dumpState.index[1];
1408 /* the bucket to be used for the next dump operation */
1409 UINT32 outBucket = 0;
1410 /* the index within the outBucket to be used for the next dump */
1411 UINT32 outIndex = 0;
1413 for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1414 PLIST_ENTRY head, link;
1415 head = &(gOvsSwitchContext->portHashArray[i]);
1416 POVS_VPORT_ENTRY vport = NULL;
1419 LIST_FORALL(head, link) {
1422 * if one or more dumps were previously done on this same bucket,
1423 * inIndex will be > 0, so we'll need to reply with the
1424 * inIndex + 1 vport from the bucket.
1426 if (outIndex >= inIndex) {
1427 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portLink);
1429 if (vport->portNo != 0) {
1430 OvsCreateMsgFromVport(vport, msgIn,
1431 usrParamsCtx->outputBuffer,
1432 usrParamsCtx->outputLength,
1433 gOvsSwitchContext->dpNo);
1449 * if no vport was found above, check the next bucket, beginning
1450 * with the first (i.e. index 0) elem from within that bucket
1457 /* XXX: what about NLMSG_DONE (as msg type)? */
1458 instance->dumpState.index[0] = outBucket;
1459 instance->dumpState.index[1] = outIndex;
1462 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1464 OvsReleaseCtrlLock();
1466 /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1467 if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1468 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1469 *replyLen = msgOut->nlMsg.nlmsgLen;
1472 * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1476 /* Free up the dump state, since there's no more data to continue. */
1477 FreeUserDumpState(instance);
1480 return STATUS_SUCCESS;
1484 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1487 NTSTATUS status = STATUS_SUCCESS;
1488 LOCK_STATE_EX lockState;
1490 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1491 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1492 POVS_VPORT_ENTRY vport = NULL;
1493 NL_ERROR nlError = NL_ERROR_SUCCESS;
1495 static const NL_POLICY ovsVportPolicy[] = {
1496 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1497 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1502 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1504 /* input buffer has been validated while validating write dev op. */
1505 ASSERT(usrParamsCtx->inputBuffer != NULL);
1507 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1508 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1509 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1510 return STATUS_INVALID_PARAMETER;
1513 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1514 return STATUS_INVALID_BUFFER_SIZE;
1517 OvsAcquireCtrlLock();
1518 if (!gOvsSwitchContext) {
1519 OvsReleaseCtrlLock();
1520 return STATUS_INVALID_PARAMETER;
1522 OvsReleaseCtrlLock();
1524 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1525 vport = OvsFindVportByOvsName(gOvsSwitchContext,
1526 NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]),
1527 NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]) - 1);
1528 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1529 vport = OvsFindVportByPortNo(gOvsSwitchContext,
1530 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
1532 nlError = NL_ERROR_INVAL;
1537 nlError = NL_ERROR_NODEV;
1541 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1542 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1543 usrParamsCtx->outputLength,
1544 gOvsSwitchContext->dpNo);
1545 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1547 *replyLen = msgOut->nlMsg.nlmsgLen;
1550 if (nlError != NL_ERROR_SUCCESS) {
1551 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1552 usrParamsCtx->outputBuffer;
1554 BuildErrorMsg(msgIn, msgError, nlError);
1555 *replyLen = msgError->nlMsg.nlmsgLen;
1558 return STATUS_SUCCESS;
1562 * --------------------------------------------------------------------------
1563 * Handler for the get vport command. The function handles the initial call to
1564 * setup the dump state, as well as subsequent calls to continue dumping data.
1565 * --------------------------------------------------------------------------
1568 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1573 switch (usrParamsCtx->devOp)
1575 case OVS_WRITE_DEV_OP:
1576 return OvsSetupDumpStart(usrParamsCtx);
1578 case OVS_READ_DEV_OP:
1579 return OvsGetVportDumpNext(usrParamsCtx, replyLen);
1581 case OVS_TRANSACTION_DEV_OP:
1582 return OvsGetVport(usrParamsCtx, replyLen);
1585 return STATUS_INVALID_DEVICE_REQUEST;
1591 * --------------------------------------------------------------------------
1592 * Utility function to map the output buffer in an IRP. The buffer is assumed
1593 * to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
1594 * --------------------------------------------------------------------------
1597 MapIrpOutputBuffer(PIRP irp,
1598 UINT32 bufferLength,
1599 UINT32 requiredLength,
1604 ASSERT(bufferLength);
1605 ASSERT(requiredLength);
1606 if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
1607 return STATUS_INVALID_PARAMETER;
1610 if (bufferLength < requiredLength) {
1611 return STATUS_NDIS_INVALID_LENGTH;
1613 if (irp->MdlAddress == NULL) {
1614 return STATUS_INVALID_PARAMETER;
1616 *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
1617 NormalPagePriority);
1618 if (*buffer == NULL) {
1619 return STATUS_INSUFFICIENT_RESOURCES;
1622 return STATUS_SUCCESS;
1626 * --------------------------------------------------------------------------
1627 * Utility function to fill up information about the state of a port in a reply
1629 * Assumes that 'gOvsCtrlLock' lock is acquired.
1630 * --------------------------------------------------------------------------
1633 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1634 POVS_EVENT_ENTRY eventEntry,
1639 OVS_MESSAGE msgOutTmp;
1641 POVS_VPORT_ENTRY vport;
1643 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
1645 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
1646 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
1648 /* driver intiated messages should have zerp seq number*/
1649 msgOutTmp.nlMsg.nlmsgSeq = 0;
1650 msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
1652 msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
1653 msgOutTmp.genlMsg.reserved = 0;
1655 /* we don't have netdev yet, treat link up/down a adding/removing a port*/
1656 if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
1657 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
1658 } else if (eventEntry->status &
1659 (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
1660 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
1663 return STATUS_UNSUCCESSFUL;
1665 msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
1667 rc = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
1669 status = STATUS_INVALID_BUFFER_SIZE;
1673 vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
1675 status = STATUS_DEVICE_DOES_NOT_EXIST;
1679 rc = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) ||
1680 NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) ||
1681 NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
1683 status = STATUS_INVALID_BUFFER_SIZE;
1687 /* XXXX Should we add the port stats attributes?*/
1688 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1689 nlMsg->nlmsgLen = NlBufSize(nlBuf);
1690 status = STATUS_SUCCESS;
1698 * --------------------------------------------------------------------------
1699 * Handler for reading events from the driver event queue. This handler is
1700 * executed when user modes issues a socket receive on a socket assocaited
1701 * with the MC group for events.
1702 * XXX user mode should read multiple events in one system call
1703 * --------------------------------------------------------------------------
1706 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1710 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1711 POVS_OPEN_INSTANCE instance =
1712 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1716 OVS_EVENT_ENTRY eventEntry;
1718 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1720 /* Should never read events with a dump socket */
1721 ASSERT(instance->dumpState.ovsMsg == NULL);
1723 /* Must have an event queue */
1724 ASSERT(instance->eventQueue != NULL);
1726 /* Output buffer has been validated while validating read dev op. */
1727 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1729 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1731 OvsAcquireCtrlLock();
1732 if (!gOvsSwitchContext) {
1733 status = STATUS_SUCCESS;
1737 /* remove an event entry from the event queue */
1738 status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
1739 if (status != STATUS_SUCCESS) {
1743 status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
1744 if (status == NDIS_STATUS_SUCCESS) {
1745 *replyLen = NlBufSize(&nlBuf);
1749 OvsReleaseCtrlLock();
1752 #endif /* OVS_USE_NL_INTERFACE */