2 * Copyright (c) 2014 VMware, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * XXX: OVS_USE_NL_INTERFACE is being used to keep the legacy DPIF interface
19 * alive while we transition over to the netlink based interface.
20 * OVS_USE_NL_INTERFACE = 0 => legacy inteface to use with dpif-windows.c
21 * OVS_USE_NL_INTERFACE = 1 => netlink inteface to use with ported dpif-linux.c
23 #if defined OVS_USE_NL_INTERFACE && OVS_USE_NL_INTERFACE == 1
40 #define OVS_DBG_MOD OVS_DBG_DATAPATH
43 #define NETLINK_FAMILY_NAME_LEN 48
47 * Netlink messages are grouped by family (aka type), and each family supports
48 * a set of commands, and can be passed both from kernel -> userspace or
49 * vice-versa. To call into the kernel, userspace uses a device operation which
50 * is outside of a netlink message.
52 * Each command results in the invocation of a handler function to implement the
53 * request functionality.
55 * Expectedly, only certain combinations of (device operation, netlink family,
58 * Here, we implement the basic infrastructure to perform validation on the
59 * incoming message, version checking, and also to invoke the corresponding
60 * handler to do the heavy-lifting.
64 * Handler for a given netlink command. Not all the parameters are used by all
67 typedef NTSTATUS(NetlinkCmdHandler)(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
70 typedef struct _NETLINK_CMD {
72 NetlinkCmdHandler *handler;
73 UINT32 supportedDevOp; /* Supported device operations. */
74 BOOLEAN validateDpIndex; /* Does command require a valid DP argument. */
75 } NETLINK_CMD, *PNETLINK_CMD;
77 /* A netlink family is a group of commands. */
78 typedef struct _NETLINK_FAMILY {
84 NETLINK_CMD *cmds; /* Array of netlink commands and handlers. */
86 } NETLINK_FAMILY, *PNETLINK_FAMILY;
89 * Device operations to tag netlink commands with. This is a bitmask since it is
90 * possible that a particular command can be invoked via different device
93 #define OVS_READ_DEV_OP (1 << 0)
94 #define OVS_WRITE_DEV_OP (1 << 1)
95 #define OVS_TRANSACTION_DEV_OP (1 << 2)
97 /* Handlers for the various netlink commands. */
98 static NetlinkCmdHandler OvsGetPidCmdHandler,
100 OvsPendEventCmdHandler,
101 OvsSubscribeEventCmdHandler;
104 * The various netlink families, along with the supported commands. Most of
105 * these families and commands are part of the openvswitch specification for a
106 * netlink datapath. In addition, each platform can implement a few families
107 * and commands as extensions.
110 /* Netlink control family: this is a Windows specific family. */
111 NETLINK_CMD nlControlFamilyCmdOps[] = {
112 { .cmd = OVS_CTRL_CMD_WIN_GET_PID,
113 .handler = OvsGetPidCmdHandler,
114 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
115 .validateDpIndex = FALSE,
117 { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ,
118 .handler = OvsPendEventCmdHandler,
119 .supportedDevOp = OVS_WRITE_DEV_OP,
120 .validateDpIndex = TRUE,
122 { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ,
123 .handler = OvsSubscribeEventCmdHandler,
124 .supportedDevOp = OVS_WRITE_DEV_OP,
125 .validateDpIndex = TRUE,
129 NETLINK_FAMILY nlControlFamilyOps = {
130 .name = OVS_WIN_CONTROL_FAMILY,
131 .id = OVS_WIN_NL_CTRL_FAMILY_ID,
132 .version = OVS_WIN_CONTROL_VERSION,
133 .maxAttr = OVS_WIN_CONTROL_ATTR_MAX,
134 .cmds = nlControlFamilyCmdOps,
135 .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
138 /* Netlink datapath family. */
139 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
140 { .cmd = OVS_DP_CMD_GET,
141 .handler = OvsGetDpCmdHandler,
142 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
143 .validateDpIndex = FALSE
147 NETLINK_FAMILY nlDatapathFamilyOps = {
148 .name = OVS_DATAPATH_FAMILY,
149 .id = OVS_WIN_NL_DATAPATH_FAMILY_ID,
150 .version = OVS_DATAPATH_VERSION,
151 .maxAttr = OVS_DP_ATTR_MAX,
152 .cmds = nlDatapathFamilyCmdOps,
153 .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
156 /* Netlink packet family. */
157 /* XXX: Add commands here. */
158 NETLINK_FAMILY nlPacketFamilyOps = {
159 .name = OVS_PACKET_FAMILY,
160 .id = OVS_WIN_NL_PACKET_FAMILY_ID,
161 .version = OVS_PACKET_VERSION,
162 .maxAttr = OVS_PACKET_ATTR_MAX,
163 .cmds = NULL, /* XXX: placeholder. */
167 /* Netlink vport family. */
168 /* XXX: Add commands here. */
169 NETLINK_FAMILY nlVportFamilyOps = {
170 .name = OVS_VPORT_FAMILY,
171 .id = OVS_WIN_NL_VPORT_FAMILY_ID,
172 .version = OVS_VPORT_VERSION,
173 .maxAttr = OVS_VPORT_ATTR_MAX,
174 .cmds = NULL, /* XXX: placeholder. */
178 /* Netlink flow family. */
179 /* XXX: Add commands here. */
180 NETLINK_FAMILY nlFLowFamilyOps = {
181 .name = OVS_FLOW_FAMILY,
182 .id = OVS_WIN_NL_FLOW_FAMILY_ID,
183 .version = OVS_FLOW_VERSION,
184 .maxAttr = OVS_FLOW_ATTR_MAX,
185 .cmds = NULL, /* XXX: placeholder. */
189 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
191 UINT32 requiredLength,
193 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
194 POVS_OPEN_INSTANCE instance,
196 NETLINK_FAMILY *nlFamilyOps);
197 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
198 NETLINK_FAMILY *nlFamilyOps,
200 static NTSTATUS OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx);
203 /* Handles to the device object for communication with userspace. */
204 NDIS_HANDLE gOvsDeviceHandle;
205 PDEVICE_OBJECT gOvsDeviceObject;
207 _Dispatch_type_(IRP_MJ_CREATE)
208 _Dispatch_type_(IRP_MJ_CLOSE)
209 DRIVER_DISPATCH OvsOpenCloseDevice;
211 _Dispatch_type_(IRP_MJ_CLEANUP)
212 DRIVER_DISPATCH OvsCleanupDevice;
214 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
215 DRIVER_DISPATCH OvsDeviceControl;
218 #pragma alloc_text(INIT, OvsCreateDeviceObject)
219 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
220 #pragma alloc_text(PAGE, OvsCleanupDevice)
221 #pragma alloc_text(PAGE, OvsDeviceControl)
222 #endif // ALLOC_PRAGMA
225 * We might hit this limit easily since userspace opens a netlink descriptor for
226 * each thread, and at least one descriptor per vport. Revisit this later.
228 #define OVS_MAX_OPEN_INSTANCES 512
229 #define OVS_SYSTEM_DP_NAME "ovs-system"
231 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
232 UINT32 ovsNumberOfOpenInstances;
233 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
235 NDIS_SPIN_LOCK ovsCtrlLockObj;
236 PNDIS_SPIN_LOCK gOvsCtrlLock;
242 gOvsCtrlLock = &ovsCtrlLockObj;
243 NdisAllocateSpinLock(gOvsCtrlLock);
251 OvsCleanupEventQueue();
253 NdisFreeSpinLock(gOvsCtrlLock);
262 NdisAcquireSpinLock(gOvsCtrlLock);
268 NdisReleaseSpinLock(gOvsCtrlLock);
273 * --------------------------------------------------------------------------
274 * Creates the communication device between user and kernel, and also
275 * initializes the data associated data structures.
276 * --------------------------------------------------------------------------
279 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
281 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
282 UNICODE_STRING deviceName;
283 UNICODE_STRING symbolicDeviceName;
284 PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
285 NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
286 OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
288 RtlZeroMemory(dispatchTable,
289 (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
290 dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
291 dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
292 dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
293 dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
295 NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
296 NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
298 RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
300 OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
301 NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
302 NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
303 sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
305 deviceAttributes.DeviceName = &deviceName;
306 deviceAttributes.SymbolicName = &symbolicDeviceName;
307 deviceAttributes.MajorFunctions = dispatchTable;
308 deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
310 status = NdisRegisterDeviceEx(ovsExtDriverHandle,
314 if (status != NDIS_STATUS_SUCCESS) {
315 POVS_DEVICE_EXTENSION ovsExt =
316 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
317 ASSERT(gOvsDeviceObject != NULL);
318 ASSERT(gOvsDeviceHandle != NULL);
321 ovsExt->numberOpenInstance = 0;
324 /* Initialize the associated data structures. */
327 OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
333 OvsDeleteDeviceObject()
335 if (gOvsDeviceHandle) {
337 POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
338 NdisGetDeviceReservedExtension(gOvsDeviceObject);
340 ASSERT(ovsExt->numberOpenInstance == 0);
344 ASSERT(gOvsDeviceObject);
345 NdisDeregisterDeviceEx(gOvsDeviceHandle);
346 gOvsDeviceHandle = NULL;
347 gOvsDeviceObject = NULL;
353 OvsGetOpenInstance(PFILE_OBJECT fileObject,
356 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
358 ASSERT(instance->fileObject == fileObject);
359 if (gOvsSwitchContext == NULL ||
360 gOvsSwitchContext->dpNo != dpNo) {
368 OvsFindOpenInstance(PFILE_OBJECT fileObject)
371 for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
372 j < ovsNumberOfOpenInstances; i++) {
373 if (ovsOpenInstanceArray[i]) {
374 if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
375 return ovsOpenInstanceArray[i];
384 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
385 PFILE_OBJECT fileObject)
387 POVS_OPEN_INSTANCE instance =
388 (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
391 if (instance == NULL) {
392 return STATUS_NO_MEMORY;
394 OvsAcquireCtrlLock();
395 ASSERT(OvsFindOpenInstance(fileObject) == NULL);
397 if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
398 OvsReleaseCtrlLock();
399 OvsFreeMemory(instance);
400 return STATUS_INSUFFICIENT_RESOURCES;
402 RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
404 for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
405 if (ovsOpenInstanceArray[i] == NULL) {
406 ovsOpenInstanceArray[i] = instance;
407 instance->cookie = i;
411 ASSERT(i < OVS_MAX_OPEN_INSTANCES);
412 instance->fileObject = fileObject;
413 ASSERT(fileObject->FsContext == NULL);
414 instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
415 if (instance->pid == 0) {
416 /* XXX: check for rollover. */
418 fileObject->FsContext = instance;
419 OvsReleaseCtrlLock();
420 return STATUS_SUCCESS;
424 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
426 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
428 ASSERT(fileObject == instance->fileObject);
429 OvsCleanupEvent(instance);
430 OvsCleanupPacketQueue(instance);
434 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
436 POVS_OPEN_INSTANCE instance;
437 ASSERT(fileObject->FsContext);
438 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
439 ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
441 OvsAcquireCtrlLock();
442 fileObject->FsContext = NULL;
443 ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
444 ovsOpenInstanceArray[instance->cookie] = NULL;
445 OvsReleaseCtrlLock();
446 ASSERT(instance->eventQueue == NULL);
447 ASSERT (instance->packetQueue == NULL);
448 OvsFreeMemory(instance);
452 OvsCompleteIrpRequest(PIRP irp,
456 irp->IoStatus.Information = infoPtr;
457 irp->IoStatus.Status = status;
458 IoCompleteRequest(irp, IO_NO_INCREMENT);
464 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
467 PIO_STACK_LOCATION irpSp;
468 NTSTATUS status = STATUS_SUCCESS;
469 PFILE_OBJECT fileObject;
470 POVS_DEVICE_EXTENSION ovsExt =
471 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
473 ASSERT(deviceObject == gOvsDeviceObject);
474 ASSERT(ovsExt != NULL);
476 irpSp = IoGetCurrentIrpStackLocation(irp);
477 fileObject = irpSp->FileObject;
478 OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
479 deviceObject, fileObject,
480 ovsExt->numberOpenInstance);
482 switch (irpSp->MajorFunction) {
484 status = OvsAddOpenInstance(ovsExt, fileObject);
485 if (STATUS_SUCCESS == status) {
486 InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
490 ASSERT(ovsExt->numberOpenInstance > 0);
491 OvsRemoveOpenInstance(fileObject);
492 InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
497 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
500 _Use_decl_annotations_
502 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
506 PIO_STACK_LOCATION irpSp;
507 PFILE_OBJECT fileObject;
509 NTSTATUS status = STATUS_SUCCESS;
511 POVS_DEVICE_EXTENSION ovsExt =
512 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
514 ASSERT(ovsExt->numberOpenInstance > 0);
517 UNREFERENCED_PARAMETER(deviceObject);
519 ASSERT(deviceObject == gOvsDeviceObject);
520 irpSp = IoGetCurrentIrpStackLocation(irp);
521 fileObject = irpSp->FileObject;
523 ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
525 OvsCleanupOpenInstance(fileObject);
527 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
532 * --------------------------------------------------------------------------
533 * IOCTL function handler for the device.
534 * --------------------------------------------------------------------------
537 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
541 PIO_STACK_LOCATION irpSp;
542 NTSTATUS status = STATUS_SUCCESS;
543 PFILE_OBJECT fileObject;
544 PVOID inputBuffer = NULL;
545 PVOID outputBuffer = NULL;
546 UINT32 inputBufferLen, outputBufferLen;
547 UINT32 code, replyLen = 0;
548 POVS_OPEN_INSTANCE instance;
550 OVS_MESSAGE ovsMsgReadOp;
552 NETLINK_FAMILY *nlFamilyOps;
553 OVS_USER_PARAMS_CONTEXT usrParamsCtx;
556 POVS_DEVICE_EXTENSION ovsExt =
557 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
558 ASSERT(deviceObject == gOvsDeviceObject);
560 ASSERT(ovsExt->numberOpenInstance > 0);
562 UNREFERENCED_PARAMETER(deviceObject);
565 irpSp = IoGetCurrentIrpStackLocation(irp);
567 ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
568 ASSERT(irpSp->FileObject != NULL);
570 fileObject = irpSp->FileObject;
571 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
572 code = irpSp->Parameters.DeviceIoControl.IoControlCode;
573 inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
574 outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
575 inputBuffer = irp->AssociatedIrp.SystemBuffer;
577 /* Concurrent netlink operations are not supported. */
578 if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
579 status = STATUS_RESOURCE_IN_USE;
584 * Validate the input/output buffer arguments depending on the type of the
588 case OVS_IOCTL_TRANSACT:
589 /* Input buffer is mandatory, output buffer is optional. */
590 if (outputBufferLen != 0) {
591 status = MapIrpOutputBuffer(irp, outputBufferLen,
592 sizeof *ovsMsg, &outputBuffer);
593 if (status != STATUS_SUCCESS) {
596 ASSERT(outputBuffer);
599 if (inputBufferLen < sizeof (*ovsMsg)) {
600 status = STATUS_NDIS_INVALID_LENGTH;
604 ovsMsg = inputBuffer;
605 devOp = OVS_TRANSACTION_DEV_OP;
609 /* Output buffer is mandatory. */
610 if (outputBufferLen != 0) {
611 status = MapIrpOutputBuffer(irp, outputBufferLen,
612 sizeof *ovsMsg, &outputBuffer);
613 if (status != STATUS_SUCCESS) {
616 ASSERT(outputBuffer);
618 status = STATUS_NDIS_INVALID_LENGTH;
623 * Operate in the mode that read ioctl is similar to ReadFile(). This
624 * might change as the userspace code gets implemented.
630 * For implementing read (ioctl or otherwise), we need to store some
631 * state in the instance to indicate the command that started the dump
632 * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
633 * that 'ovsMsgReadOp' is needed only in this function to call into the
634 * appropraite handler. The handler itself can access the state in the
637 * In the absence of a dump start, return 0 bytes.
639 if (instance->dumpState.ovsMsg == NULL) {
641 status = STATUS_SUCCESS;
644 RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
645 sizeof (ovsMsgReadOp));
647 /* Create an NL message for consumption. */
648 ovsMsg = &ovsMsgReadOp;
649 devOp = OVS_READ_DEV_OP;
653 case OVS_IOCTL_WRITE:
654 /* Input buffer is mandatory. */
655 if (inputBufferLen < sizeof (*ovsMsg)) {
656 status = STATUS_NDIS_INVALID_LENGTH;
660 ovsMsg = inputBuffer;
661 devOp = OVS_WRITE_DEV_OP;
665 status = STATUS_INVALID_DEVICE_REQUEST;
670 switch (ovsMsg->nlMsg.nlmsgType) {
671 case OVS_WIN_NL_CTRL_FAMILY_ID:
672 nlFamilyOps = &nlControlFamilyOps;
674 case OVS_WIN_NL_DATAPATH_FAMILY_ID:
675 nlFamilyOps = &nlDatapathFamilyOps;
677 case OVS_WIN_NL_PACKET_FAMILY_ID:
678 case OVS_WIN_NL_FLOW_FAMILY_ID:
679 case OVS_WIN_NL_VPORT_FAMILY_ID:
680 status = STATUS_NOT_IMPLEMENTED;
684 status = STATUS_INVALID_PARAMETER;
689 * For read operation, the netlink command has already been validated
692 if (devOp != OVS_READ_DEV_OP) {
693 status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
694 if (status != STATUS_SUCCESS) {
699 InitUserParamsCtx(irp, instance, devOp, ovsMsg,
700 inputBuffer, inputBufferLen,
701 outputBuffer, outputBufferLen,
704 status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
709 return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
714 * --------------------------------------------------------------------------
715 * Function to validate a netlink command. Only certain combinations of
716 * (device operation, netlink family, command) are valid.
718 * XXX: Take policy into consideration.
719 * --------------------------------------------------------------------------
722 ValidateNetlinkCmd(UINT32 devOp,
723 POVS_OPEN_INSTANCE instance,
725 NETLINK_FAMILY *nlFamilyOps)
727 NTSTATUS status = STATUS_INVALID_PARAMETER;
730 for (i = 0; i < nlFamilyOps->opsCount; i++) {
731 if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
732 /* Validate if the command is valid for the device operation. */
733 if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
734 status = STATUS_INVALID_PARAMETER;
738 /* Validate the version. */
739 if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
740 status = STATUS_INVALID_PARAMETER;
744 /* Validate the DP for commands that require a DP. */
745 if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
746 OvsAcquireCtrlLock();
747 if (ovsMsg->ovsHdr.dp_ifindex !=
748 (INT)gOvsSwitchContext->dpNo) {
749 status = STATUS_INVALID_PARAMETER;
750 OvsReleaseCtrlLock();
753 OvsReleaseCtrlLock();
756 /* Validate the PID. */
757 if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
758 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
759 status = STATUS_INVALID_PARAMETER;
764 status = STATUS_SUCCESS;
774 * --------------------------------------------------------------------------
775 * Function to invoke the netlink command handler.
776 * --------------------------------------------------------------------------
779 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
780 NETLINK_FAMILY *nlFamilyOps,
783 NTSTATUS status = STATUS_INVALID_PARAMETER;
786 for (i = 0; i < nlFamilyOps->opsCount; i++) {
787 if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
788 NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
791 status = handler(usrParamsCtx, replyLen);
802 * --------------------------------------------------------------------------
803 * Each handle on the device is assigned a unique PID when the handle is
804 * created. On platforms that support netlink natively, the PID is available
805 * to userspace when the netlink socket is created. However, without native
806 * netlink support on Windows, OVS datapath generates the PID and lets the
807 * userspace query it.
809 * This function implements the query.
810 * --------------------------------------------------------------------------
813 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
816 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
817 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
819 if (usrParamsCtx->outputLength >= sizeof *msgOut) {
820 POVS_OPEN_INSTANCE instance =
821 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
823 RtlZeroMemory(msgOut, sizeof *msgOut);
824 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
825 msgOut->nlMsg.nlmsgPid = instance->pid;
826 *replyLen = sizeof *msgOut;
827 /* XXX: We might need to return the DP index as well. */
829 return STATUS_NDIS_INVALID_LENGTH;
832 return STATUS_SUCCESS;
836 * --------------------------------------------------------------------------
837 * Utility function to fill up information about the datapath in a reply to
839 * Assumes that 'gOvsCtrlLock' lock is acquired.
840 * --------------------------------------------------------------------------
843 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
848 OVS_MESSAGE msgOutTmp;
849 OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
852 /* XXX: Add API for nlBuf->bufRemLen. */
853 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
855 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
856 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
857 msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
858 msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
860 msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
861 msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
862 msgOutTmp.genlMsg.reserved = 0;
864 msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
866 writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
868 writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
872 OVS_DP_STATS dpStats;
874 dpStats.n_hit = datapath->hits;
875 dpStats.n_missed = datapath->misses;
876 dpStats.n_lost = datapath->lost;
877 dpStats.n_flows = datapath->nFlows;
878 writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
879 (PCHAR)&dpStats, sizeof dpStats);
881 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
882 nlMsg->nlmsgLen = NlBufSize(nlBuf);
884 return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
889 * --------------------------------------------------------------------------
890 * Handler for queueing an IRP used for event notification. The IRP is
891 * completed when a port state changes. STATUS_PENDING is returned on
892 * success. User mode keep a pending IRP at all times.
893 * --------------------------------------------------------------------------
896 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
901 UNREFERENCED_PARAMETER(replyLen);
903 POVS_OPEN_INSTANCE instance =
904 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
905 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
908 poll.dpNo = msgIn->ovsHdr.dp_ifindex;
909 status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
915 * --------------------------------------------------------------------------
916 * Handler for the subscription for the event queue
917 * --------------------------------------------------------------------------
920 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
924 OVS_EVENT_SUBSCRIBE request;
928 const NL_POLICY policy[] = {
929 [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
930 [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
933 UNREFERENCED_PARAMETER(replyLen);
935 POVS_OPEN_INSTANCE instance =
936 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
937 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
939 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),policy, attrs, 2);
941 status = STATUS_INVALID_PARAMETER;
945 /* XXX Ignore the MC group for now */
946 join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
947 request.dpNo = msgIn->ovsHdr.dp_ifindex;
948 request.subscribe = join;
949 request.mask = OVS_EVENT_MASK_ALL;
951 status = OvsSubscribeEventIoctl(instance->fileObject, &request,
959 * --------------------------------------------------------------------------
960 * Handler for the get dp command. The function handles the initial call to
961 * setup the dump state, as well as subsequent calls to continue dumping data.
962 * --------------------------------------------------------------------------
965 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
968 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
969 POVS_OPEN_INSTANCE instance =
970 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
972 if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
974 OvsSetupDumpStart(usrParamsCtx);
978 POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
980 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
982 if (instance->dumpState.ovsMsg == NULL) {
984 return STATUS_INVALID_DEVICE_STATE;
987 /* Dump state must have been deleted after previous dump operation. */
988 ASSERT(instance->dumpState.index[0] == 0);
989 /* Output buffer has been validated while validating read dev op. */
990 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
992 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
993 usrParamsCtx->outputLength);
995 OvsAcquireCtrlLock();
996 if (!gOvsSwitchContext) {
997 /* Treat this as a dump done. */
998 OvsReleaseCtrlLock();
1000 FreeUserDumpState(instance);
1001 return STATUS_SUCCESS;
1003 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1004 OvsReleaseCtrlLock();
1006 if (status != STATUS_SUCCESS) {
1008 FreeUserDumpState(instance);
1012 /* Increment the dump index. */
1013 instance->dumpState.index[0] = 1;
1014 *replyLen = msgOut->nlMsg.nlmsgLen;
1016 /* Free up the dump state, since there's no more data to continue. */
1017 FreeUserDumpState(instance);
1020 return STATUS_SUCCESS;
1024 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1026 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1027 POVS_OPEN_INSTANCE instance =
1028 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1030 /* input buffer has been validated while validating write dev op. */
1031 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1033 /* A write operation that does not indicate dump start is invalid. */
1034 if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1035 return STATUS_INVALID_PARAMETER;
1037 /* XXX: Handle other NLM_F_* flags in the future. */
1040 * This operation should be setting up the dump state. If there's any
1041 * previous state, clear it up so as to set it up afresh.
1043 if (instance->dumpState.ovsMsg != NULL) {
1044 FreeUserDumpState(instance);
1047 return InitUserDumpState(instance, msgIn);
1051 * --------------------------------------------------------------------------
1052 * Utility function to map the output buffer in an IRP. The buffer is assumed
1053 * to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
1054 * --------------------------------------------------------------------------
1057 MapIrpOutputBuffer(PIRP irp,
1058 UINT32 bufferLength,
1059 UINT32 requiredLength,
1064 ASSERT(bufferLength);
1065 ASSERT(requiredLength);
1066 if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
1067 return STATUS_INVALID_PARAMETER;
1070 if (bufferLength < requiredLength) {
1071 return STATUS_NDIS_INVALID_LENGTH;
1073 if (irp->MdlAddress == NULL) {
1074 return STATUS_INVALID_PARAMETER;
1076 *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
1077 NormalPagePriority);
1078 if (*buffer == NULL) {
1079 return STATUS_INSUFFICIENT_RESOURCES;
1082 return STATUS_SUCCESS;
1085 #endif /* OVS_USE_NL_INTERFACE */