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 {
86 NETLINK_CMD *cmds; /* Array of netlink commands and handlers. */
88 } NETLINK_FAMILY, *PNETLINK_FAMILY;
90 /* Handlers for the various netlink commands. */
91 static NetlinkCmdHandler 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);
113 static NTSTATUS OvsGetPidHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
117 * The various netlink families, along with the supported commands. Most of
118 * these families and commands are part of the openvswitch specification for a
119 * netlink datapath. In addition, each platform can implement a few families
120 * and commands as extensions.
123 /* Netlink control family: this is a Windows specific family. */
124 NETLINK_CMD nlControlFamilyCmdOps[] = {
125 { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ,
126 .handler = OvsPendEventCmdHandler,
127 .supportedDevOp = OVS_WRITE_DEV_OP,
128 .validateDpIndex = TRUE,
130 { .cmd = OVS_CTRL_CMD_WIN_PEND_PACKET_REQ,
131 .handler = OvsPendPacketCmdHandler,
132 .supportedDevOp = OVS_WRITE_DEV_OP,
133 .validateDpIndex = TRUE,
135 { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ,
136 .handler = OvsSubscribeEventCmdHandler,
137 .supportedDevOp = OVS_WRITE_DEV_OP,
138 .validateDpIndex = TRUE,
140 { .cmd = OVS_CTRL_CMD_PACKET_SUBSCRIBE_REQ,
141 .handler = OvsSubscribePacketCmdHandler,
142 .supportedDevOp = OVS_WRITE_DEV_OP,
143 .validateDpIndex = TRUE,
145 { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY,
146 .handler = OvsReadEventCmdHandler,
147 .supportedDevOp = OVS_READ_DEV_OP,
148 .validateDpIndex = FALSE,
150 { .cmd = OVS_CTRL_CMD_READ_NOTIFY,
151 .handler = OvsReadPacketCmdHandler,
152 .supportedDevOp = OVS_READ_DEV_OP,
153 .validateDpIndex = FALSE,
157 NETLINK_FAMILY nlControlFamilyOps = {
158 .name = OVS_WIN_CONTROL_FAMILY,
159 .id = OVS_WIN_NL_CTRL_FAMILY_ID,
160 .version = OVS_WIN_CONTROL_VERSION,
161 .maxAttr = OVS_WIN_CONTROL_ATTR_MAX,
162 .cmds = nlControlFamilyCmdOps,
163 .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
166 /* Netlink datapath family. */
167 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
168 { .cmd = OVS_DP_CMD_NEW,
169 .handler = OvsNewDpCmdHandler,
170 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
171 .validateDpIndex = FALSE
173 { .cmd = OVS_DP_CMD_GET,
174 .handler = OvsGetDpCmdHandler,
175 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
176 OVS_TRANSACTION_DEV_OP,
177 .validateDpIndex = FALSE
179 { .cmd = OVS_DP_CMD_SET,
180 .handler = OvsSetDpCmdHandler,
181 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
182 OVS_TRANSACTION_DEV_OP,
183 .validateDpIndex = TRUE
187 NETLINK_FAMILY nlDatapathFamilyOps = {
188 .name = OVS_DATAPATH_FAMILY,
189 .id = OVS_WIN_NL_DATAPATH_FAMILY_ID,
190 .version = OVS_DATAPATH_VERSION,
191 .maxAttr = OVS_DP_ATTR_MAX,
192 .cmds = nlDatapathFamilyCmdOps,
193 .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
196 /* Netlink packet family. */
198 NETLINK_CMD nlPacketFamilyCmdOps[] = {
199 { .cmd = OVS_PACKET_CMD_EXECUTE,
200 .handler = OvsNlExecuteCmdHandler,
201 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
202 .validateDpIndex = TRUE
206 NETLINK_FAMILY nlPacketFamilyOps = {
207 .name = OVS_PACKET_FAMILY,
208 .id = OVS_WIN_NL_PACKET_FAMILY_ID,
209 .version = OVS_PACKET_VERSION,
210 .maxAttr = OVS_PACKET_ATTR_MAX,
211 .cmds = nlPacketFamilyCmdOps,
212 .opsCount = ARRAY_SIZE(nlPacketFamilyCmdOps)
215 /* Netlink vport family. */
216 NETLINK_CMD nlVportFamilyCmdOps[] = {
217 { .cmd = OVS_VPORT_CMD_GET,
218 .handler = OvsGetVportCmdHandler,
219 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
220 OVS_TRANSACTION_DEV_OP,
221 .validateDpIndex = TRUE
223 { .cmd = OVS_VPORT_CMD_NEW,
224 .handler = OvsNewVportCmdHandler,
225 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
226 .validateDpIndex = TRUE
228 { .cmd = OVS_VPORT_CMD_SET,
229 .handler = OvsSetVportCmdHandler,
230 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
231 .validateDpIndex = TRUE
233 { .cmd = OVS_VPORT_CMD_DEL,
234 .handler = OvsDeleteVportCmdHandler,
235 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
236 .validateDpIndex = TRUE
240 NETLINK_FAMILY nlVportFamilyOps = {
241 .name = OVS_VPORT_FAMILY,
242 .id = OVS_WIN_NL_VPORT_FAMILY_ID,
243 .version = OVS_VPORT_VERSION,
244 .maxAttr = OVS_VPORT_ATTR_MAX,
245 .cmds = nlVportFamilyCmdOps,
246 .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps)
249 /* Netlink flow family. */
251 NETLINK_CMD nlFlowFamilyCmdOps[] = {
252 { .cmd = OVS_FLOW_CMD_NEW,
253 .handler = OvsFlowNlCmdHandler,
254 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
255 .validateDpIndex = TRUE
257 { .cmd = OVS_FLOW_CMD_SET,
258 .handler = OvsFlowNlCmdHandler,
259 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
260 .validateDpIndex = TRUE
262 { .cmd = OVS_FLOW_CMD_DEL,
263 .handler = OvsFlowNlCmdHandler,
264 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
265 .validateDpIndex = TRUE
267 { .cmd = OVS_FLOW_CMD_GET,
268 .handler = OvsFlowNlGetCmdHandler,
269 .supportedDevOp = OVS_TRANSACTION_DEV_OP |
270 OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
271 .validateDpIndex = TRUE
275 NETLINK_FAMILY nlFLowFamilyOps = {
276 .name = OVS_FLOW_FAMILY,
277 .id = OVS_WIN_NL_FLOW_FAMILY_ID,
278 .version = OVS_FLOW_VERSION,
279 .maxAttr = OVS_FLOW_ATTR_MAX,
280 .cmds = nlFlowFamilyCmdOps,
281 .opsCount = ARRAY_SIZE(nlFlowFamilyCmdOps)
284 /* Netlink netdev family. */
285 NETLINK_CMD nlNetdevFamilyCmdOps[] = {
286 { .cmd = OVS_WIN_NETDEV_CMD_GET,
287 .handler = OvsGetNetdevCmdHandler,
288 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
289 .validateDpIndex = FALSE
293 NETLINK_FAMILY nlNetdevFamilyOps = {
294 .name = OVS_WIN_NETDEV_FAMILY,
295 .id = OVS_WIN_NL_NETDEV_FAMILY_ID,
296 .version = OVS_WIN_NETDEV_VERSION,
297 .maxAttr = OVS_WIN_NETDEV_ATTR_MAX,
298 .cmds = nlNetdevFamilyCmdOps,
299 .opsCount = ARRAY_SIZE(nlNetdevFamilyCmdOps)
302 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
304 UINT32 requiredLength,
306 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
307 POVS_OPEN_INSTANCE instance,
309 NETLINK_FAMILY *nlFamilyOps);
310 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
311 NETLINK_FAMILY *nlFamilyOps,
314 /* Handles to the device object for communication with userspace. */
315 NDIS_HANDLE gOvsDeviceHandle;
316 PDEVICE_OBJECT gOvsDeviceObject;
318 _Dispatch_type_(IRP_MJ_CREATE)
319 _Dispatch_type_(IRP_MJ_CLOSE)
320 DRIVER_DISPATCH OvsOpenCloseDevice;
322 _Dispatch_type_(IRP_MJ_CLEANUP)
323 DRIVER_DISPATCH OvsCleanupDevice;
325 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
326 DRIVER_DISPATCH OvsDeviceControl;
329 #pragma alloc_text(INIT, OvsCreateDeviceObject)
330 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
331 #pragma alloc_text(PAGE, OvsCleanupDevice)
332 #pragma alloc_text(PAGE, OvsDeviceControl)
333 #endif // ALLOC_PRAGMA
336 * We might hit this limit easily since userspace opens a netlink descriptor for
337 * each thread, and at least one descriptor per vport. Revisit this later.
339 #define OVS_MAX_OPEN_INSTANCES 512
340 #define OVS_SYSTEM_DP_NAME "ovs-system"
342 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
343 UINT32 ovsNumberOfOpenInstances;
344 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
346 NDIS_SPIN_LOCK ovsCtrlLockObj;
347 PNDIS_SPIN_LOCK gOvsCtrlLock;
350 InitUserDumpState(POVS_OPEN_INSTANCE instance,
353 /* Clear the dumpState from a previous dump sequence. */
354 ASSERT(instance->dumpState.ovsMsg == NULL);
357 instance->dumpState.ovsMsg =
358 (POVS_MESSAGE)OvsAllocateMemoryWithTag(sizeof(OVS_MESSAGE),
359 OVS_DATAPATH_POOL_TAG);
360 if (instance->dumpState.ovsMsg == NULL) {
361 return STATUS_NO_MEMORY;
363 RtlCopyMemory(instance->dumpState.ovsMsg, ovsMsg,
364 sizeof *instance->dumpState.ovsMsg);
365 RtlZeroMemory(instance->dumpState.index,
366 sizeof instance->dumpState.index);
368 return STATUS_SUCCESS;
372 FreeUserDumpState(POVS_OPEN_INSTANCE instance)
374 if (instance->dumpState.ovsMsg != NULL) {
375 OvsFreeMemoryWithTag(instance->dumpState.ovsMsg,
376 OVS_DATAPATH_POOL_TAG);
377 RtlZeroMemory(&instance->dumpState, sizeof instance->dumpState);
384 gOvsCtrlLock = &ovsCtrlLockObj;
385 NdisAllocateSpinLock(gOvsCtrlLock);
392 OvsCleanupEventQueue();
394 NdisFreeSpinLock(gOvsCtrlLock);
402 NdisAcquireSpinLock(gOvsCtrlLock);
408 NdisReleaseSpinLock(gOvsCtrlLock);
413 * --------------------------------------------------------------------------
414 * Creates the communication device between user and kernel, and also
415 * initializes the data associated data structures.
416 * --------------------------------------------------------------------------
419 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
421 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
422 UNICODE_STRING deviceName;
423 UNICODE_STRING symbolicDeviceName;
424 PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
425 NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
426 OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
428 RtlZeroMemory(dispatchTable,
429 (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
430 dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
431 dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
432 dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
433 dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
435 NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
436 NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
438 RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
440 OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
441 NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
442 NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
443 sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
445 deviceAttributes.DeviceName = &deviceName;
446 deviceAttributes.SymbolicName = &symbolicDeviceName;
447 deviceAttributes.MajorFunctions = dispatchTable;
448 deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
450 status = NdisRegisterDeviceEx(ovsExtDriverHandle,
454 if (status != NDIS_STATUS_SUCCESS) {
455 POVS_DEVICE_EXTENSION ovsExt =
456 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
457 ASSERT(gOvsDeviceObject != NULL);
458 ASSERT(gOvsDeviceHandle != NULL);
461 ovsExt->numberOpenInstance = 0;
464 OvsRegisterSystemProvider((PVOID)gOvsDeviceObject);
467 OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
473 OvsDeleteDeviceObject()
475 if (gOvsDeviceHandle) {
477 POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
478 NdisGetDeviceReservedExtension(gOvsDeviceObject);
480 ASSERT(ovsExt->numberOpenInstance == 0);
484 ASSERT(gOvsDeviceObject);
485 NdisDeregisterDeviceEx(gOvsDeviceHandle);
486 gOvsDeviceHandle = NULL;
487 gOvsDeviceObject = NULL;
489 OvsUnregisterSystemProvider();
494 OvsGetOpenInstance(PFILE_OBJECT fileObject,
497 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
499 ASSERT(instance->fileObject == fileObject);
500 if (gOvsSwitchContext->dpNo != dpNo) {
508 OvsFindOpenInstance(PFILE_OBJECT fileObject)
511 for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
512 j < ovsNumberOfOpenInstances; i++) {
513 if (ovsOpenInstanceArray[i]) {
514 if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
515 return ovsOpenInstanceArray[i];
524 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
525 PFILE_OBJECT fileObject)
527 POVS_OPEN_INSTANCE instance =
528 (POVS_OPEN_INSTANCE)OvsAllocateMemoryWithTag(sizeof(OVS_OPEN_INSTANCE),
529 OVS_DATAPATH_POOL_TAG);
532 if (instance == NULL) {
533 return STATUS_NO_MEMORY;
535 OvsAcquireCtrlLock();
536 ASSERT(OvsFindOpenInstance(fileObject) == NULL);
538 if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
539 OvsReleaseCtrlLock();
540 OvsFreeMemoryWithTag(instance, OVS_DATAPATH_POOL_TAG);
541 return STATUS_INSUFFICIENT_RESOURCES;
543 RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
545 for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
546 if (ovsOpenInstanceArray[i] == NULL) {
547 ovsOpenInstanceArray[i] = instance;
548 ovsNumberOfOpenInstances++;
549 instance->cookie = i;
553 ASSERT(i < OVS_MAX_OPEN_INSTANCES);
554 instance->fileObject = fileObject;
555 ASSERT(fileObject->FsContext == NULL);
556 instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
557 if (instance->pid == 0) {
558 /* XXX: check for rollover. */
560 fileObject->FsContext = instance;
561 OvsReleaseCtrlLock();
562 return STATUS_SUCCESS;
566 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
568 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
570 ASSERT(fileObject == instance->fileObject);
571 OvsCleanupEvent(instance);
572 OvsCleanupPacketQueue(instance);
576 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
578 POVS_OPEN_INSTANCE instance;
579 ASSERT(fileObject->FsContext);
580 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
581 ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
583 OvsAcquireCtrlLock();
584 fileObject->FsContext = NULL;
585 ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
586 ovsOpenInstanceArray[instance->cookie] = NULL;
587 ovsNumberOfOpenInstances--;
588 OvsReleaseCtrlLock();
589 ASSERT(instance->eventQueue == NULL);
590 ASSERT (instance->packetQueue == NULL);
591 OvsFreeMemoryWithTag(instance, OVS_DATAPATH_POOL_TAG);
595 OvsCompleteIrpRequest(PIRP irp,
599 irp->IoStatus.Information = infoPtr;
600 irp->IoStatus.Status = status;
601 IoCompleteRequest(irp, IO_NO_INCREMENT);
607 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
610 PIO_STACK_LOCATION irpSp;
611 NTSTATUS status = STATUS_SUCCESS;
612 PFILE_OBJECT fileObject;
613 POVS_DEVICE_EXTENSION ovsExt =
614 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
616 ASSERT(deviceObject == gOvsDeviceObject);
617 ASSERT(ovsExt != NULL);
619 irpSp = IoGetCurrentIrpStackLocation(irp);
620 fileObject = irpSp->FileObject;
621 OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
622 deviceObject, fileObject,
623 ovsExt->numberOpenInstance);
625 switch (irpSp->MajorFunction) {
627 status = OvsAddOpenInstance(ovsExt, fileObject);
628 if (STATUS_SUCCESS == status) {
629 InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
633 ASSERT(ovsExt->numberOpenInstance > 0);
634 OvsRemoveOpenInstance(fileObject);
635 InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
640 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
643 _Use_decl_annotations_
645 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
649 PIO_STACK_LOCATION irpSp;
650 PFILE_OBJECT fileObject;
652 NTSTATUS status = STATUS_SUCCESS;
654 POVS_DEVICE_EXTENSION ovsExt =
655 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
657 ASSERT(ovsExt->numberOpenInstance > 0);
660 UNREFERENCED_PARAMETER(deviceObject);
662 ASSERT(deviceObject == gOvsDeviceObject);
663 irpSp = IoGetCurrentIrpStackLocation(irp);
664 fileObject = irpSp->FileObject;
666 ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
668 OvsCleanupOpenInstance(fileObject);
670 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
674 * --------------------------------------------------------------------------
675 * IOCTL function handler for the device.
676 * --------------------------------------------------------------------------
679 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
682 PIO_STACK_LOCATION irpSp;
683 NTSTATUS status = STATUS_SUCCESS;
684 PFILE_OBJECT fileObject;
685 PVOID inputBuffer = NULL;
686 PVOID outputBuffer = NULL;
687 UINT32 inputBufferLen, outputBufferLen;
688 UINT32 code, replyLen = 0;
689 POVS_OPEN_INSTANCE instance;
691 OVS_MESSAGE ovsMsgReadOp;
693 NETLINK_FAMILY *nlFamilyOps;
694 OVS_USER_PARAMS_CONTEXT usrParamsCtx;
697 POVS_DEVICE_EXTENSION ovsExt =
698 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
699 ASSERT(deviceObject == gOvsDeviceObject);
701 ASSERT(ovsExt->numberOpenInstance > 0);
703 UNREFERENCED_PARAMETER(deviceObject);
706 irpSp = IoGetCurrentIrpStackLocation(irp);
708 ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
709 ASSERT(irpSp->FileObject != NULL);
711 fileObject = irpSp->FileObject;
712 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
713 code = irpSp->Parameters.DeviceIoControl.IoControlCode;
714 inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
715 outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
716 inputBuffer = irp->AssociatedIrp.SystemBuffer;
718 /* Check if the extension is enabled. */
719 if (NULL == gOvsSwitchContext) {
720 status = STATUS_NOT_FOUND;
724 if (!OvsAcquireSwitchContext()) {
725 status = STATUS_NOT_FOUND;
730 * Validate the input/output buffer arguments depending on the type of the
734 case OVS_IOCTL_GET_PID:
735 /* Both input buffer and output buffer use the same location. */
736 outputBuffer = irp->AssociatedIrp.SystemBuffer;
737 if (outputBufferLen != 0) {
738 InitUserParamsCtx(irp, instance, 0, NULL,
739 inputBuffer, inputBufferLen,
740 outputBuffer, outputBufferLen,
743 ASSERT(outputBuffer);
745 status = STATUS_NDIS_INVALID_LENGTH;
749 status = OvsGetPidHandler(&usrParamsCtx, &replyLen);
752 case OVS_IOCTL_TRANSACT:
753 /* Both input buffer and output buffer are mandatory. */
754 if (outputBufferLen != 0) {
755 status = MapIrpOutputBuffer(irp, outputBufferLen,
756 sizeof *ovsMsg, &outputBuffer);
757 if (status != STATUS_SUCCESS) {
760 ASSERT(outputBuffer);
762 status = STATUS_NDIS_INVALID_LENGTH;
766 if (inputBufferLen < sizeof (*ovsMsg)) {
767 status = STATUS_NDIS_INVALID_LENGTH;
771 ovsMsg = inputBuffer;
772 devOp = OVS_TRANSACTION_DEV_OP;
775 case OVS_IOCTL_READ_EVENT:
776 case OVS_IOCTL_READ_PACKET:
778 * Output buffer is mandatory. These IOCTLs are used to read events and
779 * packets respectively. It is convenient to have separate ioctls.
781 if (outputBufferLen != 0) {
782 status = MapIrpOutputBuffer(irp, outputBufferLen,
783 sizeof *ovsMsg, &outputBuffer);
784 if (status != STATUS_SUCCESS) {
787 ASSERT(outputBuffer);
789 status = STATUS_NDIS_INVALID_LENGTH;
795 ovsMsg = &ovsMsgReadOp;
796 RtlZeroMemory(ovsMsg, sizeof *ovsMsg);
797 ovsMsg->nlMsg.nlmsgLen = sizeof *ovsMsg;
798 ovsMsg->nlMsg.nlmsgType = nlControlFamilyOps.id;
799 ovsMsg->nlMsg.nlmsgPid = instance->pid;
801 /* An "artificial" command so we can use NL family function table*/
802 ovsMsg->genlMsg.cmd = (code == OVS_IOCTL_READ_EVENT) ?
803 OVS_CTRL_CMD_EVENT_NOTIFY :
804 OVS_CTRL_CMD_READ_NOTIFY;
805 ovsMsg->genlMsg.version = nlControlFamilyOps.version;
807 devOp = OVS_READ_DEV_OP;
811 /* Output buffer is mandatory. */
812 if (outputBufferLen != 0) {
813 status = MapIrpOutputBuffer(irp, outputBufferLen,
814 sizeof *ovsMsg, &outputBuffer);
815 if (status != STATUS_SUCCESS) {
818 ASSERT(outputBuffer);
820 status = STATUS_NDIS_INVALID_LENGTH;
825 * Operate in the mode that read ioctl is similar to ReadFile(). This
826 * might change as the userspace code gets implemented.
832 * For implementing read (ioctl or otherwise), we need to store some
833 * state in the instance to indicate the command that started the dump
834 * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
835 * that 'ovsMsgReadOp' is needed only in this function to call into the
836 * appropriate handler. The handler itself can access the state in the
839 * In the absence of a dump start, return 0 bytes.
841 if (instance->dumpState.ovsMsg == NULL) {
843 status = STATUS_SUCCESS;
846 RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
847 sizeof (ovsMsgReadOp));
849 /* Create an NL message for consumption. */
850 ovsMsg = &ovsMsgReadOp;
851 devOp = OVS_READ_DEV_OP;
855 case OVS_IOCTL_WRITE:
856 /* Input buffer is mandatory. */
857 if (inputBufferLen < sizeof (*ovsMsg)) {
858 status = STATUS_NDIS_INVALID_LENGTH;
862 ovsMsg = inputBuffer;
863 devOp = OVS_WRITE_DEV_OP;
867 status = STATUS_INVALID_DEVICE_REQUEST;
872 switch (ovsMsg->nlMsg.nlmsgType) {
873 case OVS_WIN_NL_CTRL_FAMILY_ID:
874 nlFamilyOps = &nlControlFamilyOps;
876 case OVS_WIN_NL_DATAPATH_FAMILY_ID:
877 nlFamilyOps = &nlDatapathFamilyOps;
879 case OVS_WIN_NL_FLOW_FAMILY_ID:
880 nlFamilyOps = &nlFLowFamilyOps;
882 case OVS_WIN_NL_PACKET_FAMILY_ID:
883 nlFamilyOps = &nlPacketFamilyOps;
885 case OVS_WIN_NL_VPORT_FAMILY_ID:
886 nlFamilyOps = &nlVportFamilyOps;
888 case OVS_WIN_NL_NETDEV_FAMILY_ID:
889 nlFamilyOps = &nlNetdevFamilyOps;
892 status = STATUS_INVALID_PARAMETER;
897 * For read operation, avoid duplicate validation since 'ovsMsg' is either
898 * "artificial" or was copied from a previously validated 'ovsMsg'.
900 if (devOp != OVS_READ_DEV_OP) {
901 status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
902 if (status != STATUS_SUCCESS) {
907 InitUserParamsCtx(irp, instance, devOp, ovsMsg,
908 inputBuffer, inputBufferLen,
909 outputBuffer, outputBufferLen,
912 status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
915 OvsReleaseSwitchContext(gOvsSwitchContext);
918 /* Should not complete a pending IRP unless proceesing is completed. */
919 if (status == STATUS_PENDING) {
920 /* STATUS_PENDING is returned by the NL handler when the request is
921 * to be processed later, so we mark the IRP as pending and complete
922 * it in another thread when the request is processed. */
923 IoMarkIrpPending(irp);
926 return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
931 * --------------------------------------------------------------------------
932 * Function to validate a netlink command. Only certain combinations of
933 * (device operation, netlink family, command) are valid.
934 * --------------------------------------------------------------------------
937 ValidateNetlinkCmd(UINT32 devOp,
938 POVS_OPEN_INSTANCE instance,
940 NETLINK_FAMILY *nlFamilyOps)
942 NTSTATUS status = STATUS_INVALID_PARAMETER;
945 for (i = 0; i < nlFamilyOps->opsCount; i++) {
946 if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
947 /* Validate if the command is valid for the device operation. */
948 if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
949 status = STATUS_INVALID_PARAMETER;
953 /* Validate the version. */
954 if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
955 status = STATUS_INVALID_PARAMETER;
959 /* Validate the DP for commands that require a DP. */
960 if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
961 if (ovsMsg->ovsHdr.dp_ifindex !=
962 (INT)gOvsSwitchContext->dpNo) {
963 status = STATUS_INVALID_PARAMETER;
968 /* Validate the PID. */
969 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
970 status = STATUS_INVALID_PARAMETER;
974 status = STATUS_SUCCESS;
984 * --------------------------------------------------------------------------
985 * Function to invoke the netlink command handler. The function also stores
986 * the return value of the handler function to construct a 'NL_ERROR' message,
987 * and in turn returns success to the caller.
988 * --------------------------------------------------------------------------
991 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
992 NETLINK_FAMILY *nlFamilyOps,
995 NTSTATUS status = STATUS_INVALID_PARAMETER;
998 for (i = 0; i < nlFamilyOps->opsCount; i++) {
999 if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
1000 NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
1003 status = handler(usrParamsCtx, replyLen);
1010 * Netlink socket semantics dictate that the return value of the netlink
1011 * function should be an error ONLY under fatal conditions. If the message
1012 * made it all the way to the handler function, it is not a fatal condition.
1013 * Absorb the error returned by the handler function into a 'struct
1014 * NL_ERROR' and populate the 'output buffer' to return to userspace.
1016 * This behavior is obviously applicable only to netlink commands that
1017 * specify an 'output buffer'. For other commands, we return the error as
1020 * 'STATUS_PENDING' is a special return value and userspace is equipped to
1023 if (status != STATUS_SUCCESS && status != STATUS_PENDING) {
1024 if (usrParamsCtx->devOp != OVS_WRITE_DEV_OP && *replyLen == 0) {
1025 NL_ERROR nlError = NlMapStatusToNlErr(status);
1026 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1027 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1028 usrParamsCtx->outputBuffer;
1031 NlBuildErrorMsg(msgIn, msgError, nlError);
1032 *replyLen = msgError->nlMsg.nlmsgLen;
1035 if (*replyLen != 0) {
1036 status = STATUS_SUCCESS;
1041 if (usrParamsCtx->devOp != OVS_WRITE_DEV_OP) {
1042 ASSERT(status == STATUS_PENDING || *replyLen != 0 || status == STATUS_SUCCESS);
1050 * --------------------------------------------------------------------------
1051 * Handler for 'OVS_IOCTL_GET_PID'.
1053 * Each handle on the device is assigned a unique PID when the handle is
1054 * created. This function passes the PID to userspace using METHOD_BUFFERED
1056 * --------------------------------------------------------------------------
1059 OvsGetPidHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1062 NTSTATUS status = STATUS_SUCCESS;
1063 PUINT32 msgOut = (PUINT32)usrParamsCtx->outputBuffer;
1065 if (usrParamsCtx->outputLength >= sizeof *msgOut) {
1066 POVS_OPEN_INSTANCE instance =
1067 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1069 RtlZeroMemory(msgOut, sizeof *msgOut);
1070 RtlCopyMemory(msgOut, &instance->pid, sizeof(*msgOut));
1071 *replyLen = sizeof *msgOut;
1073 *replyLen = sizeof *msgOut;
1074 status = STATUS_NDIS_INVALID_LENGTH;
1081 * --------------------------------------------------------------------------
1082 * Utility function to fill up information about the datapath in a reply to
1084 * --------------------------------------------------------------------------
1087 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
1092 OVS_MESSAGE msgOutTmp;
1093 OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
1096 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && NlBufRemLen(nlBuf) >= sizeof *msgIn);
1098 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
1099 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
1100 msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1101 msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1103 msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
1104 msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
1105 msgOutTmp.genlMsg.reserved = 0;
1107 msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
1109 writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
1111 writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
1112 OVS_SYSTEM_DP_NAME);
1115 OVS_DP_STATS dpStats;
1117 dpStats.n_hit = datapath->hits;
1118 dpStats.n_missed = datapath->misses;
1119 dpStats.n_lost = datapath->lost;
1120 dpStats.n_flows = datapath->nFlows;
1121 writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
1122 (PCHAR)&dpStats, sizeof dpStats);
1124 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1125 nlMsg->nlmsgLen = NlBufSize(nlBuf);
1127 return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
1131 * --------------------------------------------------------------------------
1132 * Handler for queueing an IRP used for event notification. The IRP is
1133 * completed when a port state changes. STATUS_PENDING is returned on
1134 * success. User mode keep a pending IRP at all times.
1135 * --------------------------------------------------------------------------
1138 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1143 UNREFERENCED_PARAMETER(replyLen);
1145 POVS_OPEN_INSTANCE instance =
1146 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1147 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1148 OVS_EVENT_POLL poll;
1150 poll.dpNo = msgIn->ovsHdr.dp_ifindex;
1151 status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
1152 &poll, sizeof poll);
1157 * --------------------------------------------------------------------------
1158 * Handler for the subscription for the event queue
1159 * --------------------------------------------------------------------------
1162 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1166 OVS_EVENT_SUBSCRIBE request;
1170 const NL_POLICY policy[] = {
1171 [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
1172 [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
1175 UNREFERENCED_PARAMETER(replyLen);
1177 POVS_OPEN_INSTANCE instance =
1178 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1179 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1181 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1182 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, ARRAY_SIZE(attrs));
1184 status = STATUS_INVALID_PARAMETER;
1188 /* XXX Ignore the MC group for now */
1189 join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1190 request.dpNo = msgIn->ovsHdr.dp_ifindex;
1191 request.subscribe = join;
1192 request.mask = OVS_EVENT_MASK_ALL;
1194 status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1201 * --------------------------------------------------------------------------
1202 * Command Handler for 'OVS_DP_CMD_NEW'.
1203 * --------------------------------------------------------------------------
1206 OvsNewDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1209 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1213 * --------------------------------------------------------------------------
1214 * Command Handler for 'OVS_DP_CMD_GET'.
1216 * The function handles both the dump based as well as the transaction based
1217 * 'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1218 * call to setup dump state, as well as subsequent calls to continue dumping
1220 * --------------------------------------------------------------------------
1223 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1226 if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1227 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1229 return HandleGetDpDump(usrParamsCtx, replyLen);
1234 * --------------------------------------------------------------------------
1235 * Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1236 * --------------------------------------------------------------------------
1239 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1242 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1247 * --------------------------------------------------------------------------
1248 * Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1249 * --------------------------------------------------------------------------
1252 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1255 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1256 POVS_OPEN_INSTANCE instance =
1257 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1259 if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1261 OvsSetupDumpStart(usrParamsCtx);
1265 POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1267 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1269 if (instance->dumpState.ovsMsg == NULL) {
1271 return STATUS_INVALID_DEVICE_STATE;
1274 /* Dump state must have been deleted after previous dump operation. */
1275 ASSERT(instance->dumpState.index[0] == 0);
1277 /* Output buffer has been validated while validating read dev op. */
1278 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1280 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1281 usrParamsCtx->outputLength);
1283 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1285 if (status != STATUS_SUCCESS) {
1287 FreeUserDumpState(instance);
1291 /* Increment the dump index. */
1292 instance->dumpState.index[0] = 1;
1293 *replyLen = msgOut->nlMsg.nlmsgLen;
1295 /* Free up the dump state, since there's no more data to continue. */
1296 FreeUserDumpState(instance);
1299 return STATUS_SUCCESS;
1304 * --------------------------------------------------------------------------
1305 * Command Handler for 'OVS_DP_CMD_SET'.
1306 * --------------------------------------------------------------------------
1309 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1312 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1316 * --------------------------------------------------------------------------
1317 * Function for handling transaction based 'OVS_DP_CMD_NEW', 'OVS_DP_CMD_GET'
1318 * and 'OVS_DP_CMD_SET' commands.
1320 * 'OVS_DP_CMD_NEW' is implemented to keep userspace code happy. Creation of a
1321 * new datapath is not supported currently.
1322 * --------------------------------------------------------------------------
1325 HandleDpTransactionCommon(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1328 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1329 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1330 NTSTATUS status = STATUS_SUCCESS;
1332 NL_ERROR nlError = NL_ERROR_SUCCESS;
1333 static const NL_POLICY ovsDatapathSetPolicy[] = {
1334 [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1335 [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1336 [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1338 PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1340 UNREFERENCED_PARAMETER(msgOut);
1342 /* input buffer has been validated while validating write dev op. */
1343 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1345 /* Parse any attributes in the request. */
1346 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET ||
1347 usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1348 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1349 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1350 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1351 ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1352 return STATUS_INVALID_PARAMETER;
1356 * XXX: Not clear at this stage if there's any role for the
1357 * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1362 RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1365 /* Output buffer has been validated while validating transact dev op. */
1366 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1368 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1370 if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1371 if (!OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1372 OVS_SYSTEM_DP_NAME)) {
1374 /* Creation of new datapaths is not supported. */
1375 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1376 nlError = NL_ERROR_NOTSUPP;
1380 nlError = NL_ERROR_NODEV;
1383 } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1384 nlError = NL_ERROR_NODEV;
1388 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1389 nlError = NL_ERROR_EXIST;
1393 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1395 *replyLen = NlBufSize(&nlBuf);
1398 if (nlError != NL_ERROR_SUCCESS) {
1399 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1400 usrParamsCtx->outputBuffer;
1402 NlBuildErrorMsg(msgIn, msgError, nlError);
1403 *replyLen = msgError->nlMsg.nlmsgLen;
1406 return STATUS_SUCCESS;
1411 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1413 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1414 POVS_OPEN_INSTANCE instance =
1415 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1417 /* input buffer has been validated while validating write dev op. */
1418 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1420 /* A write operation that does not indicate dump start is invalid. */
1421 if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1422 return STATUS_INVALID_PARAMETER;
1424 /* XXX: Handle other NLM_F_* flags in the future. */
1427 * This operation should be setting up the dump state. If there's any
1428 * previous state, clear it up so as to set it up afresh.
1430 FreeUserDumpState(instance);
1432 return InitUserDumpState(instance, msgIn);
1437 * --------------------------------------------------------------------------
1438 * Utility function to map the output buffer in an IRP. The buffer is assumed
1439 * to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
1440 * --------------------------------------------------------------------------
1443 MapIrpOutputBuffer(PIRP irp,
1444 UINT32 bufferLength,
1445 UINT32 requiredLength,
1450 ASSERT(bufferLength);
1451 ASSERT(requiredLength);
1452 if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
1453 return STATUS_INVALID_PARAMETER;
1456 if (bufferLength < requiredLength) {
1457 return STATUS_NDIS_INVALID_LENGTH;
1459 if (irp->MdlAddress == NULL) {
1460 return STATUS_INVALID_PARAMETER;
1462 *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
1463 NormalPagePriority);
1464 if (*buffer == NULL) {
1465 return STATUS_INSUFFICIENT_RESOURCES;
1468 return STATUS_SUCCESS;
1472 * --------------------------------------------------------------------------
1473 * Utility function to fill up information about the state of a port in a reply
1475 * --------------------------------------------------------------------------
1478 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1479 POVS_EVENT_ENTRY eventEntry,
1484 OVS_MESSAGE msgOutTmp;
1486 POVS_VPORT_ENTRY vport;
1488 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
1490 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
1491 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
1493 /* driver intiated messages should have zerp seq number*/
1494 msgOutTmp.nlMsg.nlmsgSeq = 0;
1495 msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
1497 msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
1498 msgOutTmp.genlMsg.reserved = 0;
1500 /* we don't have netdev yet, treat link up/down a adding/removing a port*/
1501 if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
1502 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
1503 } else if (eventEntry->status &
1504 (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
1505 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
1508 return STATUS_UNSUCCESSFUL;
1510 msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
1512 ok = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
1514 status = STATUS_INVALID_BUFFER_SIZE;
1518 vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
1520 status = STATUS_DEVICE_DOES_NOT_EXIST;
1524 ok = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) &&
1525 NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) &&
1526 NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_UPCALL_PID,
1527 vport->upcallPid) &&
1528 NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
1530 status = STATUS_INVALID_BUFFER_SIZE;
1534 /* XXXX Should we add the port stats attributes?*/
1535 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1536 nlMsg->nlmsgLen = NlBufSize(nlBuf);
1537 status = STATUS_SUCCESS;
1545 * --------------------------------------------------------------------------
1546 * Handler for reading events from the driver event queue. This handler is
1547 * executed when user modes issues a socket receive on a socket assocaited
1548 * with the MC group for events.
1549 * XXX user mode should read multiple events in one system call
1550 * --------------------------------------------------------------------------
1553 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1557 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1558 POVS_OPEN_INSTANCE instance =
1559 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1563 OVS_EVENT_ENTRY eventEntry;
1565 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1567 /* Should never read events with a dump socket */
1568 ASSERT(instance->dumpState.ovsMsg == NULL);
1570 /* Must have an event queue */
1571 ASSERT(instance->eventQueue != NULL);
1573 /* Output buffer has been validated while validating read dev op. */
1574 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1576 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1578 /* remove an event entry from the event queue */
1579 status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
1580 if (status != STATUS_SUCCESS) {
1581 /* If there were not elements, read should return no data. */
1582 status = STATUS_SUCCESS;
1587 status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
1588 if (status == NDIS_STATUS_SUCCESS) {
1589 *replyLen = NlBufSize(&nlBuf);
1597 * --------------------------------------------------------------------------
1598 * Handler for reading missed pacckets from the driver event queue. This
1599 * handler is executed when user modes issues a socket receive on a socket
1600 * --------------------------------------------------------------------------
1603 OvsReadPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1607 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1609 POVS_OPEN_INSTANCE instance =
1610 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1613 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1615 /* Should never read events with a dump socket */
1616 ASSERT(instance->dumpState.ovsMsg == NULL);
1618 /* Must have an packet queue */
1619 ASSERT(instance->packetQueue != NULL);
1621 /* Output buffer has been validated while validating read dev op. */
1622 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1624 /* Read a packet from the instance queue */
1625 status = OvsReadDpIoctl(instance->fileObject, usrParamsCtx->outputBuffer,
1626 usrParamsCtx->outputLength, replyLen);
1631 * --------------------------------------------------------------------------
1632 * Handler for the subscription for a packet queue
1633 * --------------------------------------------------------------------------
1636 OvsSubscribePacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1643 const NL_POLICY policy[] = {
1644 [OVS_NL_ATTR_PACKET_PID] = {.type = NL_A_U32 },
1645 [OVS_NL_ATTR_PACKET_SUBSCRIBE] = {.type = NL_A_U8 }
1647 PNL_ATTR attrs[ARRAY_SIZE(policy)];
1649 UNREFERENCED_PARAMETER(replyLen);
1651 POVS_OPEN_INSTANCE instance =
1652 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1653 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1655 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1656 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, ARRAY_SIZE(attrs));
1658 status = STATUS_INVALID_PARAMETER;
1662 join = NlAttrGetU8(attrs[OVS_NL_ATTR_PACKET_PID]);
1663 pid = NlAttrGetU32(attrs[OVS_NL_ATTR_PACKET_PID]);
1665 /* The socket subscribed with must be the same socket we perform receive*/
1666 ASSERT(pid == instance->pid);
1668 status = OvsSubscribeDpIoctl(instance, pid, join);
1671 * XXX Need to add this instance to a global data structure
1672 * which hold all packet based instances. The data structure (hash)
1673 * should be searched through the pid field of the instance for
1674 * placing the missed packet into the correct queue
1681 * --------------------------------------------------------------------------
1682 * Handler for queueing an IRP used for missed packet notification. The IRP is
1683 * completed when a packet received and mismatched. STATUS_PENDING is returned
1684 * on success. User mode keep a pending IRP at all times.
1685 * --------------------------------------------------------------------------
1688 OvsPendPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1691 UNREFERENCED_PARAMETER(replyLen);
1693 POVS_OPEN_INSTANCE instance =
1694 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1697 * XXX access to packet queue must be through acquiring a lock as user mode
1698 * could unsubscribe and the instnace will be freed.
1700 return OvsWaitDpIoctl(usrParamsCtx->irp, instance->fileObject);