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
43 #define OVS_DBG_MOD OVS_DBG_DATAPATH
46 #define NETLINK_FAMILY_NAME_LEN 48
50 * Netlink messages are grouped by family (aka type), and each family supports
51 * a set of commands, and can be passed both from kernel -> userspace or
52 * vice-versa. To call into the kernel, userspace uses a device operation which
53 * is outside of a netlink message.
55 * Each command results in the invocation of a handler function to implement the
56 * request functionality.
58 * Expectedly, only certain combinations of (device operation, netlink family,
61 * Here, we implement the basic infrastructure to perform validation on the
62 * incoming message, version checking, and also to invoke the corresponding
63 * handler to do the heavy-lifting.
67 * Handler for a given netlink command. Not all the parameters are used by all
70 typedef NTSTATUS(NetlinkCmdHandler)(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
73 typedef struct _NETLINK_CMD {
75 NetlinkCmdHandler *handler;
76 UINT32 supportedDevOp; /* Supported device operations. */
77 BOOLEAN validateDpIndex; /* Does command require a valid DP argument. */
78 } NETLINK_CMD, *PNETLINK_CMD;
80 /* A netlink family is a group of commands. */
81 typedef struct _NETLINK_FAMILY {
87 NETLINK_CMD *cmds; /* Array of netlink commands and handlers. */
89 } NETLINK_FAMILY, *PNETLINK_FAMILY;
91 /* Handlers for the various netlink commands. */
92 static NetlinkCmdHandler OvsGetPidCmdHandler,
93 OvsPendEventCmdHandler,
94 OvsPendPacketCmdHandler,
95 OvsSubscribeEventCmdHandler,
96 OvsSubscribePacketCmdHandler,
97 OvsReadEventCmdHandler,
98 OvsReadPacketCmdHandler,
102 OvsGetVportCmdHandler,
103 OvsSetVportCmdHandler,
104 OvsNewVportCmdHandler,
105 OvsDeleteVportCmdHandler;
107 NetlinkCmdHandler OvsGetNetdevCmdHandler;
109 static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
111 static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
113 static NTSTATUS HandleDpTransactionCommon(
114 POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen);
117 * The various netlink families, along with the supported commands. Most of
118 * these families and commands are part of the openvswitch specification for a
119 * netlink datapath. In addition, each platform can implement a few families
120 * and commands as extensions.
123 /* Netlink control family: this is a Windows specific family. */
124 NETLINK_CMD nlControlFamilyCmdOps[] = {
125 { .cmd = OVS_CTRL_CMD_WIN_GET_PID,
126 .handler = OvsGetPidCmdHandler,
127 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
128 .validateDpIndex = FALSE,
130 { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ,
131 .handler = OvsPendEventCmdHandler,
132 .supportedDevOp = OVS_WRITE_DEV_OP,
133 .validateDpIndex = TRUE,
135 { .cmd = OVS_CTRL_CMD_WIN_PEND_PACKET_REQ,
136 .handler = OvsPendPacketCmdHandler,
137 .supportedDevOp = OVS_WRITE_DEV_OP,
138 .validateDpIndex = TRUE,
140 { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ,
141 .handler = OvsSubscribeEventCmdHandler,
142 .supportedDevOp = OVS_WRITE_DEV_OP,
143 .validateDpIndex = TRUE,
145 { .cmd = OVS_CTRL_CMD_PACKET_SUBSCRIBE_REQ,
146 .handler = OvsSubscribePacketCmdHandler,
147 .supportedDevOp = OVS_WRITE_DEV_OP,
148 .validateDpIndex = TRUE,
150 { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY,
151 .handler = OvsReadEventCmdHandler,
152 .supportedDevOp = OVS_READ_EVENT_DEV_OP,
153 .validateDpIndex = FALSE,
155 { .cmd = OVS_CTRL_CMD_READ_NOTIFY,
156 .handler = OvsReadPacketCmdHandler,
157 .supportedDevOp = OVS_READ_PACKET_DEV_OP,
158 .validateDpIndex = FALSE,
162 NETLINK_FAMILY nlControlFamilyOps = {
163 .name = OVS_WIN_CONTROL_FAMILY,
164 .id = OVS_WIN_NL_CTRL_FAMILY_ID,
165 .version = OVS_WIN_CONTROL_VERSION,
166 .maxAttr = OVS_WIN_CONTROL_ATTR_MAX,
167 .cmds = nlControlFamilyCmdOps,
168 .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
171 /* Netlink datapath family. */
172 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
173 { .cmd = OVS_DP_CMD_NEW,
174 .handler = OvsNewDpCmdHandler,
175 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
176 .validateDpIndex = FALSE
178 { .cmd = OVS_DP_CMD_GET,
179 .handler = OvsGetDpCmdHandler,
180 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
181 OVS_TRANSACTION_DEV_OP,
182 .validateDpIndex = FALSE
184 { .cmd = OVS_DP_CMD_SET,
185 .handler = OvsSetDpCmdHandler,
186 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
187 OVS_TRANSACTION_DEV_OP,
188 .validateDpIndex = TRUE
192 NETLINK_FAMILY nlDatapathFamilyOps = {
193 .name = OVS_DATAPATH_FAMILY,
194 .id = OVS_WIN_NL_DATAPATH_FAMILY_ID,
195 .version = OVS_DATAPATH_VERSION,
196 .maxAttr = OVS_DP_ATTR_MAX,
197 .cmds = nlDatapathFamilyCmdOps,
198 .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
201 /* Netlink packet family. */
203 NETLINK_CMD nlPacketFamilyCmdOps[] = {
204 { .cmd = OVS_PACKET_CMD_EXECUTE,
205 .handler = OvsNlExecuteCmdHandler,
206 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
207 .validateDpIndex = TRUE
211 NETLINK_FAMILY nlPacketFamilyOps = {
212 .name = OVS_PACKET_FAMILY,
213 .id = OVS_WIN_NL_PACKET_FAMILY_ID,
214 .version = OVS_PACKET_VERSION,
215 .maxAttr = OVS_PACKET_ATTR_MAX,
216 .cmds = nlPacketFamilyCmdOps,
217 .opsCount = ARRAY_SIZE(nlPacketFamilyCmdOps)
220 /* Netlink vport family. */
221 NETLINK_CMD nlVportFamilyCmdOps[] = {
222 { .cmd = OVS_VPORT_CMD_GET,
223 .handler = OvsGetVportCmdHandler,
224 .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
225 OVS_TRANSACTION_DEV_OP,
226 .validateDpIndex = TRUE
228 { .cmd = OVS_VPORT_CMD_NEW,
229 .handler = OvsNewVportCmdHandler,
230 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
231 .validateDpIndex = TRUE
233 { .cmd = OVS_VPORT_CMD_SET,
234 .handler = OvsSetVportCmdHandler,
235 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
236 .validateDpIndex = TRUE
238 { .cmd = OVS_VPORT_CMD_DEL,
239 .handler = OvsDeleteVportCmdHandler,
240 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
241 .validateDpIndex = TRUE
245 NETLINK_FAMILY nlVportFamilyOps = {
246 .name = OVS_VPORT_FAMILY,
247 .id = OVS_WIN_NL_VPORT_FAMILY_ID,
248 .version = OVS_VPORT_VERSION,
249 .maxAttr = OVS_VPORT_ATTR_MAX,
250 .cmds = nlVportFamilyCmdOps,
251 .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps)
254 /* Netlink flow family. */
256 NETLINK_CMD nlFlowFamilyCmdOps[] = {
257 { .cmd = OVS_FLOW_CMD_NEW,
258 .handler = OvsFlowNlCmdHandler,
259 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
260 .validateDpIndex = TRUE
262 { .cmd = OVS_FLOW_CMD_SET,
263 .handler = OvsFlowNlCmdHandler,
264 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
265 .validateDpIndex = TRUE
267 { .cmd = OVS_FLOW_CMD_DEL,
268 .handler = OvsFlowNlCmdHandler,
269 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
270 .validateDpIndex = TRUE
272 { .cmd = OVS_FLOW_CMD_GET,
273 .handler = OvsFlowNlGetCmdHandler,
274 .supportedDevOp = OVS_TRANSACTION_DEV_OP |
275 OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
276 .validateDpIndex = TRUE
280 NETLINK_FAMILY nlFLowFamilyOps = {
281 .name = OVS_FLOW_FAMILY,
282 .id = OVS_WIN_NL_FLOW_FAMILY_ID,
283 .version = OVS_FLOW_VERSION,
284 .maxAttr = OVS_FLOW_ATTR_MAX,
285 .cmds = nlFlowFamilyCmdOps,
286 .opsCount = ARRAY_SIZE(nlFlowFamilyCmdOps)
289 /* Netlink netdev family. */
290 NETLINK_CMD nlNetdevFamilyCmdOps[] = {
291 { .cmd = OVS_WIN_NETDEV_CMD_GET,
292 .handler = OvsGetNetdevCmdHandler,
293 .supportedDevOp = OVS_TRANSACTION_DEV_OP,
294 .validateDpIndex = FALSE
298 NETLINK_FAMILY nlNetdevFamilyOps = {
299 .name = OVS_WIN_NETDEV_FAMILY,
300 .id = OVS_WIN_NL_NETDEV_FAMILY_ID,
301 .version = OVS_WIN_NETDEV_VERSION,
302 .maxAttr = OVS_WIN_NETDEV_ATTR_MAX,
303 .cmds = nlNetdevFamilyCmdOps,
304 .opsCount = ARRAY_SIZE(nlNetdevFamilyCmdOps)
307 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
309 UINT32 requiredLength,
311 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
312 POVS_OPEN_INSTANCE instance,
314 NETLINK_FAMILY *nlFamilyOps);
315 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
316 NETLINK_FAMILY *nlFamilyOps,
319 /* Handles to the device object for communication with userspace. */
320 NDIS_HANDLE gOvsDeviceHandle;
321 PDEVICE_OBJECT gOvsDeviceObject;
323 _Dispatch_type_(IRP_MJ_CREATE)
324 _Dispatch_type_(IRP_MJ_CLOSE)
325 DRIVER_DISPATCH OvsOpenCloseDevice;
327 _Dispatch_type_(IRP_MJ_CLEANUP)
328 DRIVER_DISPATCH OvsCleanupDevice;
330 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
331 DRIVER_DISPATCH OvsDeviceControl;
334 #pragma alloc_text(INIT, OvsCreateDeviceObject)
335 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
336 #pragma alloc_text(PAGE, OvsCleanupDevice)
337 #pragma alloc_text(PAGE, OvsDeviceControl)
338 #endif // ALLOC_PRAGMA
341 * We might hit this limit easily since userspace opens a netlink descriptor for
342 * each thread, and at least one descriptor per vport. Revisit this later.
344 #define OVS_MAX_OPEN_INSTANCES 512
345 #define OVS_SYSTEM_DP_NAME "ovs-system"
347 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
348 UINT32 ovsNumberOfOpenInstances;
349 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
351 NDIS_SPIN_LOCK ovsCtrlLockObj;
352 PNDIS_SPIN_LOCK gOvsCtrlLock;
358 gOvsCtrlLock = &ovsCtrlLockObj;
359 NdisAllocateSpinLock(gOvsCtrlLock);
366 OvsCleanupEventQueue();
368 NdisFreeSpinLock(gOvsCtrlLock);
376 NdisAcquireSpinLock(gOvsCtrlLock);
382 NdisReleaseSpinLock(gOvsCtrlLock);
387 * --------------------------------------------------------------------------
388 * Creates the communication device between user and kernel, and also
389 * initializes the data associated data structures.
390 * --------------------------------------------------------------------------
393 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
395 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
396 UNICODE_STRING deviceName;
397 UNICODE_STRING symbolicDeviceName;
398 PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
399 NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
400 OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
402 RtlZeroMemory(dispatchTable,
403 (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
404 dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
405 dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
406 dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
407 dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
409 NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
410 NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
412 RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
414 OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
415 NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
416 NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
417 sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
419 deviceAttributes.DeviceName = &deviceName;
420 deviceAttributes.SymbolicName = &symbolicDeviceName;
421 deviceAttributes.MajorFunctions = dispatchTable;
422 deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
424 status = NdisRegisterDeviceEx(ovsExtDriverHandle,
428 if (status != NDIS_STATUS_SUCCESS) {
429 POVS_DEVICE_EXTENSION ovsExt =
430 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
431 ASSERT(gOvsDeviceObject != NULL);
432 ASSERT(gOvsDeviceHandle != NULL);
435 ovsExt->numberOpenInstance = 0;
438 /* Initialize the associated data structures. */
441 OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
447 OvsDeleteDeviceObject()
449 if (gOvsDeviceHandle) {
451 POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
452 NdisGetDeviceReservedExtension(gOvsDeviceObject);
454 ASSERT(ovsExt->numberOpenInstance == 0);
458 ASSERT(gOvsDeviceObject);
459 NdisDeregisterDeviceEx(gOvsDeviceHandle);
460 gOvsDeviceHandle = NULL;
461 gOvsDeviceObject = NULL;
467 OvsGetOpenInstance(PFILE_OBJECT fileObject,
470 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
472 ASSERT(instance->fileObject == fileObject);
473 if (gOvsSwitchContext->dpNo != dpNo) {
481 OvsFindOpenInstance(PFILE_OBJECT fileObject)
484 for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
485 j < ovsNumberOfOpenInstances; i++) {
486 if (ovsOpenInstanceArray[i]) {
487 if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
488 return ovsOpenInstanceArray[i];
497 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
498 PFILE_OBJECT fileObject)
500 POVS_OPEN_INSTANCE instance =
501 (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
504 if (instance == NULL) {
505 return STATUS_NO_MEMORY;
507 OvsAcquireCtrlLock();
508 ASSERT(OvsFindOpenInstance(fileObject) == NULL);
510 if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
511 OvsReleaseCtrlLock();
512 OvsFreeMemory(instance);
513 return STATUS_INSUFFICIENT_RESOURCES;
515 RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
517 for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
518 if (ovsOpenInstanceArray[i] == NULL) {
519 ovsOpenInstanceArray[i] = instance;
520 ovsNumberOfOpenInstances++;
521 instance->cookie = i;
525 ASSERT(i < OVS_MAX_OPEN_INSTANCES);
526 instance->fileObject = fileObject;
527 ASSERT(fileObject->FsContext == NULL);
528 instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
529 if (instance->pid == 0) {
530 /* XXX: check for rollover. */
532 fileObject->FsContext = instance;
533 OvsReleaseCtrlLock();
534 return STATUS_SUCCESS;
538 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
540 POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
542 ASSERT(fileObject == instance->fileObject);
543 OvsCleanupEvent(instance);
544 OvsCleanupPacketQueue(instance);
548 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
550 POVS_OPEN_INSTANCE instance;
551 ASSERT(fileObject->FsContext);
552 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
553 ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
555 OvsAcquireCtrlLock();
556 fileObject->FsContext = NULL;
557 ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
558 ovsOpenInstanceArray[instance->cookie] = NULL;
559 ovsNumberOfOpenInstances--;
560 OvsReleaseCtrlLock();
561 ASSERT(instance->eventQueue == NULL);
562 ASSERT (instance->packetQueue == NULL);
563 OvsFreeMemory(instance);
567 OvsCompleteIrpRequest(PIRP irp,
571 irp->IoStatus.Information = infoPtr;
572 irp->IoStatus.Status = status;
573 IoCompleteRequest(irp, IO_NO_INCREMENT);
579 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
582 PIO_STACK_LOCATION irpSp;
583 NTSTATUS status = STATUS_SUCCESS;
584 PFILE_OBJECT fileObject;
585 POVS_DEVICE_EXTENSION ovsExt =
586 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
588 ASSERT(deviceObject == gOvsDeviceObject);
589 ASSERT(ovsExt != NULL);
591 irpSp = IoGetCurrentIrpStackLocation(irp);
592 fileObject = irpSp->FileObject;
593 OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
594 deviceObject, fileObject,
595 ovsExt->numberOpenInstance);
597 switch (irpSp->MajorFunction) {
599 status = OvsAddOpenInstance(ovsExt, fileObject);
600 if (STATUS_SUCCESS == status) {
601 InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
605 ASSERT(ovsExt->numberOpenInstance > 0);
606 OvsRemoveOpenInstance(fileObject);
607 InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
612 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
615 _Use_decl_annotations_
617 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
621 PIO_STACK_LOCATION irpSp;
622 PFILE_OBJECT fileObject;
624 NTSTATUS status = STATUS_SUCCESS;
626 POVS_DEVICE_EXTENSION ovsExt =
627 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
629 ASSERT(ovsExt->numberOpenInstance > 0);
632 UNREFERENCED_PARAMETER(deviceObject);
634 ASSERT(deviceObject == gOvsDeviceObject);
635 irpSp = IoGetCurrentIrpStackLocation(irp);
636 fileObject = irpSp->FileObject;
638 ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
640 OvsCleanupOpenInstance(fileObject);
642 return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
647 * --------------------------------------------------------------------------
648 * IOCTL function handler for the device.
649 * --------------------------------------------------------------------------
652 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
655 PIO_STACK_LOCATION irpSp;
656 NTSTATUS status = STATUS_SUCCESS;
657 PFILE_OBJECT fileObject;
658 PVOID inputBuffer = NULL;
659 PVOID outputBuffer = NULL;
660 UINT32 inputBufferLen, outputBufferLen;
661 UINT32 code, replyLen = 0;
662 POVS_OPEN_INSTANCE instance;
664 OVS_MESSAGE ovsMsgReadOp;
666 NETLINK_FAMILY *nlFamilyOps;
667 OVS_USER_PARAMS_CONTEXT usrParamsCtx;
670 POVS_DEVICE_EXTENSION ovsExt =
671 (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
672 ASSERT(deviceObject == gOvsDeviceObject);
674 ASSERT(ovsExt->numberOpenInstance > 0);
676 UNREFERENCED_PARAMETER(deviceObject);
679 irpSp = IoGetCurrentIrpStackLocation(irp);
681 ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
682 ASSERT(irpSp->FileObject != NULL);
684 fileObject = irpSp->FileObject;
685 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
686 code = irpSp->Parameters.DeviceIoControl.IoControlCode;
687 inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
688 outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
689 inputBuffer = irp->AssociatedIrp.SystemBuffer;
691 /* Check if the extension is enabled. */
692 if (NULL == gOvsSwitchContext) {
693 status = STATUS_DEVICE_NOT_READY;
697 /* Concurrent netlink operations are not supported. */
698 if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
699 status = STATUS_RESOURCE_IN_USE;
704 * Validate the input/output buffer arguments depending on the type of the
708 case OVS_IOCTL_TRANSACT:
709 /* Input buffer is mandatory, output buffer is optional. */
710 if (outputBufferLen != 0) {
711 status = MapIrpOutputBuffer(irp, outputBufferLen,
712 sizeof *ovsMsg, &outputBuffer);
713 if (status != STATUS_SUCCESS) {
716 ASSERT(outputBuffer);
719 if (inputBufferLen < sizeof (*ovsMsg)) {
720 status = STATUS_NDIS_INVALID_LENGTH;
724 ovsMsg = inputBuffer;
725 devOp = OVS_TRANSACTION_DEV_OP;
728 case OVS_IOCTL_READ_EVENT:
729 case OVS_IOCTL_READ_PACKET:
730 /* This IOCTL is used to read events */
731 if (outputBufferLen != 0) {
732 status = MapIrpOutputBuffer(irp, outputBufferLen,
733 sizeof *ovsMsg, &outputBuffer);
734 if (status != STATUS_SUCCESS) {
737 ASSERT(outputBuffer);
739 status = STATUS_NDIS_INVALID_LENGTH;
745 ovsMsg = &ovsMsgReadOp;
746 ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
747 ovsMsg->nlMsg.nlmsgPid = instance->pid;
748 /* An "artificial" command so we can use NL family function table*/
749 ovsMsg->genlMsg.cmd = (code == OVS_IOCTL_READ_EVENT) ?
750 OVS_CTRL_CMD_EVENT_NOTIFY :
751 OVS_CTRL_CMD_READ_NOTIFY;
752 devOp = OVS_READ_DEV_OP;
756 /* Output buffer is mandatory. */
757 if (outputBufferLen != 0) {
758 status = MapIrpOutputBuffer(irp, outputBufferLen,
759 sizeof *ovsMsg, &outputBuffer);
760 if (status != STATUS_SUCCESS) {
763 ASSERT(outputBuffer);
765 status = STATUS_NDIS_INVALID_LENGTH;
770 * Operate in the mode that read ioctl is similar to ReadFile(). This
771 * might change as the userspace code gets implemented.
777 * For implementing read (ioctl or otherwise), we need to store some
778 * state in the instance to indicate the command that started the dump
779 * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
780 * that 'ovsMsgReadOp' is needed only in this function to call into the
781 * appropraite handler. The handler itself can access the state in the
784 * In the absence of a dump start, return 0 bytes.
786 if (instance->dumpState.ovsMsg == NULL) {
788 status = STATUS_SUCCESS;
791 RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
792 sizeof (ovsMsgReadOp));
794 /* Create an NL message for consumption. */
795 ovsMsg = &ovsMsgReadOp;
796 devOp = OVS_READ_DEV_OP;
800 case OVS_IOCTL_WRITE:
801 /* Input buffer is mandatory. */
802 if (inputBufferLen < sizeof (*ovsMsg)) {
803 status = STATUS_NDIS_INVALID_LENGTH;
807 ovsMsg = inputBuffer;
808 devOp = OVS_WRITE_DEV_OP;
812 status = STATUS_INVALID_DEVICE_REQUEST;
817 switch (ovsMsg->nlMsg.nlmsgType) {
818 case OVS_WIN_NL_CTRL_FAMILY_ID:
819 nlFamilyOps = &nlControlFamilyOps;
821 case OVS_WIN_NL_DATAPATH_FAMILY_ID:
822 nlFamilyOps = &nlDatapathFamilyOps;
824 case OVS_WIN_NL_FLOW_FAMILY_ID:
825 nlFamilyOps = &nlFLowFamilyOps;
827 case OVS_WIN_NL_PACKET_FAMILY_ID:
828 nlFamilyOps = &nlPacketFamilyOps;
830 case OVS_WIN_NL_VPORT_FAMILY_ID:
831 nlFamilyOps = &nlVportFamilyOps;
833 case OVS_WIN_NL_NETDEV_FAMILY_ID:
834 nlFamilyOps = &nlNetdevFamilyOps;
837 status = STATUS_INVALID_PARAMETER;
842 * For read operation, the netlink command has already been validated
845 if (devOp != OVS_READ_DEV_OP) {
846 status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
847 if (status != STATUS_SUCCESS) {
852 InitUserParamsCtx(irp, instance, devOp, ovsMsg,
853 inputBuffer, inputBufferLen,
854 outputBuffer, outputBufferLen,
857 status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
862 return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
867 * --------------------------------------------------------------------------
868 * Function to validate a netlink command. Only certain combinations of
869 * (device operation, netlink family, command) are valid.
870 * --------------------------------------------------------------------------
873 ValidateNetlinkCmd(UINT32 devOp,
874 POVS_OPEN_INSTANCE instance,
876 NETLINK_FAMILY *nlFamilyOps)
878 NTSTATUS status = STATUS_INVALID_PARAMETER;
881 for (i = 0; i < nlFamilyOps->opsCount; i++) {
882 if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
883 /* Validate if the command is valid for the device operation. */
884 if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
885 status = STATUS_INVALID_PARAMETER;
889 /* Validate the version. */
890 if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
891 status = STATUS_INVALID_PARAMETER;
895 /* Validate the DP for commands that require a DP. */
896 if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
897 OvsAcquireCtrlLock();
898 if (ovsMsg->ovsHdr.dp_ifindex !=
899 (INT)gOvsSwitchContext->dpNo) {
900 status = STATUS_INVALID_PARAMETER;
901 OvsReleaseCtrlLock();
904 OvsReleaseCtrlLock();
907 /* Validate the PID. */
908 if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
909 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
910 status = STATUS_INVALID_PARAMETER;
915 status = STATUS_SUCCESS;
925 * --------------------------------------------------------------------------
926 * Function to invoke the netlink command handler.
927 * --------------------------------------------------------------------------
930 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
931 NETLINK_FAMILY *nlFamilyOps,
934 NTSTATUS status = STATUS_INVALID_PARAMETER;
937 for (i = 0; i < nlFamilyOps->opsCount; i++) {
938 if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
939 NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
942 status = handler(usrParamsCtx, replyLen);
952 * --------------------------------------------------------------------------
953 * Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
955 * Each handle on the device is assigned a unique PID when the handle is
956 * created. On platforms that support netlink natively, the PID is available
957 * to userspace when the netlink socket is created. However, without native
958 * netlink support on Windows, OVS datapath generates the PID and lets the
959 * userspace query it.
961 * This function implements the query.
962 * --------------------------------------------------------------------------
965 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
968 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
969 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
971 if (usrParamsCtx->outputLength >= sizeof *msgOut) {
972 POVS_OPEN_INSTANCE instance =
973 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
975 RtlZeroMemory(msgOut, sizeof *msgOut);
976 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
977 msgOut->nlMsg.nlmsgPid = instance->pid;
978 *replyLen = sizeof *msgOut;
979 /* XXX: We might need to return the DP index as well. */
981 return STATUS_NDIS_INVALID_LENGTH;
984 return STATUS_SUCCESS;
988 * --------------------------------------------------------------------------
989 * Utility function to fill up information about the datapath in a reply to
991 * Assumes that 'gOvsCtrlLock' lock is acquired.
992 * --------------------------------------------------------------------------
995 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
1000 OVS_MESSAGE msgOutTmp;
1001 OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
1004 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && NlBufRemLen(nlBuf) >= sizeof *msgIn);
1006 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
1007 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
1008 msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1009 msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1011 msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
1012 msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
1013 msgOutTmp.genlMsg.reserved = 0;
1015 msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
1017 writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
1019 writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
1020 OVS_SYSTEM_DP_NAME);
1023 OVS_DP_STATS dpStats;
1025 dpStats.n_hit = datapath->hits;
1026 dpStats.n_missed = datapath->misses;
1027 dpStats.n_lost = datapath->lost;
1028 dpStats.n_flows = datapath->nFlows;
1029 writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
1030 (PCHAR)&dpStats, sizeof dpStats);
1032 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1033 nlMsg->nlmsgLen = NlBufSize(nlBuf);
1035 return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
1039 * --------------------------------------------------------------------------
1040 * Handler for queueing an IRP used for event notification. The IRP is
1041 * completed when a port state changes. STATUS_PENDING is returned on
1042 * success. User mode keep a pending IRP at all times.
1043 * --------------------------------------------------------------------------
1046 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1051 UNREFERENCED_PARAMETER(replyLen);
1053 POVS_OPEN_INSTANCE instance =
1054 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1055 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1056 OVS_EVENT_POLL poll;
1058 poll.dpNo = msgIn->ovsHdr.dp_ifindex;
1059 status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
1060 &poll, sizeof poll);
1065 * --------------------------------------------------------------------------
1066 * Handler for the subscription for the event queue
1067 * --------------------------------------------------------------------------
1070 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1074 OVS_EVENT_SUBSCRIBE request;
1078 const NL_POLICY policy[] = {
1079 [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
1080 [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
1083 UNREFERENCED_PARAMETER(replyLen);
1085 POVS_OPEN_INSTANCE instance =
1086 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1087 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1089 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1090 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, ARRAY_SIZE(attrs));
1092 status = STATUS_INVALID_PARAMETER;
1096 /* XXX Ignore the MC group for now */
1097 join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1098 request.dpNo = msgIn->ovsHdr.dp_ifindex;
1099 request.subscribe = join;
1100 request.mask = OVS_EVENT_MASK_ALL;
1102 status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1109 * --------------------------------------------------------------------------
1110 * Command Handler for 'OVS_DP_CMD_NEW'.
1111 * --------------------------------------------------------------------------
1114 OvsNewDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1117 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1121 * --------------------------------------------------------------------------
1122 * Command Handler for 'OVS_DP_CMD_GET'.
1124 * The function handles both the dump based as well as the transaction based
1125 * 'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1126 * call to setup dump state, as well as subsequent calls to continue dumping
1128 * --------------------------------------------------------------------------
1131 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1134 if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1135 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1137 return HandleGetDpDump(usrParamsCtx, replyLen);
1142 * --------------------------------------------------------------------------
1143 * Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1144 * --------------------------------------------------------------------------
1147 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1150 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1155 * --------------------------------------------------------------------------
1156 * Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1157 * --------------------------------------------------------------------------
1160 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1163 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1164 POVS_OPEN_INSTANCE instance =
1165 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1167 if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1169 OvsSetupDumpStart(usrParamsCtx);
1173 POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1175 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1177 if (instance->dumpState.ovsMsg == NULL) {
1179 return STATUS_INVALID_DEVICE_STATE;
1182 /* Dump state must have been deleted after previous dump operation. */
1183 ASSERT(instance->dumpState.index[0] == 0);
1184 /* Output buffer has been validated while validating read dev op. */
1185 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1187 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1188 usrParamsCtx->outputLength);
1190 OvsAcquireCtrlLock();
1191 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1192 OvsReleaseCtrlLock();
1194 if (status != STATUS_SUCCESS) {
1196 FreeUserDumpState(instance);
1200 /* Increment the dump index. */
1201 instance->dumpState.index[0] = 1;
1202 *replyLen = msgOut->nlMsg.nlmsgLen;
1204 /* Free up the dump state, since there's no more data to continue. */
1205 FreeUserDumpState(instance);
1208 return STATUS_SUCCESS;
1213 * --------------------------------------------------------------------------
1214 * Command Handler for 'OVS_DP_CMD_SET'.
1215 * --------------------------------------------------------------------------
1218 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1221 return HandleDpTransactionCommon(usrParamsCtx, replyLen);
1225 * --------------------------------------------------------------------------
1226 * Function for handling transaction based 'OVS_DP_CMD_NEW', 'OVS_DP_CMD_GET'
1227 * and 'OVS_DP_CMD_SET' commands.
1229 * 'OVS_DP_CMD_NEW' is implemented to keep userspace code happy. Creation of a
1230 * new datapath is not supported currently.
1231 * --------------------------------------------------------------------------
1234 HandleDpTransactionCommon(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1237 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1238 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1239 NTSTATUS status = STATUS_SUCCESS;
1241 NL_ERROR nlError = NL_ERROR_SUCCESS;
1242 static const NL_POLICY ovsDatapathSetPolicy[] = {
1243 [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1244 [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1245 [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1247 PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1249 /* input buffer has been validated while validating write dev op. */
1250 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1252 /* Parse any attributes in the request. */
1253 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET ||
1254 usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1255 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1256 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1257 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1258 ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1259 return STATUS_INVALID_PARAMETER;
1263 * XXX: Not clear at this stage if there's any role for the
1264 * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1269 RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1272 /* Output buffer is optional for OVS_TRANSACTION_DEV_OP. */
1273 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1274 return STATUS_NDIS_INVALID_LENGTH;
1276 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1278 OvsAcquireCtrlLock();
1279 if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1280 if (!OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1281 OVS_SYSTEM_DP_NAME)) {
1282 OvsReleaseCtrlLock();
1284 /* Creation of new datapaths is not supported. */
1285 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1286 nlError = NL_ERROR_NOTSUPP;
1290 nlError = NL_ERROR_NODEV;
1293 } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1294 OvsReleaseCtrlLock();
1295 nlError = NL_ERROR_NODEV;
1299 if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
1300 OvsReleaseCtrlLock();
1301 nlError = NL_ERROR_EXIST;
1305 status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1306 OvsReleaseCtrlLock();
1308 *replyLen = NlBufSize(&nlBuf);
1311 if (nlError != NL_ERROR_SUCCESS) {
1312 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1313 usrParamsCtx->outputBuffer;
1315 BuildErrorMsg(msgIn, msgError, nlError);
1316 *replyLen = msgError->nlMsg.nlmsgLen;
1319 return STATUS_SUCCESS;
1324 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1326 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1327 POVS_OPEN_INSTANCE instance =
1328 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1330 /* input buffer has been validated while validating write dev op. */
1331 ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1333 /* A write operation that does not indicate dump start is invalid. */
1334 if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1335 return STATUS_INVALID_PARAMETER;
1337 /* XXX: Handle other NLM_F_* flags in the future. */
1340 * This operation should be setting up the dump state. If there's any
1341 * previous state, clear it up so as to set it up afresh.
1343 if (instance->dumpState.ovsMsg != NULL) {
1344 FreeUserDumpState(instance);
1347 return InitUserDumpState(instance, msgIn);
1351 BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
1352 UINT32 length, UINT16 flags)
1354 msgOut->nlMsg.nlmsgType = type;
1355 msgOut->nlMsg.nlmsgFlags = flags;
1356 msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1357 msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1358 msgOut->nlMsg.nlmsgLen = length;
1360 msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
1361 msgOut->genlMsg.version = msgIn->genlMsg.version;
1362 msgOut->genlMsg.reserved = 0;
1366 * XXX: should move out these functions to a Netlink.c or to a OvsMessage.c
1367 * or even make them inlined functions in Datapath.h. Can be done after the
1368 * first sprint once we have more code to refactor.
1371 BuildReplyMsgFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
1373 BuildMsgOut(msgIn, msgOut, msgIn->nlMsg.nlmsgType, sizeof(OVS_MESSAGE),
1378 BuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode)
1380 BuildMsgOut(msgIn, (POVS_MESSAGE)msgOut, NLMSG_ERROR,
1381 sizeof(OVS_MESSAGE_ERROR), 0);
1383 msgOut->errorMsg.error = errorCode;
1384 msgOut->errorMsg.nlMsg = msgIn->nlMsg;
1388 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1395 OVS_VPORT_FULL_STATS vportStats;
1400 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1402 BuildReplyMsgFromMsgIn(msgIn, &msgOut, NLM_F_MULTI);
1403 msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1405 ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1407 return STATUS_INSUFFICIENT_RESOURCES;
1410 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1412 return STATUS_INSUFFICIENT_RESOURCES;
1415 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1417 return STATUS_INSUFFICIENT_RESOURCES;
1420 ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1422 return STATUS_INSUFFICIENT_RESOURCES;
1426 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1427 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1428 * it means we have an array of pids, instead of a single pid.
1429 * ATM we assume we have one pid only.
1432 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1435 return STATUS_INSUFFICIENT_RESOURCES;
1439 vportStats.rxPackets = vport->stats.rxPackets;
1440 vportStats.rxBytes = vport->stats.rxBytes;
1441 vportStats.txPackets = vport->stats.txPackets;
1442 vportStats.txBytes = vport->stats.txBytes;
1443 vportStats.rxErrors = vport->errStats.rxErrors;
1444 vportStats.txErrors = vport->errStats.txErrors;
1445 vportStats.rxDropped = vport->errStats.rxDropped;
1446 vportStats.txDropped = vport->errStats.txDropped;
1448 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1450 sizeof(OVS_VPORT_FULL_STATS));
1452 return STATUS_INSUFFICIENT_RESOURCES;
1456 * XXX: when vxlan udp dest port becomes configurable, we will also need
1457 * to add vport options
1460 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1461 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1463 return STATUS_SUCCESS;
1467 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1471 POVS_OPEN_INSTANCE instance =
1472 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1473 LOCK_STATE_EX lockState;
1474 UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1477 * XXX: this function shares some code with other dump command(s).
1478 * In the future, we will need to refactor the dump functions
1481 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1483 if (instance->dumpState.ovsMsg == NULL) {
1485 return STATUS_INVALID_DEVICE_STATE;
1488 /* Output buffer has been validated while validating read dev op. */
1489 ASSERT(usrParamsCtx->outputBuffer != NULL);
1491 msgIn = instance->dumpState.ovsMsg;
1493 OvsAcquireCtrlLock();
1496 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1497 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1498 * it means we have an array of pids, instead of a single pid.
1499 * ATM we assume we have one pid only.
1501 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1502 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1503 NDIS_RWL_AT_DISPATCH_LEVEL);
1505 if (gOvsSwitchContext->numHvVports > 0 ||
1506 gOvsSwitchContext->numNonHvVports > 0) {
1507 /* inBucket: the bucket, used for lookup */
1508 UINT32 inBucket = instance->dumpState.index[0];
1509 /* inIndex: index within the given bucket, used for lookup */
1510 UINT32 inIndex = instance->dumpState.index[1];
1511 /* the bucket to be used for the next dump operation */
1512 UINT32 outBucket = 0;
1513 /* the index within the outBucket to be used for the next dump */
1514 UINT32 outIndex = 0;
1516 for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1517 PLIST_ENTRY head, link;
1518 head = &(gOvsSwitchContext->portNoHashArray[i]);
1519 POVS_VPORT_ENTRY vport = NULL;
1522 LIST_FORALL(head, link) {
1525 * if one or more dumps were previously done on this same bucket,
1526 * inIndex will be > 0, so we'll need to reply with the
1527 * inIndex + 1 vport from the bucket.
1529 if (outIndex >= inIndex) {
1530 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1532 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
1533 OvsCreateMsgFromVport(vport, msgIn,
1534 usrParamsCtx->outputBuffer,
1535 usrParamsCtx->outputLength,
1536 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 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1622 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1623 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1624 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1626 /* the port name is expected to be null-terminated */
1627 ASSERT(portName[portNameLen - 1] == '\0');
1629 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1630 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1631 portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1633 vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
1635 nlError = NL_ERROR_INVAL;
1636 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1641 nlError = NL_ERROR_NODEV;
1642 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1646 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1647 usrParamsCtx->outputLength,
1648 gOvsSwitchContext->dpNo);
1649 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1651 *replyLen = msgOut->nlMsg.nlmsgLen;
1654 if (nlError != NL_ERROR_SUCCESS) {
1655 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1656 usrParamsCtx->outputBuffer;
1658 BuildErrorMsg(msgIn, msgError, nlError);
1659 *replyLen = msgError->nlMsg.nlmsgLen;
1662 return STATUS_SUCCESS;
1666 * --------------------------------------------------------------------------
1667 * Handler for the get vport command. The function handles the initial call to
1668 * setup the dump state, as well as subsequent calls to continue dumping data.
1669 * --------------------------------------------------------------------------
1672 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1677 switch (usrParamsCtx->devOp)
1679 case OVS_WRITE_DEV_OP:
1680 return OvsSetupDumpStart(usrParamsCtx);
1682 case OVS_READ_DEV_OP:
1683 return OvsGetVportDumpNext(usrParamsCtx, replyLen);
1685 case OVS_TRANSACTION_DEV_OP:
1686 return OvsGetVport(usrParamsCtx, replyLen);
1689 return STATUS_INVALID_DEVICE_REQUEST;
1697 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
1699 /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
1700 for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
1701 POVS_VPORT_ENTRY vport;
1703 vport = OvsFindVportByPortNo(switchContext, i);
1709 return OVS_DPPORT_NUMBER_INVALID;
1713 * --------------------------------------------------------------------------
1714 * Command Handler for 'OVS_VPORT_CMD_NEW'.
1715 * --------------------------------------------------------------------------
1718 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1721 NDIS_STATUS status = STATUS_SUCCESS;
1722 LOCK_STATE_EX lockState;
1724 NL_ERROR nlError = NL_ERROR_SUCCESS;
1725 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1726 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1727 POVS_VPORT_ENTRY vport = NULL;
1731 BOOLEAN isBridgeInternal = FALSE;
1732 BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE;
1733 BOOLEAN addInternalPortAsNetdev = FALSE;
1735 static const NL_POLICY ovsVportPolicy[] = {
1736 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1737 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
1738 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
1740 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
1741 .optional = FALSE },
1742 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
1745 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1747 /* input buffer has been validated while validating write dev op. */
1748 ASSERT(usrParamsCtx->inputBuffer != NULL);
1750 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1751 return STATUS_INVALID_BUFFER_SIZE;
1754 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1755 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1756 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1757 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1758 return STATUS_INVALID_PARAMETER;
1761 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1762 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1763 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
1765 /* we are expecting null terminated strings to be passed */
1766 ASSERT(portName[portNameLen - 1] == '\0');
1768 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
1770 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1772 nlError = NL_ERROR_EXIST;
1776 if (portName && portType == OVS_VPORT_TYPE_NETDEV &&
1777 !strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
1778 addInternalPortAsNetdev = TRUE;
1781 if (portName && portType == OVS_VPORT_TYPE_INTERNAL &&
1782 strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
1783 isBridgeInternal = TRUE;
1786 if (portType == OVS_VPORT_TYPE_INTERNAL && !isBridgeInternal) {
1787 vport = gOvsSwitchContext->internalVport;
1788 } else if (portType == OVS_VPORT_TYPE_NETDEV) {
1789 /* External ports can also be looked up like VIF ports. */
1790 vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
1792 ASSERT(OvsIsTunnelVportType(portType) ||
1793 (portType == OVS_VPORT_TYPE_INTERNAL && isBridgeInternal));
1794 ASSERT(OvsGetTunnelVport(gOvsSwitchContext, portType) == NULL ||
1795 !OvsIsTunnelVportType(portType));
1797 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
1798 if (vport == NULL) {
1799 nlError = NL_ERROR_NOMEM;
1802 vportAllocated = TRUE;
1804 if (OvsIsTunnelVportType(portType)) {
1805 nlError = OvsInitTunnelVport(vport, portType, VXLAN_UDP_PORT);
1807 OvsInitBridgeInternalVport(vport);
1809 vportInitialized = TRUE;
1811 if (nlError == NL_ERROR_SUCCESS) {
1812 vport->ovsState = OVS_STATE_CONNECTED;
1813 vport->nicState = NdisSwitchNicStateConnected;
1816 * Allow the vport to be deleted, because there is no
1817 * corresponding hyper-v switch part.
1819 vport->hvDeleted = TRUE;
1824 nlError = NL_ERROR_INVAL;
1827 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
1828 nlError = NL_ERROR_EXIST;
1832 /* Initialize the vport with OVS specific properties. */
1833 if (addInternalPortAsNetdev != TRUE) {
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);
1856 /* if we don't have options, then vport->portOptions will be NULL */
1857 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
1860 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1861 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1862 * it means we have an array of pids, instead of a single pid.
1863 * ATM we assume we have one pid only.
1865 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
1867 status = InitOvsVportCommon(gOvsSwitchContext, vport);
1868 ASSERT(status == STATUS_SUCCESS);
1870 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1871 usrParamsCtx->outputLength,
1872 gOvsSwitchContext->dpNo);
1874 *replyLen = msgOut->nlMsg.nlmsgLen;
1877 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1879 if (nlError != NL_ERROR_SUCCESS) {
1880 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1881 usrParamsCtx->outputBuffer;
1883 if (vport && vportAllocated == TRUE) {
1884 if (vportInitialized == TRUE) {
1885 if (OvsIsTunnelVportType(portType)) {
1886 OvsCleanupVxlanTunnel(vport);
1889 OvsFreeMemory(vport);
1892 BuildErrorMsg(msgIn, msgError, nlError);
1893 *replyLen = msgError->nlMsg.nlmsgLen;
1896 return STATUS_SUCCESS;
1901 * --------------------------------------------------------------------------
1902 * Command Handler for 'OVS_VPORT_CMD_SET'.
1903 * --------------------------------------------------------------------------
1906 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1909 NDIS_STATUS status = STATUS_SUCCESS;
1910 LOCK_STATE_EX lockState;
1912 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1913 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1914 POVS_VPORT_ENTRY vport = NULL;
1915 NL_ERROR nlError = NL_ERROR_SUCCESS;
1917 static const NL_POLICY ovsVportPolicy[] = {
1918 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1919 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
1920 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
1922 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
1924 [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
1925 .minLen = sizeof(OVS_VPORT_FULL_STATS),
1926 .maxLen = sizeof(OVS_VPORT_FULL_STATS),
1928 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
1930 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1932 ASSERT(usrParamsCtx->inputBuffer != NULL);
1934 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1935 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1936 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1937 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1938 return STATUS_INVALID_PARAMETER;
1941 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1942 return STATUS_NDIS_INVALID_LENGTH;
1945 OvsAcquireCtrlLock();
1947 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
1948 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1949 PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1951 UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1953 /* the port name is expected to be null-terminated */
1954 ASSERT(portName[portNameLen - 1] == '\0');
1956 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1957 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1958 vport = OvsFindVportByPortNo(gOvsSwitchContext,
1959 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
1963 nlError = NL_ERROR_NODEV;
1968 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1969 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1970 * it means we have an array of pids, instead of a single pid.
1971 * Currently, we support only one pid.
1973 if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
1974 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
1977 if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
1978 OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
1979 if (type != vport->ovsType) {
1980 nlError = NL_ERROR_INVAL;
1985 if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
1986 OVS_LOG_ERROR("Vport options not supported");
1987 nlError = NL_ERROR_NOTSUPP;
1991 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1992 usrParamsCtx->outputLength,
1993 gOvsSwitchContext->dpNo);
1995 *replyLen = msgOut->nlMsg.nlmsgLen;
1998 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1999 OvsReleaseCtrlLock();
2001 if (nlError != NL_ERROR_SUCCESS) {
2002 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2003 usrParamsCtx->outputBuffer;
2005 BuildErrorMsg(msgIn, msgError, nlError);
2006 *replyLen = msgError->nlMsg.nlmsgLen;
2009 return STATUS_SUCCESS;
2013 * --------------------------------------------------------------------------
2014 * Command Handler for 'OVS_VPORT_CMD_DEL'.
2015 * --------------------------------------------------------------------------
2018 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2021 NDIS_STATUS status = STATUS_SUCCESS;
2022 LOCK_STATE_EX lockState;
2024 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2025 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2026 POVS_VPORT_ENTRY vport = NULL;
2027 NL_ERROR nlError = NL_ERROR_SUCCESS;
2028 PSTR portName = NULL;
2029 UINT32 portNameLen = 0;
2031 static const NL_POLICY ovsVportPolicy[] = {
2032 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2033 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2036 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2038 ASSERT(usrParamsCtx->inputBuffer != NULL);
2040 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2041 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2042 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2043 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2044 return STATUS_INVALID_PARAMETER;
2047 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
2048 return STATUS_NDIS_INVALID_LENGTH;
2051 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2052 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2053 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2054 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2056 /* the port name is expected to be null-terminated */
2057 ASSERT(portName[portNameLen - 1] == '\0');
2059 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2061 else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2062 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2063 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2067 nlError = NL_ERROR_NODEV;
2071 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2072 usrParamsCtx->outputLength,
2073 gOvsSwitchContext->dpNo);
2076 * Mark the port as deleted from OVS userspace. If the port does not exist
2077 * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
2079 OvsRemoveAndDeleteVport(gOvsSwitchContext, vport, FALSE, TRUE, NULL);
2081 *replyLen = msgOut->nlMsg.nlmsgLen;
2084 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2086 if (nlError != NL_ERROR_SUCCESS) {
2087 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2088 usrParamsCtx->outputBuffer;
2090 BuildErrorMsg(msgIn, msgError, nlError);
2091 *replyLen = msgError->nlMsg.nlmsgLen;
2094 return STATUS_SUCCESS;
2099 * --------------------------------------------------------------------------
2100 * Utility function to map the output buffer in an IRP. The buffer is assumed
2101 * to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
2102 * --------------------------------------------------------------------------
2105 MapIrpOutputBuffer(PIRP irp,
2106 UINT32 bufferLength,
2107 UINT32 requiredLength,
2112 ASSERT(bufferLength);
2113 ASSERT(requiredLength);
2114 if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
2115 return STATUS_INVALID_PARAMETER;
2118 if (bufferLength < requiredLength) {
2119 return STATUS_NDIS_INVALID_LENGTH;
2121 if (irp->MdlAddress == NULL) {
2122 return STATUS_INVALID_PARAMETER;
2124 *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
2125 NormalPagePriority);
2126 if (*buffer == NULL) {
2127 return STATUS_INSUFFICIENT_RESOURCES;
2130 return STATUS_SUCCESS;
2134 * --------------------------------------------------------------------------
2135 * Utility function to fill up information about the state of a port in a reply
2137 * Assumes that 'gOvsCtrlLock' lock is acquired.
2138 * --------------------------------------------------------------------------
2141 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2142 POVS_EVENT_ENTRY eventEntry,
2147 OVS_MESSAGE msgOutTmp;
2149 POVS_VPORT_ENTRY vport;
2151 ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
2153 msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
2154 msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
2156 /* driver intiated messages should have zerp seq number*/
2157 msgOutTmp.nlMsg.nlmsgSeq = 0;
2158 msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
2160 msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
2161 msgOutTmp.genlMsg.reserved = 0;
2163 /* we don't have netdev yet, treat link up/down a adding/removing a port*/
2164 if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
2165 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
2166 } else if (eventEntry->status &
2167 (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
2168 msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
2171 return STATUS_UNSUCCESSFUL;
2173 msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
2175 rc = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
2177 status = STATUS_INVALID_BUFFER_SIZE;
2181 vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
2183 status = STATUS_DEVICE_DOES_NOT_EXIST;
2187 rc = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) ||
2188 NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) ||
2189 NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
2191 status = STATUS_INVALID_BUFFER_SIZE;
2195 /* XXXX Should we add the port stats attributes?*/
2196 nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
2197 nlMsg->nlmsgLen = NlBufSize(nlBuf);
2198 status = STATUS_SUCCESS;
2206 * --------------------------------------------------------------------------
2207 * Handler for reading events from the driver event queue. This handler is
2208 * executed when user modes issues a socket receive on a socket assocaited
2209 * with the MC group for events.
2210 * XXX user mode should read multiple events in one system call
2211 * --------------------------------------------------------------------------
2214 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2218 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2219 POVS_OPEN_INSTANCE instance =
2220 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
2224 OVS_EVENT_ENTRY eventEntry;
2226 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
2228 /* Should never read events with a dump socket */
2229 ASSERT(instance->dumpState.ovsMsg == NULL);
2231 /* Must have an event queue */
2232 ASSERT(instance->eventQueue != NULL);
2234 /* Output buffer has been validated while validating read dev op. */
2235 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2237 NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
2239 OvsAcquireCtrlLock();
2241 /* remove an event entry from the event queue */
2242 status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
2243 if (status != STATUS_SUCCESS) {
2244 /* If there were not elements, read should return no data. */
2245 status = STATUS_SUCCESS;
2250 status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
2251 if (status == NDIS_STATUS_SUCCESS) {
2252 *replyLen = NlBufSize(&nlBuf);
2256 OvsReleaseCtrlLock();
2261 * --------------------------------------------------------------------------
2262 * Handler for reading missed pacckets from the driver event queue. This
2263 * handler is executed when user modes issues a socket receive on a socket
2264 * --------------------------------------------------------------------------
2267 OvsReadPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2271 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2273 POVS_OPEN_INSTANCE instance =
2274 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
2277 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
2279 /* Should never read events with a dump socket */
2280 ASSERT(instance->dumpState.ovsMsg == NULL);
2282 /* Must have an packet queue */
2283 ASSERT(instance->packetQueue != NULL);
2285 /* Output buffer has been validated while validating read dev op. */
2286 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2288 /* Read a packet from the instance queue */
2289 status = OvsReadDpIoctl(instance->fileObject, usrParamsCtx->outputBuffer,
2290 usrParamsCtx->outputLength, replyLen);
2295 * --------------------------------------------------------------------------
2296 * Handler for the subscription for a packet queue
2297 * --------------------------------------------------------------------------
2300 OvsSubscribePacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2307 const NL_POLICY policy[] = {
2308 [OVS_NL_ATTR_PACKET_PID] = {.type = NL_A_U32 },
2309 [OVS_NL_ATTR_PACKET_SUBSCRIBE] = {.type = NL_A_U8 }
2311 PNL_ATTR attrs[ARRAY_SIZE(policy)];
2313 UNREFERENCED_PARAMETER(replyLen);
2315 POVS_OPEN_INSTANCE instance =
2316 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
2317 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2319 rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
2320 NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, ARRAY_SIZE(attrs));
2322 status = STATUS_INVALID_PARAMETER;
2326 join = NlAttrGetU8(attrs[OVS_NL_ATTR_PACKET_PID]);
2327 pid = NlAttrGetU32(attrs[OVS_NL_ATTR_PACKET_PID]);
2329 /* The socket subscribed with must be the same socket we perform receive*/
2330 ASSERT(pid == instance->pid);
2332 status = OvsSubscribeDpIoctl(instance, pid, join);
2335 * XXX Need to add this instance to a global data structure
2336 * which hold all packet based instances. The data structure (hash)
2337 * should be searched through the pid field of the instance for
2338 * placing the missed packet into the correct queue
2345 * --------------------------------------------------------------------------
2346 * Handler for queueing an IRP used for missed packet notification. The IRP is
2347 * completed when a packet received and mismatched. STATUS_PENDING is returned
2348 * on success. User mode keep a pending IRP at all times.
2349 * --------------------------------------------------------------------------
2352 OvsPendPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2355 UNREFERENCED_PARAMETER(replyLen);
2357 POVS_OPEN_INSTANCE instance =
2358 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
2361 * XXX access to packet queue must be through acquiring a lock as user mode
2362 * could unsubscribe and the instnace will be freed.
2364 return OvsWaitDpIoctl(usrParamsCtx->irp, instance->fileObject);
2367 #endif /* OVS_USE_NL_INTERFACE */