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 OvsSetVportCmdHandler,
99 OvsNewVportCmdHandler,
100 OvsDeleteVportCmdHandler;
102 NetlinkCmdHandler OvsGetNetdevCmdHandler;
104 static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
106 static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
108 static NTSTATUS HandleDpTransactionCommon(
109 POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen);
112 * The various netlink families, along with the supported commands. Most of
113 * these families and commands are part of the openvswitch specification for a
114 * netlink datapath. In addition, each platform can implement a few families
115 * and commands as extensions.
118 /* Netlink control family: this is a Windows specific family. */
119 NETLINK_CMD nlControlFamilyCmdOps[] = {
120 { .cmd = OVS_CTRL_CMD_WIN_GET_PID,
121 .handler = OvsGetPidCmdHandler,
122 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
123 .validateDpIndex = FALSE,
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_MC_SUBSCRIBE_REQ,
131 .handler = OvsSubscribeEventCmdHandler,
132 .supportedDevOp = OVS_WRITE_DEV_OP,
133 .validateDpIndex = TRUE,
135 { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY,
136 .handler = OvsReadEventCmdHandler,
137 .supportedDevOp = OVS_READ_EVENT_DEV_OP,
138 .validateDpIndex = FALSE,
142 NETLINK_FAMILY nlControlFamilyOps = {
143 .name = OVS_WIN_CONTROL_FAMILY,
144 .id = OVS_WIN_NL_CTRL_FAMILY_ID,
145 .version = OVS_WIN_CONTROL_VERSION,
146 .maxAttr = OVS_WIN_CONTROL_ATTR_MAX,
147 .cmds = nlControlFamilyCmdOps,
148 .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
151 /* Netlink datapath family. */
152 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
153 { .cmd = OVS_DP_CMD_NEW,
154 .handler = OvsNewDpCmdHandler,
155 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
156 .validateDpIndex = FALSE
158 { .cmd = OVS_DP_CMD_GET,
159 .handler = OvsGetDpCmdHandler,
160 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
161 OVS_TRANSACTION_DEV_OP,
162 .validateDpIndex = FALSE
164 { .cmd = OVS_DP_CMD_SET,
165 .handler = OvsSetDpCmdHandler,
166 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
167 OVS_TRANSACTION_DEV_OP,
168 .validateDpIndex = TRUE
172 NETLINK_FAMILY nlDatapathFamilyOps = {
173 .name = OVS_DATAPATH_FAMILY,
174 .id = OVS_WIN_NL_DATAPATH_FAMILY_ID,
175 .version = OVS_DATAPATH_VERSION,
176 .maxAttr = OVS_DP_ATTR_MAX,
177 .cmds = nlDatapathFamilyCmdOps,
178 .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
181 /* Netlink packet family. */
183 NETLINK_CMD nlPacketFamilyCmdOps[] = {
184 { .cmd = OVS_PACKET_CMD_EXECUTE,
185 .handler = OvsNlExecuteCmdHandler,
186 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
187 .validateDpIndex = TRUE
191 NETLINK_FAMILY nlPacketFamilyOps = {
192 .name = OVS_PACKET_FAMILY,
193 .id = OVS_WIN_NL_PACKET_FAMILY_ID,
194 .version = OVS_PACKET_VERSION,
195 .maxAttr = OVS_PACKET_ATTR_MAX,
196 .cmds = nlPacketFamilyCmdOps,
197 .opsCount = ARRAY_SIZE(nlPacketFamilyCmdOps)
200 /* Netlink vport family. */
201 NETLINK_CMD nlVportFamilyCmdOps[] = {
202 { .cmd = OVS_VPORT_CMD_GET,
203 .handler = OvsGetVportCmdHandler,
204 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
205 OVS_TRANSACTION_DEV_OP,
206 .validateDpIndex = TRUE
208 { .cmd = OVS_VPORT_CMD_NEW,
209 .handler = OvsNewVportCmdHandler,
210 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
211 .validateDpIndex = TRUE
213 { .cmd = OVS_VPORT_CMD_SET,
214 .handler = OvsSetVportCmdHandler,
215 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
216 .validateDpIndex = TRUE
218 { .cmd = OVS_VPORT_CMD_DEL,
219 .handler = OvsDeleteVportCmdHandler,
220 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
221 .validateDpIndex = TRUE
225 NETLINK_FAMILY nlVportFamilyOps = {
226 .name = OVS_VPORT_FAMILY,
227 .id = OVS_WIN_NL_VPORT_FAMILY_ID,
228 .version = OVS_VPORT_VERSION,
229 .maxAttr = OVS_VPORT_ATTR_MAX,
230 .cmds = nlVportFamilyCmdOps,
231 .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps)
234 /* Netlink flow family. */
236 NETLINK_CMD nlFlowFamilyCmdOps[] = {
237 { .cmd = OVS_FLOW_CMD_NEW,
238 .handler = OvsFlowNlCmdHandler,
239 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
240 .validateDpIndex = TRUE
242 { .cmd = OVS_FLOW_CMD_SET,
243 .handler = OvsFlowNlCmdHandler,
244 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
245 .validateDpIndex = TRUE
247 { .cmd = OVS_FLOW_CMD_DEL,
248 .handler = OvsFlowNlCmdHandler,
249 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
250 .validateDpIndex = TRUE
252 { .cmd = OVS_FLOW_CMD_GET,
253 .handler = OvsFlowNlGetCmdHandler,
254 .supportedDevOp = OVS_TRANSACTION_DEV_OP |
255 OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
256 .validateDpIndex = TRUE
260 NETLINK_FAMILY nlFLowFamilyOps = {
261 .name = OVS_FLOW_FAMILY,
262 .id = OVS_WIN_NL_FLOW_FAMILY_ID,
263 .version = OVS_FLOW_VERSION,
264 .maxAttr = OVS_FLOW_ATTR_MAX,
265 .cmds = nlFlowFamilyCmdOps,
266 .opsCount = ARRAY_SIZE(nlFlowFamilyCmdOps)
269 /* Netlink netdev family. */
270 NETLINK_CMD nlNetdevFamilyCmdOps[] = {
271 { .cmd = OVS_WIN_NETDEV_CMD_GET,
272 .handler = OvsGetNetdevCmdHandler,
273 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
274 .validateDpIndex = FALSE
278 NETLINK_FAMILY nlNetdevFamilyOps = {
279 .name = OVS_WIN_NETDEV_FAMILY,
280 .id = OVS_WIN_NL_NETDEV_FAMILY_ID,
281 .version = OVS_WIN_NETDEV_VERSION,
282 .maxAttr = OVS_WIN_NETDEV_ATTR_MAX,
283 .cmds = nlNetdevFamilyCmdOps,
284 .opsCount = ARRAY_SIZE(nlNetdevFamilyCmdOps)
287 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
289 UINT32 requiredLength,
291 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
292 POVS_OPEN_INSTANCE instance,
294 NETLINK_FAMILY *nlFamilyOps);
295 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
296 NETLINK_FAMILY *nlFamilyOps,
299 /* Handles to the device object for communication with userspace. */
300 NDIS_HANDLE gOvsDeviceHandle;
301 PDEVICE_OBJECT gOvsDeviceObject;
303 _Dispatch_type_(IRP_MJ_CREATE)
304 _Dispatch_type_(IRP_MJ_CLOSE)
305 DRIVER_DISPATCH OvsOpenCloseDevice;
307 _Dispatch_type_(IRP_MJ_CLEANUP)
308 DRIVER_DISPATCH OvsCleanupDevice;
310 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
311 DRIVER_DISPATCH OvsDeviceControl;
314 #pragma alloc_text(INIT, OvsCreateDeviceObject)
315 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
316 #pragma alloc_text(PAGE, OvsCleanupDevice)
317 #pragma alloc_text(PAGE, OvsDeviceControl)
318 #endif // ALLOC_PRAGMA
321 * We might hit this limit easily since userspace opens a netlink descriptor for
322 * each thread, and at least one descriptor per vport. Revisit this later.
324 #define OVS_MAX_OPEN_INSTANCES 512
325 #define OVS_SYSTEM_DP_NAME "ovs-system"
327 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
328 UINT32 ovsNumberOfOpenInstances;
329 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
331 NDIS_SPIN_LOCK ovsCtrlLockObj;
332 PNDIS_SPIN_LOCK gOvsCtrlLock;
338 gOvsCtrlLock = &ovsCtrlLockObj;
339 NdisAllocateSpinLock(gOvsCtrlLock);
347 OvsCleanupEventQueue();
349 NdisFreeSpinLock(gOvsCtrlLock);
358 NdisAcquireSpinLock(gOvsCtrlLock);
364 NdisReleaseSpinLock(gOvsCtrlLock);
369 * --------------------------------------------------------------------------
370 * Creates the communication device between user and kernel, and also
371 * initializes the data associated data structures.
372 * --------------------------------------------------------------------------
375 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
377 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
378 UNICODE_STRING deviceName;
379 UNICODE_STRING symbolicDeviceName;
380 PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
381 NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
382 OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
384 RtlZeroMemory(dispatchTable,
385 (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
386 dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
387 dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
388 dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
389 dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
391 NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
392 NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
394 RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
396 OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
397 NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
398 NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
399 sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
401 deviceAttributes.DeviceName = &deviceName;
402 deviceAttributes.SymbolicName = &symbolicDeviceName;
403 deviceAttributes.MajorFunctions = dispatchTable;
404 deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
406 status = NdisRegisterDeviceEx(ovsExtDriverHandle,
410 if (status != NDIS_STATUS_SUCCESS) {
411 POVS_DEVICE_EXTENSION ovsExt =
412 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
413 ASSERT(gOvsDeviceObject != NULL);
414 ASSERT(gOvsDeviceHandle != NULL);
417 ovsExt->numberOpenInstance = 0;
420 /* Initialize the associated data structures. */
423 OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
429 OvsDeleteDeviceObject()
431 if (gOvsDeviceHandle) {
433 POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
434 NdisGetDeviceReservedExtension(gOvsDeviceObject);
436 ASSERT(ovsExt->numberOpenInstance == 0);
440 ASSERT(gOvsDeviceObject);
441 NdisDeregisterDeviceEx(gOvsDeviceHandle);
442 gOvsDeviceHandle = NULL;
443 gOvsDeviceObject = NULL;
449 OvsGetOpenInstance(PFILE_OBJECT fileObject,
452 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
454 ASSERT(instance->fileObject == fileObject);
455 if (gOvsSwitchContext == NULL ||
456 gOvsSwitchContext->dpNo != dpNo) {
464 OvsFindOpenInstance(PFILE_OBJECT fileObject)
467 for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
468 j < ovsNumberOfOpenInstances; i++) {
469 if (ovsOpenInstanceArray[i]) {
470 if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
471 return ovsOpenInstanceArray[i];
480 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
481 PFILE_OBJECT fileObject)
483 POVS_OPEN_INSTANCE instance =
484 (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
487 if (instance == NULL) {
488 return STATUS_NO_MEMORY;
490 OvsAcquireCtrlLock();
491 ASSERT(OvsFindOpenInstance(fileObject) == NULL);
493 if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
494 OvsReleaseCtrlLock();
495 OvsFreeMemory(instance);
496 return STATUS_INSUFFICIENT_RESOURCES;
498 RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
500 for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
501 if (ovsOpenInstanceArray[i] == NULL) {
502 ovsOpenInstanceArray[i] = instance;
503 ovsNumberOfOpenInstances++;
504 instance->cookie = i;
508 ASSERT(i < OVS_MAX_OPEN_INSTANCES);
509 instance->fileObject = fileObject;
510 ASSERT(fileObject->FsContext == NULL);
511 instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
512 if (instance->pid == 0) {
513 /* XXX: check for rollover. */
515 fileObject->FsContext = instance;
516 OvsReleaseCtrlLock();
517 return STATUS_SUCCESS;
521 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
523 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
525 ASSERT(fileObject == instance->fileObject);
526 OvsCleanupEvent(instance);
527 OvsCleanupPacketQueue(instance);
531 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
533 POVS_OPEN_INSTANCE instance;
534 ASSERT(fileObject->FsContext);
535 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
536 ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
538 OvsAcquireCtrlLock();
539 fileObject->FsContext = NULL;
540 ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
541 ovsOpenInstanceArray[instance->cookie] = NULL;
542 ovsNumberOfOpenInstances--;
543 OvsReleaseCtrlLock();
544 ASSERT(instance->eventQueue == NULL);
545 ASSERT (instance->packetQueue == NULL);
546 OvsFreeMemory(instance);
550 OvsCompleteIrpRequest(PIRP irp,
554 irp->IoStatus.Information = infoPtr;
555 irp->IoStatus.Status = status;
556 IoCompleteRequest(irp, IO_NO_INCREMENT);
562 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
565 PIO_STACK_LOCATION irpSp;
566 NTSTATUS status = STATUS_SUCCESS;
567 PFILE_OBJECT fileObject;
568 POVS_DEVICE_EXTENSION ovsExt =
569 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
571 ASSERT(deviceObject == gOvsDeviceObject);
572 ASSERT(ovsExt != NULL);
574 irpSp = IoGetCurrentIrpStackLocation(irp);
575 fileObject = irpSp->FileObject;
576 OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
577 deviceObject, fileObject,
578 ovsExt->numberOpenInstance);
580 switch (irpSp->MajorFunction) {
582 status = OvsAddOpenInstance(ovsExt, fileObject);
583 if (STATUS_SUCCESS == status) {
584 InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
588 ASSERT(ovsExt->numberOpenInstance > 0);
589 OvsRemoveOpenInstance(fileObject);
590 InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
595 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
598 _Use_decl_annotations_
600 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
604 PIO_STACK_LOCATION irpSp;
605 PFILE_OBJECT fileObject;
607 NTSTATUS status = STATUS_SUCCESS;
609 POVS_DEVICE_EXTENSION ovsExt =
610 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
612 ASSERT(ovsExt->numberOpenInstance > 0);
615 UNREFERENCED_PARAMETER(deviceObject);
617 ASSERT(deviceObject == gOvsDeviceObject);
618 irpSp = IoGetCurrentIrpStackLocation(irp);
619 fileObject = irpSp->FileObject;
621 ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
623 OvsCleanupOpenInstance(fileObject);
625 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
630 * --------------------------------------------------------------------------
631 * IOCTL function handler for the device.
632 * --------------------------------------------------------------------------
635 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
639 PIO_STACK_LOCATION irpSp;
640 NTSTATUS status = STATUS_SUCCESS;
641 PFILE_OBJECT fileObject;
642 PVOID inputBuffer = NULL;
643 PVOID outputBuffer = NULL;
644 UINT32 inputBufferLen, outputBufferLen;
645 UINT32 code, replyLen = 0;
646 POVS_OPEN_INSTANCE instance;
648 OVS_MESSAGE ovsMsgReadOp;
650 NETLINK_FAMILY *nlFamilyOps;
651 OVS_USER_PARAMS_CONTEXT usrParamsCtx;
654 POVS_DEVICE_EXTENSION ovsExt =
655 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
656 ASSERT(deviceObject == gOvsDeviceObject);
658 ASSERT(ovsExt->numberOpenInstance > 0);
660 UNREFERENCED_PARAMETER(deviceObject);
663 irpSp = IoGetCurrentIrpStackLocation(irp);
665 ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
666 ASSERT(irpSp->FileObject != NULL);
668 fileObject = irpSp->FileObject;
669 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
670 code = irpSp->Parameters.DeviceIoControl.IoControlCode;
671 inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
672 outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
673 inputBuffer = irp->AssociatedIrp.SystemBuffer;
675 /* Concurrent netlink operations are not supported. */
676 if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
677 status = STATUS_RESOURCE_IN_USE;
682 * Validate the input/output buffer arguments depending on the type of the
686 case OVS_IOCTL_TRANSACT:
687 /* Input buffer is mandatory, output buffer is optional. */
688 if (outputBufferLen != 0) {
689 status = MapIrpOutputBuffer(irp, outputBufferLen,
690 sizeof *ovsMsg, &outputBuffer);
691 if (status != STATUS_SUCCESS) {
694 ASSERT(outputBuffer);
697 if (inputBufferLen < sizeof (*ovsMsg)) {
698 status = STATUS_NDIS_INVALID_LENGTH;
702 ovsMsg = inputBuffer;
703 devOp = OVS_TRANSACTION_DEV_OP;
706 case OVS_IOCTL_READ_EVENT:
707 /* This IOCTL is used to read events */
708 if (outputBufferLen != 0) {
709 status = MapIrpOutputBuffer(irp, outputBufferLen,
710 sizeof *ovsMsg, &outputBuffer);
711 if (status != STATUS_SUCCESS) {
714 ASSERT(outputBuffer);
716 status = STATUS_NDIS_INVALID_LENGTH;
722 ovsMsg = &ovsMsgReadOp;
723 ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
724 /* An "artificial" command so we can use NL family function table*/
725 ovsMsg->genlMsg.cmd = OVS_CTRL_CMD_EVENT_NOTIFY;
726 devOp = OVS_READ_DEV_OP;
730 /* Output buffer is mandatory. */
731 if (outputBufferLen != 0) {
732 status = MapIrpOutputBuffer(irp, outputBufferLen,
733 sizeof *ovsMsg, &outputBuffer);
734 if (status != STATUS_SUCCESS) {
737 ASSERT(outputBuffer);
739 status = STATUS_NDIS_INVALID_LENGTH;
744 * Operate in the mode that read ioctl is similar to ReadFile(). This
745 * might change as the userspace code gets implemented.
751 * For implementing read (ioctl or otherwise), we need to store some
752 * state in the instance to indicate the command that started the dump
753 * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
754 * that 'ovsMsgReadOp' is needed only in this function to call into the
755 * appropraite handler. The handler itself can access the state in the
758 * In the absence of a dump start, return 0 bytes.
760 if (instance->dumpState.ovsMsg == NULL) {
762 status = STATUS_SUCCESS;
765 RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
766 sizeof (ovsMsgReadOp));
768 /* Create an NL message for consumption. */
769 ovsMsg = &ovsMsgReadOp;
770 devOp = OVS_READ_DEV_OP;
774 case OVS_IOCTL_WRITE:
775 /* Input buffer is mandatory. */
776 if (inputBufferLen < sizeof (*ovsMsg)) {
777 status = STATUS_NDIS_INVALID_LENGTH;
781 ovsMsg = inputBuffer;
782 devOp = OVS_WRITE_DEV_OP;
786 status = STATUS_INVALID_DEVICE_REQUEST;
791 switch (ovsMsg->nlMsg.nlmsgType) {
792 case OVS_WIN_NL_CTRL_FAMILY_ID:
793 nlFamilyOps = &nlControlFamilyOps;
795 case OVS_WIN_NL_DATAPATH_FAMILY_ID:
796 nlFamilyOps = &nlDatapathFamilyOps;
798 case OVS_WIN_NL_FLOW_FAMILY_ID:
799 nlFamilyOps = &nlFLowFamilyOps;
801 case OVS_WIN_NL_PACKET_FAMILY_ID:
802 nlFamilyOps = &nlPacketFamilyOps;
804 case OVS_WIN_NL_VPORT_FAMILY_ID:
805 nlFamilyOps = &nlVportFamilyOps;
807 case OVS_WIN_NL_NETDEV_FAMILY_ID:
808 nlFamilyOps = &nlNetdevFamilyOps;
811 status = STATUS_INVALID_PARAMETER;
816 * For read operation, the netlink command has already been validated
819 if (devOp != OVS_READ_DEV_OP) {
820 status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
821 if (status != STATUS_SUCCESS) {
826 InitUserParamsCtx(irp, instance, devOp, ovsMsg,
827 inputBuffer, inputBufferLen,
828 outputBuffer, outputBufferLen,
831 status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
836 return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
841 * --------------------------------------------------------------------------
842 * Function to validate a netlink command. Only certain combinations of
843 * (device operation, netlink family, command) are valid.
844 * --------------------------------------------------------------------------
847 ValidateNetlinkCmd(UINT32 devOp,
848 POVS_OPEN_INSTANCE instance,
850 NETLINK_FAMILY *nlFamilyOps)
852 NTSTATUS status = STATUS_INVALID_PARAMETER;
855 for (i = 0; i < nlFamilyOps->opsCount; i++) {
856 if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
857 /* Validate if the command is valid for the device operation. */
858 if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
859 status = STATUS_INVALID_PARAMETER;
863 /* Validate the version. */
864 if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
865 status = STATUS_INVALID_PARAMETER;
869 /* Validate the DP for commands that require a DP. */
870 if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
871 OvsAcquireCtrlLock();
872 if (ovsMsg->ovsHdr.dp_ifindex !=
873 (INT)gOvsSwitchContext->dpNo) {
874 status = STATUS_INVALID_PARAMETER;
875 OvsReleaseCtrlLock();
878 OvsReleaseCtrlLock();
881 /* Validate the PID. */
882 if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
883 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
884 status = STATUS_INVALID_PARAMETER;
889 status = STATUS_SUCCESS;
899 * --------------------------------------------------------------------------
900 * Function to invoke the netlink command handler.
901 * --------------------------------------------------------------------------
904 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
905 NETLINK_FAMILY *nlFamilyOps,
908 NTSTATUS status = STATUS_INVALID_PARAMETER;
911 for (i = 0; i < nlFamilyOps->opsCount; i++) {
912 if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
913 NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
916 status = handler(usrParamsCtx, replyLen);
926 * --------------------------------------------------------------------------
927 * Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
929 * Each handle on the device is assigned a unique PID when the handle is
930 * created. On platforms that support netlink natively, the PID is available
931 * to userspace when the netlink socket is created. However, without native
932 * netlink support on Windows, OVS datapath generates the PID and lets the
933 * userspace query it.
935 * This function implements the query.
936 * --------------------------------------------------------------------------
939 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
942 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
943 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
945 if (usrParamsCtx->outputLength >= sizeof *msgOut) {
946 POVS_OPEN_INSTANCE instance =
947 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
949 RtlZeroMemory(msgOut, sizeof *msgOut);
950 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
951 msgOut->nlMsg.nlmsgPid = instance->pid;
952 *replyLen = sizeof *msgOut;
953 /* XXX: We might need to return the DP index as well. */
955 return STATUS_NDIS_INVALID_LENGTH;
958 return STATUS_SUCCESS;
962 * --------------------------------------------------------------------------
963 * Utility function to fill up information about the datapath in a reply to
965 * Assumes that 'gOvsCtrlLock' lock is acquired.
966 * --------------------------------------------------------------------------
969 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
974 OVS_MESSAGE msgOutTmp;
975 OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
978 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && NlBufRemLen(nlBuf) >= sizeof *msgIn);
980 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
981 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
982 msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
983 msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
985 msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
986 msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
987 msgOutTmp.genlMsg.reserved = 0;
989 msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
991 writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
993 writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
997 OVS_DP_STATS dpStats;
999 dpStats.n_hit = datapath->hits;
1000 dpStats.n_missed = datapath->misses;
1001 dpStats.n_lost = datapath->lost;
1002 dpStats.n_flows = datapath->nFlows;
1003 writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
1004 (PCHAR)&dpStats, sizeof dpStats);
1006 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1007 nlMsg->nlmsgLen = NlBufSize(nlBuf);
1009 return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
1013 * --------------------------------------------------------------------------
1014 * Handler for queueing an IRP used for event notification. The IRP is
1015 * completed when a port state changes. STATUS_PENDING is returned on
1016 * success. User mode keep a pending IRP at all times.
1017 * --------------------------------------------------------------------------
1020 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1025 UNREFERENCED_PARAMETER(replyLen);
1027 POVS_OPEN_INSTANCE instance =
1028 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1029 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1030 OVS_EVENT_POLL poll;
1032 poll.dpNo = msgIn->ovsHdr.dp_ifindex;
1033 status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
1034 &poll, sizeof poll);
1039 * --------------------------------------------------------------------------
1040 * Handler for the subscription for the event queue
1041 * --------------------------------------------------------------------------
1044 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1048 OVS_EVENT_SUBSCRIBE request;
1052 const NL_POLICY policy[] = {
1053 [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
1054 [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
1057 UNREFERENCED_PARAMETER(replyLen);
1059 POVS_OPEN_INSTANCE instance =
1060 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1061 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1063 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1064 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, 2);
1066 status = STATUS_INVALID_PARAMETER;
1070 /* XXX Ignore the MC group for now */
1071 join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1072 request.dpNo = msgIn->ovsHdr.dp_ifindex;
1073 request.subscribe = join;
1074 request.mask = OVS_EVENT_MASK_ALL;
1076 status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1083 * --------------------------------------------------------------------------
1084 * Command Handler for 'OVS_DP_CMD_NEW'.
1085 * --------------------------------------------------------------------------
1088 OvsNewDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1091 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1095 * --------------------------------------------------------------------------
1096 * Command Handler for 'OVS_DP_CMD_GET'.
1098 * The function handles both the dump based as well as the transaction based
1099 * 'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1100 * call to setup dump state, as well as subsequent calls to continue dumping
1102 * --------------------------------------------------------------------------
1105 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1108 if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1109 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1111 return HandleGetDpDump(usrParamsCtx, replyLen);
1116 * --------------------------------------------------------------------------
1117 * Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1118 * --------------------------------------------------------------------------
1121 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1124 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1129 * --------------------------------------------------------------------------
1130 * Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1131 * --------------------------------------------------------------------------
1134 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1137 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1138 POVS_OPEN_INSTANCE instance =
1139 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1141 if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1143 OvsSetupDumpStart(usrParamsCtx);
1147 POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1149 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1151 if (instance->dumpState.ovsMsg == NULL) {
1153 return STATUS_INVALID_DEVICE_STATE;
1156 /* Dump state must have been deleted after previous dump operation. */
1157 ASSERT(instance->dumpState.index[0] == 0);
1158 /* Output buffer has been validated while validating read dev op. */
1159 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1161 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1162 usrParamsCtx->outputLength);
1164 OvsAcquireCtrlLock();
1165 if (!gOvsSwitchContext) {
1166 /* Treat this as a dump done. */
1167 OvsReleaseCtrlLock();
1169 FreeUserDumpState(instance);
1170 return STATUS_SUCCESS;
1172 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1173 OvsReleaseCtrlLock();
1175 if (status != STATUS_SUCCESS) {
1177 FreeUserDumpState(instance);
1181 /* Increment the dump index. */
1182 instance->dumpState.index[0] = 1;
1183 *replyLen = msgOut->nlMsg.nlmsgLen;
1185 /* Free up the dump state, since there's no more data to continue. */
1186 FreeUserDumpState(instance);
1189 return STATUS_SUCCESS;
1194 * --------------------------------------------------------------------------
1195 * Command Handler for 'OVS_DP_CMD_SET'.
1196 * --------------------------------------------------------------------------
1199 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1202 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1206 * --------------------------------------------------------------------------
1207 * Function for handling transaction based 'OVS_DP_CMD_NEW', 'OVS_DP_CMD_GET'
1208 * and 'OVS_DP_CMD_SET' commands.
1210 * 'OVS_DP_CMD_NEW' is implemented to keep userspace code happy. Creation of a
1211 * new datapath is not supported currently.
1212 * --------------------------------------------------------------------------
1215 HandleDpTransactionCommon(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1218 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1219 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1220 NTSTATUS status = STATUS_SUCCESS;
1222 NL_ERROR nlError = NL_ERROR_SUCCESS;
1223 static const NL_POLICY ovsDatapathSetPolicy[] = {
1224 [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1225 [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1226 [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1228 PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1230 /* input buffer has been validated while validating write dev op. */
1231 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1233 /* Parse any attributes in the request. */
1234 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET ||
1235 usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1236 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1237 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1238 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1239 ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1240 return STATUS_INVALID_PARAMETER;
1244 * XXX: Not clear at this stage if there's any role for the
1245 * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1250 RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1253 /* Output buffer is optional for OVS_TRANSACTION_DEV_OP. */
1254 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1255 return STATUS_NDIS_INVALID_LENGTH;
1257 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1259 OvsAcquireCtrlLock();
1260 if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1261 if (!gOvsSwitchContext &&
1262 !OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1263 OVS_SYSTEM_DP_NAME)) {
1264 OvsReleaseCtrlLock();
1266 /* Creation of new datapaths is not supported. */
1267 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1268 nlError = NL_ERROR_NOTSUPP;
1272 nlError = NL_ERROR_NODEV;
1275 } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1276 OvsReleaseCtrlLock();
1277 nlError = NL_ERROR_NODEV;
1281 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1282 OvsReleaseCtrlLock();
1283 nlError = NL_ERROR_EXIST;
1287 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1288 OvsReleaseCtrlLock();
1290 *replyLen = NlBufSize(&nlBuf);
1293 if (nlError != NL_ERROR_SUCCESS) {
1294 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1295 usrParamsCtx->outputBuffer;
1297 BuildErrorMsg(msgIn, msgError, nlError);
1298 *replyLen = msgError->nlMsg.nlmsgLen;
1301 return STATUS_SUCCESS;
1306 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1308 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1309 POVS_OPEN_INSTANCE instance =
1310 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1312 /* input buffer has been validated while validating write dev op. */
1313 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1315 /* A write operation that does not indicate dump start is invalid. */
1316 if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1317 return STATUS_INVALID_PARAMETER;
1319 /* XXX: Handle other NLM_F_* flags in the future. */
1322 * This operation should be setting up the dump state. If there's any
1323 * previous state, clear it up so as to set it up afresh.
1325 if (instance->dumpState.ovsMsg != NULL) {
1326 FreeUserDumpState(instance);
1329 return InitUserDumpState(instance, msgIn);
1333 BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
1334 UINT32 length, UINT16 flags)
1336 msgOut->nlMsg.nlmsgType = type;
1337 msgOut->nlMsg.nlmsgFlags = flags;
1338 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1339 msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1340 msgOut->nlMsg.nlmsgLen = length;
1342 msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
1343 msgOut->genlMsg.version = msgIn->genlMsg.version;
1344 msgOut->genlMsg.reserved = 0;
1348 * XXX: should move out these functions to a Netlink.c or to a OvsMessage.c
1349 * or even make them inlined functions in Datapath.h. Can be done after the
1350 * first sprint once we have more code to refactor.
1353 BuildReplyMsgFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
1355 BuildMsgOut(msgIn, msgOut, msgIn->nlMsg.nlmsgType, sizeof(OVS_MESSAGE),
1360 BuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode)
1362 BuildMsgOut(msgIn, (POVS_MESSAGE)msgOut, NLMSG_ERROR,
1363 sizeof(OVS_MESSAGE_ERROR), 0);
1365 msgOut->errorMsg.error = errorCode;
1366 msgOut->errorMsg.nlMsg = msgIn->nlMsg;
1370 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1377 OVS_VPORT_FULL_STATS vportStats;
1382 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1384 BuildReplyMsgFromMsgIn(msgIn, &msgOut, NLM_F_MULTI);
1385 msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1387 ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1389 return STATUS_INSUFFICIENT_RESOURCES;
1392 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1394 return STATUS_INSUFFICIENT_RESOURCES;
1397 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1399 return STATUS_INSUFFICIENT_RESOURCES;
1402 ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1404 return STATUS_INSUFFICIENT_RESOURCES;
1408 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1409 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1410 * it means we have an array of pids, instead of a single pid.
1411 * ATM we assume we have one pid only.
1414 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1417 return STATUS_INSUFFICIENT_RESOURCES;
1421 vportStats.rxPackets = vport->stats.rxPackets;
1422 vportStats.rxBytes = vport->stats.rxBytes;
1423 vportStats.txPackets = vport->stats.txPackets;
1424 vportStats.txBytes = vport->stats.txBytes;
1425 vportStats.rxErrors = vport->errStats.rxErrors;
1426 vportStats.txErrors = vport->errStats.txErrors;
1427 vportStats.rxDropped = vport->errStats.rxDropped;
1428 vportStats.txDropped = vport->errStats.txDropped;
1430 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1432 sizeof(OVS_VPORT_FULL_STATS));
1434 return STATUS_INSUFFICIENT_RESOURCES;
1438 * XXX: when vxlan udp dest port becomes configurable, we will also need
1439 * to add vport options
1442 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1443 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1445 return STATUS_SUCCESS;
1449 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1453 POVS_OPEN_INSTANCE instance =
1454 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1455 LOCK_STATE_EX lockState;
1456 UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1459 * XXX: this function shares some code with other dump command(s).
1460 * In the future, we will need to refactor the dump functions
1463 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1465 if (instance->dumpState.ovsMsg == NULL) {
1467 return STATUS_INVALID_DEVICE_STATE;
1470 /* Output buffer has been validated while validating read dev op. */
1471 ASSERT(usrParamsCtx->outputBuffer != NULL);
1473 msgIn = instance->dumpState.ovsMsg;
1475 OvsAcquireCtrlLock();
1476 if (!gOvsSwitchContext) {
1477 /* Treat this as a dump done. */
1478 OvsReleaseCtrlLock();
1480 FreeUserDumpState(instance);
1481 return STATUS_SUCCESS;
1485 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1486 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1487 * it means we have an array of pids, instead of a single pid.
1488 * ATM we assume we have one pid only.
1490 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1491 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1492 NDIS_RWL_AT_DISPATCH_LEVEL);
1494 if (gOvsSwitchContext->numVports > 0) {
1495 /* inBucket: the bucket, used for lookup */
1496 UINT32 inBucket = instance->dumpState.index[0];
1497 /* inIndex: index within the given bucket, used for lookup */
1498 UINT32 inIndex = instance->dumpState.index[1];
1499 /* the bucket to be used for the next dump operation */
1500 UINT32 outBucket = 0;
1501 /* the index within the outBucket to be used for the next dump */
1502 UINT32 outIndex = 0;
1504 for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1505 PLIST_ENTRY head, link;
1506 head = &(gOvsSwitchContext->portIdHashArray[i]);
1507 POVS_VPORT_ENTRY vport = NULL;
1510 LIST_FORALL(head, link) {
1513 * if one or more dumps were previously done on this same bucket,
1514 * inIndex will be > 0, so we'll need to reply with the
1515 * inIndex + 1 vport from the bucket.
1517 if (outIndex >= inIndex) {
1518 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1520 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
1521 OvsCreateMsgFromVport(vport, msgIn,
1522 usrParamsCtx->outputBuffer,
1523 usrParamsCtx->outputLength,
1524 gOvsSwitchContext->dpNo);
1540 * if no vport was found above, check the next bucket, beginning
1541 * with the first (i.e. index 0) elem from within that bucket
1548 /* XXX: what about NLMSG_DONE (as msg type)? */
1549 instance->dumpState.index[0] = outBucket;
1550 instance->dumpState.index[1] = outIndex;
1553 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1555 OvsReleaseCtrlLock();
1557 /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1558 if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1559 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1560 *replyLen = msgOut->nlMsg.nlmsgLen;
1563 * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1567 /* Free up the dump state, since there's no more data to continue. */
1568 FreeUserDumpState(instance);
1571 return STATUS_SUCCESS;
1575 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1578 NTSTATUS status = STATUS_SUCCESS;
1579 LOCK_STATE_EX lockState;
1581 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1582 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1583 POVS_VPORT_ENTRY vport = NULL;
1584 NL_ERROR nlError = NL_ERROR_SUCCESS;
1585 PCHAR portName = NULL;
1586 UINT32 portNameLen = 0;
1587 UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
1589 static const NL_POLICY ovsVportPolicy[] = {
1590 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1591 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1596 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1598 /* input buffer has been validated while validating write dev op. */
1599 ASSERT(usrParamsCtx->inputBuffer != NULL);
1601 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1602 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1603 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1604 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1605 return STATUS_INVALID_PARAMETER;
1608 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1609 return STATUS_INVALID_BUFFER_SIZE;
1612 OvsAcquireCtrlLock();
1613 if (!gOvsSwitchContext) {
1614 OvsReleaseCtrlLock();
1615 return STATUS_INVALID_PARAMETER;
1617 OvsReleaseCtrlLock();
1619 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1620 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1621 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1622 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1624 /* the port name is expected to be null-terminated */
1625 ASSERT(portName[portNameLen - 1] == '\0');
1627 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1628 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1629 portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1631 vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
1633 nlError = NL_ERROR_INVAL;
1634 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1639 nlError = NL_ERROR_NODEV;
1640 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1644 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1645 usrParamsCtx->outputLength,
1646 gOvsSwitchContext->dpNo);
1647 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1649 *replyLen = msgOut->nlMsg.nlmsgLen;
1652 if (nlError != NL_ERROR_SUCCESS) {
1653 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1654 usrParamsCtx->outputBuffer;
1656 BuildErrorMsg(msgIn, msgError, nlError);
1657 *replyLen = msgError->nlMsg.nlmsgLen;
1660 return STATUS_SUCCESS;
1664 * --------------------------------------------------------------------------
1665 * Handler for the get vport command. The function handles the initial call to
1666 * setup the dump state, as well as subsequent calls to continue dumping data.
1667 * --------------------------------------------------------------------------
1670 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1675 switch (usrParamsCtx->devOp)
1677 case OVS_WRITE_DEV_OP:
1678 return OvsSetupDumpStart(usrParamsCtx);
1680 case OVS_READ_DEV_OP:
1681 return OvsGetVportDumpNext(usrParamsCtx, replyLen);
1683 case OVS_TRANSACTION_DEV_OP:
1684 return OvsGetVport(usrParamsCtx, replyLen);
1687 return STATUS_INVALID_DEVICE_REQUEST;
1695 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
1697 /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
1698 for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
1699 POVS_VPORT_ENTRY vport;
1701 vport = OvsFindVportByPortNo(switchContext, i);
1707 return OVS_DPPORT_NUMBER_INVALID;
1711 * --------------------------------------------------------------------------
1712 * Command Handler for 'OVS_VPORT_CMD_NEW'.
1713 * --------------------------------------------------------------------------
1716 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1719 NDIS_STATUS status = STATUS_SUCCESS;
1720 LOCK_STATE_EX lockState;
1722 NL_ERROR nlError = NL_ERROR_SUCCESS;
1723 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1724 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1725 POVS_VPORT_ENTRY vport = NULL;
1731 static const NL_POLICY ovsVportPolicy[] = {
1732 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1733 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
1734 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
1736 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
1737 .optional = FALSE },
1738 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
1741 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1743 /* input buffer has been validated while validating write dev op. */
1744 ASSERT(usrParamsCtx->inputBuffer != NULL);
1746 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1747 return STATUS_INVALID_BUFFER_SIZE;
1750 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1751 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1752 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1753 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1754 return STATUS_INVALID_PARAMETER;
1757 OvsAcquireCtrlLock();
1758 if (!gOvsSwitchContext) {
1759 OvsReleaseCtrlLock();
1760 return STATUS_INVALID_PARAMETER;
1762 OvsReleaseCtrlLock();
1764 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1765 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1766 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
1768 /* we are expecting null terminated strings to be passed */
1769 ASSERT(portName[portNameLen - 1] == '\0');
1771 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
1773 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1775 nlError = NL_ERROR_EXIST;
1779 if (portType == OVS_VPORT_TYPE_INTERNAL) {
1780 vport = gOvsSwitchContext->internalVport;
1781 } else if (portType == OVS_VPORT_TYPE_NETDEV) {
1782 if (!strcmp(portName, "external")) {
1783 vport = gOvsSwitchContext->externalVport;
1785 vport = OvsFindVportByHvName(gOvsSwitchContext, portName);
1788 /* XXX: change when other tunneling ports are added */
1789 ASSERT(portType == OVS_VPORT_TYPE_VXLAN);
1791 if (gOvsSwitchContext->vxlanVport) {
1792 nlError = NL_ERROR_EXIST;
1796 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
1797 if (vport == NULL) {
1798 nlError = NL_ERROR_NOMEM;
1802 vport->ovsState = OVS_STATE_PORT_CREATED;
1805 * XXX: when we allow configuring the vxlan udp port, we should read
1806 * this from vport->options instead!
1808 nlError = OvsInitVxlanTunnel(vport, VXLAN_UDP_PORT);
1809 if (nlError != NL_ERROR_SUCCESS) {
1815 nlError = NL_ERROR_INVAL;
1819 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
1820 nlError = NL_ERROR_EXIST;
1824 /* Fill the data in vport */
1825 vport->ovsType = portType;
1827 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1829 * XXX: when we implement the limit for ovs port number to be
1830 * MAXUINT16, we'll need to check the port number received from the
1833 vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1835 vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
1836 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1837 nlError = NL_ERROR_NOMEM;
1842 /* The ovs port name must be uninitialized. */
1843 ASSERT(vport->ovsName[0] == '\0');
1844 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
1846 RtlCopyMemory(vport->ovsName, portName, portNameLen);
1848 /* if we don't have options, then vport->portOptions will be NULL */
1849 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
1852 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1853 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1854 * it means we have an array of pids, instead of a single pid.
1855 * ATM we assume we have one pid only.
1857 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
1859 if (vport->ovsType == OVS_VPORT_TYPE_VXLAN) {
1860 gOvsSwitchContext->vxlanVport = vport;
1861 } else if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
1862 gOvsSwitchContext->internalVport = vport;
1863 gOvsSwitchContext->internalPortId = vport->portId;
1864 } else if (vport->ovsType == OVS_VPORT_TYPE_NETDEV &&
1865 vport->isExternal) {
1866 gOvsSwitchContext->externalVport = vport;
1867 gOvsSwitchContext->externalPortId = vport->portId;
1871 * insert the port into the hash array of ports: by port number and ovs
1872 * and ovs (datapath) port name.
1873 * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1874 * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1876 hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
1877 InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
1878 &vport->portNoLink);
1880 hash = OvsJhashBytes(vport->ovsName, portNameLen, OVS_HASH_BASIS);
1881 InsertHeadList(&gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
1882 &vport->ovsNameLink);
1884 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1885 usrParamsCtx->outputLength,
1886 gOvsSwitchContext->dpNo);
1888 *replyLen = msgOut->nlMsg.nlmsgLen;
1891 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1893 if (nlError != NL_ERROR_SUCCESS) {
1894 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1895 usrParamsCtx->outputBuffer;
1897 if (vport && vport->ovsType == OVS_VPORT_TYPE_VXLAN) {
1898 OvsRemoveAndDeleteVport(gOvsSwitchContext, vport);
1901 BuildErrorMsg(msgIn, msgError, nlError);
1902 *replyLen = msgError->nlMsg.nlmsgLen;
1905 return STATUS_SUCCESS;
1910 * --------------------------------------------------------------------------
1911 * Command Handler for 'OVS_VPORT_CMD_SET'.
1912 * --------------------------------------------------------------------------
1915 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1918 NDIS_STATUS status = STATUS_SUCCESS;
1919 LOCK_STATE_EX lockState;
1921 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1922 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1923 POVS_VPORT_ENTRY vport = NULL;
1924 NL_ERROR nlError = NL_ERROR_SUCCESS;
1926 static const NL_POLICY ovsVportPolicy[] = {
1927 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1928 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
1929 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
1931 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
1933 [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
1934 .minLen = sizeof(OVS_VPORT_FULL_STATS),
1935 .maxLen = sizeof(OVS_VPORT_FULL_STATS),
1937 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
1939 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1941 ASSERT(usrParamsCtx->inputBuffer != NULL);
1943 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1944 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1945 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1946 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1947 return STATUS_INVALID_PARAMETER;
1950 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1951 return STATUS_NDIS_INVALID_LENGTH;
1954 OvsAcquireCtrlLock();
1955 if (!gOvsSwitchContext) {
1956 OvsReleaseCtrlLock();
1957 return STATUS_INVALID_PARAMETER;
1960 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
1961 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1962 PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1963 UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1965 /* the port name is expected to be null-terminated */
1966 ASSERT(portName[portNameLen - 1] == '\0');
1968 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1969 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1970 vport = OvsFindVportByPortNo(gOvsSwitchContext,
1971 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
1975 nlError = NL_ERROR_NODEV;
1980 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1981 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1982 * it means we have an array of pids, instead of a single pid.
1983 * Currently, we support only one pid.
1985 if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
1986 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
1989 if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
1990 OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
1991 if (type != vport->ovsType) {
1992 nlError = NL_ERROR_INVAL;
1997 if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
1998 OVS_LOG_ERROR("Vport options not supported");
1999 nlError = NL_ERROR_NOTSUPP;
2003 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2004 usrParamsCtx->outputLength,
2005 gOvsSwitchContext->dpNo);
2007 *replyLen = msgOut->nlMsg.nlmsgLen;
2010 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2011 OvsReleaseCtrlLock();
2013 if (nlError != NL_ERROR_SUCCESS) {
2014 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2015 usrParamsCtx->outputBuffer;
2017 BuildErrorMsg(msgIn, msgError, nlError);
2018 *replyLen = msgError->nlMsg.nlmsgLen;
2021 return STATUS_SUCCESS;
2025 * --------------------------------------------------------------------------
2026 * Command Handler for 'OVS_VPORT_CMD_DEL'.
2027 * --------------------------------------------------------------------------
2030 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2033 NDIS_STATUS status = STATUS_SUCCESS;
2034 LOCK_STATE_EX lockState;
2036 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2037 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2038 POVS_VPORT_ENTRY vport = NULL;
2039 NL_ERROR nlError = NL_ERROR_SUCCESS;
2040 PSTR portName = NULL;
2041 UINT32 portNameLen = 0;
2043 static const NL_POLICY ovsVportPolicy[] = {
2044 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2045 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ, .optional = TRUE },
2047 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2049 ASSERT(usrParamsCtx->inputBuffer != NULL);
2051 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2052 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2053 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2054 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2055 return STATUS_INVALID_PARAMETER;
2058 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
2059 return STATUS_NDIS_INVALID_LENGTH;
2062 OvsAcquireCtrlLock();
2063 if (!gOvsSwitchContext) {
2064 OvsReleaseCtrlLock();
2065 return STATUS_INVALID_PARAMETER;
2067 OvsReleaseCtrlLock();
2069 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2070 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2071 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2072 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2074 /* the port name is expected to be null-terminated */
2075 ASSERT(portName[portNameLen - 1] == '\0');
2077 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2079 else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2080 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2081 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2085 nlError = NL_ERROR_NODEV;
2089 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2090 usrParamsCtx->outputLength,
2091 gOvsSwitchContext->dpNo);
2093 if (vport->hvDeleted || OvsIsTunnelVportType(vport->ovsType)) {
2095 * The associated hyper-v switch port is not in created state, or,
2096 * there is no hyper-v switch port counterpart (for logical ports).
2097 * This means that this datapath port is not mapped to a living
2098 * hyper-v switc hport. We can destroy and remove the vport from the
2101 OvsRemoveAndDeleteVport(gOvsSwitchContext, vport);
2103 /* The associated hyper-v switch port is in the created state, and the
2104 * datapath port is mapped to a living hyper-v switch port. We cannot
2105 * destroy the vport and cannot remove it from the list of vports.
2106 * Instead, we mark the datapath (ovs) part of the vport as
2107 * "not created", i.e. we set vport->portNo = OVS_PORT_NUMBER_INVALID.
2109 RemoveEntryList(&vport->ovsNameLink);
2110 RemoveEntryList(&vport->portNoLink);
2111 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
2112 vport->ovsName[0] = '\0';
2115 *replyLen = msgOut->nlMsg.nlmsgLen;
2118 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2120 if (nlError != NL_ERROR_SUCCESS) {
2121 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2122 usrParamsCtx->outputBuffer;
2124 BuildErrorMsg(msgIn, msgError, nlError);
2125 *replyLen = msgError->nlMsg.nlmsgLen;
2128 return STATUS_SUCCESS;
2133 * --------------------------------------------------------------------------
2134 * Utility function to map the output buffer in an IRP. The buffer is assumed
2135 * to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
2136 * --------------------------------------------------------------------------
2139 MapIrpOutputBuffer(PIRP irp,
2140 UINT32 bufferLength,
2141 UINT32 requiredLength,
2146 ASSERT(bufferLength);
2147 ASSERT(requiredLength);
2148 if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
2149 return STATUS_INVALID_PARAMETER;
2152 if (bufferLength < requiredLength) {
2153 return STATUS_NDIS_INVALID_LENGTH;
2155 if (irp->MdlAddress == NULL) {
2156 return STATUS_INVALID_PARAMETER;
2158 *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
2159 NormalPagePriority);
2160 if (*buffer == NULL) {
2161 return STATUS_INSUFFICIENT_RESOURCES;
2164 return STATUS_SUCCESS;
2168 * --------------------------------------------------------------------------
2169 * Utility function to fill up information about the state of a port in a reply
2171 * Assumes that 'gOvsCtrlLock' lock is acquired.
2172 * --------------------------------------------------------------------------
2175 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2176 POVS_EVENT_ENTRY eventEntry,
2181 OVS_MESSAGE msgOutTmp;
2183 POVS_VPORT_ENTRY vport;
2185 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
2187 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
2188 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
2190 /* driver intiated messages should have zerp seq number*/
2191 msgOutTmp.nlMsg.nlmsgSeq = 0;
2192 msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
2194 msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
2195 msgOutTmp.genlMsg.reserved = 0;
2197 /* we don't have netdev yet, treat link up/down a adding/removing a port*/
2198 if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
2199 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
2200 } else if (eventEntry->status &
2201 (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
2202 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
2205 return STATUS_UNSUCCESSFUL;
2207 msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
2209 rc = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
2211 status = STATUS_INVALID_BUFFER_SIZE;
2215 vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
2217 status = STATUS_DEVICE_DOES_NOT_EXIST;
2221 rc = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) ||
2222 NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) ||
2223 NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
2225 status = STATUS_INVALID_BUFFER_SIZE;
2229 /* XXXX Should we add the port stats attributes?*/
2230 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
2231 nlMsg->nlmsgLen = NlBufSize(nlBuf);
2232 status = STATUS_SUCCESS;
2240 * --------------------------------------------------------------------------
2241 * Handler for reading events from the driver event queue. This handler is
2242 * executed when user modes issues a socket receive on a socket assocaited
2243 * with the MC group for events.
2244 * XXX user mode should read multiple events in one system call
2245 * --------------------------------------------------------------------------
2248 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2252 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2253 POVS_OPEN_INSTANCE instance =
2254 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
2258 OVS_EVENT_ENTRY eventEntry;
2260 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
2262 /* Should never read events with a dump socket */
2263 ASSERT(instance->dumpState.ovsMsg == NULL);
2265 /* Must have an event queue */
2266 ASSERT(instance->eventQueue != NULL);
2268 /* Output buffer has been validated while validating read dev op. */
2269 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2271 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
2273 OvsAcquireCtrlLock();
2274 if (!gOvsSwitchContext) {
2275 status = STATUS_SUCCESS;
2279 /* remove an event entry from the event queue */
2280 status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
2281 if (status != STATUS_SUCCESS) {
2285 status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
2286 if (status == NDIS_STATUS_SUCCESS) {
2287 *replyLen = NlBufSize(&nlBuf);
2291 OvsReleaseCtrlLock();
2294 #endif /* OVS_USE_NL_INTERFACE */