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
42 #define OVS_DBG_MOD OVS_DBG_DATAPATH
45 #define NETLINK_FAMILY_NAME_LEN 48
49 * Netlink messages are grouped by family (aka type), and each family supports
50 * a set of commands, and can be passed both from kernel -> userspace or
51 * vice-versa. To call into the kernel, userspace uses a device operation which
52 * is outside of a netlink message.
54 * Each command results in the invocation of a handler function to implement the
55 * request functionality.
57 * Expectedly, only certain combinations of (device operation, netlink family,
60 * Here, we implement the basic infrastructure to perform validation on the
61 * incoming message, version checking, and also to invoke the corresponding
62 * handler to do the heavy-lifting.
66 * Handler for a given netlink command. Not all the parameters are used by all
69 typedef NTSTATUS(NetlinkCmdHandler)(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
72 typedef struct _NETLINK_CMD {
74 NetlinkCmdHandler *handler;
75 UINT32 supportedDevOp; /* Supported device operations. */
76 BOOLEAN validateDpIndex; /* Does command require a valid DP argument. */
77 } NETLINK_CMD, *PNETLINK_CMD;
79 /* A netlink family is a group of commands. */
80 typedef struct _NETLINK_FAMILY {
86 NETLINK_CMD *cmds; /* Array of netlink commands and handlers. */
88 } NETLINK_FAMILY, *PNETLINK_FAMILY;
90 /* Handlers for the various netlink commands. */
91 static NetlinkCmdHandler OvsGetPidCmdHandler,
92 OvsPendEventCmdHandler,
93 OvsPendPacketCmdHandler,
94 OvsSubscribeEventCmdHandler,
95 OvsSubscribePacketCmdHandler,
96 OvsReadEventCmdHandler,
97 OvsReadPacketCmdHandler,
101 OvsGetVportCmdHandler,
102 OvsSetVportCmdHandler,
103 OvsNewVportCmdHandler,
104 OvsDeleteVportCmdHandler;
106 NetlinkCmdHandler OvsGetNetdevCmdHandler;
108 static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
110 static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
112 static NTSTATUS HandleDpTransactionCommon(
113 POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen);
116 * The various netlink families, along with the supported commands. Most of
117 * these families and commands are part of the openvswitch specification for a
118 * netlink datapath. In addition, each platform can implement a few families
119 * and commands as extensions.
122 /* Netlink control family: this is a Windows specific family. */
123 NETLINK_CMD nlControlFamilyCmdOps[] = {
124 { .cmd = OVS_CTRL_CMD_WIN_GET_PID,
125 .handler = OvsGetPidCmdHandler,
126 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
127 .validateDpIndex = FALSE,
129 { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ,
130 .handler = OvsPendEventCmdHandler,
131 .supportedDevOp = OVS_WRITE_DEV_OP,
132 .validateDpIndex = TRUE,
134 { .cmd = OVS_CTRL_CMD_WIN_PEND_PACKET_REQ,
135 .handler = OvsPendPacketCmdHandler,
136 .supportedDevOp = OVS_WRITE_DEV_OP,
137 .validateDpIndex = TRUE,
139 { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ,
140 .handler = OvsSubscribeEventCmdHandler,
141 .supportedDevOp = OVS_WRITE_DEV_OP,
142 .validateDpIndex = TRUE,
144 { .cmd = OVS_CTRL_CMD_PACKET_SUBSCRIBE_REQ,
145 .handler = OvsSubscribePacketCmdHandler,
146 .supportedDevOp = OVS_WRITE_DEV_OP,
147 .validateDpIndex = TRUE,
149 { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY,
150 .handler = OvsReadEventCmdHandler,
151 .supportedDevOp = OVS_READ_EVENT_DEV_OP,
152 .validateDpIndex = FALSE,
154 { .cmd = OVS_CTRL_CMD_READ_NOTIFY,
155 .handler = OvsReadPacketCmdHandler,
156 .supportedDevOp = OVS_READ_PACKET_DEV_OP,
157 .validateDpIndex = FALSE,
161 NETLINK_FAMILY nlControlFamilyOps = {
162 .name = OVS_WIN_CONTROL_FAMILY,
163 .id = OVS_WIN_NL_CTRL_FAMILY_ID,
164 .version = OVS_WIN_CONTROL_VERSION,
165 .maxAttr = OVS_WIN_CONTROL_ATTR_MAX,
166 .cmds = nlControlFamilyCmdOps,
167 .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
170 /* Netlink datapath family. */
171 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
172 { .cmd = OVS_DP_CMD_NEW,
173 .handler = OvsNewDpCmdHandler,
174 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
175 .validateDpIndex = FALSE
177 { .cmd = OVS_DP_CMD_GET,
178 .handler = OvsGetDpCmdHandler,
179 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
180 OVS_TRANSACTION_DEV_OP,
181 .validateDpIndex = FALSE
183 { .cmd = OVS_DP_CMD_SET,
184 .handler = OvsSetDpCmdHandler,
185 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
186 OVS_TRANSACTION_DEV_OP,
187 .validateDpIndex = TRUE
191 NETLINK_FAMILY nlDatapathFamilyOps = {
192 .name = OVS_DATAPATH_FAMILY,
193 .id = OVS_WIN_NL_DATAPATH_FAMILY_ID,
194 .version = OVS_DATAPATH_VERSION,
195 .maxAttr = OVS_DP_ATTR_MAX,
196 .cmds = nlDatapathFamilyCmdOps,
197 .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
200 /* Netlink packet family. */
202 NETLINK_CMD nlPacketFamilyCmdOps[] = {
203 { .cmd = OVS_PACKET_CMD_EXECUTE,
204 .handler = OvsNlExecuteCmdHandler,
205 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
206 .validateDpIndex = TRUE
210 NETLINK_FAMILY nlPacketFamilyOps = {
211 .name = OVS_PACKET_FAMILY,
212 .id = OVS_WIN_NL_PACKET_FAMILY_ID,
213 .version = OVS_PACKET_VERSION,
214 .maxAttr = OVS_PACKET_ATTR_MAX,
215 .cmds = nlPacketFamilyCmdOps,
216 .opsCount = ARRAY_SIZE(nlPacketFamilyCmdOps)
219 /* Netlink vport family. */
220 NETLINK_CMD nlVportFamilyCmdOps[] = {
221 { .cmd = OVS_VPORT_CMD_GET,
222 .handler = OvsGetVportCmdHandler,
223 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
224 OVS_TRANSACTION_DEV_OP,
225 .validateDpIndex = TRUE
227 { .cmd = OVS_VPORT_CMD_NEW,
228 .handler = OvsNewVportCmdHandler,
229 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
230 .validateDpIndex = TRUE
232 { .cmd = OVS_VPORT_CMD_SET,
233 .handler = OvsSetVportCmdHandler,
234 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
235 .validateDpIndex = TRUE
237 { .cmd = OVS_VPORT_CMD_DEL,
238 .handler = OvsDeleteVportCmdHandler,
239 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
240 .validateDpIndex = TRUE
244 NETLINK_FAMILY nlVportFamilyOps = {
245 .name = OVS_VPORT_FAMILY,
246 .id = OVS_WIN_NL_VPORT_FAMILY_ID,
247 .version = OVS_VPORT_VERSION,
248 .maxAttr = OVS_VPORT_ATTR_MAX,
249 .cmds = nlVportFamilyCmdOps,
250 .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps)
253 /* Netlink flow family. */
255 NETLINK_CMD nlFlowFamilyCmdOps[] = {
256 { .cmd = OVS_FLOW_CMD_NEW,
257 .handler = OvsFlowNlCmdHandler,
258 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
259 .validateDpIndex = TRUE
261 { .cmd = OVS_FLOW_CMD_SET,
262 .handler = OvsFlowNlCmdHandler,
263 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
264 .validateDpIndex = TRUE
266 { .cmd = OVS_FLOW_CMD_DEL,
267 .handler = OvsFlowNlCmdHandler,
268 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
269 .validateDpIndex = TRUE
271 { .cmd = OVS_FLOW_CMD_GET,
272 .handler = OvsFlowNlGetCmdHandler,
273 .supportedDevOp = OVS_TRANSACTION_DEV_OP |
274 OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
275 .validateDpIndex = TRUE
279 NETLINK_FAMILY nlFLowFamilyOps = {
280 .name = OVS_FLOW_FAMILY,
281 .id = OVS_WIN_NL_FLOW_FAMILY_ID,
282 .version = OVS_FLOW_VERSION,
283 .maxAttr = OVS_FLOW_ATTR_MAX,
284 .cmds = nlFlowFamilyCmdOps,
285 .opsCount = ARRAY_SIZE(nlFlowFamilyCmdOps)
288 /* Netlink netdev family. */
289 NETLINK_CMD nlNetdevFamilyCmdOps[] = {
290 { .cmd = OVS_WIN_NETDEV_CMD_GET,
291 .handler = OvsGetNetdevCmdHandler,
292 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
293 .validateDpIndex = FALSE
297 NETLINK_FAMILY nlNetdevFamilyOps = {
298 .name = OVS_WIN_NETDEV_FAMILY,
299 .id = OVS_WIN_NL_NETDEV_FAMILY_ID,
300 .version = OVS_WIN_NETDEV_VERSION,
301 .maxAttr = OVS_WIN_NETDEV_ATTR_MAX,
302 .cmds = nlNetdevFamilyCmdOps,
303 .opsCount = ARRAY_SIZE(nlNetdevFamilyCmdOps)
306 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
308 UINT32 requiredLength,
310 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
311 POVS_OPEN_INSTANCE instance,
313 NETLINK_FAMILY *nlFamilyOps);
314 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
315 NETLINK_FAMILY *nlFamilyOps,
318 /* Handles to the device object for communication with userspace. */
319 NDIS_HANDLE gOvsDeviceHandle;
320 PDEVICE_OBJECT gOvsDeviceObject;
322 _Dispatch_type_(IRP_MJ_CREATE)
323 _Dispatch_type_(IRP_MJ_CLOSE)
324 DRIVER_DISPATCH OvsOpenCloseDevice;
326 _Dispatch_type_(IRP_MJ_CLEANUP)
327 DRIVER_DISPATCH OvsCleanupDevice;
329 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
330 DRIVER_DISPATCH OvsDeviceControl;
333 #pragma alloc_text(INIT, OvsCreateDeviceObject)
334 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
335 #pragma alloc_text(PAGE, OvsCleanupDevice)
336 #pragma alloc_text(PAGE, OvsDeviceControl)
337 #endif // ALLOC_PRAGMA
340 * We might hit this limit easily since userspace opens a netlink descriptor for
341 * each thread, and at least one descriptor per vport. Revisit this later.
343 #define OVS_MAX_OPEN_INSTANCES 512
344 #define OVS_SYSTEM_DP_NAME "ovs-system"
346 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
347 UINT32 ovsNumberOfOpenInstances;
348 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
350 NDIS_SPIN_LOCK ovsCtrlLockObj;
351 PNDIS_SPIN_LOCK gOvsCtrlLock;
357 gOvsCtrlLock = &ovsCtrlLockObj;
358 NdisAllocateSpinLock(gOvsCtrlLock);
365 OvsCleanupEventQueue();
367 NdisFreeSpinLock(gOvsCtrlLock);
375 NdisAcquireSpinLock(gOvsCtrlLock);
381 NdisReleaseSpinLock(gOvsCtrlLock);
386 * --------------------------------------------------------------------------
387 * Creates the communication device between user and kernel, and also
388 * initializes the data associated data structures.
389 * --------------------------------------------------------------------------
392 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
394 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
395 UNICODE_STRING deviceName;
396 UNICODE_STRING symbolicDeviceName;
397 PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
398 NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
399 OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
401 RtlZeroMemory(dispatchTable,
402 (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
403 dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
404 dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
405 dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
406 dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
408 NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
409 NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
411 RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
413 OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
414 NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
415 NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
416 sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
418 deviceAttributes.DeviceName = &deviceName;
419 deviceAttributes.SymbolicName = &symbolicDeviceName;
420 deviceAttributes.MajorFunctions = dispatchTable;
421 deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
423 status = NdisRegisterDeviceEx(ovsExtDriverHandle,
427 if (status != NDIS_STATUS_SUCCESS) {
428 POVS_DEVICE_EXTENSION ovsExt =
429 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
430 ASSERT(gOvsDeviceObject != NULL);
431 ASSERT(gOvsDeviceHandle != NULL);
434 ovsExt->numberOpenInstance = 0;
437 /* Initialize the associated data structures. */
440 OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
446 OvsDeleteDeviceObject()
448 if (gOvsDeviceHandle) {
450 POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
451 NdisGetDeviceReservedExtension(gOvsDeviceObject);
453 ASSERT(ovsExt->numberOpenInstance == 0);
457 ASSERT(gOvsDeviceObject);
458 NdisDeregisterDeviceEx(gOvsDeviceHandle);
459 gOvsDeviceHandle = NULL;
460 gOvsDeviceObject = NULL;
466 OvsGetOpenInstance(PFILE_OBJECT fileObject,
469 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
471 ASSERT(instance->fileObject == fileObject);
472 if (gOvsSwitchContext->dpNo != dpNo) {
480 OvsFindOpenInstance(PFILE_OBJECT fileObject)
483 for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
484 j < ovsNumberOfOpenInstances; i++) {
485 if (ovsOpenInstanceArray[i]) {
486 if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
487 return ovsOpenInstanceArray[i];
496 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
497 PFILE_OBJECT fileObject)
499 POVS_OPEN_INSTANCE instance =
500 (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
503 if (instance == NULL) {
504 return STATUS_NO_MEMORY;
506 OvsAcquireCtrlLock();
507 ASSERT(OvsFindOpenInstance(fileObject) == NULL);
509 if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
510 OvsReleaseCtrlLock();
511 OvsFreeMemory(instance);
512 return STATUS_INSUFFICIENT_RESOURCES;
514 RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
516 for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
517 if (ovsOpenInstanceArray[i] == NULL) {
518 ovsOpenInstanceArray[i] = instance;
519 ovsNumberOfOpenInstances++;
520 instance->cookie = i;
524 ASSERT(i < OVS_MAX_OPEN_INSTANCES);
525 instance->fileObject = fileObject;
526 ASSERT(fileObject->FsContext == NULL);
527 instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
528 if (instance->pid == 0) {
529 /* XXX: check for rollover. */
531 fileObject->FsContext = instance;
532 OvsReleaseCtrlLock();
533 return STATUS_SUCCESS;
537 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
539 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
541 ASSERT(fileObject == instance->fileObject);
542 OvsCleanupEvent(instance);
543 OvsCleanupPacketQueue(instance);
547 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
549 POVS_OPEN_INSTANCE instance;
550 ASSERT(fileObject->FsContext);
551 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
552 ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
554 OvsAcquireCtrlLock();
555 fileObject->FsContext = NULL;
556 ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
557 ovsOpenInstanceArray[instance->cookie] = NULL;
558 ovsNumberOfOpenInstances--;
559 OvsReleaseCtrlLock();
560 ASSERT(instance->eventQueue == NULL);
561 ASSERT (instance->packetQueue == NULL);
562 OvsFreeMemory(instance);
566 OvsCompleteIrpRequest(PIRP irp,
570 irp->IoStatus.Information = infoPtr;
571 irp->IoStatus.Status = status;
572 IoCompleteRequest(irp, IO_NO_INCREMENT);
578 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
581 PIO_STACK_LOCATION irpSp;
582 NTSTATUS status = STATUS_SUCCESS;
583 PFILE_OBJECT fileObject;
584 POVS_DEVICE_EXTENSION ovsExt =
585 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
587 ASSERT(deviceObject == gOvsDeviceObject);
588 ASSERT(ovsExt != NULL);
590 irpSp = IoGetCurrentIrpStackLocation(irp);
591 fileObject = irpSp->FileObject;
592 OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
593 deviceObject, fileObject,
594 ovsExt->numberOpenInstance);
596 switch (irpSp->MajorFunction) {
598 status = OvsAddOpenInstance(ovsExt, fileObject);
599 if (STATUS_SUCCESS == status) {
600 InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
604 ASSERT(ovsExt->numberOpenInstance > 0);
605 OvsRemoveOpenInstance(fileObject);
606 InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
611 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
614 _Use_decl_annotations_
616 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
620 PIO_STACK_LOCATION irpSp;
621 PFILE_OBJECT fileObject;
623 NTSTATUS status = STATUS_SUCCESS;
625 POVS_DEVICE_EXTENSION ovsExt =
626 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
628 ASSERT(ovsExt->numberOpenInstance > 0);
631 UNREFERENCED_PARAMETER(deviceObject);
633 ASSERT(deviceObject == gOvsDeviceObject);
634 irpSp = IoGetCurrentIrpStackLocation(irp);
635 fileObject = irpSp->FileObject;
637 ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
639 OvsCleanupOpenInstance(fileObject);
641 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
646 * --------------------------------------------------------------------------
647 * IOCTL function handler for the device.
648 * --------------------------------------------------------------------------
651 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
654 PIO_STACK_LOCATION irpSp;
655 NTSTATUS status = STATUS_SUCCESS;
656 PFILE_OBJECT fileObject;
657 PVOID inputBuffer = NULL;
658 PVOID outputBuffer = NULL;
659 UINT32 inputBufferLen, outputBufferLen;
660 UINT32 code, replyLen = 0;
661 POVS_OPEN_INSTANCE instance;
663 OVS_MESSAGE ovsMsgReadOp;
665 NETLINK_FAMILY *nlFamilyOps;
666 OVS_USER_PARAMS_CONTEXT usrParamsCtx;
669 POVS_DEVICE_EXTENSION ovsExt =
670 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
671 ASSERT(deviceObject == gOvsDeviceObject);
673 ASSERT(ovsExt->numberOpenInstance > 0);
675 UNREFERENCED_PARAMETER(deviceObject);
678 irpSp = IoGetCurrentIrpStackLocation(irp);
680 ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
681 ASSERT(irpSp->FileObject != NULL);
683 fileObject = irpSp->FileObject;
684 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
685 code = irpSp->Parameters.DeviceIoControl.IoControlCode;
686 inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
687 outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
688 inputBuffer = irp->AssociatedIrp.SystemBuffer;
690 /* Check if the extension is enabled. */
691 if (NULL == gOvsSwitchContext) {
692 status = STATUS_DEVICE_NOT_READY;
696 /* Concurrent netlink operations are not supported. */
697 if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
698 status = STATUS_RESOURCE_IN_USE;
703 * Validate the input/output buffer arguments depending on the type of the
707 case OVS_IOCTL_TRANSACT:
708 /* Both input buffer and output buffer are mandatory. */
709 if (outputBufferLen != 0) {
710 status = MapIrpOutputBuffer(irp, outputBufferLen,
711 sizeof *ovsMsg, &outputBuffer);
712 if (status != STATUS_SUCCESS) {
715 ASSERT(outputBuffer);
717 status = STATUS_NDIS_INVALID_LENGTH;
721 if (inputBufferLen < sizeof (*ovsMsg)) {
722 status = STATUS_NDIS_INVALID_LENGTH;
726 ovsMsg = inputBuffer;
727 devOp = OVS_TRANSACTION_DEV_OP;
730 case OVS_IOCTL_READ_EVENT:
731 case OVS_IOCTL_READ_PACKET:
733 * Output buffer is mandatory. These IOCTLs are used to read events and
734 * packets respectively. It is convenient to have separate ioctls.
736 if (outputBufferLen != 0) {
737 status = MapIrpOutputBuffer(irp, outputBufferLen,
738 sizeof *ovsMsg, &outputBuffer);
739 if (status != STATUS_SUCCESS) {
742 ASSERT(outputBuffer);
744 status = STATUS_NDIS_INVALID_LENGTH;
750 ovsMsg = &ovsMsgReadOp;
751 ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
752 ovsMsg->nlMsg.nlmsgPid = instance->pid;
753 /* An "artificial" command so we can use NL family function table*/
754 ovsMsg->genlMsg.cmd = (code == OVS_IOCTL_READ_EVENT) ?
755 OVS_CTRL_CMD_EVENT_NOTIFY :
756 OVS_CTRL_CMD_READ_NOTIFY;
757 devOp = OVS_READ_DEV_OP;
761 /* Output buffer is mandatory. */
762 if (outputBufferLen != 0) {
763 status = MapIrpOutputBuffer(irp, outputBufferLen,
764 sizeof *ovsMsg, &outputBuffer);
765 if (status != STATUS_SUCCESS) {
768 ASSERT(outputBuffer);
770 status = STATUS_NDIS_INVALID_LENGTH;
775 * Operate in the mode that read ioctl is similar to ReadFile(). This
776 * might change as the userspace code gets implemented.
782 * For implementing read (ioctl or otherwise), we need to store some
783 * state in the instance to indicate the command that started the dump
784 * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
785 * that 'ovsMsgReadOp' is needed only in this function to call into the
786 * appropriate handler. The handler itself can access the state in the
789 * In the absence of a dump start, return 0 bytes.
791 if (instance->dumpState.ovsMsg == NULL) {
793 status = STATUS_SUCCESS;
796 RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
797 sizeof (ovsMsgReadOp));
799 /* Create an NL message for consumption. */
800 ovsMsg = &ovsMsgReadOp;
801 devOp = OVS_READ_DEV_OP;
805 case OVS_IOCTL_WRITE:
806 /* Input buffer is mandatory. */
807 if (inputBufferLen < sizeof (*ovsMsg)) {
808 status = STATUS_NDIS_INVALID_LENGTH;
812 ovsMsg = inputBuffer;
813 devOp = OVS_WRITE_DEV_OP;
817 status = STATUS_INVALID_DEVICE_REQUEST;
822 switch (ovsMsg->nlMsg.nlmsgType) {
823 case OVS_WIN_NL_CTRL_FAMILY_ID:
824 nlFamilyOps = &nlControlFamilyOps;
826 case OVS_WIN_NL_DATAPATH_FAMILY_ID:
827 nlFamilyOps = &nlDatapathFamilyOps;
829 case OVS_WIN_NL_FLOW_FAMILY_ID:
830 nlFamilyOps = &nlFLowFamilyOps;
832 case OVS_WIN_NL_PACKET_FAMILY_ID:
833 nlFamilyOps = &nlPacketFamilyOps;
835 case OVS_WIN_NL_VPORT_FAMILY_ID:
836 nlFamilyOps = &nlVportFamilyOps;
838 case OVS_WIN_NL_NETDEV_FAMILY_ID:
839 nlFamilyOps = &nlNetdevFamilyOps;
842 status = STATUS_INVALID_PARAMETER;
847 * For read operation, the netlink command has already been validated
850 if (devOp != OVS_READ_DEV_OP) {
851 status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
852 if (status != STATUS_SUCCESS) {
857 InitUserParamsCtx(irp, instance, devOp, ovsMsg,
858 inputBuffer, inputBufferLen,
859 outputBuffer, outputBufferLen,
862 status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
868 /* Should not complete a pending IRP unless proceesing is completed */
869 if (status == STATUS_PENDING) {
872 return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
877 * --------------------------------------------------------------------------
878 * Function to validate a netlink command. Only certain combinations of
879 * (device operation, netlink family, command) are valid.
880 * --------------------------------------------------------------------------
883 ValidateNetlinkCmd(UINT32 devOp,
884 POVS_OPEN_INSTANCE instance,
886 NETLINK_FAMILY *nlFamilyOps)
888 NTSTATUS status = STATUS_INVALID_PARAMETER;
891 for (i = 0; i < nlFamilyOps->opsCount; i++) {
892 if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
893 /* Validate if the command is valid for the device operation. */
894 if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
895 status = STATUS_INVALID_PARAMETER;
899 /* Validate the version. */
900 if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
901 status = STATUS_INVALID_PARAMETER;
905 /* Validate the DP for commands that require a DP. */
906 if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
907 OvsAcquireCtrlLock();
908 if (ovsMsg->ovsHdr.dp_ifindex !=
909 (INT)gOvsSwitchContext->dpNo) {
910 status = STATUS_INVALID_PARAMETER;
911 OvsReleaseCtrlLock();
914 OvsReleaseCtrlLock();
917 /* Validate the PID. */
918 if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
919 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
920 status = STATUS_INVALID_PARAMETER;
925 status = STATUS_SUCCESS;
935 * --------------------------------------------------------------------------
936 * Function to invoke the netlink command handler.
937 * --------------------------------------------------------------------------
940 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
941 NETLINK_FAMILY *nlFamilyOps,
944 NTSTATUS status = STATUS_INVALID_PARAMETER;
947 for (i = 0; i < nlFamilyOps->opsCount; i++) {
948 if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
949 NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
952 status = handler(usrParamsCtx, replyLen);
962 * --------------------------------------------------------------------------
963 * Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
965 * Each handle on the device is assigned a unique PID when the handle is
966 * created. On platforms that support netlink natively, the PID is available
967 * to userspace when the netlink socket is created. However, without native
968 * netlink support on Windows, OVS datapath generates the PID and lets the
969 * userspace query it.
971 * This function implements the query.
972 * --------------------------------------------------------------------------
975 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
978 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
979 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
981 if (usrParamsCtx->outputLength >= sizeof *msgOut) {
982 POVS_OPEN_INSTANCE instance =
983 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
985 RtlZeroMemory(msgOut, sizeof *msgOut);
986 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
987 msgOut->nlMsg.nlmsgPid = instance->pid;
988 *replyLen = sizeof *msgOut;
989 /* XXX: We might need to return the DP index as well. */
991 return STATUS_NDIS_INVALID_LENGTH;
994 return STATUS_SUCCESS;
998 * --------------------------------------------------------------------------
999 * Utility function to fill up information about the datapath in a reply to
1001 * Assumes that 'gOvsCtrlLock' lock is acquired.
1002 * --------------------------------------------------------------------------
1005 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
1010 OVS_MESSAGE msgOutTmp;
1011 OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
1014 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && NlBufRemLen(nlBuf) >= sizeof *msgIn);
1016 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
1017 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
1018 msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1019 msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1021 msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
1022 msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
1023 msgOutTmp.genlMsg.reserved = 0;
1025 msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
1027 writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
1029 writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
1030 OVS_SYSTEM_DP_NAME);
1033 OVS_DP_STATS dpStats;
1035 dpStats.n_hit = datapath->hits;
1036 dpStats.n_missed = datapath->misses;
1037 dpStats.n_lost = datapath->lost;
1038 dpStats.n_flows = datapath->nFlows;
1039 writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
1040 (PCHAR)&dpStats, sizeof dpStats);
1042 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1043 nlMsg->nlmsgLen = NlBufSize(nlBuf);
1045 return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
1049 * --------------------------------------------------------------------------
1050 * Handler for queueing an IRP used for event notification. The IRP is
1051 * completed when a port state changes. STATUS_PENDING is returned on
1052 * success. User mode keep a pending IRP at all times.
1053 * --------------------------------------------------------------------------
1056 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1061 UNREFERENCED_PARAMETER(replyLen);
1063 POVS_OPEN_INSTANCE instance =
1064 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1065 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1066 OVS_EVENT_POLL poll;
1068 poll.dpNo = msgIn->ovsHdr.dp_ifindex;
1069 status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
1070 &poll, sizeof poll);
1075 * --------------------------------------------------------------------------
1076 * Handler for the subscription for the event queue
1077 * --------------------------------------------------------------------------
1080 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1084 OVS_EVENT_SUBSCRIBE request;
1088 const NL_POLICY policy[] = {
1089 [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
1090 [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
1093 UNREFERENCED_PARAMETER(replyLen);
1095 POVS_OPEN_INSTANCE instance =
1096 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1097 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1099 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1100 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, ARRAY_SIZE(attrs));
1102 status = STATUS_INVALID_PARAMETER;
1106 /* XXX Ignore the MC group for now */
1107 join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1108 request.dpNo = msgIn->ovsHdr.dp_ifindex;
1109 request.subscribe = join;
1110 request.mask = OVS_EVENT_MASK_ALL;
1112 status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1119 * --------------------------------------------------------------------------
1120 * Command Handler for 'OVS_DP_CMD_NEW'.
1121 * --------------------------------------------------------------------------
1124 OvsNewDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1127 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1131 * --------------------------------------------------------------------------
1132 * Command Handler for 'OVS_DP_CMD_GET'.
1134 * The function handles both the dump based as well as the transaction based
1135 * 'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1136 * call to setup dump state, as well as subsequent calls to continue dumping
1138 * --------------------------------------------------------------------------
1141 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1144 if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1145 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1147 return HandleGetDpDump(usrParamsCtx, replyLen);
1152 * --------------------------------------------------------------------------
1153 * Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1154 * --------------------------------------------------------------------------
1157 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1160 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1165 * --------------------------------------------------------------------------
1166 * Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1167 * --------------------------------------------------------------------------
1170 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1173 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1174 POVS_OPEN_INSTANCE instance =
1175 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1177 if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1179 OvsSetupDumpStart(usrParamsCtx);
1183 POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1185 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1187 if (instance->dumpState.ovsMsg == NULL) {
1189 return STATUS_INVALID_DEVICE_STATE;
1192 /* Dump state must have been deleted after previous dump operation. */
1193 ASSERT(instance->dumpState.index[0] == 0);
1195 /* Output buffer has been validated while validating read dev op. */
1196 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1198 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1199 usrParamsCtx->outputLength);
1201 OvsAcquireCtrlLock();
1202 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1203 OvsReleaseCtrlLock();
1205 if (status != STATUS_SUCCESS) {
1207 FreeUserDumpState(instance);
1211 /* Increment the dump index. */
1212 instance->dumpState.index[0] = 1;
1213 *replyLen = msgOut->nlMsg.nlmsgLen;
1215 /* Free up the dump state, since there's no more data to continue. */
1216 FreeUserDumpState(instance);
1219 return STATUS_SUCCESS;
1224 * --------------------------------------------------------------------------
1225 * Command Handler for 'OVS_DP_CMD_SET'.
1226 * --------------------------------------------------------------------------
1229 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1232 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1236 * --------------------------------------------------------------------------
1237 * Function for handling transaction based 'OVS_DP_CMD_NEW', 'OVS_DP_CMD_GET'
1238 * and 'OVS_DP_CMD_SET' commands.
1240 * 'OVS_DP_CMD_NEW' is implemented to keep userspace code happy. Creation of a
1241 * new datapath is not supported currently.
1242 * --------------------------------------------------------------------------
1245 HandleDpTransactionCommon(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1248 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1249 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1250 NTSTATUS status = STATUS_SUCCESS;
1252 NL_ERROR nlError = NL_ERROR_SUCCESS;
1253 static const NL_POLICY ovsDatapathSetPolicy[] = {
1254 [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1255 [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1256 [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1258 PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1260 /* input buffer has been validated while validating write dev op. */
1261 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1263 /* Parse any attributes in the request. */
1264 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET ||
1265 usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1266 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1267 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1268 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1269 ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1270 return STATUS_INVALID_PARAMETER;
1274 * XXX: Not clear at this stage if there's any role for the
1275 * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1280 RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1283 /* Output buffer has been validated while validating transact dev op. */
1284 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1286 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1288 OvsAcquireCtrlLock();
1289 if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1290 if (!OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1291 OVS_SYSTEM_DP_NAME)) {
1292 OvsReleaseCtrlLock();
1294 /* Creation of new datapaths is not supported. */
1295 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1296 nlError = NL_ERROR_NOTSUPP;
1300 nlError = NL_ERROR_NODEV;
1303 } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1304 OvsReleaseCtrlLock();
1305 nlError = NL_ERROR_NODEV;
1309 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1310 OvsReleaseCtrlLock();
1311 nlError = NL_ERROR_EXIST;
1315 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1316 OvsReleaseCtrlLock();
1318 *replyLen = NlBufSize(&nlBuf);
1321 if (nlError != NL_ERROR_SUCCESS) {
1322 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1323 usrParamsCtx->outputBuffer;
1325 BuildErrorMsg(msgIn, msgError, nlError);
1326 *replyLen = msgError->nlMsg.nlmsgLen;
1329 return STATUS_SUCCESS;
1334 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1336 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1337 POVS_OPEN_INSTANCE instance =
1338 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1340 /* input buffer has been validated while validating write dev op. */
1341 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1343 /* A write operation that does not indicate dump start is invalid. */
1344 if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1345 return STATUS_INVALID_PARAMETER;
1347 /* XXX: Handle other NLM_F_* flags in the future. */
1350 * This operation should be setting up the dump state. If there's any
1351 * previous state, clear it up so as to set it up afresh.
1353 FreeUserDumpState(instance);
1355 return InitUserDumpState(instance, msgIn);
1359 BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
1360 UINT32 length, UINT16 flags)
1362 msgOut->nlMsg.nlmsgType = type;
1363 msgOut->nlMsg.nlmsgFlags = flags;
1364 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1365 msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1366 msgOut->nlMsg.nlmsgLen = length;
1368 msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
1369 msgOut->genlMsg.version = msgIn->genlMsg.version;
1370 msgOut->genlMsg.reserved = 0;
1374 * XXX: should move out these functions to a Netlink.c or to a OvsMessage.c
1375 * or even make them inlined functions in Datapath.h. Can be done after the
1376 * first sprint once we have more code to refactor.
1379 BuildReplyMsgFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
1381 BuildMsgOut(msgIn, msgOut, msgIn->nlMsg.nlmsgType, sizeof(OVS_MESSAGE),
1386 BuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode)
1388 BuildMsgOut(msgIn, (POVS_MESSAGE)msgOut, NLMSG_ERROR,
1389 sizeof(OVS_MESSAGE_ERROR), 0);
1391 msgOut->errorMsg.error = errorCode;
1392 msgOut->errorMsg.nlMsg = msgIn->nlMsg;
1396 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1403 OVS_VPORT_FULL_STATS vportStats;
1408 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1410 BuildReplyMsgFromMsgIn(msgIn, &msgOut, NLM_F_MULTI);
1411 msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1413 ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1415 return STATUS_INSUFFICIENT_RESOURCES;
1418 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1420 return STATUS_INSUFFICIENT_RESOURCES;
1423 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1425 return STATUS_INSUFFICIENT_RESOURCES;
1428 ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1430 return STATUS_INSUFFICIENT_RESOURCES;
1434 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1435 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1436 * it means we have an array of pids, instead of a single pid.
1437 * ATM we assume we have one pid only.
1440 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1443 return STATUS_INSUFFICIENT_RESOURCES;
1447 vportStats.rxPackets = vport->stats.rxPackets;
1448 vportStats.rxBytes = vport->stats.rxBytes;
1449 vportStats.txPackets = vport->stats.txPackets;
1450 vportStats.txBytes = vport->stats.txBytes;
1451 vportStats.rxErrors = vport->errStats.rxErrors;
1452 vportStats.txErrors = vport->errStats.txErrors;
1453 vportStats.rxDropped = vport->errStats.rxDropped;
1454 vportStats.txDropped = vport->errStats.txDropped;
1456 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1458 sizeof(OVS_VPORT_FULL_STATS));
1460 return STATUS_INSUFFICIENT_RESOURCES;
1464 * XXX: when vxlan udp dest port becomes configurable, we will also need
1465 * to add vport options
1468 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1469 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1471 return STATUS_SUCCESS;
1475 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1479 POVS_OPEN_INSTANCE instance =
1480 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1481 LOCK_STATE_EX lockState;
1482 UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1485 * XXX: this function shares some code with other dump command(s).
1486 * In the future, we will need to refactor the dump functions
1489 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1491 if (instance->dumpState.ovsMsg == NULL) {
1493 return STATUS_INVALID_DEVICE_STATE;
1496 /* Output buffer has been validated while validating read dev op. */
1497 ASSERT(usrParamsCtx->outputBuffer != NULL);
1499 msgIn = instance->dumpState.ovsMsg;
1501 OvsAcquireCtrlLock();
1504 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1505 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1506 * it means we have an array of pids, instead of a single pid.
1507 * ATM we assume we have one pid only.
1509 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1510 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1511 NDIS_RWL_AT_DISPATCH_LEVEL);
1513 if (gOvsSwitchContext->numHvVports > 0 ||
1514 gOvsSwitchContext->numNonHvVports > 0) {
1515 /* inBucket: the bucket, used for lookup */
1516 UINT32 inBucket = instance->dumpState.index[0];
1517 /* inIndex: index within the given bucket, used for lookup */
1518 UINT32 inIndex = instance->dumpState.index[1];
1519 /* the bucket to be used for the next dump operation */
1520 UINT32 outBucket = 0;
1521 /* the index within the outBucket to be used for the next dump */
1522 UINT32 outIndex = 0;
1524 for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1525 PLIST_ENTRY head, link;
1526 head = &(gOvsSwitchContext->portNoHashArray[i]);
1527 POVS_VPORT_ENTRY vport = NULL;
1530 LIST_FORALL(head, link) {
1533 * if one or more dumps were previously done on this same bucket,
1534 * inIndex will be > 0, so we'll need to reply with the
1535 * inIndex + 1 vport from the bucket.
1537 if (outIndex >= inIndex) {
1538 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1540 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
1541 OvsCreateMsgFromVport(vport, msgIn,
1542 usrParamsCtx->outputBuffer,
1543 usrParamsCtx->outputLength,
1544 gOvsSwitchContext->dpNo);
1557 * if no vport was found above, check the next bucket, beginning
1558 * with the first (i.e. index 0) elem from within that bucket
1565 /* XXX: what about NLMSG_DONE (as msg type)? */
1566 instance->dumpState.index[0] = outBucket;
1567 instance->dumpState.index[1] = outIndex;
1570 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1572 OvsReleaseCtrlLock();
1574 /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1575 if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1576 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1577 *replyLen = msgOut->nlMsg.nlmsgLen;
1580 * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1584 /* Free up the dump state, since there's no more data to continue. */
1585 FreeUserDumpState(instance);
1588 return STATUS_SUCCESS;
1592 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1595 NTSTATUS status = STATUS_SUCCESS;
1596 LOCK_STATE_EX lockState;
1598 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1599 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1600 POVS_VPORT_ENTRY vport = NULL;
1601 NL_ERROR nlError = NL_ERROR_SUCCESS;
1602 PCHAR portName = NULL;
1603 UINT32 portNameLen = 0;
1604 UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
1606 static const NL_POLICY ovsVportPolicy[] = {
1607 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1608 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1613 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1615 /* input buffer has been validated while validating write dev op. */
1616 ASSERT(usrParamsCtx->inputBuffer != NULL);
1618 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1619 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1620 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1621 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1622 return STATUS_INVALID_PARAMETER;
1625 /* Output buffer has been validated while validating transact dev op. */
1626 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1628 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1629 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1630 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1631 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1633 /* the port name is expected to be null-terminated */
1634 ASSERT(portName[portNameLen - 1] == '\0');
1636 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1637 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1638 portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1640 vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
1642 nlError = NL_ERROR_INVAL;
1643 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1648 nlError = NL_ERROR_NODEV;
1649 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1653 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1654 usrParamsCtx->outputLength,
1655 gOvsSwitchContext->dpNo);
1656 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1658 *replyLen = msgOut->nlMsg.nlmsgLen;
1661 if (nlError != NL_ERROR_SUCCESS) {
1662 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1663 usrParamsCtx->outputBuffer;
1665 BuildErrorMsg(msgIn, msgError, nlError);
1666 *replyLen = msgError->nlMsg.nlmsgLen;
1669 return STATUS_SUCCESS;
1673 * --------------------------------------------------------------------------
1674 * Handler for the get vport command. The function handles the initial call to
1675 * setup the dump state, as well as subsequent calls to continue dumping data.
1676 * --------------------------------------------------------------------------
1679 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1684 switch (usrParamsCtx->devOp)
1686 case OVS_WRITE_DEV_OP:
1687 return OvsSetupDumpStart(usrParamsCtx);
1689 case OVS_READ_DEV_OP:
1690 return OvsGetVportDumpNext(usrParamsCtx, replyLen);
1692 case OVS_TRANSACTION_DEV_OP:
1693 return OvsGetVport(usrParamsCtx, replyLen);
1696 return STATUS_INVALID_DEVICE_REQUEST;
1704 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
1706 /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
1707 for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
1708 POVS_VPORT_ENTRY vport;
1710 vport = OvsFindVportByPortNo(switchContext, i);
1716 return OVS_DPPORT_NUMBER_INVALID;
1720 * --------------------------------------------------------------------------
1721 * Command Handler for 'OVS_VPORT_CMD_NEW'.
1722 * --------------------------------------------------------------------------
1725 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1728 NDIS_STATUS status = STATUS_SUCCESS;
1729 LOCK_STATE_EX lockState;
1731 NL_ERROR nlError = NL_ERROR_SUCCESS;
1732 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1733 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1734 POVS_VPORT_ENTRY vport = NULL;
1738 BOOLEAN isBridgeInternal = FALSE;
1739 BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE;
1740 BOOLEAN addInternalPortAsNetdev = FALSE;
1742 static const NL_POLICY ovsVportPolicy[] = {
1743 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1744 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
1745 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
1747 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
1748 .optional = FALSE },
1749 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
1752 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1754 /* input buffer has been validated while validating write dev op. */
1755 ASSERT(usrParamsCtx->inputBuffer != NULL);
1757 /* Output buffer has been validated while validating transact dev op. */
1758 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1760 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1761 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1762 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1763 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1764 return STATUS_INVALID_PARAMETER;
1767 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1768 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1769 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
1771 /* we are expecting null terminated strings to be passed */
1772 ASSERT(portName[portNameLen - 1] == '\0');
1774 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
1776 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1778 nlError = NL_ERROR_EXIST;
1782 if (portName && portType == OVS_VPORT_TYPE_NETDEV &&
1783 !strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
1784 addInternalPortAsNetdev = TRUE;
1787 if (portName && portType == OVS_VPORT_TYPE_INTERNAL &&
1788 strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
1789 isBridgeInternal = TRUE;
1792 if (portType == OVS_VPORT_TYPE_INTERNAL && !isBridgeInternal) {
1793 vport = gOvsSwitchContext->internalVport;
1794 } else if (portType == OVS_VPORT_TYPE_NETDEV) {
1795 /* External ports can also be looked up like VIF ports. */
1796 vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
1798 ASSERT(OvsIsTunnelVportType(portType) ||
1799 (portType == OVS_VPORT_TYPE_INTERNAL && isBridgeInternal));
1800 ASSERT(OvsGetTunnelVport(gOvsSwitchContext, portType) == NULL ||
1801 !OvsIsTunnelVportType(portType));
1803 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
1804 if (vport == NULL) {
1805 nlError = NL_ERROR_NOMEM;
1808 vportAllocated = TRUE;
1810 if (OvsIsTunnelVportType(portType)) {
1811 status = OvsInitTunnelVport(vport, portType, VXLAN_UDP_PORT);
1812 nlError = NlMapStatusToNlErr(status);
1814 OvsInitBridgeInternalVport(vport);
1816 vportInitialized = TRUE;
1818 if (nlError == NL_ERROR_SUCCESS) {
1819 vport->ovsState = OVS_STATE_CONNECTED;
1820 vport->nicState = NdisSwitchNicStateConnected;
1823 * Allow the vport to be deleted, because there is no
1824 * corresponding hyper-v switch part.
1826 vport->isPresentOnHv = TRUE;
1831 nlError = NL_ERROR_INVAL;
1834 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
1835 nlError = NL_ERROR_EXIST;
1839 /* Initialize the vport with OVS specific properties. */
1840 if (addInternalPortAsNetdev != TRUE) {
1841 vport->ovsType = portType;
1843 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1845 * XXX: when we implement the limit for ovs port number to be
1846 * MAXUINT16, we'll need to check the port number received from the
1849 vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1851 vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
1852 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1853 nlError = NL_ERROR_NOMEM;
1858 /* The ovs port name must be uninitialized. */
1859 ASSERT(vport->ovsName[0] == '\0');
1860 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
1862 RtlCopyMemory(vport->ovsName, portName, portNameLen);
1863 /* if we don't have options, then vport->portOptions will be NULL */
1864 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
1867 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1868 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1869 * it means we have an array of pids, instead of a single pid.
1870 * ATM we assume we have one pid only.
1872 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
1874 status = InitOvsVportCommon(gOvsSwitchContext, vport);
1875 ASSERT(status == STATUS_SUCCESS);
1877 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1878 usrParamsCtx->outputLength,
1879 gOvsSwitchContext->dpNo);
1881 *replyLen = msgOut->nlMsg.nlmsgLen;
1884 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1886 if (nlError != NL_ERROR_SUCCESS) {
1887 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1888 usrParamsCtx->outputBuffer;
1890 if (vport && vportAllocated == TRUE) {
1891 if (vportInitialized == TRUE) {
1892 if (OvsIsTunnelVportType(portType)) {
1893 OvsCleanupVxlanTunnel(vport);
1896 OvsFreeMemory(vport);
1899 BuildErrorMsg(msgIn, msgError, nlError);
1900 *replyLen = msgError->nlMsg.nlmsgLen;
1903 return STATUS_SUCCESS;
1908 * --------------------------------------------------------------------------
1909 * Command Handler for 'OVS_VPORT_CMD_SET'.
1910 * --------------------------------------------------------------------------
1913 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1916 NDIS_STATUS status = STATUS_SUCCESS;
1917 LOCK_STATE_EX lockState;
1919 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1920 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1921 POVS_VPORT_ENTRY vport = NULL;
1922 NL_ERROR nlError = NL_ERROR_SUCCESS;
1924 static const NL_POLICY ovsVportPolicy[] = {
1925 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1926 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
1927 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
1929 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
1931 [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
1932 .minLen = sizeof(OVS_VPORT_FULL_STATS),
1933 .maxLen = sizeof(OVS_VPORT_FULL_STATS),
1935 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
1937 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1939 ASSERT(usrParamsCtx->inputBuffer != NULL);
1941 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1942 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1943 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1944 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1945 return STATUS_INVALID_PARAMETER;
1948 /* Output buffer has been validated while validating transact dev op. */
1949 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1951 OvsAcquireCtrlLock();
1953 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
1954 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1955 PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1957 UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1959 /* the port name is expected to be null-terminated */
1960 ASSERT(portName[portNameLen - 1] == '\0');
1962 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1963 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1964 vport = OvsFindVportByPortNo(gOvsSwitchContext,
1965 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
1969 nlError = NL_ERROR_NODEV;
1974 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1975 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1976 * it means we have an array of pids, instead of a single pid.
1977 * Currently, we support only one pid.
1979 if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
1980 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
1983 if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
1984 OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
1985 if (type != vport->ovsType) {
1986 nlError = NL_ERROR_INVAL;
1991 if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
1992 OVS_LOG_ERROR("Vport options not supported");
1993 nlError = NL_ERROR_NOTSUPP;
1997 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1998 usrParamsCtx->outputLength,
1999 gOvsSwitchContext->dpNo);
2001 *replyLen = msgOut->nlMsg.nlmsgLen;
2004 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2005 OvsReleaseCtrlLock();
2007 if (nlError != NL_ERROR_SUCCESS) {
2008 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2009 usrParamsCtx->outputBuffer;
2011 BuildErrorMsg(msgIn, msgError, nlError);
2012 *replyLen = msgError->nlMsg.nlmsgLen;
2015 return STATUS_SUCCESS;
2019 * --------------------------------------------------------------------------
2020 * Command Handler for 'OVS_VPORT_CMD_DEL'.
2021 * --------------------------------------------------------------------------
2024 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2027 NDIS_STATUS status = STATUS_SUCCESS;
2028 LOCK_STATE_EX lockState;
2030 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2031 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2032 POVS_VPORT_ENTRY vport = NULL;
2033 NL_ERROR nlError = NL_ERROR_SUCCESS;
2034 PSTR portName = NULL;
2035 UINT32 portNameLen = 0;
2037 static const NL_POLICY ovsVportPolicy[] = {
2038 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2039 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2042 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2044 ASSERT(usrParamsCtx->inputBuffer != NULL);
2046 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2047 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2048 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2049 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2050 return STATUS_INVALID_PARAMETER;
2053 /* Output buffer has been validated while validating transact dev op. */
2054 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2056 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2057 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2058 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2059 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2061 /* the port name is expected to be null-terminated */
2062 ASSERT(portName[portNameLen - 1] == '\0');
2064 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2066 else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2067 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2068 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2072 nlError = NL_ERROR_NODEV;
2076 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2077 usrParamsCtx->outputLength,
2078 gOvsSwitchContext->dpNo);
2081 * Mark the port as deleted from OVS userspace. If the port does not exist
2082 * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
2084 OvsRemoveAndDeleteVport(gOvsSwitchContext, vport, FALSE, TRUE, NULL);
2086 *replyLen = msgOut->nlMsg.nlmsgLen;
2089 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2091 if (nlError != NL_ERROR_SUCCESS) {
2092 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2093 usrParamsCtx->outputBuffer;
2095 BuildErrorMsg(msgIn, msgError, nlError);
2096 *replyLen = msgError->nlMsg.nlmsgLen;
2099 return STATUS_SUCCESS;
2104 * --------------------------------------------------------------------------
2105 * Utility function to map the output buffer in an IRP. The buffer is assumed
2106 * to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
2107 * --------------------------------------------------------------------------
2110 MapIrpOutputBuffer(PIRP irp,
2111 UINT32 bufferLength,
2112 UINT32 requiredLength,
2117 ASSERT(bufferLength);
2118 ASSERT(requiredLength);
2119 if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
2120 return STATUS_INVALID_PARAMETER;
2123 if (bufferLength < requiredLength) {
2124 return STATUS_NDIS_INVALID_LENGTH;
2126 if (irp->MdlAddress == NULL) {
2127 return STATUS_INVALID_PARAMETER;
2129 *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
2130 NormalPagePriority);
2131 if (*buffer == NULL) {
2132 return STATUS_INSUFFICIENT_RESOURCES;
2135 return STATUS_SUCCESS;
2139 * --------------------------------------------------------------------------
2140 * Utility function to fill up information about the state of a port in a reply
2142 * Assumes that 'gOvsCtrlLock' lock is acquired.
2143 * --------------------------------------------------------------------------
2146 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2147 POVS_EVENT_ENTRY eventEntry,
2152 OVS_MESSAGE msgOutTmp;
2154 POVS_VPORT_ENTRY vport;
2156 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
2158 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
2159 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
2161 /* driver intiated messages should have zerp seq number*/
2162 msgOutTmp.nlMsg.nlmsgSeq = 0;
2163 msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
2165 msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
2166 msgOutTmp.genlMsg.reserved = 0;
2168 /* we don't have netdev yet, treat link up/down a adding/removing a port*/
2169 if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
2170 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
2171 } else if (eventEntry->status &
2172 (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
2173 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
2176 return STATUS_UNSUCCESSFUL;
2178 msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
2180 rc = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
2182 status = STATUS_INVALID_BUFFER_SIZE;
2186 vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
2188 status = STATUS_DEVICE_DOES_NOT_EXIST;
2192 rc = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) ||
2193 NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) ||
2194 NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
2196 status = STATUS_INVALID_BUFFER_SIZE;
2200 /* XXXX Should we add the port stats attributes?*/
2201 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
2202 nlMsg->nlmsgLen = NlBufSize(nlBuf);
2203 status = STATUS_SUCCESS;
2211 * --------------------------------------------------------------------------
2212 * Handler for reading events from the driver event queue. This handler is
2213 * executed when user modes issues a socket receive on a socket assocaited
2214 * with the MC group for events.
2215 * XXX user mode should read multiple events in one system call
2216 * --------------------------------------------------------------------------
2219 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2223 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2224 POVS_OPEN_INSTANCE instance =
2225 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
2229 OVS_EVENT_ENTRY eventEntry;
2231 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
2233 /* Should never read events with a dump socket */
2234 ASSERT(instance->dumpState.ovsMsg == NULL);
2236 /* Must have an event queue */
2237 ASSERT(instance->eventQueue != NULL);
2239 /* Output buffer has been validated while validating read dev op. */
2240 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2242 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
2244 OvsAcquireCtrlLock();
2246 /* remove an event entry from the event queue */
2247 status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
2248 if (status != STATUS_SUCCESS) {
2249 /* If there were not elements, read should return no data. */
2250 status = STATUS_SUCCESS;
2255 status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
2256 if (status == NDIS_STATUS_SUCCESS) {
2257 *replyLen = NlBufSize(&nlBuf);
2261 OvsReleaseCtrlLock();
2266 * --------------------------------------------------------------------------
2267 * Handler for reading missed pacckets from the driver event queue. This
2268 * handler is executed when user modes issues a socket receive on a socket
2269 * --------------------------------------------------------------------------
2272 OvsReadPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2276 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2278 POVS_OPEN_INSTANCE instance =
2279 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
2282 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
2284 /* Should never read events with a dump socket */
2285 ASSERT(instance->dumpState.ovsMsg == NULL);
2287 /* Must have an packet queue */
2288 ASSERT(instance->packetQueue != NULL);
2290 /* Output buffer has been validated while validating read dev op. */
2291 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2293 /* Read a packet from the instance queue */
2294 status = OvsReadDpIoctl(instance->fileObject, usrParamsCtx->outputBuffer,
2295 usrParamsCtx->outputLength, replyLen);
2300 * --------------------------------------------------------------------------
2301 * Handler for the subscription for a packet queue
2302 * --------------------------------------------------------------------------
2305 OvsSubscribePacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2312 const NL_POLICY policy[] = {
2313 [OVS_NL_ATTR_PACKET_PID] = {.type = NL_A_U32 },
2314 [OVS_NL_ATTR_PACKET_SUBSCRIBE] = {.type = NL_A_U8 }
2316 PNL_ATTR attrs[ARRAY_SIZE(policy)];
2318 UNREFERENCED_PARAMETER(replyLen);
2320 POVS_OPEN_INSTANCE instance =
2321 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
2322 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2324 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
2325 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, ARRAY_SIZE(attrs));
2327 status = STATUS_INVALID_PARAMETER;
2331 join = NlAttrGetU8(attrs[OVS_NL_ATTR_PACKET_PID]);
2332 pid = NlAttrGetU32(attrs[OVS_NL_ATTR_PACKET_PID]);
2334 /* The socket subscribed with must be the same socket we perform receive*/
2335 ASSERT(pid == instance->pid);
2337 status = OvsSubscribeDpIoctl(instance, pid, join);
2340 * XXX Need to add this instance to a global data structure
2341 * which hold all packet based instances. The data structure (hash)
2342 * should be searched through the pid field of the instance for
2343 * placing the missed packet into the correct queue
2350 * --------------------------------------------------------------------------
2351 * Handler for queueing an IRP used for missed packet notification. The IRP is
2352 * completed when a packet received and mismatched. STATUS_PENDING is returned
2353 * on success. User mode keep a pending IRP at all times.
2354 * --------------------------------------------------------------------------
2357 OvsPendPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2360 UNREFERENCED_PARAMETER(replyLen);
2362 POVS_OPEN_INSTANCE instance =
2363 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
2366 * XXX access to packet queue must be through acquiring a lock as user mode
2367 * could unsubscribe and the instnace will be freed.
2369 return OvsWaitDpIoctl(usrParamsCtx->irp, instance->fileObject);