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;
88 /* Handlers for the various netlink commands. */
89 static NetlinkCmdHandler OvsGetPidCmdHandler,
90 OvsPendEventCmdHandler,
91 OvsSubscribeEventCmdHandler,
92 OvsReadEventCmdHandler,
95 OvsGetVportCmdHandler;
97 NetlinkCmdHandler OvsGetNetdevCmdHandler;
99 static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
101 static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
103 static NTSTATUS HandleDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
107 * The various netlink families, along with the supported commands. Most of
108 * these families and commands are part of the openvswitch specification for a
109 * netlink datapath. In addition, each platform can implement a few families
110 * and commands as extensions.
113 /* Netlink control family: this is a Windows specific family. */
114 NETLINK_CMD nlControlFamilyCmdOps[] = {
115 { .cmd = OVS_CTRL_CMD_WIN_GET_PID,
116 .handler = OvsGetPidCmdHandler,
117 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
118 .validateDpIndex = FALSE,
120 { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ,
121 .handler = OvsPendEventCmdHandler,
122 .supportedDevOp = OVS_WRITE_DEV_OP,
123 .validateDpIndex = TRUE,
125 { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ,
126 .handler = OvsSubscribeEventCmdHandler,
127 .supportedDevOp = OVS_WRITE_DEV_OP,
128 .validateDpIndex = TRUE,
130 { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY,
131 .handler = OvsReadEventCmdHandler,
132 .supportedDevOp = OVS_READ_EVENT_DEV_OP,
133 .validateDpIndex = FALSE,
137 NETLINK_FAMILY nlControlFamilyOps = {
138 .name = OVS_WIN_CONTROL_FAMILY,
139 .id = OVS_WIN_NL_CTRL_FAMILY_ID,
140 .version = OVS_WIN_CONTROL_VERSION,
141 .maxAttr = OVS_WIN_CONTROL_ATTR_MAX,
142 .cmds = nlControlFamilyCmdOps,
143 .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
146 /* Netlink datapath family. */
147 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
148 { .cmd = OVS_DP_CMD_GET,
149 .handler = OvsGetDpCmdHandler,
150 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
151 OVS_TRANSACTION_DEV_OP,
152 .validateDpIndex = FALSE
154 { .cmd = OVS_DP_CMD_SET,
155 .handler = OvsSetDpCmdHandler,
156 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
157 OVS_TRANSACTION_DEV_OP,
158 .validateDpIndex = TRUE
162 NETLINK_FAMILY nlDatapathFamilyOps = {
163 .name = OVS_DATAPATH_FAMILY,
164 .id = OVS_WIN_NL_DATAPATH_FAMILY_ID,
165 .version = OVS_DATAPATH_VERSION,
166 .maxAttr = OVS_DP_ATTR_MAX,
167 .cmds = nlDatapathFamilyCmdOps,
168 .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
171 /* Netlink packet family. */
172 /* XXX: Add commands here. */
173 NETLINK_FAMILY nlPacketFamilyOps = {
174 .name = OVS_PACKET_FAMILY,
175 .id = OVS_WIN_NL_PACKET_FAMILY_ID,
176 .version = OVS_PACKET_VERSION,
177 .maxAttr = OVS_PACKET_ATTR_MAX,
178 .cmds = NULL, /* XXX: placeholder. */
182 /* Netlink vport family. */
183 NETLINK_CMD nlVportFamilyCmdOps[] = {
184 { .cmd = OVS_VPORT_CMD_GET,
185 .handler = OvsGetVportCmdHandler,
186 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
187 OVS_TRANSACTION_DEV_OP,
188 .validateDpIndex = TRUE
192 NETLINK_FAMILY nlVportFamilyOps = {
193 .name = OVS_VPORT_FAMILY,
194 .id = OVS_WIN_NL_VPORT_FAMILY_ID,
195 .version = OVS_VPORT_VERSION,
196 .maxAttr = OVS_VPORT_ATTR_MAX,
197 .cmds = nlVportFamilyCmdOps,
198 .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps)
201 /* Netlink flow family. */
203 NETLINK_CMD nlFlowFamilyCmdOps[] = {
204 { .cmd = OVS_FLOW_CMD_NEW,
205 .handler = OvsFlowNlCmdHandler,
206 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
207 .validateDpIndex = TRUE
209 { .cmd = OVS_FLOW_CMD_SET,
210 .handler = OvsFlowNlCmdHandler,
211 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
212 .validateDpIndex = TRUE
214 { .cmd = OVS_FLOW_CMD_DEL,
215 .handler = OvsFlowNlCmdHandler,
216 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
217 .validateDpIndex = TRUE
219 { .cmd = OVS_FLOW_CMD_GET,
220 .handler = OvsFlowNlGetCmdHandler,
221 .supportedDevOp = OVS_TRANSACTION_DEV_OP |
222 OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
223 .validateDpIndex = TRUE
227 NETLINK_FAMILY nlFLowFamilyOps = {
228 .name = OVS_FLOW_FAMILY,
229 .id = OVS_WIN_NL_FLOW_FAMILY_ID,
230 .version = OVS_FLOW_VERSION,
231 .maxAttr = OVS_FLOW_ATTR_MAX,
232 .cmds = nlFlowFamilyCmdOps,
233 .opsCount = ARRAY_SIZE(nlFlowFamilyCmdOps)
236 /* Netlink netdev family. */
237 NETLINK_CMD nlNetdevFamilyCmdOps[] = {
238 { .cmd = OVS_WIN_NETDEV_CMD_GET,
239 .handler = OvsGetNetdevCmdHandler,
240 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
241 .validateDpIndex = FALSE
245 NETLINK_FAMILY nlNetdevFamilyOps = {
246 .name = OVS_WIN_NETDEV_FAMILY,
247 .id = OVS_WIN_NL_NETDEV_FAMILY_ID,
248 .version = OVS_WIN_NETDEV_VERSION,
249 .maxAttr = OVS_WIN_NETDEV_ATTR_MAX,
250 .cmds = nlNetdevFamilyCmdOps,
251 .opsCount = ARRAY_SIZE(nlNetdevFamilyCmdOps)
254 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
256 UINT32 requiredLength,
258 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
259 POVS_OPEN_INSTANCE instance,
261 NETLINK_FAMILY *nlFamilyOps);
262 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
263 NETLINK_FAMILY *nlFamilyOps,
266 /* Handles to the device object for communication with userspace. */
267 NDIS_HANDLE gOvsDeviceHandle;
268 PDEVICE_OBJECT gOvsDeviceObject;
270 _Dispatch_type_(IRP_MJ_CREATE)
271 _Dispatch_type_(IRP_MJ_CLOSE)
272 DRIVER_DISPATCH OvsOpenCloseDevice;
274 _Dispatch_type_(IRP_MJ_CLEANUP)
275 DRIVER_DISPATCH OvsCleanupDevice;
277 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
278 DRIVER_DISPATCH OvsDeviceControl;
281 #pragma alloc_text(INIT, OvsCreateDeviceObject)
282 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
283 #pragma alloc_text(PAGE, OvsCleanupDevice)
284 #pragma alloc_text(PAGE, OvsDeviceControl)
285 #endif // ALLOC_PRAGMA
288 * We might hit this limit easily since userspace opens a netlink descriptor for
289 * each thread, and at least one descriptor per vport. Revisit this later.
291 #define OVS_MAX_OPEN_INSTANCES 512
292 #define OVS_SYSTEM_DP_NAME "ovs-system"
294 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
295 UINT32 ovsNumberOfOpenInstances;
296 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
298 NDIS_SPIN_LOCK ovsCtrlLockObj;
299 PNDIS_SPIN_LOCK gOvsCtrlLock;
305 gOvsCtrlLock = &ovsCtrlLockObj;
306 NdisAllocateSpinLock(gOvsCtrlLock);
314 OvsCleanupEventQueue();
316 NdisFreeSpinLock(gOvsCtrlLock);
325 NdisAcquireSpinLock(gOvsCtrlLock);
331 NdisReleaseSpinLock(gOvsCtrlLock);
336 * --------------------------------------------------------------------------
337 * Creates the communication device between user and kernel, and also
338 * initializes the data associated data structures.
339 * --------------------------------------------------------------------------
342 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
344 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
345 UNICODE_STRING deviceName;
346 UNICODE_STRING symbolicDeviceName;
347 PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
348 NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
349 OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
351 RtlZeroMemory(dispatchTable,
352 (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
353 dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
354 dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
355 dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
356 dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
358 NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
359 NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
361 RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
363 OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
364 NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
365 NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
366 sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
368 deviceAttributes.DeviceName = &deviceName;
369 deviceAttributes.SymbolicName = &symbolicDeviceName;
370 deviceAttributes.MajorFunctions = dispatchTable;
371 deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
373 status = NdisRegisterDeviceEx(ovsExtDriverHandle,
377 if (status != NDIS_STATUS_SUCCESS) {
378 POVS_DEVICE_EXTENSION ovsExt =
379 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
380 ASSERT(gOvsDeviceObject != NULL);
381 ASSERT(gOvsDeviceHandle != NULL);
384 ovsExt->numberOpenInstance = 0;
387 /* Initialize the associated data structures. */
390 OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
396 OvsDeleteDeviceObject()
398 if (gOvsDeviceHandle) {
400 POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
401 NdisGetDeviceReservedExtension(gOvsDeviceObject);
403 ASSERT(ovsExt->numberOpenInstance == 0);
407 ASSERT(gOvsDeviceObject);
408 NdisDeregisterDeviceEx(gOvsDeviceHandle);
409 gOvsDeviceHandle = NULL;
410 gOvsDeviceObject = NULL;
416 OvsGetOpenInstance(PFILE_OBJECT fileObject,
419 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
421 ASSERT(instance->fileObject == fileObject);
422 if (gOvsSwitchContext == NULL ||
423 gOvsSwitchContext->dpNo != dpNo) {
431 OvsFindOpenInstance(PFILE_OBJECT fileObject)
434 for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
435 j < ovsNumberOfOpenInstances; i++) {
436 if (ovsOpenInstanceArray[i]) {
437 if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
438 return ovsOpenInstanceArray[i];
447 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
448 PFILE_OBJECT fileObject)
450 POVS_OPEN_INSTANCE instance =
451 (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
454 if (instance == NULL) {
455 return STATUS_NO_MEMORY;
457 OvsAcquireCtrlLock();
458 ASSERT(OvsFindOpenInstance(fileObject) == NULL);
460 if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
461 OvsReleaseCtrlLock();
462 OvsFreeMemory(instance);
463 return STATUS_INSUFFICIENT_RESOURCES;
465 RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
467 for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
468 if (ovsOpenInstanceArray[i] == NULL) {
469 ovsOpenInstanceArray[i] = instance;
470 ovsNumberOfOpenInstances++;
471 instance->cookie = i;
475 ASSERT(i < OVS_MAX_OPEN_INSTANCES);
476 instance->fileObject = fileObject;
477 ASSERT(fileObject->FsContext == NULL);
478 instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
479 if (instance->pid == 0) {
480 /* XXX: check for rollover. */
482 fileObject->FsContext = instance;
483 OvsReleaseCtrlLock();
484 return STATUS_SUCCESS;
488 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
490 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
492 ASSERT(fileObject == instance->fileObject);
493 OvsCleanupEvent(instance);
494 OvsCleanupPacketQueue(instance);
498 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
500 POVS_OPEN_INSTANCE instance;
501 ASSERT(fileObject->FsContext);
502 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
503 ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
505 OvsAcquireCtrlLock();
506 fileObject->FsContext = NULL;
507 ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
508 ovsOpenInstanceArray[instance->cookie] = NULL;
509 ovsNumberOfOpenInstances--;
510 OvsReleaseCtrlLock();
511 ASSERT(instance->eventQueue == NULL);
512 ASSERT (instance->packetQueue == NULL);
513 OvsFreeMemory(instance);
517 OvsCompleteIrpRequest(PIRP irp,
521 irp->IoStatus.Information = infoPtr;
522 irp->IoStatus.Status = status;
523 IoCompleteRequest(irp, IO_NO_INCREMENT);
529 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
532 PIO_STACK_LOCATION irpSp;
533 NTSTATUS status = STATUS_SUCCESS;
534 PFILE_OBJECT fileObject;
535 POVS_DEVICE_EXTENSION ovsExt =
536 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
538 ASSERT(deviceObject == gOvsDeviceObject);
539 ASSERT(ovsExt != NULL);
541 irpSp = IoGetCurrentIrpStackLocation(irp);
542 fileObject = irpSp->FileObject;
543 OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
544 deviceObject, fileObject,
545 ovsExt->numberOpenInstance);
547 switch (irpSp->MajorFunction) {
549 status = OvsAddOpenInstance(ovsExt, fileObject);
550 if (STATUS_SUCCESS == status) {
551 InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
555 ASSERT(ovsExt->numberOpenInstance > 0);
556 OvsRemoveOpenInstance(fileObject);
557 InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
562 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
565 _Use_decl_annotations_
567 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
571 PIO_STACK_LOCATION irpSp;
572 PFILE_OBJECT fileObject;
574 NTSTATUS status = STATUS_SUCCESS;
576 POVS_DEVICE_EXTENSION ovsExt =
577 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
579 ASSERT(ovsExt->numberOpenInstance > 0);
582 UNREFERENCED_PARAMETER(deviceObject);
584 ASSERT(deviceObject == gOvsDeviceObject);
585 irpSp = IoGetCurrentIrpStackLocation(irp);
586 fileObject = irpSp->FileObject;
588 ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
590 OvsCleanupOpenInstance(fileObject);
592 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
597 * --------------------------------------------------------------------------
598 * IOCTL function handler for the device.
599 * --------------------------------------------------------------------------
602 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
606 PIO_STACK_LOCATION irpSp;
607 NTSTATUS status = STATUS_SUCCESS;
608 PFILE_OBJECT fileObject;
609 PVOID inputBuffer = NULL;
610 PVOID outputBuffer = NULL;
611 UINT32 inputBufferLen, outputBufferLen;
612 UINT32 code, replyLen = 0;
613 POVS_OPEN_INSTANCE instance;
615 OVS_MESSAGE ovsMsgReadOp;
617 NETLINK_FAMILY *nlFamilyOps;
618 OVS_USER_PARAMS_CONTEXT usrParamsCtx;
621 POVS_DEVICE_EXTENSION ovsExt =
622 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
623 ASSERT(deviceObject == gOvsDeviceObject);
625 ASSERT(ovsExt->numberOpenInstance > 0);
627 UNREFERENCED_PARAMETER(deviceObject);
630 irpSp = IoGetCurrentIrpStackLocation(irp);
632 ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
633 ASSERT(irpSp->FileObject != NULL);
635 fileObject = irpSp->FileObject;
636 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
637 code = irpSp->Parameters.DeviceIoControl.IoControlCode;
638 inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
639 outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
640 inputBuffer = irp->AssociatedIrp.SystemBuffer;
642 /* Concurrent netlink operations are not supported. */
643 if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
644 status = STATUS_RESOURCE_IN_USE;
649 * Validate the input/output buffer arguments depending on the type of the
653 case OVS_IOCTL_TRANSACT:
654 /* Input buffer is mandatory, output buffer is optional. */
655 if (outputBufferLen != 0) {
656 status = MapIrpOutputBuffer(irp, outputBufferLen,
657 sizeof *ovsMsg, &outputBuffer);
658 if (status != STATUS_SUCCESS) {
661 ASSERT(outputBuffer);
664 if (inputBufferLen < sizeof (*ovsMsg)) {
665 status = STATUS_NDIS_INVALID_LENGTH;
669 ovsMsg = inputBuffer;
670 devOp = OVS_TRANSACTION_DEV_OP;
673 case OVS_IOCTL_READ_EVENT:
674 /* This IOCTL is used to read events */
675 if (outputBufferLen != 0) {
676 status = MapIrpOutputBuffer(irp, outputBufferLen,
677 sizeof *ovsMsg, &outputBuffer);
678 if (status != STATUS_SUCCESS) {
681 ASSERT(outputBuffer);
683 status = STATUS_NDIS_INVALID_LENGTH;
689 ovsMsg = &ovsMsgReadOp;
690 ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
691 /* An "artificial" command so we can use NL family function table*/
692 ovsMsg->genlMsg.cmd = OVS_CTRL_CMD_EVENT_NOTIFY;
693 devOp = OVS_READ_DEV_OP;
697 /* Output buffer is mandatory. */
698 if (outputBufferLen != 0) {
699 status = MapIrpOutputBuffer(irp, outputBufferLen,
700 sizeof *ovsMsg, &outputBuffer);
701 if (status != STATUS_SUCCESS) {
704 ASSERT(outputBuffer);
706 status = STATUS_NDIS_INVALID_LENGTH;
711 * Operate in the mode that read ioctl is similar to ReadFile(). This
712 * might change as the userspace code gets implemented.
718 * For implementing read (ioctl or otherwise), we need to store some
719 * state in the instance to indicate the command that started the dump
720 * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
721 * that 'ovsMsgReadOp' is needed only in this function to call into the
722 * appropraite handler. The handler itself can access the state in the
725 * In the absence of a dump start, return 0 bytes.
727 if (instance->dumpState.ovsMsg == NULL) {
729 status = STATUS_SUCCESS;
732 RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
733 sizeof (ovsMsgReadOp));
735 /* Create an NL message for consumption. */
736 ovsMsg = &ovsMsgReadOp;
737 devOp = OVS_READ_DEV_OP;
741 case OVS_IOCTL_WRITE:
742 /* Input buffer is mandatory. */
743 if (inputBufferLen < sizeof (*ovsMsg)) {
744 status = STATUS_NDIS_INVALID_LENGTH;
748 ovsMsg = inputBuffer;
749 devOp = OVS_WRITE_DEV_OP;
753 status = STATUS_INVALID_DEVICE_REQUEST;
758 switch (ovsMsg->nlMsg.nlmsgType) {
759 case OVS_WIN_NL_CTRL_FAMILY_ID:
760 nlFamilyOps = &nlControlFamilyOps;
762 case OVS_WIN_NL_DATAPATH_FAMILY_ID:
763 nlFamilyOps = &nlDatapathFamilyOps;
765 case OVS_WIN_NL_FLOW_FAMILY_ID:
766 nlFamilyOps = &nlFLowFamilyOps;
768 case OVS_WIN_NL_PACKET_FAMILY_ID:
769 status = STATUS_NOT_IMPLEMENTED;
771 case OVS_WIN_NL_VPORT_FAMILY_ID:
772 nlFamilyOps = &nlVportFamilyOps;
774 case OVS_WIN_NL_NETDEV_FAMILY_ID:
775 nlFamilyOps = &nlNetdevFamilyOps;
778 status = STATUS_INVALID_PARAMETER;
783 * For read operation, the netlink command has already been validated
786 if (devOp != OVS_READ_DEV_OP) {
787 status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
788 if (status != STATUS_SUCCESS) {
793 InitUserParamsCtx(irp, instance, devOp, ovsMsg,
794 inputBuffer, inputBufferLen,
795 outputBuffer, outputBufferLen,
798 status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
803 return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
808 * --------------------------------------------------------------------------
809 * Function to validate a netlink command. Only certain combinations of
810 * (device operation, netlink family, command) are valid.
811 * --------------------------------------------------------------------------
814 ValidateNetlinkCmd(UINT32 devOp,
815 POVS_OPEN_INSTANCE instance,
817 NETLINK_FAMILY *nlFamilyOps)
819 NTSTATUS status = STATUS_INVALID_PARAMETER;
822 for (i = 0; i < nlFamilyOps->opsCount; i++) {
823 if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
824 /* Validate if the command is valid for the device operation. */
825 if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
826 status = STATUS_INVALID_PARAMETER;
830 /* Validate the version. */
831 if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
832 status = STATUS_INVALID_PARAMETER;
836 /* Validate the DP for commands that require a DP. */
837 if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
838 OvsAcquireCtrlLock();
839 if (ovsMsg->ovsHdr.dp_ifindex !=
840 (INT)gOvsSwitchContext->dpNo) {
841 status = STATUS_INVALID_PARAMETER;
842 OvsReleaseCtrlLock();
845 OvsReleaseCtrlLock();
848 /* Validate the PID. */
849 if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
850 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
851 status = STATUS_INVALID_PARAMETER;
856 status = STATUS_SUCCESS;
866 * --------------------------------------------------------------------------
867 * Function to invoke the netlink command handler.
868 * --------------------------------------------------------------------------
871 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
872 NETLINK_FAMILY *nlFamilyOps,
875 NTSTATUS status = STATUS_INVALID_PARAMETER;
878 for (i = 0; i < nlFamilyOps->opsCount; i++) {
879 if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
880 NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
883 status = handler(usrParamsCtx, replyLen);
893 * --------------------------------------------------------------------------
894 * Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
896 * Each handle on the device is assigned a unique PID when the handle is
897 * created. On platforms that support netlink natively, the PID is available
898 * to userspace when the netlink socket is created. However, without native
899 * netlink support on Windows, OVS datapath generates the PID and lets the
900 * userspace query it.
902 * This function implements the query.
903 * --------------------------------------------------------------------------
906 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
909 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
910 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
912 if (usrParamsCtx->outputLength >= sizeof *msgOut) {
913 POVS_OPEN_INSTANCE instance =
914 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
916 RtlZeroMemory(msgOut, sizeof *msgOut);
917 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
918 msgOut->nlMsg.nlmsgPid = instance->pid;
919 *replyLen = sizeof *msgOut;
920 /* XXX: We might need to return the DP index as well. */
922 return STATUS_NDIS_INVALID_LENGTH;
925 return STATUS_SUCCESS;
929 * --------------------------------------------------------------------------
930 * Utility function to fill up information about the datapath in a reply to
932 * Assumes that 'gOvsCtrlLock' lock is acquired.
933 * --------------------------------------------------------------------------
936 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
941 OVS_MESSAGE msgOutTmp;
942 OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
945 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && NlBufRemLen(nlBuf) >= sizeof *msgIn);
947 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
948 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
949 msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
950 msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
952 msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
953 msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
954 msgOutTmp.genlMsg.reserved = 0;
956 msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
958 writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
960 writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
964 OVS_DP_STATS dpStats;
966 dpStats.n_hit = datapath->hits;
967 dpStats.n_missed = datapath->misses;
968 dpStats.n_lost = datapath->lost;
969 dpStats.n_flows = datapath->nFlows;
970 writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
971 (PCHAR)&dpStats, sizeof dpStats);
973 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
974 nlMsg->nlmsgLen = NlBufSize(nlBuf);
976 return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
980 * --------------------------------------------------------------------------
981 * Handler for queueing an IRP used for event notification. The IRP is
982 * completed when a port state changes. STATUS_PENDING is returned on
983 * success. User mode keep a pending IRP at all times.
984 * --------------------------------------------------------------------------
987 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
992 UNREFERENCED_PARAMETER(replyLen);
994 POVS_OPEN_INSTANCE instance =
995 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
996 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
999 poll.dpNo = msgIn->ovsHdr.dp_ifindex;
1000 status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
1001 &poll, sizeof poll);
1006 * --------------------------------------------------------------------------
1007 * Handler for the subscription for the event queue
1008 * --------------------------------------------------------------------------
1011 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1015 OVS_EVENT_SUBSCRIBE request;
1019 const NL_POLICY policy[] = {
1020 [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
1021 [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
1024 UNREFERENCED_PARAMETER(replyLen);
1026 POVS_OPEN_INSTANCE instance =
1027 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1028 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1030 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1031 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, 2);
1033 status = STATUS_INVALID_PARAMETER;
1037 /* XXX Ignore the MC group for now */
1038 join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1039 request.dpNo = msgIn->ovsHdr.dp_ifindex;
1040 request.subscribe = join;
1041 request.mask = OVS_EVENT_MASK_ALL;
1043 status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1051 * --------------------------------------------------------------------------
1052 * Command Handler for 'OVS_DP_CMD_GET'.
1054 * The function handles both the dump based as well as the transaction based
1055 * 'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1056 * call to setup dump state, as well as subsequent calls to continue dumping
1058 * --------------------------------------------------------------------------
1061 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1064 if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1065 return HandleGetDpTransaction(usrParamsCtx, replyLen);
1067 return HandleGetDpDump(usrParamsCtx, replyLen);
1072 * --------------------------------------------------------------------------
1073 * Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1074 * --------------------------------------------------------------------------
1077 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1080 return HandleDpTransaction(usrParamsCtx, replyLen);
1085 * --------------------------------------------------------------------------
1086 * Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1087 * --------------------------------------------------------------------------
1090 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1093 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1094 POVS_OPEN_INSTANCE instance =
1095 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1097 if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1099 OvsSetupDumpStart(usrParamsCtx);
1103 POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1105 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1107 if (instance->dumpState.ovsMsg == NULL) {
1109 return STATUS_INVALID_DEVICE_STATE;
1112 /* Dump state must have been deleted after previous dump operation. */
1113 ASSERT(instance->dumpState.index[0] == 0);
1114 /* Output buffer has been validated while validating read dev op. */
1115 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1117 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1118 usrParamsCtx->outputLength);
1120 OvsAcquireCtrlLock();
1121 if (!gOvsSwitchContext) {
1122 /* Treat this as a dump done. */
1123 OvsReleaseCtrlLock();
1125 FreeUserDumpState(instance);
1126 return STATUS_SUCCESS;
1128 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1129 OvsReleaseCtrlLock();
1131 if (status != STATUS_SUCCESS) {
1133 FreeUserDumpState(instance);
1137 /* Increment the dump index. */
1138 instance->dumpState.index[0] = 1;
1139 *replyLen = msgOut->nlMsg.nlmsgLen;
1141 /* Free up the dump state, since there's no more data to continue. */
1142 FreeUserDumpState(instance);
1145 return STATUS_SUCCESS;
1150 * --------------------------------------------------------------------------
1151 * Command Handler for 'OVS_DP_CMD_SET'.
1152 * --------------------------------------------------------------------------
1155 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1158 return HandleDpTransaction(usrParamsCtx, replyLen);
1162 * --------------------------------------------------------------------------
1163 * Function for handling transaction based 'OVS_DP_CMD_GET' and
1164 * 'OVS_DP_CMD_SET' commands.
1165 * --------------------------------------------------------------------------
1168 HandleDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1171 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1172 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1173 NTSTATUS status = STATUS_SUCCESS;
1175 static const NL_POLICY ovsDatapathSetPolicy[] = {
1176 [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1177 [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1178 [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1180 PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1182 /* input buffer has been validated while validating write dev op. */
1183 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1185 /* Parse any attributes in the request. */
1186 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1187 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1188 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1189 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1190 ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1191 return STATUS_INVALID_PARAMETER;
1195 * XXX: Not clear at this stage if there's any role for the
1196 * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1201 RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1204 /* Output buffer is optional for OVS_TRANSACTION_DEV_OP. */
1205 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1206 return STATUS_NDIS_INVALID_LENGTH;
1208 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1210 OvsAcquireCtrlLock();
1211 if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1212 if (!gOvsSwitchContext &&
1213 !OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1214 OVS_SYSTEM_DP_NAME)) {
1215 OvsReleaseCtrlLock();
1216 status = STATUS_NOT_FOUND;
1219 } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1220 OvsReleaseCtrlLock();
1221 status = STATUS_NOT_FOUND;
1225 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1226 OvsReleaseCtrlLock();
1228 *replyLen = NlBufSize(&nlBuf);
1236 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1238 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1239 POVS_OPEN_INSTANCE instance =
1240 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1242 /* input buffer has been validated while validating write dev op. */
1243 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1245 /* A write operation that does not indicate dump start is invalid. */
1246 if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1247 return STATUS_INVALID_PARAMETER;
1249 /* XXX: Handle other NLM_F_* flags in the future. */
1252 * This operation should be setting up the dump state. If there's any
1253 * previous state, clear it up so as to set it up afresh.
1255 if (instance->dumpState.ovsMsg != NULL) {
1256 FreeUserDumpState(instance);
1259 return InitUserDumpState(instance, msgIn);
1263 BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
1264 UINT32 length, UINT16 flags)
1266 msgOut->nlMsg.nlmsgType = type;
1267 msgOut->nlMsg.nlmsgFlags = flags;
1268 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1269 msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1270 msgOut->nlMsg.nlmsgLen = length;
1272 msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
1273 msgOut->genlMsg.version = nlDatapathFamilyOps.version;
1274 msgOut->genlMsg.reserved = 0;
1278 BuildReplyMsgFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
1280 BuildMsgOut(msgIn, msgOut, msgIn->nlMsg.nlmsgType, sizeof(OVS_MESSAGE),
1285 BuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode)
1287 BuildMsgOut(msgIn, (POVS_MESSAGE)msgOut, NLMSG_ERROR,
1288 sizeof(OVS_MESSAGE_ERROR), 0);
1290 msgOut->errorMsg.error = errorCode;
1291 msgOut->errorMsg.nlMsg = msgIn->nlMsg;
1295 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1302 OVS_VPORT_FULL_STATS vportStats;
1307 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1309 BuildReplyMsgFromMsgIn(msgIn, &msgOut, NLM_F_MULTI);
1310 msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1312 ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1314 return STATUS_INSUFFICIENT_RESOURCES;
1317 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1319 return STATUS_INSUFFICIENT_RESOURCES;
1322 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1324 return STATUS_INSUFFICIENT_RESOURCES;
1327 ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1329 return STATUS_INSUFFICIENT_RESOURCES;
1333 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1334 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1335 * it means we have an array of pids, instead of a single pid.
1336 * ATM we assume we have one pid only.
1339 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1342 return STATUS_INSUFFICIENT_RESOURCES;
1346 vportStats.rxPackets = vport->stats.rxPackets;
1347 vportStats.rxBytes = vport->stats.rxBytes;
1348 vportStats.txPackets = vport->stats.txPackets;
1349 vportStats.txBytes = vport->stats.txBytes;
1350 vportStats.rxErrors = vport->errStats.rxErrors;
1351 vportStats.txErrors = vport->errStats.txErrors;
1352 vportStats.rxDropped = vport->errStats.rxDropped;
1353 vportStats.txDropped = vport->errStats.txDropped;
1355 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1357 sizeof(OVS_VPORT_FULL_STATS));
1359 return STATUS_INSUFFICIENT_RESOURCES;
1363 * XXX: when vxlan udp dest port becomes configurable, we will also need
1364 * to add vport options
1367 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1368 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1370 return STATUS_SUCCESS;
1374 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1378 POVS_OPEN_INSTANCE instance =
1379 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1380 LOCK_STATE_EX lockState;
1381 UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1384 * XXX: this function shares some code with other dump command(s).
1385 * In the future, we will need to refactor the dump functions
1388 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1390 if (instance->dumpState.ovsMsg == NULL) {
1392 return STATUS_INVALID_DEVICE_STATE;
1395 /* Output buffer has been validated while validating read dev op. */
1396 ASSERT(usrParamsCtx->outputBuffer != NULL);
1398 msgIn = instance->dumpState.ovsMsg;
1400 OvsAcquireCtrlLock();
1401 if (!gOvsSwitchContext) {
1402 /* Treat this as a dump done. */
1403 OvsReleaseCtrlLock();
1405 FreeUserDumpState(instance);
1406 return STATUS_SUCCESS;
1410 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1411 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1412 * it means we have an array of pids, instead of a single pid.
1413 * ATM we assume we have one pid only.
1415 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1416 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1417 NDIS_RWL_AT_DISPATCH_LEVEL);
1419 if (gOvsSwitchContext->numVports > 0) {
1420 /* inBucket: the bucket, used for lookup */
1421 UINT32 inBucket = instance->dumpState.index[0];
1422 /* inIndex: index within the given bucket, used for lookup */
1423 UINT32 inIndex = instance->dumpState.index[1];
1424 /* the bucket to be used for the next dump operation */
1425 UINT32 outBucket = 0;
1426 /* the index within the outBucket to be used for the next dump */
1427 UINT32 outIndex = 0;
1429 for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1430 PLIST_ENTRY head, link;
1431 head = &(gOvsSwitchContext->portHashArray[i]);
1432 POVS_VPORT_ENTRY vport = NULL;
1435 LIST_FORALL(head, link) {
1438 * if one or more dumps were previously done on this same bucket,
1439 * inIndex will be > 0, so we'll need to reply with the
1440 * inIndex + 1 vport from the bucket.
1442 if (outIndex >= inIndex) {
1443 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portLink);
1445 if (vport->portNo != 0) {
1446 OvsCreateMsgFromVport(vport, msgIn,
1447 usrParamsCtx->outputBuffer,
1448 usrParamsCtx->outputLength,
1449 gOvsSwitchContext->dpNo);
1465 * if no vport was found above, check the next bucket, beginning
1466 * with the first (i.e. index 0) elem from within that bucket
1473 /* XXX: what about NLMSG_DONE (as msg type)? */
1474 instance->dumpState.index[0] = outBucket;
1475 instance->dumpState.index[1] = outIndex;
1478 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1480 OvsReleaseCtrlLock();
1482 /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1483 if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1484 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1485 *replyLen = msgOut->nlMsg.nlmsgLen;
1488 * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1492 /* Free up the dump state, since there's no more data to continue. */
1493 FreeUserDumpState(instance);
1496 return STATUS_SUCCESS;
1500 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1503 NTSTATUS status = STATUS_SUCCESS;
1504 LOCK_STATE_EX lockState;
1506 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1507 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1508 POVS_VPORT_ENTRY vport = NULL;
1509 NL_ERROR nlError = NL_ERROR_SUCCESS;
1511 static const NL_POLICY ovsVportPolicy[] = {
1512 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1513 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1518 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1520 /* input buffer has been validated while validating write dev op. */
1521 ASSERT(usrParamsCtx->inputBuffer != NULL);
1523 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1524 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1525 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1526 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1527 return STATUS_INVALID_PARAMETER;
1530 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1531 return STATUS_INVALID_BUFFER_SIZE;
1534 OvsAcquireCtrlLock();
1535 if (!gOvsSwitchContext) {
1536 OvsReleaseCtrlLock();
1537 return STATUS_INVALID_PARAMETER;
1539 OvsReleaseCtrlLock();
1541 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1542 vport = OvsFindVportByOvsName(gOvsSwitchContext,
1543 NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]),
1544 NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]) - 1);
1545 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1546 vport = OvsFindVportByPortNo(gOvsSwitchContext,
1547 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
1549 nlError = NL_ERROR_INVAL;
1554 nlError = NL_ERROR_NODEV;
1558 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1559 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1560 usrParamsCtx->outputLength,
1561 gOvsSwitchContext->dpNo);
1562 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1564 *replyLen = msgOut->nlMsg.nlmsgLen;
1567 if (nlError != NL_ERROR_SUCCESS) {
1568 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1569 usrParamsCtx->outputBuffer;
1571 BuildErrorMsg(msgIn, msgError, nlError);
1572 *replyLen = msgError->nlMsg.nlmsgLen;
1575 return STATUS_SUCCESS;
1579 * --------------------------------------------------------------------------
1580 * Handler for the get vport command. The function handles the initial call to
1581 * setup the dump state, as well as subsequent calls to continue dumping data.
1582 * --------------------------------------------------------------------------
1585 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1590 switch (usrParamsCtx->devOp)
1592 case OVS_WRITE_DEV_OP:
1593 return OvsSetupDumpStart(usrParamsCtx);
1595 case OVS_READ_DEV_OP:
1596 return OvsGetVportDumpNext(usrParamsCtx, replyLen);
1598 case OVS_TRANSACTION_DEV_OP:
1599 return OvsGetVport(usrParamsCtx, replyLen);
1602 return STATUS_INVALID_DEVICE_REQUEST;
1608 * --------------------------------------------------------------------------
1609 * Utility function to map the output buffer in an IRP. The buffer is assumed
1610 * to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
1611 * --------------------------------------------------------------------------
1614 MapIrpOutputBuffer(PIRP irp,
1615 UINT32 bufferLength,
1616 UINT32 requiredLength,
1621 ASSERT(bufferLength);
1622 ASSERT(requiredLength);
1623 if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
1624 return STATUS_INVALID_PARAMETER;
1627 if (bufferLength < requiredLength) {
1628 return STATUS_NDIS_INVALID_LENGTH;
1630 if (irp->MdlAddress == NULL) {
1631 return STATUS_INVALID_PARAMETER;
1633 *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
1634 NormalPagePriority);
1635 if (*buffer == NULL) {
1636 return STATUS_INSUFFICIENT_RESOURCES;
1639 return STATUS_SUCCESS;
1643 * --------------------------------------------------------------------------
1644 * Utility function to fill up information about the state of a port in a reply
1646 * Assumes that 'gOvsCtrlLock' lock is acquired.
1647 * --------------------------------------------------------------------------
1650 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1651 POVS_EVENT_ENTRY eventEntry,
1656 OVS_MESSAGE msgOutTmp;
1658 POVS_VPORT_ENTRY vport;
1660 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
1662 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
1663 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
1665 /* driver intiated messages should have zerp seq number*/
1666 msgOutTmp.nlMsg.nlmsgSeq = 0;
1667 msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
1669 msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
1670 msgOutTmp.genlMsg.reserved = 0;
1672 /* we don't have netdev yet, treat link up/down a adding/removing a port*/
1673 if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
1674 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
1675 } else if (eventEntry->status &
1676 (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
1677 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
1680 return STATUS_UNSUCCESSFUL;
1682 msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
1684 rc = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
1686 status = STATUS_INVALID_BUFFER_SIZE;
1690 vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
1692 status = STATUS_DEVICE_DOES_NOT_EXIST;
1696 rc = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) ||
1697 NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) ||
1698 NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
1700 status = STATUS_INVALID_BUFFER_SIZE;
1704 /* XXXX Should we add the port stats attributes?*/
1705 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1706 nlMsg->nlmsgLen = NlBufSize(nlBuf);
1707 status = STATUS_SUCCESS;
1715 * --------------------------------------------------------------------------
1716 * Handler for reading events from the driver event queue. This handler is
1717 * executed when user modes issues a socket receive on a socket assocaited
1718 * with the MC group for events.
1719 * XXX user mode should read multiple events in one system call
1720 * --------------------------------------------------------------------------
1723 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1727 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1728 POVS_OPEN_INSTANCE instance =
1729 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1733 OVS_EVENT_ENTRY eventEntry;
1735 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1737 /* Should never read events with a dump socket */
1738 ASSERT(instance->dumpState.ovsMsg == NULL);
1740 /* Must have an event queue */
1741 ASSERT(instance->eventQueue != NULL);
1743 /* Output buffer has been validated while validating read dev op. */
1744 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1746 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1748 OvsAcquireCtrlLock();
1749 if (!gOvsSwitchContext) {
1750 status = STATUS_SUCCESS;
1754 /* remove an event entry from the event queue */
1755 status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
1756 if (status != STATUS_SUCCESS) {
1760 status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
1761 if (status == NDIS_STATUS_SUCCESS) {
1762 *replyLen = NlBufSize(&nlBuf);
1766 OvsReleaseCtrlLock();
1769 #endif /* OVS_USE_NL_INTERFACE */