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,
96 OvsGetVportCmdHandler;
98 NetlinkCmdHandler OvsGetNetdevCmdHandler;
100 static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
102 static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
104 static NTSTATUS HandleDpTransactionCommon(
105 POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen);
108 * The various netlink families, along with the supported commands. Most of
109 * these families and commands are part of the openvswitch specification for a
110 * netlink datapath. In addition, each platform can implement a few families
111 * and commands as extensions.
114 /* Netlink control family: this is a Windows specific family. */
115 NETLINK_CMD nlControlFamilyCmdOps[] = {
116 { .cmd = OVS_CTRL_CMD_WIN_GET_PID,
117 .handler = OvsGetPidCmdHandler,
118 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
119 .validateDpIndex = FALSE,
121 { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ,
122 .handler = OvsPendEventCmdHandler,
123 .supportedDevOp = OVS_WRITE_DEV_OP,
124 .validateDpIndex = TRUE,
126 { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ,
127 .handler = OvsSubscribeEventCmdHandler,
128 .supportedDevOp = OVS_WRITE_DEV_OP,
129 .validateDpIndex = TRUE,
131 { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY,
132 .handler = OvsReadEventCmdHandler,
133 .supportedDevOp = OVS_READ_EVENT_DEV_OP,
134 .validateDpIndex = FALSE,
138 NETLINK_FAMILY nlControlFamilyOps = {
139 .name = OVS_WIN_CONTROL_FAMILY,
140 .id = OVS_WIN_NL_CTRL_FAMILY_ID,
141 .version = OVS_WIN_CONTROL_VERSION,
142 .maxAttr = OVS_WIN_CONTROL_ATTR_MAX,
143 .cmds = nlControlFamilyCmdOps,
144 .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
147 /* Netlink datapath family. */
148 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
149 { .cmd = OVS_DP_CMD_NEW,
150 .handler = OvsNewDpCmdHandler,
151 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
152 .validateDpIndex = FALSE
154 { .cmd = OVS_DP_CMD_GET,
155 .handler = OvsGetDpCmdHandler,
156 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
157 OVS_TRANSACTION_DEV_OP,
158 .validateDpIndex = FALSE
160 { .cmd = OVS_DP_CMD_SET,
161 .handler = OvsSetDpCmdHandler,
162 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
163 OVS_TRANSACTION_DEV_OP,
164 .validateDpIndex = TRUE
168 NETLINK_FAMILY nlDatapathFamilyOps = {
169 .name = OVS_DATAPATH_FAMILY,
170 .id = OVS_WIN_NL_DATAPATH_FAMILY_ID,
171 .version = OVS_DATAPATH_VERSION,
172 .maxAttr = OVS_DP_ATTR_MAX,
173 .cmds = nlDatapathFamilyCmdOps,
174 .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
177 /* Netlink packet family. */
178 /* XXX: Add commands here. */
179 NETLINK_FAMILY nlPacketFamilyOps = {
180 .name = OVS_PACKET_FAMILY,
181 .id = OVS_WIN_NL_PACKET_FAMILY_ID,
182 .version = OVS_PACKET_VERSION,
183 .maxAttr = OVS_PACKET_ATTR_MAX,
184 .cmds = NULL, /* XXX: placeholder. */
188 /* Netlink vport family. */
189 NETLINK_CMD nlVportFamilyCmdOps[] = {
190 { .cmd = OVS_VPORT_CMD_GET,
191 .handler = OvsGetVportCmdHandler,
192 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
193 OVS_TRANSACTION_DEV_OP,
194 .validateDpIndex = TRUE
198 NETLINK_FAMILY nlVportFamilyOps = {
199 .name = OVS_VPORT_FAMILY,
200 .id = OVS_WIN_NL_VPORT_FAMILY_ID,
201 .version = OVS_VPORT_VERSION,
202 .maxAttr = OVS_VPORT_ATTR_MAX,
203 .cmds = nlVportFamilyCmdOps,
204 .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps)
207 /* Netlink flow family. */
209 NETLINK_CMD nlFlowFamilyCmdOps[] = {
210 { .cmd = OVS_FLOW_CMD_NEW,
211 .handler = OvsFlowNlCmdHandler,
212 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
213 .validateDpIndex = TRUE
215 { .cmd = OVS_FLOW_CMD_SET,
216 .handler = OvsFlowNlCmdHandler,
217 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
218 .validateDpIndex = TRUE
220 { .cmd = OVS_FLOW_CMD_DEL,
221 .handler = OvsFlowNlCmdHandler,
222 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
223 .validateDpIndex = TRUE
225 { .cmd = OVS_FLOW_CMD_GET,
226 .handler = OvsFlowNlGetCmdHandler,
227 .supportedDevOp = OVS_TRANSACTION_DEV_OP |
228 OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
229 .validateDpIndex = TRUE
233 NETLINK_FAMILY nlFLowFamilyOps = {
234 .name = OVS_FLOW_FAMILY,
235 .id = OVS_WIN_NL_FLOW_FAMILY_ID,
236 .version = OVS_FLOW_VERSION,
237 .maxAttr = OVS_FLOW_ATTR_MAX,
238 .cmds = nlFlowFamilyCmdOps,
239 .opsCount = ARRAY_SIZE(nlFlowFamilyCmdOps)
242 /* Netlink netdev family. */
243 NETLINK_CMD nlNetdevFamilyCmdOps[] = {
244 { .cmd = OVS_WIN_NETDEV_CMD_GET,
245 .handler = OvsGetNetdevCmdHandler,
246 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
247 .validateDpIndex = FALSE
251 NETLINK_FAMILY nlNetdevFamilyOps = {
252 .name = OVS_WIN_NETDEV_FAMILY,
253 .id = OVS_WIN_NL_NETDEV_FAMILY_ID,
254 .version = OVS_WIN_NETDEV_VERSION,
255 .maxAttr = OVS_WIN_NETDEV_ATTR_MAX,
256 .cmds = nlNetdevFamilyCmdOps,
257 .opsCount = ARRAY_SIZE(nlNetdevFamilyCmdOps)
260 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
262 UINT32 requiredLength,
264 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
265 POVS_OPEN_INSTANCE instance,
267 NETLINK_FAMILY *nlFamilyOps);
268 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
269 NETLINK_FAMILY *nlFamilyOps,
272 /* Handles to the device object for communication with userspace. */
273 NDIS_HANDLE gOvsDeviceHandle;
274 PDEVICE_OBJECT gOvsDeviceObject;
276 _Dispatch_type_(IRP_MJ_CREATE)
277 _Dispatch_type_(IRP_MJ_CLOSE)
278 DRIVER_DISPATCH OvsOpenCloseDevice;
280 _Dispatch_type_(IRP_MJ_CLEANUP)
281 DRIVER_DISPATCH OvsCleanupDevice;
283 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
284 DRIVER_DISPATCH OvsDeviceControl;
287 #pragma alloc_text(INIT, OvsCreateDeviceObject)
288 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
289 #pragma alloc_text(PAGE, OvsCleanupDevice)
290 #pragma alloc_text(PAGE, OvsDeviceControl)
291 #endif // ALLOC_PRAGMA
294 * We might hit this limit easily since userspace opens a netlink descriptor for
295 * each thread, and at least one descriptor per vport. Revisit this later.
297 #define OVS_MAX_OPEN_INSTANCES 512
298 #define OVS_SYSTEM_DP_NAME "ovs-system"
300 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
301 UINT32 ovsNumberOfOpenInstances;
302 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
304 NDIS_SPIN_LOCK ovsCtrlLockObj;
305 PNDIS_SPIN_LOCK gOvsCtrlLock;
311 gOvsCtrlLock = &ovsCtrlLockObj;
312 NdisAllocateSpinLock(gOvsCtrlLock);
320 OvsCleanupEventQueue();
322 NdisFreeSpinLock(gOvsCtrlLock);
331 NdisAcquireSpinLock(gOvsCtrlLock);
337 NdisReleaseSpinLock(gOvsCtrlLock);
342 * --------------------------------------------------------------------------
343 * Creates the communication device between user and kernel, and also
344 * initializes the data associated data structures.
345 * --------------------------------------------------------------------------
348 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
350 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
351 UNICODE_STRING deviceName;
352 UNICODE_STRING symbolicDeviceName;
353 PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
354 NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
355 OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
357 RtlZeroMemory(dispatchTable,
358 (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
359 dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
360 dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
361 dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
362 dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
364 NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
365 NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
367 RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
369 OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
370 NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
371 NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
372 sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
374 deviceAttributes.DeviceName = &deviceName;
375 deviceAttributes.SymbolicName = &symbolicDeviceName;
376 deviceAttributes.MajorFunctions = dispatchTable;
377 deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
379 status = NdisRegisterDeviceEx(ovsExtDriverHandle,
383 if (status != NDIS_STATUS_SUCCESS) {
384 POVS_DEVICE_EXTENSION ovsExt =
385 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
386 ASSERT(gOvsDeviceObject != NULL);
387 ASSERT(gOvsDeviceHandle != NULL);
390 ovsExt->numberOpenInstance = 0;
393 /* Initialize the associated data structures. */
396 OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
402 OvsDeleteDeviceObject()
404 if (gOvsDeviceHandle) {
406 POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
407 NdisGetDeviceReservedExtension(gOvsDeviceObject);
409 ASSERT(ovsExt->numberOpenInstance == 0);
413 ASSERT(gOvsDeviceObject);
414 NdisDeregisterDeviceEx(gOvsDeviceHandle);
415 gOvsDeviceHandle = NULL;
416 gOvsDeviceObject = NULL;
422 OvsGetOpenInstance(PFILE_OBJECT fileObject,
425 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
427 ASSERT(instance->fileObject == fileObject);
428 if (gOvsSwitchContext == NULL ||
429 gOvsSwitchContext->dpNo != dpNo) {
437 OvsFindOpenInstance(PFILE_OBJECT fileObject)
440 for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
441 j < ovsNumberOfOpenInstances; i++) {
442 if (ovsOpenInstanceArray[i]) {
443 if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
444 return ovsOpenInstanceArray[i];
453 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
454 PFILE_OBJECT fileObject)
456 POVS_OPEN_INSTANCE instance =
457 (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
460 if (instance == NULL) {
461 return STATUS_NO_MEMORY;
463 OvsAcquireCtrlLock();
464 ASSERT(OvsFindOpenInstance(fileObject) == NULL);
466 if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
467 OvsReleaseCtrlLock();
468 OvsFreeMemory(instance);
469 return STATUS_INSUFFICIENT_RESOURCES;
471 RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
473 for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
474 if (ovsOpenInstanceArray[i] == NULL) {
475 ovsOpenInstanceArray[i] = instance;
476 ovsNumberOfOpenInstances++;
477 instance->cookie = i;
481 ASSERT(i < OVS_MAX_OPEN_INSTANCES);
482 instance->fileObject = fileObject;
483 ASSERT(fileObject->FsContext == NULL);
484 instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
485 if (instance->pid == 0) {
486 /* XXX: check for rollover. */
488 fileObject->FsContext = instance;
489 OvsReleaseCtrlLock();
490 return STATUS_SUCCESS;
494 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
496 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
498 ASSERT(fileObject == instance->fileObject);
499 OvsCleanupEvent(instance);
500 OvsCleanupPacketQueue(instance);
504 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
506 POVS_OPEN_INSTANCE instance;
507 ASSERT(fileObject->FsContext);
508 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
509 ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
511 OvsAcquireCtrlLock();
512 fileObject->FsContext = NULL;
513 ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
514 ovsOpenInstanceArray[instance->cookie] = NULL;
515 ovsNumberOfOpenInstances--;
516 OvsReleaseCtrlLock();
517 ASSERT(instance->eventQueue == NULL);
518 ASSERT (instance->packetQueue == NULL);
519 OvsFreeMemory(instance);
523 OvsCompleteIrpRequest(PIRP irp,
527 irp->IoStatus.Information = infoPtr;
528 irp->IoStatus.Status = status;
529 IoCompleteRequest(irp, IO_NO_INCREMENT);
535 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
538 PIO_STACK_LOCATION irpSp;
539 NTSTATUS status = STATUS_SUCCESS;
540 PFILE_OBJECT fileObject;
541 POVS_DEVICE_EXTENSION ovsExt =
542 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
544 ASSERT(deviceObject == gOvsDeviceObject);
545 ASSERT(ovsExt != NULL);
547 irpSp = IoGetCurrentIrpStackLocation(irp);
548 fileObject = irpSp->FileObject;
549 OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
550 deviceObject, fileObject,
551 ovsExt->numberOpenInstance);
553 switch (irpSp->MajorFunction) {
555 status = OvsAddOpenInstance(ovsExt, fileObject);
556 if (STATUS_SUCCESS == status) {
557 InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
561 ASSERT(ovsExt->numberOpenInstance > 0);
562 OvsRemoveOpenInstance(fileObject);
563 InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
568 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
571 _Use_decl_annotations_
573 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
577 PIO_STACK_LOCATION irpSp;
578 PFILE_OBJECT fileObject;
580 NTSTATUS status = STATUS_SUCCESS;
582 POVS_DEVICE_EXTENSION ovsExt =
583 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
585 ASSERT(ovsExt->numberOpenInstance > 0);
588 UNREFERENCED_PARAMETER(deviceObject);
590 ASSERT(deviceObject == gOvsDeviceObject);
591 irpSp = IoGetCurrentIrpStackLocation(irp);
592 fileObject = irpSp->FileObject;
594 ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
596 OvsCleanupOpenInstance(fileObject);
598 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
603 * --------------------------------------------------------------------------
604 * IOCTL function handler for the device.
605 * --------------------------------------------------------------------------
608 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
612 PIO_STACK_LOCATION irpSp;
613 NTSTATUS status = STATUS_SUCCESS;
614 PFILE_OBJECT fileObject;
615 PVOID inputBuffer = NULL;
616 PVOID outputBuffer = NULL;
617 UINT32 inputBufferLen, outputBufferLen;
618 UINT32 code, replyLen = 0;
619 POVS_OPEN_INSTANCE instance;
621 OVS_MESSAGE ovsMsgReadOp;
623 NETLINK_FAMILY *nlFamilyOps;
624 OVS_USER_PARAMS_CONTEXT usrParamsCtx;
627 POVS_DEVICE_EXTENSION ovsExt =
628 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
629 ASSERT(deviceObject == gOvsDeviceObject);
631 ASSERT(ovsExt->numberOpenInstance > 0);
633 UNREFERENCED_PARAMETER(deviceObject);
636 irpSp = IoGetCurrentIrpStackLocation(irp);
638 ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
639 ASSERT(irpSp->FileObject != NULL);
641 fileObject = irpSp->FileObject;
642 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
643 code = irpSp->Parameters.DeviceIoControl.IoControlCode;
644 inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
645 outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
646 inputBuffer = irp->AssociatedIrp.SystemBuffer;
648 /* Concurrent netlink operations are not supported. */
649 if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
650 status = STATUS_RESOURCE_IN_USE;
655 * Validate the input/output buffer arguments depending on the type of the
659 case OVS_IOCTL_TRANSACT:
660 /* Input buffer is mandatory, output buffer is optional. */
661 if (outputBufferLen != 0) {
662 status = MapIrpOutputBuffer(irp, outputBufferLen,
663 sizeof *ovsMsg, &outputBuffer);
664 if (status != STATUS_SUCCESS) {
667 ASSERT(outputBuffer);
670 if (inputBufferLen < sizeof (*ovsMsg)) {
671 status = STATUS_NDIS_INVALID_LENGTH;
675 ovsMsg = inputBuffer;
676 devOp = OVS_TRANSACTION_DEV_OP;
679 case OVS_IOCTL_READ_EVENT:
680 /* This IOCTL is used to read events */
681 if (outputBufferLen != 0) {
682 status = MapIrpOutputBuffer(irp, outputBufferLen,
683 sizeof *ovsMsg, &outputBuffer);
684 if (status != STATUS_SUCCESS) {
687 ASSERT(outputBuffer);
689 status = STATUS_NDIS_INVALID_LENGTH;
695 ovsMsg = &ovsMsgReadOp;
696 ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
697 /* An "artificial" command so we can use NL family function table*/
698 ovsMsg->genlMsg.cmd = OVS_CTRL_CMD_EVENT_NOTIFY;
699 devOp = OVS_READ_DEV_OP;
703 /* Output buffer is mandatory. */
704 if (outputBufferLen != 0) {
705 status = MapIrpOutputBuffer(irp, outputBufferLen,
706 sizeof *ovsMsg, &outputBuffer);
707 if (status != STATUS_SUCCESS) {
710 ASSERT(outputBuffer);
712 status = STATUS_NDIS_INVALID_LENGTH;
717 * Operate in the mode that read ioctl is similar to ReadFile(). This
718 * might change as the userspace code gets implemented.
724 * For implementing read (ioctl or otherwise), we need to store some
725 * state in the instance to indicate the command that started the dump
726 * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
727 * that 'ovsMsgReadOp' is needed only in this function to call into the
728 * appropraite handler. The handler itself can access the state in the
731 * In the absence of a dump start, return 0 bytes.
733 if (instance->dumpState.ovsMsg == NULL) {
735 status = STATUS_SUCCESS;
738 RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
739 sizeof (ovsMsgReadOp));
741 /* Create an NL message for consumption. */
742 ovsMsg = &ovsMsgReadOp;
743 devOp = OVS_READ_DEV_OP;
747 case OVS_IOCTL_WRITE:
748 /* Input buffer is mandatory. */
749 if (inputBufferLen < sizeof (*ovsMsg)) {
750 status = STATUS_NDIS_INVALID_LENGTH;
754 ovsMsg = inputBuffer;
755 devOp = OVS_WRITE_DEV_OP;
759 status = STATUS_INVALID_DEVICE_REQUEST;
764 switch (ovsMsg->nlMsg.nlmsgType) {
765 case OVS_WIN_NL_CTRL_FAMILY_ID:
766 nlFamilyOps = &nlControlFamilyOps;
768 case OVS_WIN_NL_DATAPATH_FAMILY_ID:
769 nlFamilyOps = &nlDatapathFamilyOps;
771 case OVS_WIN_NL_FLOW_FAMILY_ID:
772 nlFamilyOps = &nlFLowFamilyOps;
774 case OVS_WIN_NL_PACKET_FAMILY_ID:
775 status = STATUS_NOT_IMPLEMENTED;
777 case OVS_WIN_NL_VPORT_FAMILY_ID:
778 nlFamilyOps = &nlVportFamilyOps;
780 case OVS_WIN_NL_NETDEV_FAMILY_ID:
781 nlFamilyOps = &nlNetdevFamilyOps;
784 status = STATUS_INVALID_PARAMETER;
789 * For read operation, the netlink command has already been validated
792 if (devOp != OVS_READ_DEV_OP) {
793 status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
794 if (status != STATUS_SUCCESS) {
799 InitUserParamsCtx(irp, instance, devOp, ovsMsg,
800 inputBuffer, inputBufferLen,
801 outputBuffer, outputBufferLen,
804 status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
809 return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
814 * --------------------------------------------------------------------------
815 * Function to validate a netlink command. Only certain combinations of
816 * (device operation, netlink family, command) are valid.
817 * --------------------------------------------------------------------------
820 ValidateNetlinkCmd(UINT32 devOp,
821 POVS_OPEN_INSTANCE instance,
823 NETLINK_FAMILY *nlFamilyOps)
825 NTSTATUS status = STATUS_INVALID_PARAMETER;
828 for (i = 0; i < nlFamilyOps->opsCount; i++) {
829 if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
830 /* Validate if the command is valid for the device operation. */
831 if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
832 status = STATUS_INVALID_PARAMETER;
836 /* Validate the version. */
837 if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
838 status = STATUS_INVALID_PARAMETER;
842 /* Validate the DP for commands that require a DP. */
843 if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
844 OvsAcquireCtrlLock();
845 if (ovsMsg->ovsHdr.dp_ifindex !=
846 (INT)gOvsSwitchContext->dpNo) {
847 status = STATUS_INVALID_PARAMETER;
848 OvsReleaseCtrlLock();
851 OvsReleaseCtrlLock();
854 /* Validate the PID. */
855 if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
856 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
857 status = STATUS_INVALID_PARAMETER;
862 status = STATUS_SUCCESS;
872 * --------------------------------------------------------------------------
873 * Function to invoke the netlink command handler.
874 * --------------------------------------------------------------------------
877 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
878 NETLINK_FAMILY *nlFamilyOps,
881 NTSTATUS status = STATUS_INVALID_PARAMETER;
884 for (i = 0; i < nlFamilyOps->opsCount; i++) {
885 if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
886 NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
889 status = handler(usrParamsCtx, replyLen);
899 * --------------------------------------------------------------------------
900 * Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
902 * Each handle on the device is assigned a unique PID when the handle is
903 * created. On platforms that support netlink natively, the PID is available
904 * to userspace when the netlink socket is created. However, without native
905 * netlink support on Windows, OVS datapath generates the PID and lets the
906 * userspace query it.
908 * This function implements the query.
909 * --------------------------------------------------------------------------
912 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
915 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
916 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
918 if (usrParamsCtx->outputLength >= sizeof *msgOut) {
919 POVS_OPEN_INSTANCE instance =
920 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
922 RtlZeroMemory(msgOut, sizeof *msgOut);
923 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
924 msgOut->nlMsg.nlmsgPid = instance->pid;
925 *replyLen = sizeof *msgOut;
926 /* XXX: We might need to return the DP index as well. */
928 return STATUS_NDIS_INVALID_LENGTH;
931 return STATUS_SUCCESS;
935 * --------------------------------------------------------------------------
936 * Utility function to fill up information about the datapath in a reply to
938 * Assumes that 'gOvsCtrlLock' lock is acquired.
939 * --------------------------------------------------------------------------
942 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
947 OVS_MESSAGE msgOutTmp;
948 OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
951 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && NlBufRemLen(nlBuf) >= sizeof *msgIn);
953 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
954 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
955 msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
956 msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
958 msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
959 msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
960 msgOutTmp.genlMsg.reserved = 0;
962 msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
964 writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
966 writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
970 OVS_DP_STATS dpStats;
972 dpStats.n_hit = datapath->hits;
973 dpStats.n_missed = datapath->misses;
974 dpStats.n_lost = datapath->lost;
975 dpStats.n_flows = datapath->nFlows;
976 writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
977 (PCHAR)&dpStats, sizeof dpStats);
979 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
980 nlMsg->nlmsgLen = NlBufSize(nlBuf);
982 return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
986 * --------------------------------------------------------------------------
987 * Handler for queueing an IRP used for event notification. The IRP is
988 * completed when a port state changes. STATUS_PENDING is returned on
989 * success. User mode keep a pending IRP at all times.
990 * --------------------------------------------------------------------------
993 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
998 UNREFERENCED_PARAMETER(replyLen);
1000 POVS_OPEN_INSTANCE instance =
1001 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1002 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1003 OVS_EVENT_POLL poll;
1005 poll.dpNo = msgIn->ovsHdr.dp_ifindex;
1006 status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
1007 &poll, sizeof poll);
1012 * --------------------------------------------------------------------------
1013 * Handler for the subscription for the event queue
1014 * --------------------------------------------------------------------------
1017 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1021 OVS_EVENT_SUBSCRIBE request;
1025 const NL_POLICY policy[] = {
1026 [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
1027 [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
1030 UNREFERENCED_PARAMETER(replyLen);
1032 POVS_OPEN_INSTANCE instance =
1033 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1034 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1036 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1037 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, 2);
1039 status = STATUS_INVALID_PARAMETER;
1043 /* XXX Ignore the MC group for now */
1044 join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1045 request.dpNo = msgIn->ovsHdr.dp_ifindex;
1046 request.subscribe = join;
1047 request.mask = OVS_EVENT_MASK_ALL;
1049 status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1056 * --------------------------------------------------------------------------
1057 * Command Handler for 'OVS_DP_CMD_NEW'.
1058 * --------------------------------------------------------------------------
1061 OvsNewDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1064 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1068 * --------------------------------------------------------------------------
1069 * Command Handler for 'OVS_DP_CMD_GET'.
1071 * The function handles both the dump based as well as the transaction based
1072 * 'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1073 * call to setup dump state, as well as subsequent calls to continue dumping
1075 * --------------------------------------------------------------------------
1078 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1081 if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1082 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1084 return HandleGetDpDump(usrParamsCtx, replyLen);
1089 * --------------------------------------------------------------------------
1090 * Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1091 * --------------------------------------------------------------------------
1094 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1097 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1102 * --------------------------------------------------------------------------
1103 * Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1104 * --------------------------------------------------------------------------
1107 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1110 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1111 POVS_OPEN_INSTANCE instance =
1112 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1114 if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1116 OvsSetupDumpStart(usrParamsCtx);
1120 POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1122 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1124 if (instance->dumpState.ovsMsg == NULL) {
1126 return STATUS_INVALID_DEVICE_STATE;
1129 /* Dump state must have been deleted after previous dump operation. */
1130 ASSERT(instance->dumpState.index[0] == 0);
1131 /* Output buffer has been validated while validating read dev op. */
1132 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1134 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1135 usrParamsCtx->outputLength);
1137 OvsAcquireCtrlLock();
1138 if (!gOvsSwitchContext) {
1139 /* Treat this as a dump done. */
1140 OvsReleaseCtrlLock();
1142 FreeUserDumpState(instance);
1143 return STATUS_SUCCESS;
1145 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1146 OvsReleaseCtrlLock();
1148 if (status != STATUS_SUCCESS) {
1150 FreeUserDumpState(instance);
1154 /* Increment the dump index. */
1155 instance->dumpState.index[0] = 1;
1156 *replyLen = msgOut->nlMsg.nlmsgLen;
1158 /* Free up the dump state, since there's no more data to continue. */
1159 FreeUserDumpState(instance);
1162 return STATUS_SUCCESS;
1167 * --------------------------------------------------------------------------
1168 * Command Handler for 'OVS_DP_CMD_SET'.
1169 * --------------------------------------------------------------------------
1172 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1175 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1179 * --------------------------------------------------------------------------
1180 * Function for handling transaction based 'OVS_DP_CMD_NEW', 'OVS_DP_CMD_GET'
1181 * and 'OVS_DP_CMD_SET' commands.
1183 * 'OVS_DP_CMD_NEW' is implemented to keep userspace code happy. Creation of a
1184 * new datapath is not supported currently.
1185 * --------------------------------------------------------------------------
1188 HandleDpTransactionCommon(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1191 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1192 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1193 NTSTATUS status = STATUS_SUCCESS;
1195 NL_ERROR nlError = NL_ERROR_SUCCESS;
1196 static const NL_POLICY ovsDatapathSetPolicy[] = {
1197 [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1198 [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1199 [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1201 PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1203 /* input buffer has been validated while validating write dev op. */
1204 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1206 /* Parse any attributes in the request. */
1207 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET ||
1208 usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1209 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1210 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1211 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1212 ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1213 return STATUS_INVALID_PARAMETER;
1217 * XXX: Not clear at this stage if there's any role for the
1218 * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1223 RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1226 /* Output buffer is optional for OVS_TRANSACTION_DEV_OP. */
1227 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1228 return STATUS_NDIS_INVALID_LENGTH;
1230 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1232 OvsAcquireCtrlLock();
1233 if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1234 if (!gOvsSwitchContext &&
1235 !OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1236 OVS_SYSTEM_DP_NAME)) {
1237 OvsReleaseCtrlLock();
1239 /* Creation of new datapaths is not supported. */
1240 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1241 nlError = NL_ERROR_NOTSUPP;
1245 nlError = NL_ERROR_NODEV;
1248 } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1249 OvsReleaseCtrlLock();
1250 nlError = NL_ERROR_NODEV;
1254 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1255 OvsReleaseCtrlLock();
1256 nlError = NL_ERROR_EXIST;
1260 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1261 OvsReleaseCtrlLock();
1263 *replyLen = NlBufSize(&nlBuf);
1266 if (nlError != NL_ERROR_SUCCESS) {
1267 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1268 usrParamsCtx->outputBuffer;
1270 BuildErrorMsg(msgIn, msgError, nlError);
1271 *replyLen = msgError->nlMsg.nlmsgLen;
1274 return STATUS_SUCCESS;
1279 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1281 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1282 POVS_OPEN_INSTANCE instance =
1283 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1285 /* input buffer has been validated while validating write dev op. */
1286 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1288 /* A write operation that does not indicate dump start is invalid. */
1289 if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1290 return STATUS_INVALID_PARAMETER;
1292 /* XXX: Handle other NLM_F_* flags in the future. */
1295 * This operation should be setting up the dump state. If there's any
1296 * previous state, clear it up so as to set it up afresh.
1298 if (instance->dumpState.ovsMsg != NULL) {
1299 FreeUserDumpState(instance);
1302 return InitUserDumpState(instance, msgIn);
1306 BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
1307 UINT32 length, UINT16 flags)
1309 msgOut->nlMsg.nlmsgType = type;
1310 msgOut->nlMsg.nlmsgFlags = flags;
1311 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1312 msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1313 msgOut->nlMsg.nlmsgLen = length;
1315 msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
1316 msgOut->genlMsg.version = msgIn->genlMsg.version;
1317 msgOut->genlMsg.reserved = 0;
1321 * XXX: should move out these functions to a Netlink.c or to a OvsMessage.c
1322 * or even make them inlined functions in Datapath.h. Can be done after the
1323 * first sprint once we have more code to refactor.
1326 BuildReplyMsgFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
1328 BuildMsgOut(msgIn, msgOut, msgIn->nlMsg.nlmsgType, sizeof(OVS_MESSAGE),
1333 BuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode)
1335 BuildMsgOut(msgIn, (POVS_MESSAGE)msgOut, NLMSG_ERROR,
1336 sizeof(OVS_MESSAGE_ERROR), 0);
1338 msgOut->errorMsg.error = errorCode;
1339 msgOut->errorMsg.nlMsg = msgIn->nlMsg;
1343 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1350 OVS_VPORT_FULL_STATS vportStats;
1355 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1357 BuildReplyMsgFromMsgIn(msgIn, &msgOut, NLM_F_MULTI);
1358 msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1360 ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1362 return STATUS_INSUFFICIENT_RESOURCES;
1365 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1367 return STATUS_INSUFFICIENT_RESOURCES;
1370 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1372 return STATUS_INSUFFICIENT_RESOURCES;
1375 ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1377 return STATUS_INSUFFICIENT_RESOURCES;
1381 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1382 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1383 * it means we have an array of pids, instead of a single pid.
1384 * ATM we assume we have one pid only.
1387 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1390 return STATUS_INSUFFICIENT_RESOURCES;
1394 vportStats.rxPackets = vport->stats.rxPackets;
1395 vportStats.rxBytes = vport->stats.rxBytes;
1396 vportStats.txPackets = vport->stats.txPackets;
1397 vportStats.txBytes = vport->stats.txBytes;
1398 vportStats.rxErrors = vport->errStats.rxErrors;
1399 vportStats.txErrors = vport->errStats.txErrors;
1400 vportStats.rxDropped = vport->errStats.rxDropped;
1401 vportStats.txDropped = vport->errStats.txDropped;
1403 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1405 sizeof(OVS_VPORT_FULL_STATS));
1407 return STATUS_INSUFFICIENT_RESOURCES;
1411 * XXX: when vxlan udp dest port becomes configurable, we will also need
1412 * to add vport options
1415 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1416 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1418 return STATUS_SUCCESS;
1422 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1426 POVS_OPEN_INSTANCE instance =
1427 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1428 LOCK_STATE_EX lockState;
1429 UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1432 * XXX: this function shares some code with other dump command(s).
1433 * In the future, we will need to refactor the dump functions
1436 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1438 if (instance->dumpState.ovsMsg == NULL) {
1440 return STATUS_INVALID_DEVICE_STATE;
1443 /* Output buffer has been validated while validating read dev op. */
1444 ASSERT(usrParamsCtx->outputBuffer != NULL);
1446 msgIn = instance->dumpState.ovsMsg;
1448 OvsAcquireCtrlLock();
1449 if (!gOvsSwitchContext) {
1450 /* Treat this as a dump done. */
1451 OvsReleaseCtrlLock();
1453 FreeUserDumpState(instance);
1454 return STATUS_SUCCESS;
1458 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1459 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1460 * it means we have an array of pids, instead of a single pid.
1461 * ATM we assume we have one pid only.
1463 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1464 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1465 NDIS_RWL_AT_DISPATCH_LEVEL);
1467 if (gOvsSwitchContext->numVports > 0) {
1468 /* inBucket: the bucket, used for lookup */
1469 UINT32 inBucket = instance->dumpState.index[0];
1470 /* inIndex: index within the given bucket, used for lookup */
1471 UINT32 inIndex = instance->dumpState.index[1];
1472 /* the bucket to be used for the next dump operation */
1473 UINT32 outBucket = 0;
1474 /* the index within the outBucket to be used for the next dump */
1475 UINT32 outIndex = 0;
1477 for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1478 PLIST_ENTRY head, link;
1479 head = &(gOvsSwitchContext->portHashArray[i]);
1480 POVS_VPORT_ENTRY vport = NULL;
1483 LIST_FORALL(head, link) {
1486 * if one or more dumps were previously done on this same bucket,
1487 * inIndex will be > 0, so we'll need to reply with the
1488 * inIndex + 1 vport from the bucket.
1490 if (outIndex >= inIndex) {
1491 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portLink);
1493 if (vport->portNo != 0) {
1494 OvsCreateMsgFromVport(vport, msgIn,
1495 usrParamsCtx->outputBuffer,
1496 usrParamsCtx->outputLength,
1497 gOvsSwitchContext->dpNo);
1513 * if no vport was found above, check the next bucket, beginning
1514 * with the first (i.e. index 0) elem from within that bucket
1521 /* XXX: what about NLMSG_DONE (as msg type)? */
1522 instance->dumpState.index[0] = outBucket;
1523 instance->dumpState.index[1] = outIndex;
1526 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1528 OvsReleaseCtrlLock();
1530 /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1531 if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1532 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1533 *replyLen = msgOut->nlMsg.nlmsgLen;
1536 * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1540 /* Free up the dump state, since there's no more data to continue. */
1541 FreeUserDumpState(instance);
1544 return STATUS_SUCCESS;
1548 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1551 NTSTATUS status = STATUS_SUCCESS;
1552 LOCK_STATE_EX lockState;
1554 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1555 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1556 POVS_VPORT_ENTRY vport = NULL;
1557 NL_ERROR nlError = NL_ERROR_SUCCESS;
1559 static const NL_POLICY ovsVportPolicy[] = {
1560 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1561 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1566 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1568 /* input buffer has been validated while validating write dev op. */
1569 ASSERT(usrParamsCtx->inputBuffer != NULL);
1571 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1572 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1573 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1574 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1575 return STATUS_INVALID_PARAMETER;
1578 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1579 return STATUS_INVALID_BUFFER_SIZE;
1582 OvsAcquireCtrlLock();
1583 if (!gOvsSwitchContext) {
1584 OvsReleaseCtrlLock();
1585 return STATUS_INVALID_PARAMETER;
1587 OvsReleaseCtrlLock();
1589 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1590 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1591 vport = OvsFindVportByOvsName(gOvsSwitchContext,
1592 NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]),
1593 NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]) - 1);
1594 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1595 vport = OvsFindVportByPortNo(gOvsSwitchContext,
1596 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
1598 nlError = NL_ERROR_INVAL;
1599 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1604 nlError = NL_ERROR_NODEV;
1605 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1609 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1610 usrParamsCtx->outputLength,
1611 gOvsSwitchContext->dpNo);
1612 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1614 *replyLen = msgOut->nlMsg.nlmsgLen;
1617 if (nlError != NL_ERROR_SUCCESS) {
1618 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1619 usrParamsCtx->outputBuffer;
1621 BuildErrorMsg(msgIn, msgError, nlError);
1622 *replyLen = msgError->nlMsg.nlmsgLen;
1625 return STATUS_SUCCESS;
1629 * --------------------------------------------------------------------------
1630 * Handler for the get vport command. The function handles the initial call to
1631 * setup the dump state, as well as subsequent calls to continue dumping data.
1632 * --------------------------------------------------------------------------
1635 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1640 switch (usrParamsCtx->devOp)
1642 case OVS_WRITE_DEV_OP:
1643 return OvsSetupDumpStart(usrParamsCtx);
1645 case OVS_READ_DEV_OP:
1646 return OvsGetVportDumpNext(usrParamsCtx, replyLen);
1648 case OVS_TRANSACTION_DEV_OP:
1649 return OvsGetVport(usrParamsCtx, replyLen);
1652 return STATUS_INVALID_DEVICE_REQUEST;
1658 * --------------------------------------------------------------------------
1659 * Utility function to map the output buffer in an IRP. The buffer is assumed
1660 * to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
1661 * --------------------------------------------------------------------------
1664 MapIrpOutputBuffer(PIRP irp,
1665 UINT32 bufferLength,
1666 UINT32 requiredLength,
1671 ASSERT(bufferLength);
1672 ASSERT(requiredLength);
1673 if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
1674 return STATUS_INVALID_PARAMETER;
1677 if (bufferLength < requiredLength) {
1678 return STATUS_NDIS_INVALID_LENGTH;
1680 if (irp->MdlAddress == NULL) {
1681 return STATUS_INVALID_PARAMETER;
1683 *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
1684 NormalPagePriority);
1685 if (*buffer == NULL) {
1686 return STATUS_INSUFFICIENT_RESOURCES;
1689 return STATUS_SUCCESS;
1693 * --------------------------------------------------------------------------
1694 * Utility function to fill up information about the state of a port in a reply
1696 * Assumes that 'gOvsCtrlLock' lock is acquired.
1697 * --------------------------------------------------------------------------
1700 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1701 POVS_EVENT_ENTRY eventEntry,
1706 OVS_MESSAGE msgOutTmp;
1708 POVS_VPORT_ENTRY vport;
1710 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
1712 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
1713 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
1715 /* driver intiated messages should have zerp seq number*/
1716 msgOutTmp.nlMsg.nlmsgSeq = 0;
1717 msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
1719 msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
1720 msgOutTmp.genlMsg.reserved = 0;
1722 /* we don't have netdev yet, treat link up/down a adding/removing a port*/
1723 if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
1724 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
1725 } else if (eventEntry->status &
1726 (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
1727 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
1730 return STATUS_UNSUCCESSFUL;
1732 msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
1734 rc = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
1736 status = STATUS_INVALID_BUFFER_SIZE;
1740 vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
1742 status = STATUS_DEVICE_DOES_NOT_EXIST;
1746 rc = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) ||
1747 NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) ||
1748 NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
1750 status = STATUS_INVALID_BUFFER_SIZE;
1754 /* XXXX Should we add the port stats attributes?*/
1755 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1756 nlMsg->nlmsgLen = NlBufSize(nlBuf);
1757 status = STATUS_SUCCESS;
1765 * --------------------------------------------------------------------------
1766 * Handler for reading events from the driver event queue. This handler is
1767 * executed when user modes issues a socket receive on a socket assocaited
1768 * with the MC group for events.
1769 * XXX user mode should read multiple events in one system call
1770 * --------------------------------------------------------------------------
1773 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1777 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1778 POVS_OPEN_INSTANCE instance =
1779 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1783 OVS_EVENT_ENTRY eventEntry;
1785 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1787 /* Should never read events with a dump socket */
1788 ASSERT(instance->dumpState.ovsMsg == NULL);
1790 /* Must have an event queue */
1791 ASSERT(instance->eventQueue != NULL);
1793 /* Output buffer has been validated while validating read dev op. */
1794 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1796 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1798 OvsAcquireCtrlLock();
1799 if (!gOvsSwitchContext) {
1800 status = STATUS_SUCCESS;
1804 /* remove an event entry from the event queue */
1805 status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
1806 if (status != STATUS_SUCCESS) {
1810 status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
1811 if (status == NDIS_STATUS_SUCCESS) {
1812 *replyLen = NlBufSize(&nlBuf);
1816 OvsReleaseCtrlLock();
1819 #endif /* OVS_USE_NL_INTERFACE */