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
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 OvsSubscribeEventCmdHandler,
93 OvsReadEventCmdHandler,
97 OvsGetVportCmdHandler,
98 OvsNewVportCmdHandler,
99 OvsDeleteVportCmdHandler;
101 NetlinkCmdHandler OvsGetNetdevCmdHandler;
103 static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
105 static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
107 static NTSTATUS HandleDpTransactionCommon(
108 POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen);
111 * The various netlink families, along with the supported commands. Most of
112 * these families and commands are part of the openvswitch specification for a
113 * netlink datapath. In addition, each platform can implement a few families
114 * and commands as extensions.
117 /* Netlink control family: this is a Windows specific family. */
118 NETLINK_CMD nlControlFamilyCmdOps[] = {
119 { .cmd = OVS_CTRL_CMD_WIN_GET_PID,
120 .handler = OvsGetPidCmdHandler,
121 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
122 .validateDpIndex = FALSE,
124 { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ,
125 .handler = OvsPendEventCmdHandler,
126 .supportedDevOp = OVS_WRITE_DEV_OP,
127 .validateDpIndex = TRUE,
129 { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ,
130 .handler = OvsSubscribeEventCmdHandler,
131 .supportedDevOp = OVS_WRITE_DEV_OP,
132 .validateDpIndex = TRUE,
134 { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY,
135 .handler = OvsReadEventCmdHandler,
136 .supportedDevOp = OVS_READ_EVENT_DEV_OP,
137 .validateDpIndex = FALSE,
141 NETLINK_FAMILY nlControlFamilyOps = {
142 .name = OVS_WIN_CONTROL_FAMILY,
143 .id = OVS_WIN_NL_CTRL_FAMILY_ID,
144 .version = OVS_WIN_CONTROL_VERSION,
145 .maxAttr = OVS_WIN_CONTROL_ATTR_MAX,
146 .cmds = nlControlFamilyCmdOps,
147 .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
150 /* Netlink datapath family. */
151 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
152 { .cmd = OVS_DP_CMD_NEW,
153 .handler = OvsNewDpCmdHandler,
154 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
155 .validateDpIndex = FALSE
157 { .cmd = OVS_DP_CMD_GET,
158 .handler = OvsGetDpCmdHandler,
159 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
160 OVS_TRANSACTION_DEV_OP,
161 .validateDpIndex = FALSE
163 { .cmd = OVS_DP_CMD_SET,
164 .handler = OvsSetDpCmdHandler,
165 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
166 OVS_TRANSACTION_DEV_OP,
167 .validateDpIndex = TRUE
171 NETLINK_FAMILY nlDatapathFamilyOps = {
172 .name = OVS_DATAPATH_FAMILY,
173 .id = OVS_WIN_NL_DATAPATH_FAMILY_ID,
174 .version = OVS_DATAPATH_VERSION,
175 .maxAttr = OVS_DP_ATTR_MAX,
176 .cmds = nlDatapathFamilyCmdOps,
177 .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
180 /* Netlink packet family. */
181 /* XXX: Add commands here. */
182 NETLINK_FAMILY nlPacketFamilyOps = {
183 .name = OVS_PACKET_FAMILY,
184 .id = OVS_WIN_NL_PACKET_FAMILY_ID,
185 .version = OVS_PACKET_VERSION,
186 .maxAttr = OVS_PACKET_ATTR_MAX,
187 .cmds = NULL, /* XXX: placeholder. */
191 /* Netlink vport family. */
192 NETLINK_CMD nlVportFamilyCmdOps[] = {
193 { .cmd = OVS_VPORT_CMD_GET,
194 .handler = OvsGetVportCmdHandler,
195 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
196 OVS_TRANSACTION_DEV_OP,
197 .validateDpIndex = TRUE
199 { .cmd = OVS_VPORT_CMD_NEW,
200 .handler = OvsNewVportCmdHandler,
201 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
202 .validateDpIndex = TRUE
204 { .cmd = OVS_VPORT_CMD_DEL,
205 .handler = OvsDeleteVportCmdHandler,
206 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
207 .validateDpIndex = TRUE
211 NETLINK_FAMILY nlVportFamilyOps = {
212 .name = OVS_VPORT_FAMILY,
213 .id = OVS_WIN_NL_VPORT_FAMILY_ID,
214 .version = OVS_VPORT_VERSION,
215 .maxAttr = OVS_VPORT_ATTR_MAX,
216 .cmds = nlVportFamilyCmdOps,
217 .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps)
220 /* Netlink flow family. */
222 NETLINK_CMD nlFlowFamilyCmdOps[] = {
223 { .cmd = OVS_FLOW_CMD_NEW,
224 .handler = OvsFlowNlCmdHandler,
225 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
226 .validateDpIndex = TRUE
228 { .cmd = OVS_FLOW_CMD_SET,
229 .handler = OvsFlowNlCmdHandler,
230 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
231 .validateDpIndex = TRUE
233 { .cmd = OVS_FLOW_CMD_DEL,
234 .handler = OvsFlowNlCmdHandler,
235 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
236 .validateDpIndex = TRUE
238 { .cmd = OVS_FLOW_CMD_GET,
239 .handler = OvsFlowNlGetCmdHandler,
240 .supportedDevOp = OVS_TRANSACTION_DEV_OP |
241 OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
242 .validateDpIndex = TRUE
246 NETLINK_FAMILY nlFLowFamilyOps = {
247 .name = OVS_FLOW_FAMILY,
248 .id = OVS_WIN_NL_FLOW_FAMILY_ID,
249 .version = OVS_FLOW_VERSION,
250 .maxAttr = OVS_FLOW_ATTR_MAX,
251 .cmds = nlFlowFamilyCmdOps,
252 .opsCount = ARRAY_SIZE(nlFlowFamilyCmdOps)
255 /* Netlink netdev family. */
256 NETLINK_CMD nlNetdevFamilyCmdOps[] = {
257 { .cmd = OVS_WIN_NETDEV_CMD_GET,
258 .handler = OvsGetNetdevCmdHandler,
259 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
260 .validateDpIndex = FALSE
264 NETLINK_FAMILY nlNetdevFamilyOps = {
265 .name = OVS_WIN_NETDEV_FAMILY,
266 .id = OVS_WIN_NL_NETDEV_FAMILY_ID,
267 .version = OVS_WIN_NETDEV_VERSION,
268 .maxAttr = OVS_WIN_NETDEV_ATTR_MAX,
269 .cmds = nlNetdevFamilyCmdOps,
270 .opsCount = ARRAY_SIZE(nlNetdevFamilyCmdOps)
273 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
275 UINT32 requiredLength,
277 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
278 POVS_OPEN_INSTANCE instance,
280 NETLINK_FAMILY *nlFamilyOps);
281 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
282 NETLINK_FAMILY *nlFamilyOps,
285 /* Handles to the device object for communication with userspace. */
286 NDIS_HANDLE gOvsDeviceHandle;
287 PDEVICE_OBJECT gOvsDeviceObject;
289 _Dispatch_type_(IRP_MJ_CREATE)
290 _Dispatch_type_(IRP_MJ_CLOSE)
291 DRIVER_DISPATCH OvsOpenCloseDevice;
293 _Dispatch_type_(IRP_MJ_CLEANUP)
294 DRIVER_DISPATCH OvsCleanupDevice;
296 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
297 DRIVER_DISPATCH OvsDeviceControl;
300 #pragma alloc_text(INIT, OvsCreateDeviceObject)
301 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
302 #pragma alloc_text(PAGE, OvsCleanupDevice)
303 #pragma alloc_text(PAGE, OvsDeviceControl)
304 #endif // ALLOC_PRAGMA
307 * We might hit this limit easily since userspace opens a netlink descriptor for
308 * each thread, and at least one descriptor per vport. Revisit this later.
310 #define OVS_MAX_OPEN_INSTANCES 512
311 #define OVS_SYSTEM_DP_NAME "ovs-system"
313 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
314 UINT32 ovsNumberOfOpenInstances;
315 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
317 NDIS_SPIN_LOCK ovsCtrlLockObj;
318 PNDIS_SPIN_LOCK gOvsCtrlLock;
324 gOvsCtrlLock = &ovsCtrlLockObj;
325 NdisAllocateSpinLock(gOvsCtrlLock);
333 OvsCleanupEventQueue();
335 NdisFreeSpinLock(gOvsCtrlLock);
344 NdisAcquireSpinLock(gOvsCtrlLock);
350 NdisReleaseSpinLock(gOvsCtrlLock);
355 * --------------------------------------------------------------------------
356 * Creates the communication device between user and kernel, and also
357 * initializes the data associated data structures.
358 * --------------------------------------------------------------------------
361 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
363 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
364 UNICODE_STRING deviceName;
365 UNICODE_STRING symbolicDeviceName;
366 PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
367 NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
368 OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
370 RtlZeroMemory(dispatchTable,
371 (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
372 dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
373 dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
374 dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
375 dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
377 NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
378 NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
380 RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
382 OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
383 NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
384 NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
385 sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
387 deviceAttributes.DeviceName = &deviceName;
388 deviceAttributes.SymbolicName = &symbolicDeviceName;
389 deviceAttributes.MajorFunctions = dispatchTable;
390 deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
392 status = NdisRegisterDeviceEx(ovsExtDriverHandle,
396 if (status != NDIS_STATUS_SUCCESS) {
397 POVS_DEVICE_EXTENSION ovsExt =
398 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
399 ASSERT(gOvsDeviceObject != NULL);
400 ASSERT(gOvsDeviceHandle != NULL);
403 ovsExt->numberOpenInstance = 0;
406 /* Initialize the associated data structures. */
409 OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
415 OvsDeleteDeviceObject()
417 if (gOvsDeviceHandle) {
419 POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
420 NdisGetDeviceReservedExtension(gOvsDeviceObject);
422 ASSERT(ovsExt->numberOpenInstance == 0);
426 ASSERT(gOvsDeviceObject);
427 NdisDeregisterDeviceEx(gOvsDeviceHandle);
428 gOvsDeviceHandle = NULL;
429 gOvsDeviceObject = NULL;
435 OvsGetOpenInstance(PFILE_OBJECT fileObject,
438 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
440 ASSERT(instance->fileObject == fileObject);
441 if (gOvsSwitchContext == NULL ||
442 gOvsSwitchContext->dpNo != dpNo) {
450 OvsFindOpenInstance(PFILE_OBJECT fileObject)
453 for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
454 j < ovsNumberOfOpenInstances; i++) {
455 if (ovsOpenInstanceArray[i]) {
456 if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
457 return ovsOpenInstanceArray[i];
466 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
467 PFILE_OBJECT fileObject)
469 POVS_OPEN_INSTANCE instance =
470 (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
473 if (instance == NULL) {
474 return STATUS_NO_MEMORY;
476 OvsAcquireCtrlLock();
477 ASSERT(OvsFindOpenInstance(fileObject) == NULL);
479 if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
480 OvsReleaseCtrlLock();
481 OvsFreeMemory(instance);
482 return STATUS_INSUFFICIENT_RESOURCES;
484 RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
486 for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
487 if (ovsOpenInstanceArray[i] == NULL) {
488 ovsOpenInstanceArray[i] = instance;
489 ovsNumberOfOpenInstances++;
490 instance->cookie = i;
494 ASSERT(i < OVS_MAX_OPEN_INSTANCES);
495 instance->fileObject = fileObject;
496 ASSERT(fileObject->FsContext == NULL);
497 instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
498 if (instance->pid == 0) {
499 /* XXX: check for rollover. */
501 fileObject->FsContext = instance;
502 OvsReleaseCtrlLock();
503 return STATUS_SUCCESS;
507 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
509 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
511 ASSERT(fileObject == instance->fileObject);
512 OvsCleanupEvent(instance);
513 OvsCleanupPacketQueue(instance);
517 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
519 POVS_OPEN_INSTANCE instance;
520 ASSERT(fileObject->FsContext);
521 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
522 ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
524 OvsAcquireCtrlLock();
525 fileObject->FsContext = NULL;
526 ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
527 ovsOpenInstanceArray[instance->cookie] = NULL;
528 ovsNumberOfOpenInstances--;
529 OvsReleaseCtrlLock();
530 ASSERT(instance->eventQueue == NULL);
531 ASSERT (instance->packetQueue == NULL);
532 OvsFreeMemory(instance);
536 OvsCompleteIrpRequest(PIRP irp,
540 irp->IoStatus.Information = infoPtr;
541 irp->IoStatus.Status = status;
542 IoCompleteRequest(irp, IO_NO_INCREMENT);
548 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
551 PIO_STACK_LOCATION irpSp;
552 NTSTATUS status = STATUS_SUCCESS;
553 PFILE_OBJECT fileObject;
554 POVS_DEVICE_EXTENSION ovsExt =
555 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
557 ASSERT(deviceObject == gOvsDeviceObject);
558 ASSERT(ovsExt != NULL);
560 irpSp = IoGetCurrentIrpStackLocation(irp);
561 fileObject = irpSp->FileObject;
562 OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
563 deviceObject, fileObject,
564 ovsExt->numberOpenInstance);
566 switch (irpSp->MajorFunction) {
568 status = OvsAddOpenInstance(ovsExt, fileObject);
569 if (STATUS_SUCCESS == status) {
570 InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
574 ASSERT(ovsExt->numberOpenInstance > 0);
575 OvsRemoveOpenInstance(fileObject);
576 InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
581 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
584 _Use_decl_annotations_
586 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
590 PIO_STACK_LOCATION irpSp;
591 PFILE_OBJECT fileObject;
593 NTSTATUS status = STATUS_SUCCESS;
595 POVS_DEVICE_EXTENSION ovsExt =
596 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
598 ASSERT(ovsExt->numberOpenInstance > 0);
601 UNREFERENCED_PARAMETER(deviceObject);
603 ASSERT(deviceObject == gOvsDeviceObject);
604 irpSp = IoGetCurrentIrpStackLocation(irp);
605 fileObject = irpSp->FileObject;
607 ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
609 OvsCleanupOpenInstance(fileObject);
611 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
616 * --------------------------------------------------------------------------
617 * IOCTL function handler for the device.
618 * --------------------------------------------------------------------------
621 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
625 PIO_STACK_LOCATION irpSp;
626 NTSTATUS status = STATUS_SUCCESS;
627 PFILE_OBJECT fileObject;
628 PVOID inputBuffer = NULL;
629 PVOID outputBuffer = NULL;
630 UINT32 inputBufferLen, outputBufferLen;
631 UINT32 code, replyLen = 0;
632 POVS_OPEN_INSTANCE instance;
634 OVS_MESSAGE ovsMsgReadOp;
636 NETLINK_FAMILY *nlFamilyOps;
637 OVS_USER_PARAMS_CONTEXT usrParamsCtx;
640 POVS_DEVICE_EXTENSION ovsExt =
641 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
642 ASSERT(deviceObject == gOvsDeviceObject);
644 ASSERT(ovsExt->numberOpenInstance > 0);
646 UNREFERENCED_PARAMETER(deviceObject);
649 irpSp = IoGetCurrentIrpStackLocation(irp);
651 ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
652 ASSERT(irpSp->FileObject != NULL);
654 fileObject = irpSp->FileObject;
655 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
656 code = irpSp->Parameters.DeviceIoControl.IoControlCode;
657 inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
658 outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
659 inputBuffer = irp->AssociatedIrp.SystemBuffer;
661 /* Concurrent netlink operations are not supported. */
662 if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
663 status = STATUS_RESOURCE_IN_USE;
668 * Validate the input/output buffer arguments depending on the type of the
672 case OVS_IOCTL_TRANSACT:
673 /* Input buffer is mandatory, output buffer is optional. */
674 if (outputBufferLen != 0) {
675 status = MapIrpOutputBuffer(irp, outputBufferLen,
676 sizeof *ovsMsg, &outputBuffer);
677 if (status != STATUS_SUCCESS) {
680 ASSERT(outputBuffer);
683 if (inputBufferLen < sizeof (*ovsMsg)) {
684 status = STATUS_NDIS_INVALID_LENGTH;
688 ovsMsg = inputBuffer;
689 devOp = OVS_TRANSACTION_DEV_OP;
692 case OVS_IOCTL_READ_EVENT:
693 /* This IOCTL is used to read events */
694 if (outputBufferLen != 0) {
695 status = MapIrpOutputBuffer(irp, outputBufferLen,
696 sizeof *ovsMsg, &outputBuffer);
697 if (status != STATUS_SUCCESS) {
700 ASSERT(outputBuffer);
702 status = STATUS_NDIS_INVALID_LENGTH;
708 ovsMsg = &ovsMsgReadOp;
709 ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
710 /* An "artificial" command so we can use NL family function table*/
711 ovsMsg->genlMsg.cmd = OVS_CTRL_CMD_EVENT_NOTIFY;
712 devOp = OVS_READ_DEV_OP;
716 /* Output buffer is mandatory. */
717 if (outputBufferLen != 0) {
718 status = MapIrpOutputBuffer(irp, outputBufferLen,
719 sizeof *ovsMsg, &outputBuffer);
720 if (status != STATUS_SUCCESS) {
723 ASSERT(outputBuffer);
725 status = STATUS_NDIS_INVALID_LENGTH;
730 * Operate in the mode that read ioctl is similar to ReadFile(). This
731 * might change as the userspace code gets implemented.
737 * For implementing read (ioctl or otherwise), we need to store some
738 * state in the instance to indicate the command that started the dump
739 * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
740 * that 'ovsMsgReadOp' is needed only in this function to call into the
741 * appropraite handler. The handler itself can access the state in the
744 * In the absence of a dump start, return 0 bytes.
746 if (instance->dumpState.ovsMsg == NULL) {
748 status = STATUS_SUCCESS;
751 RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
752 sizeof (ovsMsgReadOp));
754 /* Create an NL message for consumption. */
755 ovsMsg = &ovsMsgReadOp;
756 devOp = OVS_READ_DEV_OP;
760 case OVS_IOCTL_WRITE:
761 /* Input buffer is mandatory. */
762 if (inputBufferLen < sizeof (*ovsMsg)) {
763 status = STATUS_NDIS_INVALID_LENGTH;
767 ovsMsg = inputBuffer;
768 devOp = OVS_WRITE_DEV_OP;
772 status = STATUS_INVALID_DEVICE_REQUEST;
777 switch (ovsMsg->nlMsg.nlmsgType) {
778 case OVS_WIN_NL_CTRL_FAMILY_ID:
779 nlFamilyOps = &nlControlFamilyOps;
781 case OVS_WIN_NL_DATAPATH_FAMILY_ID:
782 nlFamilyOps = &nlDatapathFamilyOps;
784 case OVS_WIN_NL_FLOW_FAMILY_ID:
785 nlFamilyOps = &nlFLowFamilyOps;
787 case OVS_WIN_NL_PACKET_FAMILY_ID:
788 status = STATUS_NOT_IMPLEMENTED;
790 case OVS_WIN_NL_VPORT_FAMILY_ID:
791 nlFamilyOps = &nlVportFamilyOps;
793 case OVS_WIN_NL_NETDEV_FAMILY_ID:
794 nlFamilyOps = &nlNetdevFamilyOps;
797 status = STATUS_INVALID_PARAMETER;
802 * For read operation, the netlink command has already been validated
805 if (devOp != OVS_READ_DEV_OP) {
806 status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
807 if (status != STATUS_SUCCESS) {
812 InitUserParamsCtx(irp, instance, devOp, ovsMsg,
813 inputBuffer, inputBufferLen,
814 outputBuffer, outputBufferLen,
817 status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
822 return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
827 * --------------------------------------------------------------------------
828 * Function to validate a netlink command. Only certain combinations of
829 * (device operation, netlink family, command) are valid.
830 * --------------------------------------------------------------------------
833 ValidateNetlinkCmd(UINT32 devOp,
834 POVS_OPEN_INSTANCE instance,
836 NETLINK_FAMILY *nlFamilyOps)
838 NTSTATUS status = STATUS_INVALID_PARAMETER;
841 for (i = 0; i < nlFamilyOps->opsCount; i++) {
842 if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
843 /* Validate if the command is valid for the device operation. */
844 if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
845 status = STATUS_INVALID_PARAMETER;
849 /* Validate the version. */
850 if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
851 status = STATUS_INVALID_PARAMETER;
855 /* Validate the DP for commands that require a DP. */
856 if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
857 OvsAcquireCtrlLock();
858 if (ovsMsg->ovsHdr.dp_ifindex !=
859 (INT)gOvsSwitchContext->dpNo) {
860 status = STATUS_INVALID_PARAMETER;
861 OvsReleaseCtrlLock();
864 OvsReleaseCtrlLock();
867 /* Validate the PID. */
868 if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
869 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
870 status = STATUS_INVALID_PARAMETER;
875 status = STATUS_SUCCESS;
885 * --------------------------------------------------------------------------
886 * Function to invoke the netlink command handler.
887 * --------------------------------------------------------------------------
890 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
891 NETLINK_FAMILY *nlFamilyOps,
894 NTSTATUS status = STATUS_INVALID_PARAMETER;
897 for (i = 0; i < nlFamilyOps->opsCount; i++) {
898 if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
899 NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
902 status = handler(usrParamsCtx, replyLen);
912 * --------------------------------------------------------------------------
913 * Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
915 * Each handle on the device is assigned a unique PID when the handle is
916 * created. On platforms that support netlink natively, the PID is available
917 * to userspace when the netlink socket is created. However, without native
918 * netlink support on Windows, OVS datapath generates the PID and lets the
919 * userspace query it.
921 * This function implements the query.
922 * --------------------------------------------------------------------------
925 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
928 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
929 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
931 if (usrParamsCtx->outputLength >= sizeof *msgOut) {
932 POVS_OPEN_INSTANCE instance =
933 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
935 RtlZeroMemory(msgOut, sizeof *msgOut);
936 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
937 msgOut->nlMsg.nlmsgPid = instance->pid;
938 *replyLen = sizeof *msgOut;
939 /* XXX: We might need to return the DP index as well. */
941 return STATUS_NDIS_INVALID_LENGTH;
944 return STATUS_SUCCESS;
948 * --------------------------------------------------------------------------
949 * Utility function to fill up information about the datapath in a reply to
951 * Assumes that 'gOvsCtrlLock' lock is acquired.
952 * --------------------------------------------------------------------------
955 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
960 OVS_MESSAGE msgOutTmp;
961 OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
964 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && NlBufRemLen(nlBuf) >= sizeof *msgIn);
966 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
967 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
968 msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
969 msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
971 msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
972 msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
973 msgOutTmp.genlMsg.reserved = 0;
975 msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
977 writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
979 writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
983 OVS_DP_STATS dpStats;
985 dpStats.n_hit = datapath->hits;
986 dpStats.n_missed = datapath->misses;
987 dpStats.n_lost = datapath->lost;
988 dpStats.n_flows = datapath->nFlows;
989 writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
990 (PCHAR)&dpStats, sizeof dpStats);
992 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
993 nlMsg->nlmsgLen = NlBufSize(nlBuf);
995 return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
999 * --------------------------------------------------------------------------
1000 * Handler for queueing an IRP used for event notification. The IRP is
1001 * completed when a port state changes. STATUS_PENDING is returned on
1002 * success. User mode keep a pending IRP at all times.
1003 * --------------------------------------------------------------------------
1006 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1011 UNREFERENCED_PARAMETER(replyLen);
1013 POVS_OPEN_INSTANCE instance =
1014 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1015 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1016 OVS_EVENT_POLL poll;
1018 poll.dpNo = msgIn->ovsHdr.dp_ifindex;
1019 status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
1020 &poll, sizeof poll);
1025 * --------------------------------------------------------------------------
1026 * Handler for the subscription for the event queue
1027 * --------------------------------------------------------------------------
1030 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1034 OVS_EVENT_SUBSCRIBE request;
1038 const NL_POLICY policy[] = {
1039 [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
1040 [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
1043 UNREFERENCED_PARAMETER(replyLen);
1045 POVS_OPEN_INSTANCE instance =
1046 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1047 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1049 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1050 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, 2);
1052 status = STATUS_INVALID_PARAMETER;
1056 /* XXX Ignore the MC group for now */
1057 join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1058 request.dpNo = msgIn->ovsHdr.dp_ifindex;
1059 request.subscribe = join;
1060 request.mask = OVS_EVENT_MASK_ALL;
1062 status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1069 * --------------------------------------------------------------------------
1070 * Command Handler for 'OVS_DP_CMD_NEW'.
1071 * --------------------------------------------------------------------------
1074 OvsNewDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1077 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1081 * --------------------------------------------------------------------------
1082 * Command Handler for 'OVS_DP_CMD_GET'.
1084 * The function handles both the dump based as well as the transaction based
1085 * 'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1086 * call to setup dump state, as well as subsequent calls to continue dumping
1088 * --------------------------------------------------------------------------
1091 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1094 if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1095 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1097 return HandleGetDpDump(usrParamsCtx, replyLen);
1102 * --------------------------------------------------------------------------
1103 * Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1104 * --------------------------------------------------------------------------
1107 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1110 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1115 * --------------------------------------------------------------------------
1116 * Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1117 * --------------------------------------------------------------------------
1120 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1123 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1124 POVS_OPEN_INSTANCE instance =
1125 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1127 if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1129 OvsSetupDumpStart(usrParamsCtx);
1133 POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1135 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1137 if (instance->dumpState.ovsMsg == NULL) {
1139 return STATUS_INVALID_DEVICE_STATE;
1142 /* Dump state must have been deleted after previous dump operation. */
1143 ASSERT(instance->dumpState.index[0] == 0);
1144 /* Output buffer has been validated while validating read dev op. */
1145 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1147 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1148 usrParamsCtx->outputLength);
1150 OvsAcquireCtrlLock();
1151 if (!gOvsSwitchContext) {
1152 /* Treat this as a dump done. */
1153 OvsReleaseCtrlLock();
1155 FreeUserDumpState(instance);
1156 return STATUS_SUCCESS;
1158 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1159 OvsReleaseCtrlLock();
1161 if (status != STATUS_SUCCESS) {
1163 FreeUserDumpState(instance);
1167 /* Increment the dump index. */
1168 instance->dumpState.index[0] = 1;
1169 *replyLen = msgOut->nlMsg.nlmsgLen;
1171 /* Free up the dump state, since there's no more data to continue. */
1172 FreeUserDumpState(instance);
1175 return STATUS_SUCCESS;
1180 * --------------------------------------------------------------------------
1181 * Command Handler for 'OVS_DP_CMD_SET'.
1182 * --------------------------------------------------------------------------
1185 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1188 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1192 * --------------------------------------------------------------------------
1193 * Function for handling transaction based 'OVS_DP_CMD_NEW', 'OVS_DP_CMD_GET'
1194 * and 'OVS_DP_CMD_SET' commands.
1196 * 'OVS_DP_CMD_NEW' is implemented to keep userspace code happy. Creation of a
1197 * new datapath is not supported currently.
1198 * --------------------------------------------------------------------------
1201 HandleDpTransactionCommon(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1204 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1205 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1206 NTSTATUS status = STATUS_SUCCESS;
1208 NL_ERROR nlError = NL_ERROR_SUCCESS;
1209 static const NL_POLICY ovsDatapathSetPolicy[] = {
1210 [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1211 [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1212 [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1214 PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1216 /* input buffer has been validated while validating write dev op. */
1217 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1219 /* Parse any attributes in the request. */
1220 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET ||
1221 usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1222 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1223 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1224 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1225 ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1226 return STATUS_INVALID_PARAMETER;
1230 * XXX: Not clear at this stage if there's any role for the
1231 * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1236 RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1239 /* Output buffer is optional for OVS_TRANSACTION_DEV_OP. */
1240 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1241 return STATUS_NDIS_INVALID_LENGTH;
1243 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1245 OvsAcquireCtrlLock();
1246 if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1247 if (!gOvsSwitchContext &&
1248 !OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1249 OVS_SYSTEM_DP_NAME)) {
1250 OvsReleaseCtrlLock();
1252 /* Creation of new datapaths is not supported. */
1253 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1254 nlError = NL_ERROR_NOTSUPP;
1258 nlError = NL_ERROR_NODEV;
1261 } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1262 OvsReleaseCtrlLock();
1263 nlError = NL_ERROR_NODEV;
1267 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1268 OvsReleaseCtrlLock();
1269 nlError = NL_ERROR_EXIST;
1273 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1274 OvsReleaseCtrlLock();
1276 *replyLen = NlBufSize(&nlBuf);
1279 if (nlError != NL_ERROR_SUCCESS) {
1280 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1281 usrParamsCtx->outputBuffer;
1283 BuildErrorMsg(msgIn, msgError, nlError);
1284 *replyLen = msgError->nlMsg.nlmsgLen;
1287 return STATUS_SUCCESS;
1292 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1294 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1295 POVS_OPEN_INSTANCE instance =
1296 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1298 /* input buffer has been validated while validating write dev op. */
1299 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1301 /* A write operation that does not indicate dump start is invalid. */
1302 if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1303 return STATUS_INVALID_PARAMETER;
1305 /* XXX: Handle other NLM_F_* flags in the future. */
1308 * This operation should be setting up the dump state. If there's any
1309 * previous state, clear it up so as to set it up afresh.
1311 if (instance->dumpState.ovsMsg != NULL) {
1312 FreeUserDumpState(instance);
1315 return InitUserDumpState(instance, msgIn);
1319 BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
1320 UINT32 length, UINT16 flags)
1322 msgOut->nlMsg.nlmsgType = type;
1323 msgOut->nlMsg.nlmsgFlags = flags;
1324 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1325 msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1326 msgOut->nlMsg.nlmsgLen = length;
1328 msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
1329 msgOut->genlMsg.version = msgIn->genlMsg.version;
1330 msgOut->genlMsg.reserved = 0;
1334 * XXX: should move out these functions to a Netlink.c or to a OvsMessage.c
1335 * or even make them inlined functions in Datapath.h. Can be done after the
1336 * first sprint once we have more code to refactor.
1339 BuildReplyMsgFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
1341 BuildMsgOut(msgIn, msgOut, msgIn->nlMsg.nlmsgType, sizeof(OVS_MESSAGE),
1346 BuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode)
1348 BuildMsgOut(msgIn, (POVS_MESSAGE)msgOut, NLMSG_ERROR,
1349 sizeof(OVS_MESSAGE_ERROR), 0);
1351 msgOut->errorMsg.error = errorCode;
1352 msgOut->errorMsg.nlMsg = msgIn->nlMsg;
1356 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1363 OVS_VPORT_FULL_STATS vportStats;
1368 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1370 BuildReplyMsgFromMsgIn(msgIn, &msgOut, NLM_F_MULTI);
1371 msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1373 ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1375 return STATUS_INSUFFICIENT_RESOURCES;
1378 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1380 return STATUS_INSUFFICIENT_RESOURCES;
1383 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1385 return STATUS_INSUFFICIENT_RESOURCES;
1388 ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1390 return STATUS_INSUFFICIENT_RESOURCES;
1394 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1395 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1396 * it means we have an array of pids, instead of a single pid.
1397 * ATM we assume we have one pid only.
1400 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1403 return STATUS_INSUFFICIENT_RESOURCES;
1407 vportStats.rxPackets = vport->stats.rxPackets;
1408 vportStats.rxBytes = vport->stats.rxBytes;
1409 vportStats.txPackets = vport->stats.txPackets;
1410 vportStats.txBytes = vport->stats.txBytes;
1411 vportStats.rxErrors = vport->errStats.rxErrors;
1412 vportStats.txErrors = vport->errStats.txErrors;
1413 vportStats.rxDropped = vport->errStats.rxDropped;
1414 vportStats.txDropped = vport->errStats.txDropped;
1416 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1418 sizeof(OVS_VPORT_FULL_STATS));
1420 return STATUS_INSUFFICIENT_RESOURCES;
1424 * XXX: when vxlan udp dest port becomes configurable, we will also need
1425 * to add vport options
1428 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1429 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1431 return STATUS_SUCCESS;
1435 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1439 POVS_OPEN_INSTANCE instance =
1440 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1441 LOCK_STATE_EX lockState;
1442 UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1445 * XXX: this function shares some code with other dump command(s).
1446 * In the future, we will need to refactor the dump functions
1449 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1451 if (instance->dumpState.ovsMsg == NULL) {
1453 return STATUS_INVALID_DEVICE_STATE;
1456 /* Output buffer has been validated while validating read dev op. */
1457 ASSERT(usrParamsCtx->outputBuffer != NULL);
1459 msgIn = instance->dumpState.ovsMsg;
1461 OvsAcquireCtrlLock();
1462 if (!gOvsSwitchContext) {
1463 /* Treat this as a dump done. */
1464 OvsReleaseCtrlLock();
1466 FreeUserDumpState(instance);
1467 return STATUS_SUCCESS;
1471 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1472 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1473 * it means we have an array of pids, instead of a single pid.
1474 * ATM we assume we have one pid only.
1476 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1477 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1478 NDIS_RWL_AT_DISPATCH_LEVEL);
1480 if (gOvsSwitchContext->numVports > 0) {
1481 /* inBucket: the bucket, used for lookup */
1482 UINT32 inBucket = instance->dumpState.index[0];
1483 /* inIndex: index within the given bucket, used for lookup */
1484 UINT32 inIndex = instance->dumpState.index[1];
1485 /* the bucket to be used for the next dump operation */
1486 UINT32 outBucket = 0;
1487 /* the index within the outBucket to be used for the next dump */
1488 UINT32 outIndex = 0;
1490 for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1491 PLIST_ENTRY head, link;
1492 head = &(gOvsSwitchContext->portIdHashArray[i]);
1493 POVS_VPORT_ENTRY vport = NULL;
1496 LIST_FORALL(head, link) {
1499 * if one or more dumps were previously done on this same bucket,
1500 * inIndex will be > 0, so we'll need to reply with the
1501 * inIndex + 1 vport from the bucket.
1503 if (outIndex >= inIndex) {
1504 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1506 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
1507 OvsCreateMsgFromVport(vport, msgIn,
1508 usrParamsCtx->outputBuffer,
1509 usrParamsCtx->outputLength,
1510 gOvsSwitchContext->dpNo);
1526 * if no vport was found above, check the next bucket, beginning
1527 * with the first (i.e. index 0) elem from within that bucket
1534 /* XXX: what about NLMSG_DONE (as msg type)? */
1535 instance->dumpState.index[0] = outBucket;
1536 instance->dumpState.index[1] = outIndex;
1539 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1541 OvsReleaseCtrlLock();
1543 /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1544 if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1545 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1546 *replyLen = msgOut->nlMsg.nlmsgLen;
1549 * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1553 /* Free up the dump state, since there's no more data to continue. */
1554 FreeUserDumpState(instance);
1557 return STATUS_SUCCESS;
1561 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1564 NTSTATUS status = STATUS_SUCCESS;
1565 LOCK_STATE_EX lockState;
1567 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1568 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1569 POVS_VPORT_ENTRY vport = NULL;
1570 NL_ERROR nlError = NL_ERROR_SUCCESS;
1571 PCHAR portName = NULL;
1572 UINT32 portNameLen = 0;
1573 UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
1575 static const NL_POLICY ovsVportPolicy[] = {
1576 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1577 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1582 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1584 /* input buffer has been validated while validating write dev op. */
1585 ASSERT(usrParamsCtx->inputBuffer != NULL);
1587 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1588 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1589 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1590 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1591 return STATUS_INVALID_PARAMETER;
1594 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1595 return STATUS_INVALID_BUFFER_SIZE;
1598 OvsAcquireCtrlLock();
1599 if (!gOvsSwitchContext) {
1600 OvsReleaseCtrlLock();
1601 return STATUS_INVALID_PARAMETER;
1603 OvsReleaseCtrlLock();
1605 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1606 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1607 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1608 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1610 /* the port name is expected to be null-terminated */
1611 ASSERT(portName[portNameLen - 1] == '\0');
1613 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1614 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1615 portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1617 vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
1619 nlError = NL_ERROR_INVAL;
1620 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1625 nlError = NL_ERROR_NODEV;
1626 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1630 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1631 usrParamsCtx->outputLength,
1632 gOvsSwitchContext->dpNo);
1633 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1635 *replyLen = msgOut->nlMsg.nlmsgLen;
1638 if (nlError != NL_ERROR_SUCCESS) {
1639 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1640 usrParamsCtx->outputBuffer;
1642 BuildErrorMsg(msgIn, msgError, nlError);
1643 *replyLen = msgError->nlMsg.nlmsgLen;
1646 return STATUS_SUCCESS;
1650 * --------------------------------------------------------------------------
1651 * Handler for the get vport command. The function handles the initial call to
1652 * setup the dump state, as well as subsequent calls to continue dumping data.
1653 * --------------------------------------------------------------------------
1656 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1661 switch (usrParamsCtx->devOp)
1663 case OVS_WRITE_DEV_OP:
1664 return OvsSetupDumpStart(usrParamsCtx);
1666 case OVS_READ_DEV_OP:
1667 return OvsGetVportDumpNext(usrParamsCtx, replyLen);
1669 case OVS_TRANSACTION_DEV_OP:
1670 return OvsGetVport(usrParamsCtx, replyLen);
1673 return STATUS_INVALID_DEVICE_REQUEST;
1681 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
1683 /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
1684 for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
1685 POVS_VPORT_ENTRY vport;
1687 vport = OvsFindVportByPortNo(switchContext, i);
1693 return OVS_DPPORT_NUMBER_INVALID;
1697 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1700 NDIS_STATUS status = STATUS_SUCCESS;
1701 LOCK_STATE_EX lockState;
1703 NL_ERROR nlError = NL_ERROR_SUCCESS;
1704 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1705 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1706 POVS_VPORT_ENTRY vport = NULL;
1712 static const NL_POLICY ovsVportPolicy[] = {
1713 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1714 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
1715 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
1717 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
1718 .optional = FALSE },
1719 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
1722 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1724 /* input buffer has been validated while validating write dev op. */
1725 ASSERT(usrParamsCtx->inputBuffer != NULL);
1727 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1728 return STATUS_INVALID_BUFFER_SIZE;
1731 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1732 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1733 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1734 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1735 return STATUS_INVALID_PARAMETER;
1738 OvsAcquireCtrlLock();
1739 if (!gOvsSwitchContext) {
1740 OvsReleaseCtrlLock();
1741 return STATUS_INVALID_PARAMETER;
1743 OvsReleaseCtrlLock();
1745 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1746 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1747 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
1749 /* we are expecting null terminated strings to be passed */
1750 ASSERT(portName[portNameLen - 1] == '\0');
1752 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
1754 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1756 nlError = NL_ERROR_EXIST;
1760 if (portType == OVS_VPORT_TYPE_INTERNAL) {
1761 vport = gOvsSwitchContext->internalVport;
1762 } else if (portType == OVS_VPORT_TYPE_NETDEV) {
1763 if (!strcmp(portName, "external")) {
1764 vport = gOvsSwitchContext->externalVport;
1766 vport = OvsFindVportByHvName(gOvsSwitchContext, portName);
1769 /* XXX: change when other tunneling ports are added */
1770 ASSERT(portType == OVS_VPORT_TYPE_VXLAN);
1772 if (gOvsSwitchContext->vxlanVport) {
1773 nlError = NL_ERROR_EXIST;
1777 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
1778 if (vport == NULL) {
1779 nlError = NL_ERROR_NOMEM;
1783 vport->ovsState = OVS_STATE_PORT_CREATED;
1786 * XXX: when we allow configuring the vxlan udp port, we should read
1787 * this from vport->options instead!
1789 nlError = OvsInitVxlanTunnel(vport, VXLAN_UDP_PORT);
1790 if (nlError != NL_ERROR_SUCCESS) {
1796 nlError = NL_ERROR_INVAL;
1800 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
1801 nlError = NL_ERROR_EXIST;
1805 /* Fill the data in vport */
1806 vport->ovsType = portType;
1808 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1810 * XXX: when we implement the limit for ovs port number to be
1811 * MAXUINT16, we'll need to check the port number received from the
1814 vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1816 vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
1817 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1818 nlError = NL_ERROR_NOMEM;
1823 /* The ovs port name must be uninitialized. */
1824 ASSERT(vport->ovsName[0] == '\0');
1825 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
1827 RtlCopyMemory(vport->ovsName, portName, portNameLen);
1829 /* if we don't have options, then vport->portOptions will be NULL */
1830 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
1833 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1834 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1835 * it means we have an array of pids, instead of a single pid.
1836 * ATM we assume we have one pid only.
1838 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
1840 if (vport->ovsType == OVS_VPORT_TYPE_VXLAN) {
1841 gOvsSwitchContext->vxlanVport = vport;
1842 } else if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
1843 gOvsSwitchContext->internalVport = vport;
1844 gOvsSwitchContext->internalPortId = vport->portId;
1845 } else if (vport->ovsType == OVS_VPORT_TYPE_NETDEV &&
1846 vport->isExternal) {
1847 gOvsSwitchContext->externalVport = vport;
1848 gOvsSwitchContext->externalPortId = vport->portId;
1852 * insert the port into the hash array of ports: by port number and ovs
1853 * and ovs (datapath) port name.
1854 * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1855 * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1857 hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
1858 InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
1859 &vport->portNoLink);
1861 hash = OvsJhashBytes(vport->ovsName, portNameLen, OVS_HASH_BASIS);
1862 InsertHeadList(&gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
1863 &vport->ovsNameLink);
1865 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1866 usrParamsCtx->outputLength,
1867 gOvsSwitchContext->dpNo);
1869 *replyLen = msgOut->nlMsg.nlmsgLen;
1872 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1874 if (nlError != NL_ERROR_SUCCESS) {
1875 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1876 usrParamsCtx->outputBuffer;
1878 if (vport && vport->ovsType == OVS_VPORT_TYPE_VXLAN) {
1879 OvsRemoveAndDeleteVport(gOvsSwitchContext, vport);
1882 BuildErrorMsg(msgIn, msgError, nlError);
1883 *replyLen = msgError->nlMsg.nlmsgLen;
1886 return STATUS_SUCCESS;
1891 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1894 NDIS_STATUS status = STATUS_SUCCESS;
1895 LOCK_STATE_EX lockState;
1897 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1898 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1899 POVS_VPORT_ENTRY vport = NULL;
1900 NL_ERROR nlError = NL_ERROR_SUCCESS;
1901 PSTR portName = NULL;
1902 UINT32 portNameLen = 0;
1904 static const NL_POLICY ovsVportPolicy[] = {
1905 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1906 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ, .optional = TRUE },
1908 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1910 ASSERT(usrParamsCtx->inputBuffer != NULL);
1912 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1913 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1914 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1915 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1916 return STATUS_INVALID_PARAMETER;
1919 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1920 return STATUS_NDIS_INVALID_LENGTH;
1923 OvsAcquireCtrlLock();
1924 if (!gOvsSwitchContext) {
1925 OvsReleaseCtrlLock();
1926 return STATUS_INVALID_PARAMETER;
1928 OvsReleaseCtrlLock();
1930 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
1931 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1932 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1933 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1935 /* the port name is expected to be null-terminated */
1936 ASSERT(portName[portNameLen - 1] == '\0');
1938 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1940 else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1941 vport = OvsFindVportByPortNo(gOvsSwitchContext,
1942 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
1946 nlError = NL_ERROR_NODEV;
1950 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1951 usrParamsCtx->outputLength,
1952 gOvsSwitchContext->dpNo);
1954 if (vport->hvDeleted || OvsIsTunnelVportType(vport->ovsType)) {
1956 * The associated hyper-v switch port is not in created state, or,
1957 * there is no hyper-v switch port counterpart (for logical ports).
1958 * This means that this datapath port is not mapped to a living
1959 * hyper-v switc hport. We can destroy and remove the vport from the
1962 OvsRemoveAndDeleteVport(gOvsSwitchContext, vport);
1964 /* The associated hyper-v switch port is in the created state, and the
1965 * datapath port is mapped to a living hyper-v switch port. We cannot
1966 * destroy the vport and cannot remove it from the list of vports.
1967 * Instead, we mark the datapath (ovs) part of the vport as
1968 * "not created", i.e. we set vport->portNo = OVS_PORT_NUMBER_INVALID.
1970 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
1971 vport->ovsName[0] = '\0';
1974 *replyLen = msgOut->nlMsg.nlmsgLen;
1977 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1979 if (nlError != NL_ERROR_SUCCESS) {
1980 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1981 usrParamsCtx->outputBuffer;
1983 BuildErrorMsg(msgIn, msgError, nlError);
1984 *replyLen = msgError->nlMsg.nlmsgLen;
1987 return STATUS_SUCCESS;
1992 * --------------------------------------------------------------------------
1993 * Utility function to map the output buffer in an IRP. The buffer is assumed
1994 * to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
1995 * --------------------------------------------------------------------------
1998 MapIrpOutputBuffer(PIRP irp,
1999 UINT32 bufferLength,
2000 UINT32 requiredLength,
2005 ASSERT(bufferLength);
2006 ASSERT(requiredLength);
2007 if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
2008 return STATUS_INVALID_PARAMETER;
2011 if (bufferLength < requiredLength) {
2012 return STATUS_NDIS_INVALID_LENGTH;
2014 if (irp->MdlAddress == NULL) {
2015 return STATUS_INVALID_PARAMETER;
2017 *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
2018 NormalPagePriority);
2019 if (*buffer == NULL) {
2020 return STATUS_INSUFFICIENT_RESOURCES;
2023 return STATUS_SUCCESS;
2027 * --------------------------------------------------------------------------
2028 * Utility function to fill up information about the state of a port in a reply
2030 * Assumes that 'gOvsCtrlLock' lock is acquired.
2031 * --------------------------------------------------------------------------
2034 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2035 POVS_EVENT_ENTRY eventEntry,
2040 OVS_MESSAGE msgOutTmp;
2042 POVS_VPORT_ENTRY vport;
2044 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
2046 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
2047 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
2049 /* driver intiated messages should have zerp seq number*/
2050 msgOutTmp.nlMsg.nlmsgSeq = 0;
2051 msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
2053 msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
2054 msgOutTmp.genlMsg.reserved = 0;
2056 /* we don't have netdev yet, treat link up/down a adding/removing a port*/
2057 if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
2058 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
2059 } else if (eventEntry->status &
2060 (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
2061 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
2064 return STATUS_UNSUCCESSFUL;
2066 msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
2068 rc = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
2070 status = STATUS_INVALID_BUFFER_SIZE;
2074 vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
2076 status = STATUS_DEVICE_DOES_NOT_EXIST;
2080 rc = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) ||
2081 NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) ||
2082 NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
2084 status = STATUS_INVALID_BUFFER_SIZE;
2088 /* XXXX Should we add the port stats attributes?*/
2089 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
2090 nlMsg->nlmsgLen = NlBufSize(nlBuf);
2091 status = STATUS_SUCCESS;
2099 * --------------------------------------------------------------------------
2100 * Handler for reading events from the driver event queue. This handler is
2101 * executed when user modes issues a socket receive on a socket assocaited
2102 * with the MC group for events.
2103 * XXX user mode should read multiple events in one system call
2104 * --------------------------------------------------------------------------
2107 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2111 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2112 POVS_OPEN_INSTANCE instance =
2113 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
2117 OVS_EVENT_ENTRY eventEntry;
2119 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
2121 /* Should never read events with a dump socket */
2122 ASSERT(instance->dumpState.ovsMsg == NULL);
2124 /* Must have an event queue */
2125 ASSERT(instance->eventQueue != NULL);
2127 /* Output buffer has been validated while validating read dev op. */
2128 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2130 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
2132 OvsAcquireCtrlLock();
2133 if (!gOvsSwitchContext) {
2134 status = STATUS_SUCCESS;
2138 /* remove an event entry from the event queue */
2139 status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
2140 if (status != STATUS_SUCCESS) {
2144 status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
2145 if (status == NDIS_STATUS_SUCCESS) {
2146 *replyLen = NlBufSize(&nlBuf);
2150 OvsReleaseCtrlLock();
2153 #endif /* OVS_USE_NL_INTERFACE */