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,
94 OvsReadPacketCmdHandler,
98 OvsGetVportCmdHandler,
99 OvsSetVportCmdHandler,
100 OvsNewVportCmdHandler,
101 OvsDeleteVportCmdHandler;
103 NetlinkCmdHandler OvsGetNetdevCmdHandler;
105 static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
107 static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
109 static NTSTATUS HandleDpTransactionCommon(
110 POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen);
113 * The various netlink families, along with the supported commands. Most of
114 * these families and commands are part of the openvswitch specification for a
115 * netlink datapath. In addition, each platform can implement a few families
116 * and commands as extensions.
119 /* Netlink control family: this is a Windows specific family. */
120 NETLINK_CMD nlControlFamilyCmdOps[] = {
121 { .cmd = OVS_CTRL_CMD_WIN_GET_PID,
122 .handler = OvsGetPidCmdHandler,
123 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
124 .validateDpIndex = FALSE,
126 { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ,
127 .handler = OvsPendEventCmdHandler,
128 .supportedDevOp = OVS_WRITE_DEV_OP,
129 .validateDpIndex = TRUE,
131 { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ,
132 .handler = OvsSubscribeEventCmdHandler,
133 .supportedDevOp = OVS_WRITE_DEV_OP,
134 .validateDpIndex = TRUE,
136 { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY,
137 .handler = OvsReadEventCmdHandler,
138 .supportedDevOp = OVS_READ_EVENT_DEV_OP,
139 .validateDpIndex = FALSE,
141 { .cmd = OVS_CTRL_CMD_READ_NOTIFY,
142 .handler = OvsReadPacketCmdHandler,
143 .supportedDevOp = OVS_READ_PACKET_DEV_OP,
144 .validateDpIndex = FALSE,
148 NETLINK_FAMILY nlControlFamilyOps = {
149 .name = OVS_WIN_CONTROL_FAMILY,
150 .id = OVS_WIN_NL_CTRL_FAMILY_ID,
151 .version = OVS_WIN_CONTROL_VERSION,
152 .maxAttr = OVS_WIN_CONTROL_ATTR_MAX,
153 .cmds = nlControlFamilyCmdOps,
154 .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
157 /* Netlink datapath family. */
158 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
159 { .cmd = OVS_DP_CMD_NEW,
160 .handler = OvsNewDpCmdHandler,
161 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
162 .validateDpIndex = FALSE
164 { .cmd = OVS_DP_CMD_GET,
165 .handler = OvsGetDpCmdHandler,
166 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
167 OVS_TRANSACTION_DEV_OP,
168 .validateDpIndex = FALSE
170 { .cmd = OVS_DP_CMD_SET,
171 .handler = OvsSetDpCmdHandler,
172 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
173 OVS_TRANSACTION_DEV_OP,
174 .validateDpIndex = TRUE
178 NETLINK_FAMILY nlDatapathFamilyOps = {
179 .name = OVS_DATAPATH_FAMILY,
180 .id = OVS_WIN_NL_DATAPATH_FAMILY_ID,
181 .version = OVS_DATAPATH_VERSION,
182 .maxAttr = OVS_DP_ATTR_MAX,
183 .cmds = nlDatapathFamilyCmdOps,
184 .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
187 /* Netlink packet family. */
189 NETLINK_CMD nlPacketFamilyCmdOps[] = {
190 { .cmd = OVS_PACKET_CMD_EXECUTE,
191 .handler = OvsNlExecuteCmdHandler,
192 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
193 .validateDpIndex = TRUE
197 NETLINK_FAMILY nlPacketFamilyOps = {
198 .name = OVS_PACKET_FAMILY,
199 .id = OVS_WIN_NL_PACKET_FAMILY_ID,
200 .version = OVS_PACKET_VERSION,
201 .maxAttr = OVS_PACKET_ATTR_MAX,
202 .cmds = nlPacketFamilyCmdOps,
203 .opsCount = ARRAY_SIZE(nlPacketFamilyCmdOps)
206 /* Netlink vport family. */
207 NETLINK_CMD nlVportFamilyCmdOps[] = {
208 { .cmd = OVS_VPORT_CMD_GET,
209 .handler = OvsGetVportCmdHandler,
210 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
211 OVS_TRANSACTION_DEV_OP,
212 .validateDpIndex = TRUE
214 { .cmd = OVS_VPORT_CMD_NEW,
215 .handler = OvsNewVportCmdHandler,
216 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
217 .validateDpIndex = TRUE
219 { .cmd = OVS_VPORT_CMD_SET,
220 .handler = OvsSetVportCmdHandler,
221 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
222 .validateDpIndex = TRUE
224 { .cmd = OVS_VPORT_CMD_DEL,
225 .handler = OvsDeleteVportCmdHandler,
226 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
227 .validateDpIndex = TRUE
231 NETLINK_FAMILY nlVportFamilyOps = {
232 .name = OVS_VPORT_FAMILY,
233 .id = OVS_WIN_NL_VPORT_FAMILY_ID,
234 .version = OVS_VPORT_VERSION,
235 .maxAttr = OVS_VPORT_ATTR_MAX,
236 .cmds = nlVportFamilyCmdOps,
237 .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps)
240 /* Netlink flow family. */
242 NETLINK_CMD nlFlowFamilyCmdOps[] = {
243 { .cmd = OVS_FLOW_CMD_NEW,
244 .handler = OvsFlowNlCmdHandler,
245 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
246 .validateDpIndex = TRUE
248 { .cmd = OVS_FLOW_CMD_SET,
249 .handler = OvsFlowNlCmdHandler,
250 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
251 .validateDpIndex = TRUE
253 { .cmd = OVS_FLOW_CMD_DEL,
254 .handler = OvsFlowNlCmdHandler,
255 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
256 .validateDpIndex = TRUE
258 { .cmd = OVS_FLOW_CMD_GET,
259 .handler = OvsFlowNlGetCmdHandler,
260 .supportedDevOp = OVS_TRANSACTION_DEV_OP |
261 OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
262 .validateDpIndex = TRUE
266 NETLINK_FAMILY nlFLowFamilyOps = {
267 .name = OVS_FLOW_FAMILY,
268 .id = OVS_WIN_NL_FLOW_FAMILY_ID,
269 .version = OVS_FLOW_VERSION,
270 .maxAttr = OVS_FLOW_ATTR_MAX,
271 .cmds = nlFlowFamilyCmdOps,
272 .opsCount = ARRAY_SIZE(nlFlowFamilyCmdOps)
275 /* Netlink netdev family. */
276 NETLINK_CMD nlNetdevFamilyCmdOps[] = {
277 { .cmd = OVS_WIN_NETDEV_CMD_GET,
278 .handler = OvsGetNetdevCmdHandler,
279 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
280 .validateDpIndex = FALSE
284 NETLINK_FAMILY nlNetdevFamilyOps = {
285 .name = OVS_WIN_NETDEV_FAMILY,
286 .id = OVS_WIN_NL_NETDEV_FAMILY_ID,
287 .version = OVS_WIN_NETDEV_VERSION,
288 .maxAttr = OVS_WIN_NETDEV_ATTR_MAX,
289 .cmds = nlNetdevFamilyCmdOps,
290 .opsCount = ARRAY_SIZE(nlNetdevFamilyCmdOps)
293 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
295 UINT32 requiredLength,
297 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
298 POVS_OPEN_INSTANCE instance,
300 NETLINK_FAMILY *nlFamilyOps);
301 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
302 NETLINK_FAMILY *nlFamilyOps,
305 /* Handles to the device object for communication with userspace. */
306 NDIS_HANDLE gOvsDeviceHandle;
307 PDEVICE_OBJECT gOvsDeviceObject;
309 _Dispatch_type_(IRP_MJ_CREATE)
310 _Dispatch_type_(IRP_MJ_CLOSE)
311 DRIVER_DISPATCH OvsOpenCloseDevice;
313 _Dispatch_type_(IRP_MJ_CLEANUP)
314 DRIVER_DISPATCH OvsCleanupDevice;
316 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
317 DRIVER_DISPATCH OvsDeviceControl;
320 #pragma alloc_text(INIT, OvsCreateDeviceObject)
321 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
322 #pragma alloc_text(PAGE, OvsCleanupDevice)
323 #pragma alloc_text(PAGE, OvsDeviceControl)
324 #endif // ALLOC_PRAGMA
327 * We might hit this limit easily since userspace opens a netlink descriptor for
328 * each thread, and at least one descriptor per vport. Revisit this later.
330 #define OVS_MAX_OPEN_INSTANCES 512
331 #define OVS_SYSTEM_DP_NAME "ovs-system"
333 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
334 UINT32 ovsNumberOfOpenInstances;
335 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
337 NDIS_SPIN_LOCK ovsCtrlLockObj;
338 PNDIS_SPIN_LOCK gOvsCtrlLock;
344 gOvsCtrlLock = &ovsCtrlLockObj;
345 NdisAllocateSpinLock(gOvsCtrlLock);
353 OvsCleanupEventQueue();
355 NdisFreeSpinLock(gOvsCtrlLock);
364 NdisAcquireSpinLock(gOvsCtrlLock);
370 NdisReleaseSpinLock(gOvsCtrlLock);
375 * --------------------------------------------------------------------------
376 * Creates the communication device between user and kernel, and also
377 * initializes the data associated data structures.
378 * --------------------------------------------------------------------------
381 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
383 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
384 UNICODE_STRING deviceName;
385 UNICODE_STRING symbolicDeviceName;
386 PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
387 NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
388 OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
390 RtlZeroMemory(dispatchTable,
391 (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
392 dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
393 dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
394 dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
395 dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
397 NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
398 NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
400 RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
402 OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
403 NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
404 NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
405 sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
407 deviceAttributes.DeviceName = &deviceName;
408 deviceAttributes.SymbolicName = &symbolicDeviceName;
409 deviceAttributes.MajorFunctions = dispatchTable;
410 deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
412 status = NdisRegisterDeviceEx(ovsExtDriverHandle,
416 if (status != NDIS_STATUS_SUCCESS) {
417 POVS_DEVICE_EXTENSION ovsExt =
418 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
419 ASSERT(gOvsDeviceObject != NULL);
420 ASSERT(gOvsDeviceHandle != NULL);
423 ovsExt->numberOpenInstance = 0;
426 /* Initialize the associated data structures. */
429 OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
435 OvsDeleteDeviceObject()
437 if (gOvsDeviceHandle) {
439 POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
440 NdisGetDeviceReservedExtension(gOvsDeviceObject);
442 ASSERT(ovsExt->numberOpenInstance == 0);
446 ASSERT(gOvsDeviceObject);
447 NdisDeregisterDeviceEx(gOvsDeviceHandle);
448 gOvsDeviceHandle = NULL;
449 gOvsDeviceObject = NULL;
455 OvsGetOpenInstance(PFILE_OBJECT fileObject,
458 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
460 ASSERT(instance->fileObject == fileObject);
461 if (gOvsSwitchContext == NULL ||
462 gOvsSwitchContext->dpNo != dpNo) {
470 OvsFindOpenInstance(PFILE_OBJECT fileObject)
473 for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
474 j < ovsNumberOfOpenInstances; i++) {
475 if (ovsOpenInstanceArray[i]) {
476 if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
477 return ovsOpenInstanceArray[i];
486 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
487 PFILE_OBJECT fileObject)
489 POVS_OPEN_INSTANCE instance =
490 (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
493 if (instance == NULL) {
494 return STATUS_NO_MEMORY;
496 OvsAcquireCtrlLock();
497 ASSERT(OvsFindOpenInstance(fileObject) == NULL);
499 if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
500 OvsReleaseCtrlLock();
501 OvsFreeMemory(instance);
502 return STATUS_INSUFFICIENT_RESOURCES;
504 RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
506 for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
507 if (ovsOpenInstanceArray[i] == NULL) {
508 ovsOpenInstanceArray[i] = instance;
509 ovsNumberOfOpenInstances++;
510 instance->cookie = i;
514 ASSERT(i < OVS_MAX_OPEN_INSTANCES);
515 instance->fileObject = fileObject;
516 ASSERT(fileObject->FsContext == NULL);
517 instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
518 if (instance->pid == 0) {
519 /* XXX: check for rollover. */
521 fileObject->FsContext = instance;
522 OvsReleaseCtrlLock();
523 return STATUS_SUCCESS;
527 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
529 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
531 ASSERT(fileObject == instance->fileObject);
532 OvsCleanupEvent(instance);
533 OvsCleanupPacketQueue(instance);
537 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
539 POVS_OPEN_INSTANCE instance;
540 ASSERT(fileObject->FsContext);
541 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
542 ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
544 OvsAcquireCtrlLock();
545 fileObject->FsContext = NULL;
546 ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
547 ovsOpenInstanceArray[instance->cookie] = NULL;
548 ovsNumberOfOpenInstances--;
549 OvsReleaseCtrlLock();
550 ASSERT(instance->eventQueue == NULL);
551 ASSERT (instance->packetQueue == NULL);
552 OvsFreeMemory(instance);
556 OvsCompleteIrpRequest(PIRP irp,
560 irp->IoStatus.Information = infoPtr;
561 irp->IoStatus.Status = status;
562 IoCompleteRequest(irp, IO_NO_INCREMENT);
568 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
571 PIO_STACK_LOCATION irpSp;
572 NTSTATUS status = STATUS_SUCCESS;
573 PFILE_OBJECT fileObject;
574 POVS_DEVICE_EXTENSION ovsExt =
575 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
577 ASSERT(deviceObject == gOvsDeviceObject);
578 ASSERT(ovsExt != NULL);
580 irpSp = IoGetCurrentIrpStackLocation(irp);
581 fileObject = irpSp->FileObject;
582 OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
583 deviceObject, fileObject,
584 ovsExt->numberOpenInstance);
586 switch (irpSp->MajorFunction) {
588 status = OvsAddOpenInstance(ovsExt, fileObject);
589 if (STATUS_SUCCESS == status) {
590 InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
594 ASSERT(ovsExt->numberOpenInstance > 0);
595 OvsRemoveOpenInstance(fileObject);
596 InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
601 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
604 _Use_decl_annotations_
606 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
610 PIO_STACK_LOCATION irpSp;
611 PFILE_OBJECT fileObject;
613 NTSTATUS status = STATUS_SUCCESS;
615 POVS_DEVICE_EXTENSION ovsExt =
616 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
618 ASSERT(ovsExt->numberOpenInstance > 0);
621 UNREFERENCED_PARAMETER(deviceObject);
623 ASSERT(deviceObject == gOvsDeviceObject);
624 irpSp = IoGetCurrentIrpStackLocation(irp);
625 fileObject = irpSp->FileObject;
627 ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
629 OvsCleanupOpenInstance(fileObject);
631 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
636 * --------------------------------------------------------------------------
637 * IOCTL function handler for the device.
638 * --------------------------------------------------------------------------
641 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
645 PIO_STACK_LOCATION irpSp;
646 NTSTATUS status = STATUS_SUCCESS;
647 PFILE_OBJECT fileObject;
648 PVOID inputBuffer = NULL;
649 PVOID outputBuffer = NULL;
650 UINT32 inputBufferLen, outputBufferLen;
651 UINT32 code, replyLen = 0;
652 POVS_OPEN_INSTANCE instance;
654 OVS_MESSAGE ovsMsgReadOp;
656 NETLINK_FAMILY *nlFamilyOps;
657 OVS_USER_PARAMS_CONTEXT usrParamsCtx;
660 POVS_DEVICE_EXTENSION ovsExt =
661 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
662 ASSERT(deviceObject == gOvsDeviceObject);
664 ASSERT(ovsExt->numberOpenInstance > 0);
666 UNREFERENCED_PARAMETER(deviceObject);
669 irpSp = IoGetCurrentIrpStackLocation(irp);
671 ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
672 ASSERT(irpSp->FileObject != NULL);
674 fileObject = irpSp->FileObject;
675 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
676 code = irpSp->Parameters.DeviceIoControl.IoControlCode;
677 inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
678 outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
679 inputBuffer = irp->AssociatedIrp.SystemBuffer;
681 /* Concurrent netlink operations are not supported. */
682 if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
683 status = STATUS_RESOURCE_IN_USE;
688 * Validate the input/output buffer arguments depending on the type of the
692 case OVS_IOCTL_TRANSACT:
693 /* Input buffer is mandatory, output buffer is optional. */
694 if (outputBufferLen != 0) {
695 status = MapIrpOutputBuffer(irp, outputBufferLen,
696 sizeof *ovsMsg, &outputBuffer);
697 if (status != STATUS_SUCCESS) {
700 ASSERT(outputBuffer);
703 if (inputBufferLen < sizeof (*ovsMsg)) {
704 status = STATUS_NDIS_INVALID_LENGTH;
708 ovsMsg = inputBuffer;
709 devOp = OVS_TRANSACTION_DEV_OP;
712 case OVS_IOCTL_READ_EVENT:
713 case OVS_IOCTL_READ_PACKET:
714 /* This IOCTL is used to read events */
715 if (outputBufferLen != 0) {
716 status = MapIrpOutputBuffer(irp, outputBufferLen,
717 sizeof *ovsMsg, &outputBuffer);
718 if (status != STATUS_SUCCESS) {
721 ASSERT(outputBuffer);
723 status = STATUS_NDIS_INVALID_LENGTH;
729 ovsMsg = &ovsMsgReadOp;
730 ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
731 /* An "artificial" command so we can use NL family function table*/
732 ovsMsg->genlMsg.cmd = (code == OVS_IOCTL_READ_EVENT) ?
733 OVS_CTRL_CMD_EVENT_NOTIFY :
734 OVS_CTRL_CMD_READ_NOTIFY;
735 devOp = OVS_READ_DEV_OP;
739 /* Output buffer is mandatory. */
740 if (outputBufferLen != 0) {
741 status = MapIrpOutputBuffer(irp, outputBufferLen,
742 sizeof *ovsMsg, &outputBuffer);
743 if (status != STATUS_SUCCESS) {
746 ASSERT(outputBuffer);
748 status = STATUS_NDIS_INVALID_LENGTH;
753 * Operate in the mode that read ioctl is similar to ReadFile(). This
754 * might change as the userspace code gets implemented.
760 * For implementing read (ioctl or otherwise), we need to store some
761 * state in the instance to indicate the command that started the dump
762 * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
763 * that 'ovsMsgReadOp' is needed only in this function to call into the
764 * appropraite handler. The handler itself can access the state in the
767 * In the absence of a dump start, return 0 bytes.
769 if (instance->dumpState.ovsMsg == NULL) {
771 status = STATUS_SUCCESS;
774 RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
775 sizeof (ovsMsgReadOp));
777 /* Create an NL message for consumption. */
778 ovsMsg = &ovsMsgReadOp;
779 devOp = OVS_READ_DEV_OP;
783 case OVS_IOCTL_WRITE:
784 /* Input buffer is mandatory. */
785 if (inputBufferLen < sizeof (*ovsMsg)) {
786 status = STATUS_NDIS_INVALID_LENGTH;
790 ovsMsg = inputBuffer;
791 devOp = OVS_WRITE_DEV_OP;
795 status = STATUS_INVALID_DEVICE_REQUEST;
800 switch (ovsMsg->nlMsg.nlmsgType) {
801 case OVS_WIN_NL_CTRL_FAMILY_ID:
802 nlFamilyOps = &nlControlFamilyOps;
804 case OVS_WIN_NL_DATAPATH_FAMILY_ID:
805 nlFamilyOps = &nlDatapathFamilyOps;
807 case OVS_WIN_NL_FLOW_FAMILY_ID:
808 nlFamilyOps = &nlFLowFamilyOps;
810 case OVS_WIN_NL_PACKET_FAMILY_ID:
811 nlFamilyOps = &nlPacketFamilyOps;
813 case OVS_WIN_NL_VPORT_FAMILY_ID:
814 nlFamilyOps = &nlVportFamilyOps;
816 case OVS_WIN_NL_NETDEV_FAMILY_ID:
817 nlFamilyOps = &nlNetdevFamilyOps;
820 status = STATUS_INVALID_PARAMETER;
825 * For read operation, the netlink command has already been validated
828 if (devOp != OVS_READ_DEV_OP) {
829 status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
830 if (status != STATUS_SUCCESS) {
835 InitUserParamsCtx(irp, instance, devOp, ovsMsg,
836 inputBuffer, inputBufferLen,
837 outputBuffer, outputBufferLen,
840 status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
845 return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
850 * --------------------------------------------------------------------------
851 * Function to validate a netlink command. Only certain combinations of
852 * (device operation, netlink family, command) are valid.
853 * --------------------------------------------------------------------------
856 ValidateNetlinkCmd(UINT32 devOp,
857 POVS_OPEN_INSTANCE instance,
859 NETLINK_FAMILY *nlFamilyOps)
861 NTSTATUS status = STATUS_INVALID_PARAMETER;
864 for (i = 0; i < nlFamilyOps->opsCount; i++) {
865 if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
866 /* Validate if the command is valid for the device operation. */
867 if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
868 status = STATUS_INVALID_PARAMETER;
872 /* Validate the version. */
873 if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
874 status = STATUS_INVALID_PARAMETER;
878 /* Validate the DP for commands that require a DP. */
879 if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
880 OvsAcquireCtrlLock();
881 if (ovsMsg->ovsHdr.dp_ifindex !=
882 (INT)gOvsSwitchContext->dpNo) {
883 status = STATUS_INVALID_PARAMETER;
884 OvsReleaseCtrlLock();
887 OvsReleaseCtrlLock();
890 /* Validate the PID. */
891 if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
892 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
893 status = STATUS_INVALID_PARAMETER;
898 status = STATUS_SUCCESS;
908 * --------------------------------------------------------------------------
909 * Function to invoke the netlink command handler.
910 * --------------------------------------------------------------------------
913 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
914 NETLINK_FAMILY *nlFamilyOps,
917 NTSTATUS status = STATUS_INVALID_PARAMETER;
920 for (i = 0; i < nlFamilyOps->opsCount; i++) {
921 if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
922 NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
925 status = handler(usrParamsCtx, replyLen);
935 * --------------------------------------------------------------------------
936 * Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
938 * Each handle on the device is assigned a unique PID when the handle is
939 * created. On platforms that support netlink natively, the PID is available
940 * to userspace when the netlink socket is created. However, without native
941 * netlink support on Windows, OVS datapath generates the PID and lets the
942 * userspace query it.
944 * This function implements the query.
945 * --------------------------------------------------------------------------
948 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
951 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
952 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
954 if (usrParamsCtx->outputLength >= sizeof *msgOut) {
955 POVS_OPEN_INSTANCE instance =
956 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
958 RtlZeroMemory(msgOut, sizeof *msgOut);
959 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
960 msgOut->nlMsg.nlmsgPid = instance->pid;
961 *replyLen = sizeof *msgOut;
962 /* XXX: We might need to return the DP index as well. */
964 return STATUS_NDIS_INVALID_LENGTH;
967 return STATUS_SUCCESS;
971 * --------------------------------------------------------------------------
972 * Utility function to fill up information about the datapath in a reply to
974 * Assumes that 'gOvsCtrlLock' lock is acquired.
975 * --------------------------------------------------------------------------
978 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
983 OVS_MESSAGE msgOutTmp;
984 OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
987 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && NlBufRemLen(nlBuf) >= sizeof *msgIn);
989 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
990 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
991 msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
992 msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
994 msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
995 msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
996 msgOutTmp.genlMsg.reserved = 0;
998 msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
1000 writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
1002 writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
1003 OVS_SYSTEM_DP_NAME);
1006 OVS_DP_STATS dpStats;
1008 dpStats.n_hit = datapath->hits;
1009 dpStats.n_missed = datapath->misses;
1010 dpStats.n_lost = datapath->lost;
1011 dpStats.n_flows = datapath->nFlows;
1012 writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
1013 (PCHAR)&dpStats, sizeof dpStats);
1015 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1016 nlMsg->nlmsgLen = NlBufSize(nlBuf);
1018 return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
1022 * --------------------------------------------------------------------------
1023 * Handler for queueing an IRP used for event notification. The IRP is
1024 * completed when a port state changes. STATUS_PENDING is returned on
1025 * success. User mode keep a pending IRP at all times.
1026 * --------------------------------------------------------------------------
1029 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1034 UNREFERENCED_PARAMETER(replyLen);
1036 POVS_OPEN_INSTANCE instance =
1037 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1038 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1039 OVS_EVENT_POLL poll;
1041 poll.dpNo = msgIn->ovsHdr.dp_ifindex;
1042 status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
1043 &poll, sizeof poll);
1048 * --------------------------------------------------------------------------
1049 * Handler for the subscription for the event queue
1050 * --------------------------------------------------------------------------
1053 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1057 OVS_EVENT_SUBSCRIBE request;
1061 const NL_POLICY policy[] = {
1062 [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
1063 [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
1066 UNREFERENCED_PARAMETER(replyLen);
1068 POVS_OPEN_INSTANCE instance =
1069 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1070 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1072 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1073 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, 2);
1075 status = STATUS_INVALID_PARAMETER;
1079 /* XXX Ignore the MC group for now */
1080 join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1081 request.dpNo = msgIn->ovsHdr.dp_ifindex;
1082 request.subscribe = join;
1083 request.mask = OVS_EVENT_MASK_ALL;
1085 status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1092 * --------------------------------------------------------------------------
1093 * Command Handler for 'OVS_DP_CMD_NEW'.
1094 * --------------------------------------------------------------------------
1097 OvsNewDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1100 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1104 * --------------------------------------------------------------------------
1105 * Command Handler for 'OVS_DP_CMD_GET'.
1107 * The function handles both the dump based as well as the transaction based
1108 * 'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1109 * call to setup dump state, as well as subsequent calls to continue dumping
1111 * --------------------------------------------------------------------------
1114 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1117 if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1118 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1120 return HandleGetDpDump(usrParamsCtx, replyLen);
1125 * --------------------------------------------------------------------------
1126 * Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1127 * --------------------------------------------------------------------------
1130 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1133 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1138 * --------------------------------------------------------------------------
1139 * Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1140 * --------------------------------------------------------------------------
1143 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1146 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1147 POVS_OPEN_INSTANCE instance =
1148 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1150 if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1152 OvsSetupDumpStart(usrParamsCtx);
1156 POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1158 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1160 if (instance->dumpState.ovsMsg == NULL) {
1162 return STATUS_INVALID_DEVICE_STATE;
1165 /* Dump state must have been deleted after previous dump operation. */
1166 ASSERT(instance->dumpState.index[0] == 0);
1167 /* Output buffer has been validated while validating read dev op. */
1168 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1170 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1171 usrParamsCtx->outputLength);
1173 OvsAcquireCtrlLock();
1174 if (!gOvsSwitchContext) {
1175 /* Treat this as a dump done. */
1176 OvsReleaseCtrlLock();
1178 FreeUserDumpState(instance);
1179 return STATUS_SUCCESS;
1181 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1182 OvsReleaseCtrlLock();
1184 if (status != STATUS_SUCCESS) {
1186 FreeUserDumpState(instance);
1190 /* Increment the dump index. */
1191 instance->dumpState.index[0] = 1;
1192 *replyLen = msgOut->nlMsg.nlmsgLen;
1194 /* Free up the dump state, since there's no more data to continue. */
1195 FreeUserDumpState(instance);
1198 return STATUS_SUCCESS;
1203 * --------------------------------------------------------------------------
1204 * Command Handler for 'OVS_DP_CMD_SET'.
1205 * --------------------------------------------------------------------------
1208 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1211 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1215 * --------------------------------------------------------------------------
1216 * Function for handling transaction based 'OVS_DP_CMD_NEW', 'OVS_DP_CMD_GET'
1217 * and 'OVS_DP_CMD_SET' commands.
1219 * 'OVS_DP_CMD_NEW' is implemented to keep userspace code happy. Creation of a
1220 * new datapath is not supported currently.
1221 * --------------------------------------------------------------------------
1224 HandleDpTransactionCommon(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1227 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1228 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1229 NTSTATUS status = STATUS_SUCCESS;
1231 NL_ERROR nlError = NL_ERROR_SUCCESS;
1232 static const NL_POLICY ovsDatapathSetPolicy[] = {
1233 [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1234 [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1235 [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1237 PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1239 /* input buffer has been validated while validating write dev op. */
1240 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1242 /* Parse any attributes in the request. */
1243 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET ||
1244 usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1245 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1246 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1247 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1248 ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1249 return STATUS_INVALID_PARAMETER;
1253 * XXX: Not clear at this stage if there's any role for the
1254 * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1259 RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1262 /* Output buffer is optional for OVS_TRANSACTION_DEV_OP. */
1263 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1264 return STATUS_NDIS_INVALID_LENGTH;
1266 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1268 OvsAcquireCtrlLock();
1269 if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1270 if (!gOvsSwitchContext &&
1271 !OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1272 OVS_SYSTEM_DP_NAME)) {
1273 OvsReleaseCtrlLock();
1275 /* Creation of new datapaths is not supported. */
1276 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1277 nlError = NL_ERROR_NOTSUPP;
1281 nlError = NL_ERROR_NODEV;
1284 } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1285 OvsReleaseCtrlLock();
1286 nlError = NL_ERROR_NODEV;
1290 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1291 OvsReleaseCtrlLock();
1292 nlError = NL_ERROR_EXIST;
1296 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1297 OvsReleaseCtrlLock();
1299 *replyLen = NlBufSize(&nlBuf);
1302 if (nlError != NL_ERROR_SUCCESS) {
1303 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1304 usrParamsCtx->outputBuffer;
1306 BuildErrorMsg(msgIn, msgError, nlError);
1307 *replyLen = msgError->nlMsg.nlmsgLen;
1310 return STATUS_SUCCESS;
1315 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1317 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1318 POVS_OPEN_INSTANCE instance =
1319 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1321 /* input buffer has been validated while validating write dev op. */
1322 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1324 /* A write operation that does not indicate dump start is invalid. */
1325 if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1326 return STATUS_INVALID_PARAMETER;
1328 /* XXX: Handle other NLM_F_* flags in the future. */
1331 * This operation should be setting up the dump state. If there's any
1332 * previous state, clear it up so as to set it up afresh.
1334 if (instance->dumpState.ovsMsg != NULL) {
1335 FreeUserDumpState(instance);
1338 return InitUserDumpState(instance, msgIn);
1342 BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
1343 UINT32 length, UINT16 flags)
1345 msgOut->nlMsg.nlmsgType = type;
1346 msgOut->nlMsg.nlmsgFlags = flags;
1347 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1348 msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1349 msgOut->nlMsg.nlmsgLen = length;
1351 msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
1352 msgOut->genlMsg.version = msgIn->genlMsg.version;
1353 msgOut->genlMsg.reserved = 0;
1357 * XXX: should move out these functions to a Netlink.c or to a OvsMessage.c
1358 * or even make them inlined functions in Datapath.h. Can be done after the
1359 * first sprint once we have more code to refactor.
1362 BuildReplyMsgFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
1364 BuildMsgOut(msgIn, msgOut, msgIn->nlMsg.nlmsgType, sizeof(OVS_MESSAGE),
1369 BuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode)
1371 BuildMsgOut(msgIn, (POVS_MESSAGE)msgOut, NLMSG_ERROR,
1372 sizeof(OVS_MESSAGE_ERROR), 0);
1374 msgOut->errorMsg.error = errorCode;
1375 msgOut->errorMsg.nlMsg = msgIn->nlMsg;
1379 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1386 OVS_VPORT_FULL_STATS vportStats;
1391 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1393 BuildReplyMsgFromMsgIn(msgIn, &msgOut, NLM_F_MULTI);
1394 msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1396 ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1398 return STATUS_INSUFFICIENT_RESOURCES;
1401 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1403 return STATUS_INSUFFICIENT_RESOURCES;
1406 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1408 return STATUS_INSUFFICIENT_RESOURCES;
1411 ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1413 return STATUS_INSUFFICIENT_RESOURCES;
1417 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1418 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1419 * it means we have an array of pids, instead of a single pid.
1420 * ATM we assume we have one pid only.
1423 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1426 return STATUS_INSUFFICIENT_RESOURCES;
1430 vportStats.rxPackets = vport->stats.rxPackets;
1431 vportStats.rxBytes = vport->stats.rxBytes;
1432 vportStats.txPackets = vport->stats.txPackets;
1433 vportStats.txBytes = vport->stats.txBytes;
1434 vportStats.rxErrors = vport->errStats.rxErrors;
1435 vportStats.txErrors = vport->errStats.txErrors;
1436 vportStats.rxDropped = vport->errStats.rxDropped;
1437 vportStats.txDropped = vport->errStats.txDropped;
1439 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1441 sizeof(OVS_VPORT_FULL_STATS));
1443 return STATUS_INSUFFICIENT_RESOURCES;
1447 * XXX: when vxlan udp dest port becomes configurable, we will also need
1448 * to add vport options
1451 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1452 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1454 return STATUS_SUCCESS;
1458 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1462 POVS_OPEN_INSTANCE instance =
1463 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1464 LOCK_STATE_EX lockState;
1465 UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1468 * XXX: this function shares some code with other dump command(s).
1469 * In the future, we will need to refactor the dump functions
1472 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1474 if (instance->dumpState.ovsMsg == NULL) {
1476 return STATUS_INVALID_DEVICE_STATE;
1479 /* Output buffer has been validated while validating read dev op. */
1480 ASSERT(usrParamsCtx->outputBuffer != NULL);
1482 msgIn = instance->dumpState.ovsMsg;
1484 OvsAcquireCtrlLock();
1485 if (!gOvsSwitchContext) {
1486 /* Treat this as a dump done. */
1487 OvsReleaseCtrlLock();
1489 FreeUserDumpState(instance);
1490 return STATUS_SUCCESS;
1494 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1495 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1496 * it means we have an array of pids, instead of a single pid.
1497 * ATM we assume we have one pid only.
1499 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1500 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1501 NDIS_RWL_AT_DISPATCH_LEVEL);
1503 if (gOvsSwitchContext->numVports > 0) {
1504 /* inBucket: the bucket, used for lookup */
1505 UINT32 inBucket = instance->dumpState.index[0];
1506 /* inIndex: index within the given bucket, used for lookup */
1507 UINT32 inIndex = instance->dumpState.index[1];
1508 /* the bucket to be used for the next dump operation */
1509 UINT32 outBucket = 0;
1510 /* the index within the outBucket to be used for the next dump */
1511 UINT32 outIndex = 0;
1513 for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1514 PLIST_ENTRY head, link;
1515 head = &(gOvsSwitchContext->portIdHashArray[i]);
1516 POVS_VPORT_ENTRY vport = NULL;
1519 LIST_FORALL(head, link) {
1522 * if one or more dumps were previously done on this same bucket,
1523 * inIndex will be > 0, so we'll need to reply with the
1524 * inIndex + 1 vport from the bucket.
1526 if (outIndex >= inIndex) {
1527 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1529 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
1530 OvsCreateMsgFromVport(vport, msgIn,
1531 usrParamsCtx->outputBuffer,
1532 usrParamsCtx->outputLength,
1533 gOvsSwitchContext->dpNo);
1549 * if no vport was found above, check the next bucket, beginning
1550 * with the first (i.e. index 0) elem from within that bucket
1557 /* XXX: what about NLMSG_DONE (as msg type)? */
1558 instance->dumpState.index[0] = outBucket;
1559 instance->dumpState.index[1] = outIndex;
1562 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1564 OvsReleaseCtrlLock();
1566 /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1567 if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1568 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1569 *replyLen = msgOut->nlMsg.nlmsgLen;
1572 * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1576 /* Free up the dump state, since there's no more data to continue. */
1577 FreeUserDumpState(instance);
1580 return STATUS_SUCCESS;
1584 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1587 NTSTATUS status = STATUS_SUCCESS;
1588 LOCK_STATE_EX lockState;
1590 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1591 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1592 POVS_VPORT_ENTRY vport = NULL;
1593 NL_ERROR nlError = NL_ERROR_SUCCESS;
1594 PCHAR portName = NULL;
1595 UINT32 portNameLen = 0;
1596 UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
1598 static const NL_POLICY ovsVportPolicy[] = {
1599 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1600 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1605 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1607 /* input buffer has been validated while validating write dev op. */
1608 ASSERT(usrParamsCtx->inputBuffer != NULL);
1610 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1611 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1612 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1613 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1614 return STATUS_INVALID_PARAMETER;
1617 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1618 return STATUS_INVALID_BUFFER_SIZE;
1621 OvsAcquireCtrlLock();
1622 if (!gOvsSwitchContext) {
1623 OvsReleaseCtrlLock();
1624 return STATUS_INVALID_PARAMETER;
1626 OvsReleaseCtrlLock();
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;
1740 static const NL_POLICY ovsVportPolicy[] = {
1741 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1742 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
1743 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
1745 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
1746 .optional = FALSE },
1747 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
1750 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1752 /* input buffer has been validated while validating write dev op. */
1753 ASSERT(usrParamsCtx->inputBuffer != NULL);
1755 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1756 return STATUS_INVALID_BUFFER_SIZE;
1759 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1760 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1761 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1762 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1763 return STATUS_INVALID_PARAMETER;
1766 OvsAcquireCtrlLock();
1767 if (!gOvsSwitchContext) {
1768 OvsReleaseCtrlLock();
1769 return STATUS_INVALID_PARAMETER;
1771 OvsReleaseCtrlLock();
1773 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1774 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1775 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
1777 /* we are expecting null terminated strings to be passed */
1778 ASSERT(portName[portNameLen - 1] == '\0');
1780 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
1782 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1784 nlError = NL_ERROR_EXIST;
1788 if (portType == OVS_VPORT_TYPE_INTERNAL) {
1789 vport = gOvsSwitchContext->internalVport;
1790 } else if (portType == OVS_VPORT_TYPE_NETDEV) {
1791 if (!strcmp(portName, "external")) {
1792 vport = gOvsSwitchContext->externalVport;
1794 vport = OvsFindVportByHvName(gOvsSwitchContext, portName);
1797 /* XXX: change when other tunneling ports are added */
1798 ASSERT(portType == OVS_VPORT_TYPE_VXLAN);
1800 if (gOvsSwitchContext->vxlanVport) {
1801 nlError = NL_ERROR_EXIST;
1805 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
1806 if (vport == NULL) {
1807 nlError = NL_ERROR_NOMEM;
1811 vport->ovsState = OVS_STATE_PORT_CREATED;
1814 * XXX: when we allow configuring the vxlan udp port, we should read
1815 * this from vport->options instead!
1817 nlError = OvsInitVxlanTunnel(vport, VXLAN_UDP_PORT);
1818 if (nlError != NL_ERROR_SUCCESS) {
1824 nlError = NL_ERROR_INVAL;
1828 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
1829 nlError = NL_ERROR_EXIST;
1833 /* Fill the data in vport */
1834 vport->ovsType = portType;
1836 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1838 * XXX: when we implement the limit for ovs port number to be
1839 * MAXUINT16, we'll need to check the port number received from the
1842 vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1844 vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
1845 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1846 nlError = NL_ERROR_NOMEM;
1851 /* The ovs port name must be uninitialized. */
1852 ASSERT(vport->ovsName[0] == '\0');
1853 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
1855 RtlCopyMemory(vport->ovsName, portName, portNameLen);
1857 /* if we don't have options, then vport->portOptions will be NULL */
1858 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
1861 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1862 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1863 * it means we have an array of pids, instead of a single pid.
1864 * ATM we assume we have one pid only.
1866 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
1868 if (vport->ovsType == OVS_VPORT_TYPE_VXLAN) {
1869 gOvsSwitchContext->vxlanVport = vport;
1870 } else if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
1871 gOvsSwitchContext->internalVport = vport;
1872 gOvsSwitchContext->internalPortId = vport->portId;
1873 } else if (vport->ovsType == OVS_VPORT_TYPE_NETDEV &&
1874 vport->isExternal) {
1875 gOvsSwitchContext->externalVport = vport;
1876 gOvsSwitchContext->externalPortId = vport->portId;
1880 * insert the port into the hash array of ports: by port number and ovs
1881 * and ovs (datapath) port name.
1882 * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1883 * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1885 hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
1886 InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
1887 &vport->portNoLink);
1889 hash = OvsJhashBytes(vport->ovsName, portNameLen, OVS_HASH_BASIS);
1890 InsertHeadList(&gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
1891 &vport->ovsNameLink);
1893 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1894 usrParamsCtx->outputLength,
1895 gOvsSwitchContext->dpNo);
1897 *replyLen = msgOut->nlMsg.nlmsgLen;
1900 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1902 if (nlError != NL_ERROR_SUCCESS) {
1903 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1904 usrParamsCtx->outputBuffer;
1906 if (vport && vport->ovsType == OVS_VPORT_TYPE_VXLAN) {
1907 OvsRemoveAndDeleteVport(gOvsSwitchContext, vport);
1910 BuildErrorMsg(msgIn, msgError, nlError);
1911 *replyLen = msgError->nlMsg.nlmsgLen;
1914 return STATUS_SUCCESS;
1919 * --------------------------------------------------------------------------
1920 * Command Handler for 'OVS_VPORT_CMD_SET'.
1921 * --------------------------------------------------------------------------
1924 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1927 NDIS_STATUS status = STATUS_SUCCESS;
1928 LOCK_STATE_EX lockState;
1930 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1931 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1932 POVS_VPORT_ENTRY vport = NULL;
1933 NL_ERROR nlError = NL_ERROR_SUCCESS;
1935 static const NL_POLICY ovsVportPolicy[] = {
1936 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1937 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
1938 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
1940 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
1942 [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
1943 .minLen = sizeof(OVS_VPORT_FULL_STATS),
1944 .maxLen = sizeof(OVS_VPORT_FULL_STATS),
1946 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
1948 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1950 ASSERT(usrParamsCtx->inputBuffer != NULL);
1952 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1953 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1954 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1955 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1956 return STATUS_INVALID_PARAMETER;
1959 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1960 return STATUS_NDIS_INVALID_LENGTH;
1963 OvsAcquireCtrlLock();
1964 if (!gOvsSwitchContext) {
1965 OvsReleaseCtrlLock();
1966 return STATUS_INVALID_PARAMETER;
1969 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
1970 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1971 PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1973 UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1975 /* the port name is expected to be null-terminated */
1976 ASSERT(portName[portNameLen - 1] == '\0');
1978 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1979 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1980 vport = OvsFindVportByPortNo(gOvsSwitchContext,
1981 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
1985 nlError = NL_ERROR_NODEV;
1990 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1991 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1992 * it means we have an array of pids, instead of a single pid.
1993 * Currently, we support only one pid.
1995 if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
1996 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
1999 if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
2000 OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2001 if (type != vport->ovsType) {
2002 nlError = NL_ERROR_INVAL;
2007 if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2008 OVS_LOG_ERROR("Vport options not supported");
2009 nlError = NL_ERROR_NOTSUPP;
2013 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2014 usrParamsCtx->outputLength,
2015 gOvsSwitchContext->dpNo);
2017 *replyLen = msgOut->nlMsg.nlmsgLen;
2020 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2021 OvsReleaseCtrlLock();
2023 if (nlError != NL_ERROR_SUCCESS) {
2024 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2025 usrParamsCtx->outputBuffer;
2027 BuildErrorMsg(msgIn, msgError, nlError);
2028 *replyLen = msgError->nlMsg.nlmsgLen;
2031 return STATUS_SUCCESS;
2035 * --------------------------------------------------------------------------
2036 * Command Handler for 'OVS_VPORT_CMD_DEL'.
2037 * --------------------------------------------------------------------------
2040 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2043 NDIS_STATUS status = STATUS_SUCCESS;
2044 LOCK_STATE_EX lockState;
2046 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2047 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2048 POVS_VPORT_ENTRY vport = NULL;
2049 NL_ERROR nlError = NL_ERROR_SUCCESS;
2050 PSTR portName = NULL;
2051 UINT32 portNameLen = 0;
2053 static const NL_POLICY ovsVportPolicy[] = {
2054 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2055 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ, .optional = TRUE },
2057 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2059 ASSERT(usrParamsCtx->inputBuffer != NULL);
2061 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2062 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2063 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2064 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2065 return STATUS_INVALID_PARAMETER;
2068 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
2069 return STATUS_NDIS_INVALID_LENGTH;
2072 OvsAcquireCtrlLock();
2073 if (!gOvsSwitchContext) {
2074 OvsReleaseCtrlLock();
2075 return STATUS_INVALID_PARAMETER;
2077 OvsReleaseCtrlLock();
2079 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2080 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2081 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2082 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2084 /* the port name is expected to be null-terminated */
2085 ASSERT(portName[portNameLen - 1] == '\0');
2087 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2089 else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2090 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2091 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2095 nlError = NL_ERROR_NODEV;
2099 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2100 usrParamsCtx->outputLength,
2101 gOvsSwitchContext->dpNo);
2103 if (vport->hvDeleted || OvsIsTunnelVportType(vport->ovsType)) {
2105 * The associated hyper-v switch port is not in created state, or,
2106 * there is no hyper-v switch port counterpart (for logical ports).
2107 * This means that this datapath port is not mapped to a living
2108 * hyper-v switc hport. We can destroy and remove the vport from the
2111 OvsRemoveAndDeleteVport(gOvsSwitchContext, vport);
2113 /* The associated hyper-v switch port is in the created state, and the
2114 * datapath port is mapped to a living hyper-v switch port. We cannot
2115 * destroy the vport and cannot remove it from the list of vports.
2116 * Instead, we mark the datapath (ovs) part of the vport as
2117 * "not created", i.e. we set vport->portNo = OVS_PORT_NUMBER_INVALID.
2119 RemoveEntryList(&vport->ovsNameLink);
2120 RemoveEntryList(&vport->portNoLink);
2121 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
2122 vport->ovsName[0] = '\0';
2125 *replyLen = msgOut->nlMsg.nlmsgLen;
2128 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2130 if (nlError != NL_ERROR_SUCCESS) {
2131 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2132 usrParamsCtx->outputBuffer;
2134 BuildErrorMsg(msgIn, msgError, nlError);
2135 *replyLen = msgError->nlMsg.nlmsgLen;
2138 return STATUS_SUCCESS;
2143 * --------------------------------------------------------------------------
2144 * Utility function to map the output buffer in an IRP. The buffer is assumed
2145 * to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
2146 * --------------------------------------------------------------------------
2149 MapIrpOutputBuffer(PIRP irp,
2150 UINT32 bufferLength,
2151 UINT32 requiredLength,
2156 ASSERT(bufferLength);
2157 ASSERT(requiredLength);
2158 if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
2159 return STATUS_INVALID_PARAMETER;
2162 if (bufferLength < requiredLength) {
2163 return STATUS_NDIS_INVALID_LENGTH;
2165 if (irp->MdlAddress == NULL) {
2166 return STATUS_INVALID_PARAMETER;
2168 *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
2169 NormalPagePriority);
2170 if (*buffer == NULL) {
2171 return STATUS_INSUFFICIENT_RESOURCES;
2174 return STATUS_SUCCESS;
2178 * --------------------------------------------------------------------------
2179 * Utility function to fill up information about the state of a port in a reply
2181 * Assumes that 'gOvsCtrlLock' lock is acquired.
2182 * --------------------------------------------------------------------------
2185 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2186 POVS_EVENT_ENTRY eventEntry,
2191 OVS_MESSAGE msgOutTmp;
2193 POVS_VPORT_ENTRY vport;
2195 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
2197 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
2198 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
2200 /* driver intiated messages should have zerp seq number*/
2201 msgOutTmp.nlMsg.nlmsgSeq = 0;
2202 msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
2204 msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
2205 msgOutTmp.genlMsg.reserved = 0;
2207 /* we don't have netdev yet, treat link up/down a adding/removing a port*/
2208 if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
2209 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
2210 } else if (eventEntry->status &
2211 (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
2212 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
2215 return STATUS_UNSUCCESSFUL;
2217 msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
2219 rc = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
2221 status = STATUS_INVALID_BUFFER_SIZE;
2225 vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
2227 status = STATUS_DEVICE_DOES_NOT_EXIST;
2231 rc = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) ||
2232 NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) ||
2233 NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
2235 status = STATUS_INVALID_BUFFER_SIZE;
2239 /* XXXX Should we add the port stats attributes?*/
2240 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
2241 nlMsg->nlmsgLen = NlBufSize(nlBuf);
2242 status = STATUS_SUCCESS;
2250 * --------------------------------------------------------------------------
2251 * Handler for reading events from the driver event queue. This handler is
2252 * executed when user modes issues a socket receive on a socket assocaited
2253 * with the MC group for events.
2254 * XXX user mode should read multiple events in one system call
2255 * --------------------------------------------------------------------------
2258 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2262 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2263 POVS_OPEN_INSTANCE instance =
2264 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
2268 OVS_EVENT_ENTRY eventEntry;
2270 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
2272 /* Should never read events with a dump socket */
2273 ASSERT(instance->dumpState.ovsMsg == NULL);
2275 /* Must have an event queue */
2276 ASSERT(instance->eventQueue != NULL);
2278 /* Output buffer has been validated while validating read dev op. */
2279 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2281 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
2283 OvsAcquireCtrlLock();
2284 if (!gOvsSwitchContext) {
2285 status = STATUS_SUCCESS;
2289 /* remove an event entry from the event queue */
2290 status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
2291 if (status != STATUS_SUCCESS) {
2295 status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
2296 if (status == NDIS_STATUS_SUCCESS) {
2297 *replyLen = NlBufSize(&nlBuf);
2301 OvsReleaseCtrlLock();
2306 * --------------------------------------------------------------------------
2307 * Handler for reading missed pacckets from the driver event queue. This
2308 * handler is executed when user modes issues a socket receive on a socket
2309 * --------------------------------------------------------------------------
2312 OvsReadPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2316 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2318 POVS_OPEN_INSTANCE instance =
2319 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
2322 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
2324 /* Should never read events with a dump socket */
2325 ASSERT(instance->dumpState.ovsMsg == NULL);
2327 /* Must have an packet queue */
2328 ASSERT(instance->packetQueue != NULL);
2330 /* Output buffer has been validated while validating read dev op. */
2331 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2333 OvsAcquireCtrlLock();
2334 if (!gOvsSwitchContext) {
2335 status = STATUS_SUCCESS;
2336 OvsReleaseCtrlLock();
2339 OvsReleaseCtrlLock();
2341 /* Read a packet from the instance queue */
2342 status = OvsReadDpIoctl(instance->fileObject, usrParamsCtx->outputBuffer,
2343 usrParamsCtx->outputLength, replyLen);
2346 #endif /* OVS_USE_NL_INTERFACE */