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
41 #define OVS_DBG_MOD OVS_DBG_DATAPATH
44 #define NETLINK_FAMILY_NAME_LEN 48
48 * Netlink messages are grouped by family (aka type), and each family supports
49 * a set of commands, and can be passed both from kernel -> userspace or
50 * vice-versa. To call into the kernel, userspace uses a device operation which
51 * is outside of a netlink message.
53 * Each command results in the invocation of a handler function to implement the
54 * request functionality.
56 * Expectedly, only certain combinations of (device operation, netlink family,
59 * Here, we implement the basic infrastructure to perform validation on the
60 * incoming message, version checking, and also to invoke the corresponding
61 * handler to do the heavy-lifting.
65 * Handler for a given netlink command. Not all the parameters are used by all
68 typedef NTSTATUS(NetlinkCmdHandler)(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
71 typedef struct _NETLINK_CMD {
73 NetlinkCmdHandler *handler;
74 UINT32 supportedDevOp; /* Supported device operations. */
75 BOOLEAN validateDpIndex; /* Does command require a valid DP argument. */
76 } NETLINK_CMD, *PNETLINK_CMD;
78 /* A netlink family is a group of commands. */
79 typedef struct _NETLINK_FAMILY {
85 NETLINK_CMD *cmds; /* Array of netlink commands and handlers. */
87 } NETLINK_FAMILY, *PNETLINK_FAMILY;
89 /* Handlers for the various netlink commands. */
90 static NetlinkCmdHandler OvsGetPidCmdHandler,
91 OvsPendEventCmdHandler,
92 OvsPendPacketCmdHandler,
93 OvsSubscribeEventCmdHandler,
94 OvsSubscribePacketCmdHandler,
95 OvsReadEventCmdHandler,
96 OvsReadPacketCmdHandler,
101 NetlinkCmdHandler OvsGetNetdevCmdHandler,
102 OvsGetVportCmdHandler,
103 OvsSetVportCmdHandler,
104 OvsNewVportCmdHandler,
105 OvsDeleteVportCmdHandler;
107 static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
109 static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
111 static NTSTATUS HandleDpTransactionCommon(
112 POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen);
115 * The various netlink families, along with the supported commands. Most of
116 * these families and commands are part of the openvswitch specification for a
117 * netlink datapath. In addition, each platform can implement a few families
118 * and commands as extensions.
121 /* Netlink control family: this is a Windows specific family. */
122 NETLINK_CMD nlControlFamilyCmdOps[] = {
123 { .cmd = OVS_CTRL_CMD_WIN_GET_PID,
124 .handler = OvsGetPidCmdHandler,
125 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
126 .validateDpIndex = FALSE,
128 { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ,
129 .handler = OvsPendEventCmdHandler,
130 .supportedDevOp = OVS_WRITE_DEV_OP,
131 .validateDpIndex = TRUE,
133 { .cmd = OVS_CTRL_CMD_WIN_PEND_PACKET_REQ,
134 .handler = OvsPendPacketCmdHandler,
135 .supportedDevOp = OVS_WRITE_DEV_OP,
136 .validateDpIndex = TRUE,
138 { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ,
139 .handler = OvsSubscribeEventCmdHandler,
140 .supportedDevOp = OVS_WRITE_DEV_OP,
141 .validateDpIndex = TRUE,
143 { .cmd = OVS_CTRL_CMD_PACKET_SUBSCRIBE_REQ,
144 .handler = OvsSubscribePacketCmdHandler,
145 .supportedDevOp = OVS_WRITE_DEV_OP,
146 .validateDpIndex = TRUE,
148 { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY,
149 .handler = OvsReadEventCmdHandler,
150 .supportedDevOp = OVS_READ_EVENT_DEV_OP,
151 .validateDpIndex = FALSE,
153 { .cmd = OVS_CTRL_CMD_READ_NOTIFY,
154 .handler = OvsReadPacketCmdHandler,
155 .supportedDevOp = OVS_READ_PACKET_DEV_OP,
156 .validateDpIndex = FALSE,
160 NETLINK_FAMILY nlControlFamilyOps = {
161 .name = OVS_WIN_CONTROL_FAMILY,
162 .id = OVS_WIN_NL_CTRL_FAMILY_ID,
163 .version = OVS_WIN_CONTROL_VERSION,
164 .maxAttr = OVS_WIN_CONTROL_ATTR_MAX,
165 .cmds = nlControlFamilyCmdOps,
166 .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
169 /* Netlink datapath family. */
170 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
171 { .cmd = OVS_DP_CMD_NEW,
172 .handler = OvsNewDpCmdHandler,
173 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
174 .validateDpIndex = FALSE
176 { .cmd = OVS_DP_CMD_GET,
177 .handler = OvsGetDpCmdHandler,
178 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
179 OVS_TRANSACTION_DEV_OP,
180 .validateDpIndex = FALSE
182 { .cmd = OVS_DP_CMD_SET,
183 .handler = OvsSetDpCmdHandler,
184 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
185 OVS_TRANSACTION_DEV_OP,
186 .validateDpIndex = TRUE
190 NETLINK_FAMILY nlDatapathFamilyOps = {
191 .name = OVS_DATAPATH_FAMILY,
192 .id = OVS_WIN_NL_DATAPATH_FAMILY_ID,
193 .version = OVS_DATAPATH_VERSION,
194 .maxAttr = OVS_DP_ATTR_MAX,
195 .cmds = nlDatapathFamilyCmdOps,
196 .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
199 /* Netlink packet family. */
201 NETLINK_CMD nlPacketFamilyCmdOps[] = {
202 { .cmd = OVS_PACKET_CMD_EXECUTE,
203 .handler = OvsNlExecuteCmdHandler,
204 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
205 .validateDpIndex = TRUE
209 NETLINK_FAMILY nlPacketFamilyOps = {
210 .name = OVS_PACKET_FAMILY,
211 .id = OVS_WIN_NL_PACKET_FAMILY_ID,
212 .version = OVS_PACKET_VERSION,
213 .maxAttr = OVS_PACKET_ATTR_MAX,
214 .cmds = nlPacketFamilyCmdOps,
215 .opsCount = ARRAY_SIZE(nlPacketFamilyCmdOps)
218 /* Netlink vport family. */
219 NETLINK_CMD nlVportFamilyCmdOps[] = {
220 { .cmd = OVS_VPORT_CMD_GET,
221 .handler = OvsGetVportCmdHandler,
222 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
223 OVS_TRANSACTION_DEV_OP,
224 .validateDpIndex = TRUE
226 { .cmd = OVS_VPORT_CMD_NEW,
227 .handler = OvsNewVportCmdHandler,
228 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
229 .validateDpIndex = TRUE
231 { .cmd = OVS_VPORT_CMD_SET,
232 .handler = OvsSetVportCmdHandler,
233 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
234 .validateDpIndex = TRUE
236 { .cmd = OVS_VPORT_CMD_DEL,
237 .handler = OvsDeleteVportCmdHandler,
238 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
239 .validateDpIndex = TRUE
243 NETLINK_FAMILY nlVportFamilyOps = {
244 .name = OVS_VPORT_FAMILY,
245 .id = OVS_WIN_NL_VPORT_FAMILY_ID,
246 .version = OVS_VPORT_VERSION,
247 .maxAttr = OVS_VPORT_ATTR_MAX,
248 .cmds = nlVportFamilyCmdOps,
249 .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps)
252 /* Netlink flow family. */
254 NETLINK_CMD nlFlowFamilyCmdOps[] = {
255 { .cmd = OVS_FLOW_CMD_NEW,
256 .handler = OvsFlowNlCmdHandler,
257 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
258 .validateDpIndex = TRUE
260 { .cmd = OVS_FLOW_CMD_SET,
261 .handler = OvsFlowNlCmdHandler,
262 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
263 .validateDpIndex = TRUE
265 { .cmd = OVS_FLOW_CMD_DEL,
266 .handler = OvsFlowNlCmdHandler,
267 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
268 .validateDpIndex = TRUE
270 { .cmd = OVS_FLOW_CMD_GET,
271 .handler = OvsFlowNlGetCmdHandler,
272 .supportedDevOp = OVS_TRANSACTION_DEV_OP |
273 OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
274 .validateDpIndex = TRUE
278 NETLINK_FAMILY nlFLowFamilyOps = {
279 .name = OVS_FLOW_FAMILY,
280 .id = OVS_WIN_NL_FLOW_FAMILY_ID,
281 .version = OVS_FLOW_VERSION,
282 .maxAttr = OVS_FLOW_ATTR_MAX,
283 .cmds = nlFlowFamilyCmdOps,
284 .opsCount = ARRAY_SIZE(nlFlowFamilyCmdOps)
287 /* Netlink netdev family. */
288 NETLINK_CMD nlNetdevFamilyCmdOps[] = {
289 { .cmd = OVS_WIN_NETDEV_CMD_GET,
290 .handler = OvsGetNetdevCmdHandler,
291 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
292 .validateDpIndex = FALSE
296 NETLINK_FAMILY nlNetdevFamilyOps = {
297 .name = OVS_WIN_NETDEV_FAMILY,
298 .id = OVS_WIN_NL_NETDEV_FAMILY_ID,
299 .version = OVS_WIN_NETDEV_VERSION,
300 .maxAttr = OVS_WIN_NETDEV_ATTR_MAX,
301 .cmds = nlNetdevFamilyCmdOps,
302 .opsCount = ARRAY_SIZE(nlNetdevFamilyCmdOps)
305 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
307 UINT32 requiredLength,
309 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
310 POVS_OPEN_INSTANCE instance,
312 NETLINK_FAMILY *nlFamilyOps);
313 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
314 NETLINK_FAMILY *nlFamilyOps,
317 /* Handles to the device object for communication with userspace. */
318 NDIS_HANDLE gOvsDeviceHandle;
319 PDEVICE_OBJECT gOvsDeviceObject;
321 _Dispatch_type_(IRP_MJ_CREATE)
322 _Dispatch_type_(IRP_MJ_CLOSE)
323 DRIVER_DISPATCH OvsOpenCloseDevice;
325 _Dispatch_type_(IRP_MJ_CLEANUP)
326 DRIVER_DISPATCH OvsCleanupDevice;
328 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
329 DRIVER_DISPATCH OvsDeviceControl;
332 #pragma alloc_text(INIT, OvsCreateDeviceObject)
333 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
334 #pragma alloc_text(PAGE, OvsCleanupDevice)
335 #pragma alloc_text(PAGE, OvsDeviceControl)
336 #endif // ALLOC_PRAGMA
339 * We might hit this limit easily since userspace opens a netlink descriptor for
340 * each thread, and at least one descriptor per vport. Revisit this later.
342 #define OVS_MAX_OPEN_INSTANCES 512
343 #define OVS_SYSTEM_DP_NAME "ovs-system"
345 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
346 UINT32 ovsNumberOfOpenInstances;
347 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
349 NDIS_SPIN_LOCK ovsCtrlLockObj;
350 PNDIS_SPIN_LOCK gOvsCtrlLock;
356 HANDLE handle = NULL;
358 gOvsCtrlLock = &ovsCtrlLockObj;
359 NdisAllocateSpinLock(gOvsCtrlLock);
362 OvsTunnelEngineOpen(&handle);
364 OvsTunnelAddSystemProvider(handle);
366 OvsTunnelEngineClose(&handle);
372 HANDLE handle = NULL;
374 OvsCleanupEventQueue();
376 NdisFreeSpinLock(gOvsCtrlLock);
380 OvsTunnelEngineOpen(&handle);
382 OvsTunnelRemoveSystemProvider(handle);
384 OvsTunnelEngineClose(&handle);
390 NdisAcquireSpinLock(gOvsCtrlLock);
396 NdisReleaseSpinLock(gOvsCtrlLock);
401 * --------------------------------------------------------------------------
402 * Creates the communication device between user and kernel, and also
403 * initializes the data associated data structures.
404 * --------------------------------------------------------------------------
407 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
409 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
410 UNICODE_STRING deviceName;
411 UNICODE_STRING symbolicDeviceName;
412 PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
413 NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
414 OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
416 RtlZeroMemory(dispatchTable,
417 (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
418 dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
419 dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
420 dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
421 dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
423 NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
424 NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
426 RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
428 OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
429 NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
430 NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
431 sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
433 deviceAttributes.DeviceName = &deviceName;
434 deviceAttributes.SymbolicName = &symbolicDeviceName;
435 deviceAttributes.MajorFunctions = dispatchTable;
436 deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
438 status = NdisRegisterDeviceEx(ovsExtDriverHandle,
442 if (status != NDIS_STATUS_SUCCESS) {
443 POVS_DEVICE_EXTENSION ovsExt =
444 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
445 ASSERT(gOvsDeviceObject != NULL);
446 ASSERT(gOvsDeviceHandle != NULL);
449 ovsExt->numberOpenInstance = 0;
453 OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
459 OvsDeleteDeviceObject()
461 if (gOvsDeviceHandle) {
463 POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
464 NdisGetDeviceReservedExtension(gOvsDeviceObject);
466 ASSERT(ovsExt->numberOpenInstance == 0);
470 ASSERT(gOvsDeviceObject);
471 NdisDeregisterDeviceEx(gOvsDeviceHandle);
472 gOvsDeviceHandle = NULL;
473 gOvsDeviceObject = NULL;
478 OvsGetOpenInstance(PFILE_OBJECT fileObject,
481 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
483 ASSERT(instance->fileObject == fileObject);
484 if (gOvsSwitchContext->dpNo != dpNo) {
492 OvsFindOpenInstance(PFILE_OBJECT fileObject)
495 for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
496 j < ovsNumberOfOpenInstances; i++) {
497 if (ovsOpenInstanceArray[i]) {
498 if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
499 return ovsOpenInstanceArray[i];
508 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
509 PFILE_OBJECT fileObject)
511 POVS_OPEN_INSTANCE instance =
512 (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
515 if (instance == NULL) {
516 return STATUS_NO_MEMORY;
518 OvsAcquireCtrlLock();
519 ASSERT(OvsFindOpenInstance(fileObject) == NULL);
521 if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
522 OvsReleaseCtrlLock();
523 OvsFreeMemory(instance);
524 return STATUS_INSUFFICIENT_RESOURCES;
526 RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
528 for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
529 if (ovsOpenInstanceArray[i] == NULL) {
530 ovsOpenInstanceArray[i] = instance;
531 ovsNumberOfOpenInstances++;
532 instance->cookie = i;
536 ASSERT(i < OVS_MAX_OPEN_INSTANCES);
537 instance->fileObject = fileObject;
538 ASSERT(fileObject->FsContext == NULL);
539 instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
540 if (instance->pid == 0) {
541 /* XXX: check for rollover. */
543 fileObject->FsContext = instance;
544 OvsReleaseCtrlLock();
545 return STATUS_SUCCESS;
549 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
551 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
553 ASSERT(fileObject == instance->fileObject);
554 OvsCleanupEvent(instance);
555 OvsCleanupPacketQueue(instance);
559 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
561 POVS_OPEN_INSTANCE instance;
562 ASSERT(fileObject->FsContext);
563 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
564 ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
566 OvsAcquireCtrlLock();
567 fileObject->FsContext = NULL;
568 ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
569 ovsOpenInstanceArray[instance->cookie] = NULL;
570 ovsNumberOfOpenInstances--;
571 OvsReleaseCtrlLock();
572 ASSERT(instance->eventQueue == NULL);
573 ASSERT (instance->packetQueue == NULL);
574 OvsFreeMemory(instance);
578 OvsCompleteIrpRequest(PIRP irp,
582 irp->IoStatus.Information = infoPtr;
583 irp->IoStatus.Status = status;
584 IoCompleteRequest(irp, IO_NO_INCREMENT);
590 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
593 PIO_STACK_LOCATION irpSp;
594 NTSTATUS status = STATUS_SUCCESS;
595 PFILE_OBJECT fileObject;
596 POVS_DEVICE_EXTENSION ovsExt =
597 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
599 ASSERT(deviceObject == gOvsDeviceObject);
600 ASSERT(ovsExt != NULL);
602 irpSp = IoGetCurrentIrpStackLocation(irp);
603 fileObject = irpSp->FileObject;
604 OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
605 deviceObject, fileObject,
606 ovsExt->numberOpenInstance);
608 switch (irpSp->MajorFunction) {
610 status = OvsAddOpenInstance(ovsExt, fileObject);
611 if (STATUS_SUCCESS == status) {
612 InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
616 ASSERT(ovsExt->numberOpenInstance > 0);
617 OvsRemoveOpenInstance(fileObject);
618 InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
623 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
626 _Use_decl_annotations_
628 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
632 PIO_STACK_LOCATION irpSp;
633 PFILE_OBJECT fileObject;
635 NTSTATUS status = STATUS_SUCCESS;
637 POVS_DEVICE_EXTENSION ovsExt =
638 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
640 ASSERT(ovsExt->numberOpenInstance > 0);
643 UNREFERENCED_PARAMETER(deviceObject);
645 ASSERT(deviceObject == gOvsDeviceObject);
646 irpSp = IoGetCurrentIrpStackLocation(irp);
647 fileObject = irpSp->FileObject;
649 ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
651 OvsCleanupOpenInstance(fileObject);
653 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
658 * --------------------------------------------------------------------------
659 * IOCTL function handler for the device.
660 * --------------------------------------------------------------------------
663 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
666 PIO_STACK_LOCATION irpSp;
667 NTSTATUS status = STATUS_SUCCESS;
668 PFILE_OBJECT fileObject;
669 PVOID inputBuffer = NULL;
670 PVOID outputBuffer = NULL;
671 UINT32 inputBufferLen, outputBufferLen;
672 UINT32 code, replyLen = 0;
673 POVS_OPEN_INSTANCE instance;
675 OVS_MESSAGE ovsMsgReadOp;
677 NETLINK_FAMILY *nlFamilyOps;
678 OVS_USER_PARAMS_CONTEXT usrParamsCtx;
681 POVS_DEVICE_EXTENSION ovsExt =
682 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
683 ASSERT(deviceObject == gOvsDeviceObject);
685 ASSERT(ovsExt->numberOpenInstance > 0);
687 UNREFERENCED_PARAMETER(deviceObject);
690 irpSp = IoGetCurrentIrpStackLocation(irp);
692 ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
693 ASSERT(irpSp->FileObject != NULL);
695 fileObject = irpSp->FileObject;
696 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
697 code = irpSp->Parameters.DeviceIoControl.IoControlCode;
698 inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
699 outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
700 inputBuffer = irp->AssociatedIrp.SystemBuffer;
702 /* Check if the extension is enabled. */
703 if (NULL == gOvsSwitchContext) {
704 status = STATUS_DEVICE_NOT_READY;
708 /* Concurrent netlink operations are not supported. */
709 if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
710 status = STATUS_RESOURCE_IN_USE;
715 * Validate the input/output buffer arguments depending on the type of the
719 case OVS_IOCTL_TRANSACT:
720 /* Both input buffer and output buffer are mandatory. */
721 if (outputBufferLen != 0) {
722 status = MapIrpOutputBuffer(irp, outputBufferLen,
723 sizeof *ovsMsg, &outputBuffer);
724 if (status != STATUS_SUCCESS) {
727 ASSERT(outputBuffer);
729 status = STATUS_NDIS_INVALID_LENGTH;
733 if (inputBufferLen < sizeof (*ovsMsg)) {
734 status = STATUS_NDIS_INVALID_LENGTH;
738 ovsMsg = inputBuffer;
739 devOp = OVS_TRANSACTION_DEV_OP;
742 case OVS_IOCTL_READ_EVENT:
743 case OVS_IOCTL_READ_PACKET:
745 * Output buffer is mandatory. These IOCTLs are used to read events and
746 * packets respectively. It is convenient to have separate ioctls.
748 if (outputBufferLen != 0) {
749 status = MapIrpOutputBuffer(irp, outputBufferLen,
750 sizeof *ovsMsg, &outputBuffer);
751 if (status != STATUS_SUCCESS) {
754 ASSERT(outputBuffer);
756 status = STATUS_NDIS_INVALID_LENGTH;
762 ovsMsg = &ovsMsgReadOp;
763 ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
764 ovsMsg->nlMsg.nlmsgPid = instance->pid;
765 /* An "artificial" command so we can use NL family function table*/
766 ovsMsg->genlMsg.cmd = (code == OVS_IOCTL_READ_EVENT) ?
767 OVS_CTRL_CMD_EVENT_NOTIFY :
768 OVS_CTRL_CMD_READ_NOTIFY;
769 devOp = OVS_READ_DEV_OP;
773 /* Output buffer is mandatory. */
774 if (outputBufferLen != 0) {
775 status = MapIrpOutputBuffer(irp, outputBufferLen,
776 sizeof *ovsMsg, &outputBuffer);
777 if (status != STATUS_SUCCESS) {
780 ASSERT(outputBuffer);
782 status = STATUS_NDIS_INVALID_LENGTH;
787 * Operate in the mode that read ioctl is similar to ReadFile(). This
788 * might change as the userspace code gets implemented.
794 * For implementing read (ioctl or otherwise), we need to store some
795 * state in the instance to indicate the command that started the dump
796 * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
797 * that 'ovsMsgReadOp' is needed only in this function to call into the
798 * appropriate handler. The handler itself can access the state in the
801 * In the absence of a dump start, return 0 bytes.
803 if (instance->dumpState.ovsMsg == NULL) {
805 status = STATUS_SUCCESS;
808 RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
809 sizeof (ovsMsgReadOp));
811 /* Create an NL message for consumption. */
812 ovsMsg = &ovsMsgReadOp;
813 devOp = OVS_READ_DEV_OP;
817 case OVS_IOCTL_WRITE:
818 /* Input buffer is mandatory. */
819 if (inputBufferLen < sizeof (*ovsMsg)) {
820 status = STATUS_NDIS_INVALID_LENGTH;
824 ovsMsg = inputBuffer;
825 devOp = OVS_WRITE_DEV_OP;
829 status = STATUS_INVALID_DEVICE_REQUEST;
834 switch (ovsMsg->nlMsg.nlmsgType) {
835 case OVS_WIN_NL_CTRL_FAMILY_ID:
836 nlFamilyOps = &nlControlFamilyOps;
838 case OVS_WIN_NL_DATAPATH_FAMILY_ID:
839 nlFamilyOps = &nlDatapathFamilyOps;
841 case OVS_WIN_NL_FLOW_FAMILY_ID:
842 nlFamilyOps = &nlFLowFamilyOps;
844 case OVS_WIN_NL_PACKET_FAMILY_ID:
845 nlFamilyOps = &nlPacketFamilyOps;
847 case OVS_WIN_NL_VPORT_FAMILY_ID:
848 nlFamilyOps = &nlVportFamilyOps;
850 case OVS_WIN_NL_NETDEV_FAMILY_ID:
851 nlFamilyOps = &nlNetdevFamilyOps;
854 status = STATUS_INVALID_PARAMETER;
859 * For read operation, the netlink command has already been validated
862 if (devOp != OVS_READ_DEV_OP) {
863 status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
864 if (status != STATUS_SUCCESS) {
869 InitUserParamsCtx(irp, instance, devOp, ovsMsg,
870 inputBuffer, inputBufferLen,
871 outputBuffer, outputBufferLen,
874 status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
880 /* Should not complete a pending IRP unless proceesing is completed */
881 if (status == STATUS_PENDING) {
884 return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
889 * --------------------------------------------------------------------------
890 * Function to validate a netlink command. Only certain combinations of
891 * (device operation, netlink family, command) are valid.
892 * --------------------------------------------------------------------------
895 ValidateNetlinkCmd(UINT32 devOp,
896 POVS_OPEN_INSTANCE instance,
898 NETLINK_FAMILY *nlFamilyOps)
900 NTSTATUS status = STATUS_INVALID_PARAMETER;
903 for (i = 0; i < nlFamilyOps->opsCount; i++) {
904 if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
905 /* Validate if the command is valid for the device operation. */
906 if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
907 status = STATUS_INVALID_PARAMETER;
911 /* Validate the version. */
912 if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
913 status = STATUS_INVALID_PARAMETER;
917 /* Validate the DP for commands that require a DP. */
918 if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
919 OvsAcquireCtrlLock();
920 if (ovsMsg->ovsHdr.dp_ifindex !=
921 (INT)gOvsSwitchContext->dpNo) {
922 status = STATUS_INVALID_PARAMETER;
923 OvsReleaseCtrlLock();
926 OvsReleaseCtrlLock();
929 /* Validate the PID. */
930 if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
931 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
932 status = STATUS_INVALID_PARAMETER;
937 status = STATUS_SUCCESS;
947 * --------------------------------------------------------------------------
948 * Function to invoke the netlink command handler.
949 * --------------------------------------------------------------------------
952 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
953 NETLINK_FAMILY *nlFamilyOps,
956 NTSTATUS status = STATUS_INVALID_PARAMETER;
959 for (i = 0; i < nlFamilyOps->opsCount; i++) {
960 if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
961 NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
964 status = handler(usrParamsCtx, replyLen);
974 * --------------------------------------------------------------------------
975 * Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
977 * Each handle on the device is assigned a unique PID when the handle is
978 * created. On platforms that support netlink natively, the PID is available
979 * to userspace when the netlink socket is created. However, without native
980 * netlink support on Windows, OVS datapath generates the PID and lets the
981 * userspace query it.
983 * This function implements the query.
984 * --------------------------------------------------------------------------
987 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
990 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
991 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
993 if (usrParamsCtx->outputLength >= sizeof *msgOut) {
994 POVS_OPEN_INSTANCE instance =
995 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
997 RtlZeroMemory(msgOut, sizeof *msgOut);
998 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
999 msgOut->nlMsg.nlmsgPid = instance->pid;
1000 *replyLen = sizeof *msgOut;
1001 /* XXX: We might need to return the DP index as well. */
1003 return STATUS_NDIS_INVALID_LENGTH;
1006 return STATUS_SUCCESS;
1010 * --------------------------------------------------------------------------
1011 * Utility function to fill up information about the datapath in a reply to
1013 * Assumes that 'gOvsCtrlLock' lock is acquired.
1014 * --------------------------------------------------------------------------
1017 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
1022 OVS_MESSAGE msgOutTmp;
1023 OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
1026 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && NlBufRemLen(nlBuf) >= sizeof *msgIn);
1028 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
1029 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
1030 msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1031 msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1033 msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
1034 msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
1035 msgOutTmp.genlMsg.reserved = 0;
1037 msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
1039 writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
1041 writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
1042 OVS_SYSTEM_DP_NAME);
1045 OVS_DP_STATS dpStats;
1047 dpStats.n_hit = datapath->hits;
1048 dpStats.n_missed = datapath->misses;
1049 dpStats.n_lost = datapath->lost;
1050 dpStats.n_flows = datapath->nFlows;
1051 writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
1052 (PCHAR)&dpStats, sizeof dpStats);
1054 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1055 nlMsg->nlmsgLen = NlBufSize(nlBuf);
1057 return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
1061 * --------------------------------------------------------------------------
1062 * Handler for queueing an IRP used for event notification. The IRP is
1063 * completed when a port state changes. STATUS_PENDING is returned on
1064 * success. User mode keep a pending IRP at all times.
1065 * --------------------------------------------------------------------------
1068 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1073 UNREFERENCED_PARAMETER(replyLen);
1075 POVS_OPEN_INSTANCE instance =
1076 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1077 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1078 OVS_EVENT_POLL poll;
1080 poll.dpNo = msgIn->ovsHdr.dp_ifindex;
1081 status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
1082 &poll, sizeof poll);
1087 * --------------------------------------------------------------------------
1088 * Handler for the subscription for the event queue
1089 * --------------------------------------------------------------------------
1092 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1096 OVS_EVENT_SUBSCRIBE request;
1100 const NL_POLICY policy[] = {
1101 [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
1102 [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
1105 UNREFERENCED_PARAMETER(replyLen);
1107 POVS_OPEN_INSTANCE instance =
1108 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1109 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1111 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1112 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, ARRAY_SIZE(attrs));
1114 status = STATUS_INVALID_PARAMETER;
1118 /* XXX Ignore the MC group for now */
1119 join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1120 request.dpNo = msgIn->ovsHdr.dp_ifindex;
1121 request.subscribe = join;
1122 request.mask = OVS_EVENT_MASK_ALL;
1124 status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1131 * --------------------------------------------------------------------------
1132 * Command Handler for 'OVS_DP_CMD_NEW'.
1133 * --------------------------------------------------------------------------
1136 OvsNewDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1139 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1143 * --------------------------------------------------------------------------
1144 * Command Handler for 'OVS_DP_CMD_GET'.
1146 * The function handles both the dump based as well as the transaction based
1147 * 'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1148 * call to setup dump state, as well as subsequent calls to continue dumping
1150 * --------------------------------------------------------------------------
1153 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1156 if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1157 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1159 return HandleGetDpDump(usrParamsCtx, replyLen);
1164 * --------------------------------------------------------------------------
1165 * Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1166 * --------------------------------------------------------------------------
1169 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1172 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1177 * --------------------------------------------------------------------------
1178 * Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1179 * --------------------------------------------------------------------------
1182 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1185 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1186 POVS_OPEN_INSTANCE instance =
1187 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1189 if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1191 OvsSetupDumpStart(usrParamsCtx);
1195 POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1197 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1199 if (instance->dumpState.ovsMsg == NULL) {
1201 return STATUS_INVALID_DEVICE_STATE;
1204 /* Dump state must have been deleted after previous dump operation. */
1205 ASSERT(instance->dumpState.index[0] == 0);
1207 /* Output buffer has been validated while validating read dev op. */
1208 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1210 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1211 usrParamsCtx->outputLength);
1213 OvsAcquireCtrlLock();
1214 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1215 OvsReleaseCtrlLock();
1217 if (status != STATUS_SUCCESS) {
1219 FreeUserDumpState(instance);
1223 /* Increment the dump index. */
1224 instance->dumpState.index[0] = 1;
1225 *replyLen = msgOut->nlMsg.nlmsgLen;
1227 /* Free up the dump state, since there's no more data to continue. */
1228 FreeUserDumpState(instance);
1231 return STATUS_SUCCESS;
1236 * --------------------------------------------------------------------------
1237 * Command Handler for 'OVS_DP_CMD_SET'.
1238 * --------------------------------------------------------------------------
1241 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1244 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1248 * --------------------------------------------------------------------------
1249 * Function for handling transaction based 'OVS_DP_CMD_NEW', 'OVS_DP_CMD_GET'
1250 * and 'OVS_DP_CMD_SET' commands.
1252 * 'OVS_DP_CMD_NEW' is implemented to keep userspace code happy. Creation of a
1253 * new datapath is not supported currently.
1254 * --------------------------------------------------------------------------
1257 HandleDpTransactionCommon(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1260 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1261 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1262 NTSTATUS status = STATUS_SUCCESS;
1264 NL_ERROR nlError = NL_ERROR_SUCCESS;
1265 static const NL_POLICY ovsDatapathSetPolicy[] = {
1266 [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1267 [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1268 [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1270 PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1272 UNREFERENCED_PARAMETER(msgOut);
1274 /* input buffer has been validated while validating write dev op. */
1275 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1277 /* Parse any attributes in the request. */
1278 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET ||
1279 usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1280 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1281 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1282 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1283 ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1284 return STATUS_INVALID_PARAMETER;
1288 * XXX: Not clear at this stage if there's any role for the
1289 * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1294 RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1297 /* Output buffer has been validated while validating transact dev op. */
1298 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1300 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1302 OvsAcquireCtrlLock();
1303 if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1304 if (!OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1305 OVS_SYSTEM_DP_NAME)) {
1306 OvsReleaseCtrlLock();
1308 /* Creation of new datapaths is not supported. */
1309 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1310 nlError = NL_ERROR_NOTSUPP;
1314 nlError = NL_ERROR_NODEV;
1317 } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1318 OvsReleaseCtrlLock();
1319 nlError = NL_ERROR_NODEV;
1323 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1324 OvsReleaseCtrlLock();
1325 nlError = NL_ERROR_EXIST;
1329 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1330 OvsReleaseCtrlLock();
1332 *replyLen = NlBufSize(&nlBuf);
1335 if (nlError != NL_ERROR_SUCCESS) {
1336 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1337 usrParamsCtx->outputBuffer;
1339 NlBuildErrorMsg(msgIn, msgError, nlError);
1340 *replyLen = msgError->nlMsg.nlmsgLen;
1343 return STATUS_SUCCESS;
1348 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1350 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1351 POVS_OPEN_INSTANCE instance =
1352 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1354 /* input buffer has been validated while validating write dev op. */
1355 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1357 /* A write operation that does not indicate dump start is invalid. */
1358 if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1359 return STATUS_INVALID_PARAMETER;
1361 /* XXX: Handle other NLM_F_* flags in the future. */
1364 * This operation should be setting up the dump state. If there's any
1365 * previous state, clear it up so as to set it up afresh.
1367 FreeUserDumpState(instance);
1369 return InitUserDumpState(instance, msgIn);
1374 * --------------------------------------------------------------------------
1375 * Utility function to map the output buffer in an IRP. The buffer is assumed
1376 * to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
1377 * --------------------------------------------------------------------------
1380 MapIrpOutputBuffer(PIRP irp,
1381 UINT32 bufferLength,
1382 UINT32 requiredLength,
1387 ASSERT(bufferLength);
1388 ASSERT(requiredLength);
1389 if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
1390 return STATUS_INVALID_PARAMETER;
1393 if (bufferLength < requiredLength) {
1394 return STATUS_NDIS_INVALID_LENGTH;
1396 if (irp->MdlAddress == NULL) {
1397 return STATUS_INVALID_PARAMETER;
1399 *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
1400 NormalPagePriority);
1401 if (*buffer == NULL) {
1402 return STATUS_INSUFFICIENT_RESOURCES;
1405 return STATUS_SUCCESS;
1409 * --------------------------------------------------------------------------
1410 * Utility function to fill up information about the state of a port in a reply
1412 * Assumes that 'gOvsCtrlLock' lock is acquired.
1413 * --------------------------------------------------------------------------
1416 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1417 POVS_EVENT_ENTRY eventEntry,
1422 OVS_MESSAGE msgOutTmp;
1424 POVS_VPORT_ENTRY vport;
1426 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
1428 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
1429 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
1431 /* driver intiated messages should have zerp seq number*/
1432 msgOutTmp.nlMsg.nlmsgSeq = 0;
1433 msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
1435 msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
1436 msgOutTmp.genlMsg.reserved = 0;
1438 /* we don't have netdev yet, treat link up/down a adding/removing a port*/
1439 if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
1440 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
1441 } else if (eventEntry->status &
1442 (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
1443 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
1446 return STATUS_UNSUCCESSFUL;
1448 msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
1450 ok = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
1452 status = STATUS_INVALID_BUFFER_SIZE;
1456 vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
1458 status = STATUS_DEVICE_DOES_NOT_EXIST;
1462 ok = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) &&
1463 NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) &&
1464 NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_UPCALL_PID,
1465 vport->upcallPid) &&
1466 NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
1468 status = STATUS_INVALID_BUFFER_SIZE;
1472 /* XXXX Should we add the port stats attributes?*/
1473 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1474 nlMsg->nlmsgLen = NlBufSize(nlBuf);
1475 status = STATUS_SUCCESS;
1483 * --------------------------------------------------------------------------
1484 * Handler for reading events from the driver event queue. This handler is
1485 * executed when user modes issues a socket receive on a socket assocaited
1486 * with the MC group for events.
1487 * XXX user mode should read multiple events in one system call
1488 * --------------------------------------------------------------------------
1491 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1495 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1496 POVS_OPEN_INSTANCE instance =
1497 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1501 OVS_EVENT_ENTRY eventEntry;
1503 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1505 /* Should never read events with a dump socket */
1506 ASSERT(instance->dumpState.ovsMsg == NULL);
1508 /* Must have an event queue */
1509 ASSERT(instance->eventQueue != NULL);
1511 /* Output buffer has been validated while validating read dev op. */
1512 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1514 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1516 OvsAcquireCtrlLock();
1518 /* remove an event entry from the event queue */
1519 status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
1520 if (status != STATUS_SUCCESS) {
1521 /* If there were not elements, read should return no data. */
1522 status = STATUS_SUCCESS;
1527 status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
1528 if (status == NDIS_STATUS_SUCCESS) {
1529 *replyLen = NlBufSize(&nlBuf);
1533 OvsReleaseCtrlLock();
1538 * --------------------------------------------------------------------------
1539 * Handler for reading missed pacckets from the driver event queue. This
1540 * handler is executed when user modes issues a socket receive on a socket
1541 * --------------------------------------------------------------------------
1544 OvsReadPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1548 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1550 POVS_OPEN_INSTANCE instance =
1551 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1554 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1556 /* Should never read events with a dump socket */
1557 ASSERT(instance->dumpState.ovsMsg == NULL);
1559 /* Must have an packet queue */
1560 ASSERT(instance->packetQueue != NULL);
1562 /* Output buffer has been validated while validating read dev op. */
1563 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1565 /* Read a packet from the instance queue */
1566 status = OvsReadDpIoctl(instance->fileObject, usrParamsCtx->outputBuffer,
1567 usrParamsCtx->outputLength, replyLen);
1572 * --------------------------------------------------------------------------
1573 * Handler for the subscription for a packet queue
1574 * --------------------------------------------------------------------------
1577 OvsSubscribePacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1584 const NL_POLICY policy[] = {
1585 [OVS_NL_ATTR_PACKET_PID] = {.type = NL_A_U32 },
1586 [OVS_NL_ATTR_PACKET_SUBSCRIBE] = {.type = NL_A_U8 }
1588 PNL_ATTR attrs[ARRAY_SIZE(policy)];
1590 UNREFERENCED_PARAMETER(replyLen);
1592 POVS_OPEN_INSTANCE instance =
1593 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1594 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1596 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1597 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, ARRAY_SIZE(attrs));
1599 status = STATUS_INVALID_PARAMETER;
1603 join = NlAttrGetU8(attrs[OVS_NL_ATTR_PACKET_PID]);
1604 pid = NlAttrGetU32(attrs[OVS_NL_ATTR_PACKET_PID]);
1606 /* The socket subscribed with must be the same socket we perform receive*/
1607 ASSERT(pid == instance->pid);
1609 status = OvsSubscribeDpIoctl(instance, pid, join);
1612 * XXX Need to add this instance to a global data structure
1613 * which hold all packet based instances. The data structure (hash)
1614 * should be searched through the pid field of the instance for
1615 * placing the missed packet into the correct queue
1622 * --------------------------------------------------------------------------
1623 * Handler for queueing an IRP used for missed packet notification. The IRP is
1624 * completed when a packet received and mismatched. STATUS_PENDING is returned
1625 * on success. User mode keep a pending IRP at all times.
1626 * --------------------------------------------------------------------------
1629 OvsPendPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1632 UNREFERENCED_PARAMETER(replyLen);
1634 POVS_OPEN_INSTANCE instance =
1635 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1638 * XXX access to packet queue must be through acquiring a lock as user mode
1639 * could unsubscribe and the instnace will be freed.
1641 return OvsWaitDpIoctl(usrParamsCtx->irp, instance->fileObject);