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
35 #define OVS_DBG_MOD OVS_DBG_DATAPATH
38 #define NETLINK_FAMILY_NAME_LEN 48
42 * Netlink messages are grouped by family (aka type), and each family supports
43 * a set of commands, and can be passed both from kernel -> userspace or
44 * vice-versa. To call into the kernel, userspace uses a device operation which
45 * is outside of a netlink message.
47 * Each command results in the invocation of a handler function to implement the
48 * request functionality.
50 * Expectedly, only certain combinations of (device operation, netlink family,
53 * Here, we implement the basic infrastructure to perform validation on the
54 * incoming message, version checking, and also to invoke the corresponding
55 * handler to do the heavy-lifting.
59 * Handler for a given netlink command. Not all the parameters are used by all
62 typedef NTSTATUS(NetlinkCmdHandler
)(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
65 typedef struct _NETLINK_CMD
{
67 NetlinkCmdHandler
*handler
;
68 UINT32 supportedDevOp
; /* Supported device operations. */
69 BOOLEAN validateDpIndex
; /* Does command require a valid DP argument. */
70 } NETLINK_CMD
, *PNETLINK_CMD
;
72 /* A netlink family is a group of commands. */
73 typedef struct _NETLINK_FAMILY
{
80 NETLINK_CMD
*cmds
; /* Array of netlink commands and handlers. */
82 } NETLINK_FAMILY
, *PNETLINK_FAMILY
;
84 /* Handlers for the various netlink commands. */
85 static NetlinkCmdHandler OvsPendEventCmdHandler
,
86 OvsSubscribeEventCmdHandler
,
87 OvsReadEventCmdHandler
,
91 OvsSockPropCmdHandler
;
93 NetlinkCmdHandler OvsGetNetdevCmdHandler
,
94 OvsGetVportCmdHandler
,
95 OvsSetVportCmdHandler
,
96 OvsNewVportCmdHandler
,
97 OvsDeleteVportCmdHandler
,
98 OvsPendPacketCmdHandler
,
99 OvsSubscribePacketCmdHandler
,
100 OvsReadPacketCmdHandler
,
101 OvsCtDeleteCmdHandler
,
104 static NTSTATUS
HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
106 static NTSTATUS
HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
108 static NTSTATUS
HandleDpTransactionCommon(
109 POVS_USER_PARAMS_CONTEXT usrParamsCtx
, UINT32
*replyLen
);
110 static NTSTATUS
OvsGetPidHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
114 * The various netlink families, along with the supported commands. Most of
115 * these families and commands are part of the openvswitch specification for a
116 * netlink datapath. In addition, each platform can implement a few families
117 * and commands as extensions.
120 /* Netlink control family: this is a Windows specific family. */
121 NETLINK_CMD nlControlFamilyCmdOps
[] = {
122 { .cmd
= OVS_CTRL_CMD_WIN_PEND_REQ
,
123 .handler
= OvsPendEventCmdHandler
,
124 .supportedDevOp
= OVS_WRITE_DEV_OP
,
125 .validateDpIndex
= TRUE
,
127 { .cmd
= OVS_CTRL_CMD_WIN_PEND_PACKET_REQ
,
128 .handler
= OvsPendPacketCmdHandler
,
129 .supportedDevOp
= OVS_WRITE_DEV_OP
,
130 .validateDpIndex
= TRUE
,
132 { .cmd
= OVS_CTRL_CMD_MC_SUBSCRIBE_REQ
,
133 .handler
= OvsSubscribeEventCmdHandler
,
134 .supportedDevOp
= OVS_WRITE_DEV_OP
,
135 .validateDpIndex
= TRUE
,
137 { .cmd
= OVS_CTRL_CMD_PACKET_SUBSCRIBE_REQ
,
138 .handler
= OvsSubscribePacketCmdHandler
,
139 .supportedDevOp
= OVS_WRITE_DEV_OP
,
140 .validateDpIndex
= TRUE
,
142 { .cmd
= OVS_CTRL_CMD_EVENT_NOTIFY
,
143 .handler
= OvsReadEventCmdHandler
,
144 .supportedDevOp
= OVS_READ_DEV_OP
,
145 .validateDpIndex
= FALSE
,
147 { .cmd
= OVS_CTRL_CMD_READ_NOTIFY
,
148 .handler
= OvsReadPacketCmdHandler
,
149 .supportedDevOp
= OVS_READ_DEV_OP
,
150 .validateDpIndex
= FALSE
,
152 { .cmd
= OVS_CTRL_CMD_SOCK_PROP
,
153 .handler
= OvsSockPropCmdHandler
,
154 .supportedDevOp
= OVS_TRANSACTION_DEV_OP
,
155 .validateDpIndex
= FALSE
,
159 NETLINK_FAMILY nlControlFamilyOps
= {
160 .name
= OVS_WIN_CONTROL_FAMILY
,
161 .id
= OVS_WIN_NL_CTRL_FAMILY_ID
,
162 .version
= OVS_WIN_CONTROL_VERSION
,
163 .maxAttr
= OVS_WIN_CONTROL_ATTR_MAX
,
164 .cmds
= nlControlFamilyCmdOps
,
165 .opsCount
= ARRAY_SIZE(nlControlFamilyCmdOps
)
168 /* Netlink datapath family. */
169 NETLINK_CMD nlDatapathFamilyCmdOps
[] = {
170 { .cmd
= OVS_DP_CMD_NEW
,
171 .handler
= OvsNewDpCmdHandler
,
172 .supportedDevOp
= OVS_TRANSACTION_DEV_OP
,
173 .validateDpIndex
= FALSE
175 { .cmd
= OVS_DP_CMD_GET
,
176 .handler
= OvsGetDpCmdHandler
,
177 .supportedDevOp
= OVS_WRITE_DEV_OP
| OVS_READ_DEV_OP
|
178 OVS_TRANSACTION_DEV_OP
,
179 .validateDpIndex
= FALSE
181 { .cmd
= OVS_DP_CMD_SET
,
182 .handler
= OvsSetDpCmdHandler
,
183 .supportedDevOp
= OVS_WRITE_DEV_OP
| OVS_READ_DEV_OP
|
184 OVS_TRANSACTION_DEV_OP
,
185 .validateDpIndex
= TRUE
189 NETLINK_FAMILY nlDatapathFamilyOps
= {
190 .name
= OVS_DATAPATH_FAMILY
,
191 .id
= OVS_WIN_NL_DATAPATH_FAMILY_ID
,
192 .version
= OVS_DATAPATH_VERSION
,
193 .maxAttr
= OVS_DP_ATTR_MAX
,
194 .cmds
= nlDatapathFamilyCmdOps
,
195 .opsCount
= ARRAY_SIZE(nlDatapathFamilyCmdOps
)
198 /* Netlink packet family. */
200 NETLINK_CMD nlPacketFamilyCmdOps
[] = {
201 { .cmd
= OVS_PACKET_CMD_EXECUTE
,
202 .handler
= OvsNlExecuteCmdHandler
,
203 .supportedDevOp
= OVS_TRANSACTION_DEV_OP
,
204 .validateDpIndex
= TRUE
208 NETLINK_FAMILY nlPacketFamilyOps
= {
209 .name
= OVS_PACKET_FAMILY
,
210 .id
= OVS_WIN_NL_PACKET_FAMILY_ID
,
211 .version
= OVS_PACKET_VERSION
,
212 .maxAttr
= OVS_PACKET_ATTR_MAX
,
213 .cmds
= nlPacketFamilyCmdOps
,
214 .opsCount
= ARRAY_SIZE(nlPacketFamilyCmdOps
)
217 /* Netlink vport family. */
218 NETLINK_CMD nlVportFamilyCmdOps
[] = {
219 { .cmd
= OVS_VPORT_CMD_GET
,
220 .handler
= OvsGetVportCmdHandler
,
221 .supportedDevOp
= OVS_WRITE_DEV_OP
| OVS_READ_DEV_OP
|
222 OVS_TRANSACTION_DEV_OP
,
223 .validateDpIndex
= TRUE
225 { .cmd
= OVS_VPORT_CMD_NEW
,
226 .handler
= OvsNewVportCmdHandler
,
227 .supportedDevOp
= OVS_TRANSACTION_DEV_OP
,
228 .validateDpIndex
= TRUE
230 { .cmd
= OVS_VPORT_CMD_SET
,
231 .handler
= OvsSetVportCmdHandler
,
232 .supportedDevOp
= OVS_TRANSACTION_DEV_OP
,
233 .validateDpIndex
= TRUE
235 { .cmd
= OVS_VPORT_CMD_DEL
,
236 .handler
= OvsDeleteVportCmdHandler
,
237 .supportedDevOp
= OVS_TRANSACTION_DEV_OP
,
238 .validateDpIndex
= TRUE
242 NETLINK_FAMILY nlVportFamilyOps
= {
243 .name
= OVS_VPORT_FAMILY
,
244 .id
= OVS_WIN_NL_VPORT_FAMILY_ID
,
245 .version
= OVS_VPORT_VERSION
,
246 .maxAttr
= OVS_VPORT_ATTR_MAX
,
247 .cmds
= nlVportFamilyCmdOps
,
248 .opsCount
= ARRAY_SIZE(nlVportFamilyCmdOps
)
251 /* Netlink flow family. */
253 NETLINK_CMD nlFlowFamilyCmdOps
[] = {
254 { .cmd
= OVS_FLOW_CMD_NEW
,
255 .handler
= OvsFlowNlCmdHandler
,
256 .supportedDevOp
= OVS_TRANSACTION_DEV_OP
,
257 .validateDpIndex
= TRUE
259 { .cmd
= OVS_FLOW_CMD_SET
,
260 .handler
= OvsFlowNlCmdHandler
,
261 .supportedDevOp
= OVS_TRANSACTION_DEV_OP
,
262 .validateDpIndex
= TRUE
264 { .cmd
= OVS_FLOW_CMD_DEL
,
265 .handler
= OvsFlowNlCmdHandler
,
266 .supportedDevOp
= OVS_TRANSACTION_DEV_OP
,
267 .validateDpIndex
= TRUE
269 { .cmd
= OVS_FLOW_CMD_GET
,
270 .handler
= OvsFlowNlGetCmdHandler
,
271 .supportedDevOp
= OVS_TRANSACTION_DEV_OP
|
272 OVS_WRITE_DEV_OP
| OVS_READ_DEV_OP
,
273 .validateDpIndex
= TRUE
277 NETLINK_FAMILY nlFLowFamilyOps
= {
278 .name
= OVS_FLOW_FAMILY
,
279 .id
= OVS_WIN_NL_FLOW_FAMILY_ID
,
280 .version
= OVS_FLOW_VERSION
,
281 .maxAttr
= OVS_FLOW_ATTR_MAX
,
282 .cmds
= nlFlowFamilyCmdOps
,
283 .opsCount
= ARRAY_SIZE(nlFlowFamilyCmdOps
)
286 /* Netlink Ct family. */
287 NETLINK_CMD nlCtFamilyCmdOps
[] = {
288 { .cmd
= IPCTNL_MSG_CT_DELETE
,
289 .handler
= OvsCtDeleteCmdHandler
,
290 .supportedDevOp
= OVS_TRANSACTION_DEV_OP
,
291 .validateDpIndex
= FALSE
293 { .cmd
= IPCTNL_MSG_CT_GET
,
294 .handler
= OvsCtDumpCmdHandler
,
295 .supportedDevOp
= OVS_WRITE_DEV_OP
| OVS_READ_DEV_OP
,
296 .validateDpIndex
= FALSE
300 NETLINK_FAMILY nlCtFamilyOps
= {
301 .name
= OVS_CT_FAMILY
, /* Keep this for consistency*/
302 .id
= OVS_WIN_NL_CT_FAMILY_ID
, /* Keep this for consistency*/
303 .version
= OVS_CT_VERSION
, /* Keep this for consistency*/
304 .maxAttr
= OVS_NL_CT_ATTR_MAX
,
305 .cmds
= nlCtFamilyCmdOps
,
306 .opsCount
= ARRAY_SIZE(nlCtFamilyCmdOps
)
309 /* Netlink netdev family. */
310 NETLINK_CMD nlNetdevFamilyCmdOps
[] = {
311 { .cmd
= OVS_WIN_NETDEV_CMD_GET
,
312 .handler
= OvsGetNetdevCmdHandler
,
313 .supportedDevOp
= OVS_TRANSACTION_DEV_OP
,
314 .validateDpIndex
= FALSE
318 NETLINK_FAMILY nlNetdevFamilyOps
= {
319 .name
= OVS_WIN_NETDEV_FAMILY
,
320 .id
= OVS_WIN_NL_NETDEV_FAMILY_ID
,
321 .version
= OVS_WIN_NETDEV_VERSION
,
322 .maxAttr
= OVS_WIN_NETDEV_ATTR_MAX
,
323 .cmds
= nlNetdevFamilyCmdOps
,
324 .opsCount
= ARRAY_SIZE(nlNetdevFamilyCmdOps
)
327 static NTSTATUS
MapIrpOutputBuffer(PIRP irp
,
329 UINT32 requiredLength
,
331 static NTSTATUS
ValidateNetlinkCmd(UINT32 devOp
,
332 POVS_OPEN_INSTANCE instance
,
335 NETLINK_FAMILY
*nlFamilyOps
);
336 static NTSTATUS
InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
337 NETLINK_FAMILY
*nlFamilyOps
,
340 /* Handles to the device object for communication with userspace. */
341 NDIS_HANDLE gOvsDeviceHandle
;
342 PDEVICE_OBJECT gOvsDeviceObject
;
344 _Dispatch_type_(IRP_MJ_CREATE
)
345 _Dispatch_type_(IRP_MJ_CLOSE
)
346 DRIVER_DISPATCH OvsOpenCloseDevice
;
348 _Dispatch_type_(IRP_MJ_CLEANUP
)
349 DRIVER_DISPATCH OvsCleanupDevice
;
351 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL
)
352 DRIVER_DISPATCH OvsDeviceControl
;
355 #pragma alloc_text(INIT, OvsCreateDeviceObject)
356 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
357 #pragma alloc_text(PAGE, OvsCleanupDevice)
358 #pragma alloc_text(PAGE, OvsDeviceControl)
359 #endif // ALLOC_PRAGMA
362 * We might hit this limit easily since userspace opens a netlink descriptor for
363 * each thread, and at least one descriptor per vport. Revisit this later.
365 #define OVS_MAX_OPEN_INSTANCES 512
366 #define OVS_SYSTEM_DP_NAME "ovs-system"
368 POVS_OPEN_INSTANCE ovsOpenInstanceArray
[OVS_MAX_OPEN_INSTANCES
];
369 UINT32 ovsNumberOfOpenInstances
;
370 extern POVS_SWITCH_CONTEXT gOvsSwitchContext
;
372 NDIS_SPIN_LOCK ovsCtrlLockObj
;
373 PNDIS_SPIN_LOCK gOvsCtrlLock
;
376 InitUserDumpState(POVS_OPEN_INSTANCE instance
,
379 /* Clear the dumpState from a previous dump sequence. */
380 ASSERT(instance
->dumpState
.ovsMsg
== NULL
);
383 instance
->dumpState
.ovsMsg
=
384 (POVS_MESSAGE
)OvsAllocateMemoryWithTag(sizeof(OVS_MESSAGE
),
385 OVS_DATAPATH_POOL_TAG
);
386 if (instance
->dumpState
.ovsMsg
== NULL
) {
387 return STATUS_NO_MEMORY
;
389 RtlCopyMemory(instance
->dumpState
.ovsMsg
, ovsMsg
,
390 sizeof *instance
->dumpState
.ovsMsg
);
391 RtlZeroMemory(instance
->dumpState
.index
,
392 sizeof instance
->dumpState
.index
);
394 return STATUS_SUCCESS
;
398 FreeUserDumpState(POVS_OPEN_INSTANCE instance
)
400 if (instance
->dumpState
.ovsMsg
!= NULL
) {
401 OvsFreeMemoryWithTag(instance
->dumpState
.ovsMsg
,
402 OVS_DATAPATH_POOL_TAG
);
403 RtlZeroMemory(&instance
->dumpState
, sizeof instance
->dumpState
);
410 NDIS_STATUS status
= NDIS_STATUS_SUCCESS
;
412 gOvsCtrlLock
= &ovsCtrlLockObj
;
413 NdisAllocateSpinLock(gOvsCtrlLock
);
416 status
= OvsPerCpuDataInit();
424 OvsPerCpuDataCleanup();
425 OvsCleanupEventQueue();
427 NdisFreeSpinLock(gOvsCtrlLock
);
432 _Use_decl_annotations_
436 NdisAcquireSpinLock(gOvsCtrlLock
);
442 NdisReleaseSpinLock(gOvsCtrlLock
);
447 * --------------------------------------------------------------------------
448 * Creates the communication device between user and kernel, and also
449 * initializes the data associated data structures.
450 * --------------------------------------------------------------------------
453 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle
)
455 NDIS_STATUS status
= NDIS_STATUS_SUCCESS
;
456 UNICODE_STRING deviceName
;
457 UNICODE_STRING symbolicDeviceName
;
458 PDRIVER_DISPATCH dispatchTable
[IRP_MJ_MAXIMUM_FUNCTION
+1];
459 NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes
;
460 OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle
);
462 RtlZeroMemory(dispatchTable
,
463 (IRP_MJ_MAXIMUM_FUNCTION
+ 1) * sizeof (PDRIVER_DISPATCH
));
464 dispatchTable
[IRP_MJ_CREATE
] = OvsOpenCloseDevice
;
465 dispatchTable
[IRP_MJ_CLOSE
] = OvsOpenCloseDevice
;
466 dispatchTable
[IRP_MJ_CLEANUP
] = OvsCleanupDevice
;
467 dispatchTable
[IRP_MJ_DEVICE_CONTROL
] = OvsDeviceControl
;
469 NdisInitUnicodeString(&deviceName
, OVS_DEVICE_NAME_NT
);
470 NdisInitUnicodeString(&symbolicDeviceName
, OVS_DEVICE_NAME_DOS
);
472 RtlZeroMemory(&deviceAttributes
, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES
));
474 OVS_INIT_OBJECT_HEADER(&deviceAttributes
.Header
,
475 NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES
,
476 NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1
,
477 sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES
));
479 deviceAttributes
.DeviceName
= &deviceName
;
480 deviceAttributes
.SymbolicName
= &symbolicDeviceName
;
481 deviceAttributes
.MajorFunctions
= dispatchTable
;
482 deviceAttributes
.ExtensionSize
= sizeof (OVS_DEVICE_EXTENSION
);
484 status
= NdisRegisterDeviceEx(ovsExtDriverHandle
,
488 if (status
== NDIS_STATUS_SUCCESS
) {
489 OvsRegisterSystemProvider((PVOID
)gOvsDeviceObject
);
491 OVS_LOG_ERROR("Failed to regiser pseudo device, error: 0x%08x",
495 OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject
);
501 OvsDeleteDeviceObject()
503 if (gOvsDeviceHandle
) {
505 POVS_DEVICE_EXTENSION ovsExt
= (POVS_DEVICE_EXTENSION
)
506 NdisGetDeviceReservedExtension(gOvsDeviceObject
);
508 ASSERT(ovsExt
->numberOpenInstance
== 0);
512 ASSERT(gOvsDeviceObject
);
513 NdisDeregisterDeviceEx(gOvsDeviceHandle
);
514 gOvsDeviceHandle
= NULL
;
515 gOvsDeviceObject
= NULL
;
517 OvsUnregisterSystemProvider();
522 OvsGetOpenInstance(PFILE_OBJECT fileObject
,
525 LOCK_STATE_EX lockState
;
526 POVS_OPEN_INSTANCE instance
= (POVS_OPEN_INSTANCE
)fileObject
->FsContext
;
528 ASSERT(instance
->fileObject
== fileObject
);
529 NdisAcquireRWLockWrite(gOvsSwitchContext
->dispatchLock
, &lockState
, 0);
531 if (gOvsSwitchContext
->dpNo
!= dpNo
) {
535 NdisReleaseRWLock(gOvsSwitchContext
->dispatchLock
, &lockState
);
541 OvsFindOpenInstance(PFILE_OBJECT fileObject
)
544 for (i
= 0, j
= 0; i
< OVS_MAX_OPEN_INSTANCES
&&
545 j
< ovsNumberOfOpenInstances
; i
++) {
546 if (ovsOpenInstanceArray
[i
]) {
547 if (ovsOpenInstanceArray
[i
]->fileObject
== fileObject
) {
548 return ovsOpenInstanceArray
[i
];
557 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt
,
558 PFILE_OBJECT fileObject
)
560 POVS_OPEN_INSTANCE instance
=
561 (POVS_OPEN_INSTANCE
)OvsAllocateMemoryWithTag(sizeof(OVS_OPEN_INSTANCE
),
562 OVS_DATAPATH_POOL_TAG
);
565 if (instance
== NULL
) {
566 return STATUS_NO_MEMORY
;
568 OvsAcquireCtrlLock();
569 ASSERT(OvsFindOpenInstance(fileObject
) == NULL
);
571 if (ovsNumberOfOpenInstances
>= OVS_MAX_OPEN_INSTANCES
) {
572 OvsReleaseCtrlLock();
573 OvsFreeMemoryWithTag(instance
, OVS_DATAPATH_POOL_TAG
);
574 return STATUS_INSUFFICIENT_RESOURCES
;
576 RtlZeroMemory(instance
, sizeof (OVS_OPEN_INSTANCE
));
578 for (i
= 0; i
< OVS_MAX_OPEN_INSTANCES
; i
++) {
579 if (ovsOpenInstanceArray
[i
] == NULL
) {
580 ovsOpenInstanceArray
[i
] = instance
;
581 ovsNumberOfOpenInstances
++;
582 instance
->cookie
= i
;
586 ASSERT(i
< OVS_MAX_OPEN_INSTANCES
);
587 instance
->fileObject
= fileObject
;
588 ASSERT(fileObject
->FsContext
== NULL
);
589 instance
->pid
= (UINT32
)InterlockedIncrement((LONG
volatile *)&ovsExt
->pidCount
);
590 if (instance
->pid
== 0) {
591 /* XXX: check for rollover. */
593 fileObject
->FsContext
= instance
;
594 OvsReleaseCtrlLock();
595 return STATUS_SUCCESS
;
599 OvsCleanupOpenInstance(PFILE_OBJECT fileObject
)
601 POVS_OPEN_INSTANCE instance
= (POVS_OPEN_INSTANCE
)fileObject
->FsContext
;
603 ASSERT(fileObject
== instance
->fileObject
);
604 OvsCleanupEvent(instance
);
605 OvsCleanupPacketQueue(instance
);
609 OvsRemoveOpenInstance(PFILE_OBJECT fileObject
)
611 POVS_OPEN_INSTANCE instance
;
612 ASSERT(fileObject
->FsContext
);
613 instance
= (POVS_OPEN_INSTANCE
)fileObject
->FsContext
;
614 ASSERT(instance
->cookie
< OVS_MAX_OPEN_INSTANCES
);
616 OvsAcquireCtrlLock();
617 fileObject
->FsContext
= NULL
;
618 ASSERT(ovsOpenInstanceArray
[instance
->cookie
] == instance
);
619 ovsOpenInstanceArray
[instance
->cookie
] = NULL
;
620 ovsNumberOfOpenInstances
--;
621 OvsReleaseCtrlLock();
622 ASSERT(instance
->eventQueue
== NULL
);
623 ASSERT (instance
->packetQueue
== NULL
);
624 FreeUserDumpState(instance
);
625 OvsFreeMemoryWithTag(instance
, OVS_DATAPATH_POOL_TAG
);
629 OvsCompleteIrpRequest(PIRP irp
,
633 irp
->IoStatus
.Information
= infoPtr
;
634 irp
->IoStatus
.Status
= status
;
635 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
641 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject
,
644 PIO_STACK_LOCATION irpSp
;
645 NTSTATUS status
= STATUS_SUCCESS
;
646 PFILE_OBJECT fileObject
;
647 POVS_DEVICE_EXTENSION ovsExt
=
648 (POVS_DEVICE_EXTENSION
)NdisGetDeviceReservedExtension(deviceObject
);
650 #pragma warning(suppress: 28118)
652 ASSERT(deviceObject
== gOvsDeviceObject
);
653 ASSERT(ovsExt
!= NULL
);
655 irpSp
= IoGetCurrentIrpStackLocation(irp
);
656 fileObject
= irpSp
->FileObject
;
657 OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
658 deviceObject
, fileObject
,
659 ovsExt
->numberOpenInstance
);
661 switch (irpSp
->MajorFunction
) {
663 status
= OvsAddOpenInstance(ovsExt
, fileObject
);
664 if (STATUS_SUCCESS
== status
) {
665 InterlockedIncrement((LONG
volatile *)&ovsExt
->numberOpenInstance
);
669 ASSERT(ovsExt
->numberOpenInstance
> 0);
670 OvsRemoveOpenInstance(fileObject
);
671 InterlockedDecrement((LONG
volatile *)&ovsExt
->numberOpenInstance
);
676 return OvsCompleteIrpRequest(irp
, (ULONG_PTR
)0, status
);
679 _Use_decl_annotations_
681 OvsCleanupDevice(PDEVICE_OBJECT deviceObject
,
684 #pragma warning(suppress: 28118)
686 PIO_STACK_LOCATION irpSp
;
687 PFILE_OBJECT fileObject
;
689 NTSTATUS status
= STATUS_SUCCESS
;
691 POVS_DEVICE_EXTENSION ovsExt
=
692 (POVS_DEVICE_EXTENSION
)NdisGetDeviceReservedExtension(deviceObject
);
694 ASSERT(ovsExt
->numberOpenInstance
> 0);
697 UNREFERENCED_PARAMETER(deviceObject
);
699 ASSERT(deviceObject
== gOvsDeviceObject
);
700 irpSp
= IoGetCurrentIrpStackLocation(irp
);
701 fileObject
= irpSp
->FileObject
;
703 ASSERT(irpSp
->MajorFunction
== IRP_MJ_CLEANUP
);
705 OvsCleanupOpenInstance(fileObject
);
707 return OvsCompleteIrpRequest(irp
, (ULONG_PTR
)0, status
);
711 * --------------------------------------------------------------------------
712 * IOCTL function handler for the device.
713 * --------------------------------------------------------------------------
716 OvsDeviceControl(PDEVICE_OBJECT deviceObject
,
719 PIO_STACK_LOCATION irpSp
;
720 NTSTATUS status
= STATUS_SUCCESS
;
721 PFILE_OBJECT fileObject
;
722 PVOID inputBuffer
= NULL
;
723 PVOID outputBuffer
= NULL
;
724 UINT32 inputBufferLen
, outputBufferLen
;
725 UINT32 code
, replyLen
= 0;
726 POVS_OPEN_INSTANCE instance
;
728 OVS_MESSAGE ovsMsgReadOp
;
730 UINT32 ovsMsgLength
= 0;
731 NETLINK_FAMILY
*nlFamilyOps
;
732 OVS_USER_PARAMS_CONTEXT usrParamsCtx
;
734 #pragma warning(suppress: 28118)
737 POVS_DEVICE_EXTENSION ovsExt
=
738 (POVS_DEVICE_EXTENSION
)NdisGetDeviceReservedExtension(deviceObject
);
739 ASSERT(deviceObject
== gOvsDeviceObject
);
741 ASSERT(ovsExt
->numberOpenInstance
> 0);
743 UNREFERENCED_PARAMETER(deviceObject
);
746 irpSp
= IoGetCurrentIrpStackLocation(irp
);
748 ASSERT(irpSp
->MajorFunction
== IRP_MJ_DEVICE_CONTROL
);
749 ASSERT(irpSp
->FileObject
!= NULL
);
751 fileObject
= irpSp
->FileObject
;
752 instance
= (POVS_OPEN_INSTANCE
)fileObject
->FsContext
;
753 code
= irpSp
->Parameters
.DeviceIoControl
.IoControlCode
;
754 inputBufferLen
= irpSp
->Parameters
.DeviceIoControl
.InputBufferLength
;
755 outputBufferLen
= irpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
;
756 inputBuffer
= irp
->AssociatedIrp
.SystemBuffer
;
758 /* Check if the extension is enabled. */
759 if (NULL
== gOvsSwitchContext
) {
760 status
= STATUS_NOT_FOUND
;
764 if (!OvsAcquireSwitchContext()) {
765 status
= STATUS_NOT_FOUND
;
770 * Validate the input/output buffer arguments depending on the type of the
774 case OVS_IOCTL_GET_PID
:
775 /* Both input buffer and output buffer use the same location. */
776 outputBuffer
= irp
->AssociatedIrp
.SystemBuffer
;
777 if (outputBufferLen
!= 0) {
778 InitUserParamsCtx(irp
, instance
, 0, NULL
,
779 inputBuffer
, inputBufferLen
,
780 outputBuffer
, outputBufferLen
,
783 ASSERT(outputBuffer
);
785 status
= STATUS_NDIS_INVALID_LENGTH
;
789 status
= OvsGetPidHandler(&usrParamsCtx
, &replyLen
);
792 case OVS_IOCTL_TRANSACT
:
793 /* Both input buffer and output buffer are mandatory. */
794 if (outputBufferLen
!= 0) {
795 ASSERT(sizeof(OVS_MESSAGE_ERROR
) >= sizeof *ovsMsg
);
796 status
= MapIrpOutputBuffer(irp
, outputBufferLen
,
797 sizeof(OVS_MESSAGE_ERROR
),
799 if (status
!= STATUS_SUCCESS
) {
802 ASSERT(outputBuffer
);
804 status
= STATUS_NDIS_INVALID_LENGTH
;
808 if (inputBufferLen
< sizeof (*ovsMsg
)) {
809 status
= STATUS_NDIS_INVALID_LENGTH
;
813 ovsMsg
= inputBuffer
;
814 ovsMsgLength
= inputBufferLen
;
815 devOp
= OVS_TRANSACTION_DEV_OP
;
818 case OVS_IOCTL_READ_EVENT
:
819 case OVS_IOCTL_READ_PACKET
:
821 * Output buffer is mandatory. These IOCTLs are used to read events and
822 * packets respectively. It is convenient to have separate ioctls.
824 if (outputBufferLen
!= 0) {
825 status
= MapIrpOutputBuffer(irp
, outputBufferLen
,
826 sizeof(OVS_MESSAGE_ERROR
),
828 if (status
!= STATUS_SUCCESS
) {
831 ASSERT(outputBuffer
);
833 status
= STATUS_NDIS_INVALID_LENGTH
;
839 ovsMsg
= &ovsMsgReadOp
;
840 RtlZeroMemory(ovsMsg
, sizeof *ovsMsg
);
841 ovsMsg
->nlMsg
.nlmsgLen
= sizeof *ovsMsg
;
842 ovsMsg
->nlMsg
.nlmsgType
= nlControlFamilyOps
.id
;
843 ovsMsg
->nlMsg
.nlmsgPid
= instance
->pid
;
845 /* An "artificial" command so we can use NL family function table*/
846 ovsMsg
->genlMsg
.cmd
= (code
== OVS_IOCTL_READ_EVENT
) ?
847 OVS_CTRL_CMD_EVENT_NOTIFY
:
848 OVS_CTRL_CMD_READ_NOTIFY
;
849 ovsMsg
->genlMsg
.version
= nlControlFamilyOps
.version
;
850 ovsMsgLength
= outputBufferLen
;
852 devOp
= OVS_READ_DEV_OP
;
856 /* Output buffer is mandatory. */
857 if (outputBufferLen
!= 0) {
858 status
= MapIrpOutputBuffer(irp
, outputBufferLen
,
859 sizeof(OVS_MESSAGE_ERROR
),
861 if (status
!= STATUS_SUCCESS
) {
864 ASSERT(outputBuffer
);
866 status
= STATUS_NDIS_INVALID_LENGTH
;
871 * Operate in the mode that read ioctl is similar to ReadFile(). This
872 * might change as the userspace code gets implemented.
878 * For implementing read (ioctl or otherwise), we need to store some
879 * state in the instance to indicate the command that started the dump
880 * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
881 * that 'ovsMsgReadOp' is needed only in this function to call into the
882 * appropriate handler. The handler itself can access the state in the
885 * In the absence of a dump start, return 0 bytes.
887 if (instance
->dumpState
.ovsMsg
== NULL
) {
889 status
= STATUS_SUCCESS
;
892 RtlCopyMemory(&ovsMsgReadOp
, instance
->dumpState
.ovsMsg
,
893 sizeof (ovsMsgReadOp
));
895 /* Create an NL message for consumption. */
896 ovsMsg
= &ovsMsgReadOp
;
897 ovsMsgLength
= sizeof (ovsMsgReadOp
);
898 devOp
= OVS_READ_DEV_OP
;
902 case OVS_IOCTL_WRITE
:
903 /* Input buffer is mandatory. */
904 if (inputBufferLen
< sizeof (*ovsMsg
)) {
905 status
= STATUS_NDIS_INVALID_LENGTH
;
909 ovsMsg
= inputBuffer
;
910 ovsMsgLength
= inputBufferLen
;
911 devOp
= OVS_WRITE_DEV_OP
;
915 status
= STATUS_INVALID_DEVICE_REQUEST
;
920 switch (ovsMsg
->nlMsg
.nlmsgType
) {
921 case NFNL_TYPE_CT_GET
:
922 case NFNL_TYPE_CT_DEL
:
923 nlFamilyOps
= &nlCtFamilyOps
;
925 case OVS_WIN_NL_CTRL_FAMILY_ID
:
926 nlFamilyOps
= &nlControlFamilyOps
;
928 case OVS_WIN_NL_DATAPATH_FAMILY_ID
:
929 nlFamilyOps
= &nlDatapathFamilyOps
;
931 case OVS_WIN_NL_FLOW_FAMILY_ID
:
932 nlFamilyOps
= &nlFLowFamilyOps
;
934 case OVS_WIN_NL_PACKET_FAMILY_ID
:
935 nlFamilyOps
= &nlPacketFamilyOps
;
937 case OVS_WIN_NL_VPORT_FAMILY_ID
:
938 nlFamilyOps
= &nlVportFamilyOps
;
940 case OVS_WIN_NL_NETDEV_FAMILY_ID
:
941 nlFamilyOps
= &nlNetdevFamilyOps
;
944 status
= STATUS_INVALID_PARAMETER
;
949 * For read operation, avoid duplicate validation since 'ovsMsg' is either
950 * "artificial" or was copied from a previously validated 'ovsMsg'.
952 if (devOp
!= OVS_READ_DEV_OP
) {
953 status
= ValidateNetlinkCmd(devOp
, instance
, ovsMsg
,
954 ovsMsgLength
, nlFamilyOps
);
955 if (status
!= STATUS_SUCCESS
) {
960 InitUserParamsCtx(irp
, instance
, devOp
, ovsMsg
,
961 inputBuffer
, inputBufferLen
,
962 outputBuffer
, outputBufferLen
,
965 status
= InvokeNetlinkCmdHandler(&usrParamsCtx
, nlFamilyOps
, &replyLen
);
968 OvsReleaseSwitchContext(gOvsSwitchContext
);
971 /* Should not complete a pending IRP unless proceesing is completed. */
972 if (status
== STATUS_PENDING
) {
975 return OvsCompleteIrpRequest(irp
, (ULONG_PTR
)replyLen
, status
);
980 * --------------------------------------------------------------------------
981 * Function to validate a netlink command. Only certain combinations of
982 * (device operation, netlink family, command) are valid.
983 * --------------------------------------------------------------------------
986 ValidateNetlinkCmd(UINT32 devOp
,
987 POVS_OPEN_INSTANCE instance
,
990 NETLINK_FAMILY
*nlFamilyOps
)
992 NTSTATUS status
= STATUS_INVALID_PARAMETER
;
995 // We need to ensure we have enough data to process
996 if (NlMsgSize(&ovsMsg
->nlMsg
) > ovsMsgLength
) {
997 status
= STATUS_INVALID_PARAMETER
;
1002 * Verify if the Netlink message is part of Netfilter Netlink
1003 * This is currently used by Conntrack
1005 if (IS_NFNL_CMD(ovsMsg
->nlMsg
.nlmsgType
)) {
1007 /* Validate Netfilter Netlink version is 0 */
1008 if (ovsMsg
->nfGenMsg
.version
!= NFNETLINK_V0
) {
1009 status
= STATUS_INVALID_PARAMETER
;
1013 /* Validate Netfilter Netlink Subsystem */
1014 if (NFNL_SUBSYS_ID(ovsMsg
->nlMsg
.nlmsgType
)
1015 != NFNL_SUBSYS_CTNETLINK
) {
1016 status
= STATUS_INVALID_PARAMETER
;
1020 /* Exit the function because there aren't any other validations */
1021 status
= STATUS_SUCCESS
;
1025 for (i
= 0; i
< nlFamilyOps
->opsCount
; i
++) {
1026 if (nlFamilyOps
->cmds
[i
].cmd
== ovsMsg
->genlMsg
.cmd
) {
1027 /* Validate if the command is valid for the device operation. */
1028 if ((devOp
& nlFamilyOps
->cmds
[i
].supportedDevOp
) == 0) {
1029 status
= STATUS_INVALID_PARAMETER
;
1033 /* Validate the version. */
1034 if (nlFamilyOps
->version
> ovsMsg
->genlMsg
.version
) {
1035 status
= STATUS_INVALID_PARAMETER
;
1039 /* Validate the DP for commands that require a DP. */
1040 if (nlFamilyOps
->cmds
[i
].validateDpIndex
== TRUE
) {
1041 if (ovsMsg
->ovsHdr
.dp_ifindex
!=
1042 (INT
)gOvsSwitchContext
->dpNo
) {
1043 status
= STATUS_INVALID_PARAMETER
;
1048 /* Validate the PID. */
1049 if (ovsMsg
->nlMsg
.nlmsgPid
!= instance
->pid
) {
1050 status
= STATUS_INVALID_PARAMETER
;
1054 status
= STATUS_SUCCESS
;
1059 // validate all NlAttrs
1060 if (!NlValidateAllAttrs(&ovsMsg
->nlMsg
, sizeof(*ovsMsg
),
1061 NlMsgAttrsLen((PNL_MSG_HDR
)ovsMsg
),
1063 status
= STATUS_INVALID_PARAMETER
;
1072 * --------------------------------------------------------------------------
1073 * Function to invoke the netlink command handler. The function also stores
1074 * the return value of the handler function to construct a 'NL_ERROR' message,
1075 * and in turn returns success to the caller.
1076 * --------------------------------------------------------------------------
1079 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1080 NETLINK_FAMILY
*nlFamilyOps
,
1083 NTSTATUS status
= STATUS_INVALID_PARAMETER
;
1087 if (IS_NFNL_CMD(usrParamsCtx
->ovsMsg
->nlMsg
.nlmsgType
)) {
1088 /* If nlMsg is of type Netfilter-Netlink parse the Cmd accordingly */
1089 cmd
= NFNL_MSG_TYPE(usrParamsCtx
->ovsMsg
->nlMsg
.nlmsgType
);
1091 cmd
= usrParamsCtx
->ovsMsg
->genlMsg
.cmd
;
1094 for (i
= 0; i
< nlFamilyOps
->opsCount
; i
++) {
1095 if (nlFamilyOps
->cmds
[i
].cmd
== cmd
) {
1096 NetlinkCmdHandler
*handler
= nlFamilyOps
->cmds
[i
].handler
;
1099 status
= handler(usrParamsCtx
, replyLen
);
1106 * Netlink socket semantics dictate that the return value of the netlink
1107 * function should be an error ONLY under fatal conditions. If the message
1108 * made it all the way to the handler function, it is not a fatal condition.
1109 * Absorb the error returned by the handler function into a 'struct
1110 * NL_ERROR' and populate the 'output buffer' to return to userspace.
1112 * This behavior is obviously applicable only to netlink commands that
1113 * specify an 'output buffer'. For other commands, we return the error as
1116 * 'STATUS_PENDING' is a special return value and userspace is equipped to
1119 if (status
!= STATUS_SUCCESS
&& status
!= STATUS_PENDING
) {
1120 if (usrParamsCtx
->devOp
!= OVS_WRITE_DEV_OP
&& *replyLen
== 0) {
1121 NL_ERROR nlError
= NlMapStatusToNlErr(status
);
1122 OVS_MESSAGE msgInTmp
= { 0 };
1123 POVS_MESSAGE msgIn
= NULL
;
1124 POVS_MESSAGE_ERROR msgError
= (POVS_MESSAGE_ERROR
)
1125 usrParamsCtx
->outputBuffer
;
1127 if (!IS_NFNL_CMD(usrParamsCtx
->ovsMsg
->nlMsg
.nlmsgType
) &&
1128 (usrParamsCtx
->ovsMsg
->genlMsg
.cmd
== OVS_CTRL_CMD_EVENT_NOTIFY
||
1129 usrParamsCtx
->ovsMsg
->genlMsg
.cmd
== OVS_CTRL_CMD_READ_NOTIFY
)) {
1130 /* There's no input buffer associated with such requests. */
1133 NlBufInit(&nlBuffer
, (PCHAR
)msgIn
, sizeof *msgIn
);
1134 NlFillNlHdr(&nlBuffer
, nlFamilyOps
->id
, 0, 0,
1135 usrParamsCtx
->ovsInstance
->pid
);
1137 msgIn
= (POVS_MESSAGE
)usrParamsCtx
->inputBuffer
;
1142 NlBuildErrorMsg(msgIn
, msgError
, nlError
, replyLen
);
1143 ASSERT(*replyLen
!= 0);
1146 if (*replyLen
!= 0) {
1147 status
= STATUS_SUCCESS
;
1152 if (usrParamsCtx
->devOp
!= OVS_WRITE_DEV_OP
) {
1153 ASSERT(status
== STATUS_PENDING
|| *replyLen
!= 0 || status
== STATUS_SUCCESS
);
1161 * --------------------------------------------------------------------------
1162 * Handler for 'OVS_IOCTL_GET_PID'.
1164 * Each handle on the device is assigned a unique PID when the handle is
1165 * created. This function passes the PID to userspace using METHOD_BUFFERED
1167 * --------------------------------------------------------------------------
1170 OvsGetPidHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1173 NTSTATUS status
= STATUS_SUCCESS
;
1174 PUINT32 msgOut
= (PUINT32
)usrParamsCtx
->outputBuffer
;
1176 if (usrParamsCtx
->outputLength
>= sizeof *msgOut
) {
1177 POVS_OPEN_INSTANCE instance
=
1178 (POVS_OPEN_INSTANCE
)usrParamsCtx
->ovsInstance
;
1180 RtlZeroMemory(msgOut
, sizeof *msgOut
);
1181 RtlCopyMemory(msgOut
, &instance
->pid
, sizeof(*msgOut
));
1182 *replyLen
= sizeof *msgOut
;
1184 *replyLen
= sizeof *msgOut
;
1185 status
= STATUS_NDIS_INVALID_LENGTH
;
1192 * --------------------------------------------------------------------------
1193 * Utility function to fill up information about the datapath in a reply to
1195 * --------------------------------------------------------------------------
1198 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext
,
1203 OVS_MESSAGE msgOutTmp
;
1204 OVS_DATAPATH
*datapath
= &ovsSwitchContext
->datapath
;
1207 ASSERT(NlBufAt(nlBuf
, 0, 0) != 0 && NlBufRemLen(nlBuf
) >= sizeof *msgIn
);
1209 msgOutTmp
.nlMsg
.nlmsgType
= OVS_WIN_NL_DATAPATH_FAMILY_ID
;
1210 msgOutTmp
.nlMsg
.nlmsgFlags
= 0; /* XXX: ? */
1211 msgOutTmp
.nlMsg
.nlmsgSeq
= msgIn
->nlMsg
.nlmsgSeq
;
1212 msgOutTmp
.nlMsg
.nlmsgPid
= msgIn
->nlMsg
.nlmsgPid
;
1214 msgOutTmp
.genlMsg
.cmd
= OVS_DP_CMD_GET
;
1215 msgOutTmp
.genlMsg
.version
= nlDatapathFamilyOps
.version
;
1216 msgOutTmp
.genlMsg
.reserved
= 0;
1218 msgOutTmp
.ovsHdr
.dp_ifindex
= ovsSwitchContext
->dpNo
;
1220 writeOk
= NlMsgPutHead(nlBuf
, (PCHAR
)&msgOutTmp
, sizeof msgOutTmp
);
1222 writeOk
= NlMsgPutTailString(nlBuf
, OVS_DP_ATTR_NAME
,
1223 OVS_SYSTEM_DP_NAME
);
1226 OVS_DP_STATS dpStats
;
1228 dpStats
.n_hit
= datapath
->hits
;
1229 dpStats
.n_missed
= datapath
->misses
;
1230 dpStats
.n_lost
= datapath
->lost
;
1231 dpStats
.n_flows
= datapath
->nFlows
;
1232 writeOk
= NlMsgPutTailUnspec(nlBuf
, OVS_DP_ATTR_STATS
,
1233 (PCHAR
)&dpStats
, sizeof dpStats
);
1235 nlMsg
= (PNL_MSG_HDR
)NlBufAt(nlBuf
, 0, 0);
1236 nlMsg
->nlmsgLen
= NlBufSize(nlBuf
);
1238 return writeOk
? STATUS_SUCCESS
: STATUS_INVALID_BUFFER_SIZE
;
1242 * --------------------------------------------------------------------------
1243 * Handler for queueing an IRP used for event notification. The IRP is
1244 * completed when a port state changes. STATUS_PENDING is returned on
1245 * success. User mode keep a pending IRP at all times.
1246 * --------------------------------------------------------------------------
1249 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1254 UNREFERENCED_PARAMETER(replyLen
);
1256 POVS_OPEN_INSTANCE instance
=
1257 (POVS_OPEN_INSTANCE
)usrParamsCtx
->ovsInstance
;
1258 POVS_MESSAGE msgIn
= (POVS_MESSAGE
)usrParamsCtx
->inputBuffer
;
1259 OVS_EVENT_POLL poll
;
1261 poll
.dpNo
= msgIn
->ovsHdr
.dp_ifindex
;
1262 status
= OvsWaitEventIoctl(usrParamsCtx
->irp
, instance
->fileObject
,
1263 &poll
, sizeof poll
);
1268 * --------------------------------------------------------------------------
1269 * Handler for the subscription for the event queue
1270 * --------------------------------------------------------------------------
1273 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1277 OVS_EVENT_SUBSCRIBE request
;
1282 const NL_POLICY policy
[] = {
1283 [OVS_NL_ATTR_MCAST_GRP
] = {.type
= NL_A_U32
},
1284 [OVS_NL_ATTR_MCAST_JOIN
] = {.type
= NL_A_U8
},
1287 UNREFERENCED_PARAMETER(replyLen
);
1289 POVS_OPEN_INSTANCE instance
=
1290 (POVS_OPEN_INSTANCE
)usrParamsCtx
->ovsInstance
;
1291 POVS_MESSAGE msgIn
= (POVS_MESSAGE
)usrParamsCtx
->inputBuffer
;
1293 rc
= NlAttrParse(&msgIn
->nlMsg
, sizeof (*msgIn
),
1294 NlMsgAttrsLen((PNL_MSG_HDR
)msgIn
), policy
, ARRAY_SIZE(policy
),
1295 attrs
, ARRAY_SIZE(attrs
));
1297 status
= STATUS_INVALID_PARAMETER
;
1301 mcastGrp
= NlAttrGetU32(attrs
[OVS_NL_ATTR_MCAST_GRP
]);
1302 join
= NlAttrGetU8(attrs
[OVS_NL_ATTR_MCAST_JOIN
]);
1303 request
.dpNo
= msgIn
->ovsHdr
.dp_ifindex
;
1304 request
.subscribe
= join
;
1305 request
.mcastGrp
= mcastGrp
;
1306 request
.protocol
= instance
->protocol
;
1309 /* We currently support Vport and CT related events */
1310 if (instance
->protocol
== NETLINK_GENERIC
) {
1311 request
.mask
= OVS_EVENT_MASK_ALL
;
1312 } else if (instance
->protocol
== NETLINK_NETFILTER
) {
1313 if (mcastGrp
== NFNLGRP_CONNTRACK_NEW
) {
1314 request
.mask
= OVS_EVENT_CT_NEW
;
1316 if (mcastGrp
== NFNLGRP_CONNTRACK_DESTROY
) {
1317 request
.mask
= OVS_EVENT_CT_DELETE
;
1319 if (mcastGrp
== NFNLGRP_CONNTRACK_UPDATE
) {
1320 request
.mask
= OVS_EVENT_CT_UPDATE
;
1324 status
= OvsSubscribeEventIoctl(instance
->fileObject
, &request
,
1331 * --------------------------------------------------------------------------
1332 * Command Handler for 'OVS_DP_CMD_NEW'.
1333 * --------------------------------------------------------------------------
1336 OvsNewDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1339 return HandleDpTransactionCommon(usrParamsCtx
, replyLen
);
1343 * --------------------------------------------------------------------------
1344 * Command Handler for 'OVS_DP_CMD_GET'.
1346 * The function handles both the dump based as well as the transaction based
1347 * 'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1348 * call to setup dump state, as well as subsequent calls to continue dumping
1350 * --------------------------------------------------------------------------
1353 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1356 if (usrParamsCtx
->devOp
== OVS_TRANSACTION_DEV_OP
) {
1357 return HandleDpTransactionCommon(usrParamsCtx
, replyLen
);
1359 return HandleGetDpDump(usrParamsCtx
, replyLen
);
1364 * --------------------------------------------------------------------------
1365 * Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1366 * --------------------------------------------------------------------------
1369 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1372 return HandleDpTransactionCommon(usrParamsCtx
, replyLen
);
1377 * --------------------------------------------------------------------------
1378 * Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1379 * --------------------------------------------------------------------------
1382 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1385 POVS_MESSAGE msgOut
= (POVS_MESSAGE
)usrParamsCtx
->outputBuffer
;
1386 POVS_OPEN_INSTANCE instance
=
1387 (POVS_OPEN_INSTANCE
)usrParamsCtx
->ovsInstance
;
1389 if (usrParamsCtx
->devOp
== OVS_WRITE_DEV_OP
) {
1391 OvsSetupDumpStart(usrParamsCtx
);
1395 POVS_MESSAGE msgIn
= instance
->dumpState
.ovsMsg
;
1397 ASSERT(usrParamsCtx
->devOp
== OVS_READ_DEV_OP
);
1399 if (instance
->dumpState
.ovsMsg
== NULL
) {
1401 return STATUS_INVALID_DEVICE_STATE
;
1404 /* Dump state must have been deleted after previous dump operation. */
1405 ASSERT(instance
->dumpState
.index
[0] == 0);
1407 /* Output buffer has been validated while validating read dev op. */
1408 ASSERT(msgOut
!= NULL
&& usrParamsCtx
->outputLength
>= sizeof *msgOut
);
1410 NlBufInit(&nlBuf
, usrParamsCtx
->outputBuffer
,
1411 usrParamsCtx
->outputLength
);
1413 status
= OvsDpFillInfo(gOvsSwitchContext
, msgIn
, &nlBuf
);
1415 if (status
!= STATUS_SUCCESS
) {
1417 FreeUserDumpState(instance
);
1421 /* Increment the dump index. */
1422 instance
->dumpState
.index
[0] = 1;
1423 *replyLen
= msgOut
->nlMsg
.nlmsgLen
;
1425 /* Free up the dump state, since there's no more data to continue. */
1426 FreeUserDumpState(instance
);
1429 return STATUS_SUCCESS
;
1434 * --------------------------------------------------------------------------
1435 * Command Handler for 'OVS_DP_CMD_SET'.
1436 * --------------------------------------------------------------------------
1439 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1442 return HandleDpTransactionCommon(usrParamsCtx
, replyLen
);
1446 * --------------------------------------------------------------------------
1447 * Function for handling transaction based 'OVS_DP_CMD_NEW', 'OVS_DP_CMD_GET'
1448 * and 'OVS_DP_CMD_SET' commands.
1450 * 'OVS_DP_CMD_NEW' is implemented to keep userspace code happy. Creation of a
1451 * new datapath is not supported currently.
1452 * --------------------------------------------------------------------------
1455 HandleDpTransactionCommon(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1458 POVS_MESSAGE msgIn
= (POVS_MESSAGE
)usrParamsCtx
->inputBuffer
;
1459 POVS_MESSAGE msgOut
= (POVS_MESSAGE
)usrParamsCtx
->outputBuffer
;
1460 NTSTATUS status
= STATUS_SUCCESS
;
1462 NL_ERROR nlError
= NL_ERROR_SUCCESS
;
1463 static const NL_POLICY ovsDatapathSetPolicy
[] = {
1464 [OVS_DP_ATTR_NAME
] = { .type
= NL_A_STRING
, .maxLen
= IFNAMSIZ
},
1465 [OVS_DP_ATTR_UPCALL_PID
] = { .type
= NL_A_U32
, .optional
= TRUE
},
1466 [OVS_DP_ATTR_USER_FEATURES
] = { .type
= NL_A_U32
, .optional
= TRUE
},
1468 PNL_ATTR dpAttrs
[ARRAY_SIZE(ovsDatapathSetPolicy
)];
1470 UNREFERENCED_PARAMETER(msgOut
);
1472 /* input buffer has been validated while validating write dev op. */
1473 ASSERT(msgIn
!= NULL
&& usrParamsCtx
->inputLength
>= sizeof *msgIn
);
1475 /* Parse any attributes in the request. */
1476 if (usrParamsCtx
->ovsMsg
->genlMsg
.cmd
== OVS_DP_CMD_SET
||
1477 usrParamsCtx
->ovsMsg
->genlMsg
.cmd
== OVS_DP_CMD_NEW
) {
1478 if (!NlAttrParse((PNL_MSG_HDR
)msgIn
,
1479 NLMSG_HDRLEN
+ GENL_HDRLEN
+ OVS_HDRLEN
,
1480 NlMsgAttrsLen((PNL_MSG_HDR
)msgIn
),
1481 ovsDatapathSetPolicy
,
1482 ARRAY_SIZE(ovsDatapathSetPolicy
),
1483 dpAttrs
, ARRAY_SIZE(dpAttrs
))) {
1484 return STATUS_INVALID_PARAMETER
;
1488 * XXX: Not clear at this stage if there's any role for the
1489 * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1494 RtlZeroMemory(dpAttrs
, sizeof dpAttrs
);
1497 /* Output buffer has been validated while validating transact dev op. */
1498 ASSERT(msgOut
!= NULL
&& usrParamsCtx
->outputLength
>= sizeof *msgOut
);
1500 NlBufInit(&nlBuf
, usrParamsCtx
->outputBuffer
, usrParamsCtx
->outputLength
);
1502 if (dpAttrs
[OVS_DP_ATTR_NAME
] != NULL
) {
1503 if (!OvsCompareString(NlAttrGet(dpAttrs
[OVS_DP_ATTR_NAME
]),
1504 OVS_SYSTEM_DP_NAME
)) {
1506 /* Creation of new datapaths is not supported. */
1507 if (usrParamsCtx
->ovsMsg
->genlMsg
.cmd
== OVS_DP_CMD_SET
) {
1508 nlError
= NL_ERROR_NOTSUPP
;
1512 nlError
= NL_ERROR_NODEV
;
1515 } else if ((UINT32
)msgIn
->ovsHdr
.dp_ifindex
!= gOvsSwitchContext
->dpNo
) {
1516 nlError
= NL_ERROR_NODEV
;
1520 if (usrParamsCtx
->ovsMsg
->genlMsg
.cmd
== OVS_DP_CMD_NEW
) {
1521 nlError
= NL_ERROR_EXIST
;
1525 status
= OvsDpFillInfo(gOvsSwitchContext
, msgIn
, &nlBuf
);
1527 *replyLen
= NlBufSize(&nlBuf
);
1530 if (nlError
!= NL_ERROR_SUCCESS
) {
1531 POVS_MESSAGE_ERROR msgError
= (POVS_MESSAGE_ERROR
)
1532 usrParamsCtx
->outputBuffer
;
1535 NlBuildErrorMsg(msgIn
, msgError
, nlError
, replyLen
);
1536 ASSERT(*replyLen
!= 0);
1539 return STATUS_SUCCESS
;
1544 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx
)
1546 POVS_MESSAGE msgIn
= (POVS_MESSAGE
)usrParamsCtx
->inputBuffer
;
1547 POVS_OPEN_INSTANCE instance
=
1548 (POVS_OPEN_INSTANCE
)usrParamsCtx
->ovsInstance
;
1550 /* input buffer has been validated while validating write dev op. */
1551 ASSERT(msgIn
!= NULL
&& usrParamsCtx
->inputLength
>= sizeof *msgIn
);
1553 /* A write operation that does not indicate dump start is invalid. */
1554 if ((msgIn
->nlMsg
.nlmsgFlags
& NLM_F_DUMP
) != NLM_F_DUMP
) {
1555 return STATUS_INVALID_PARAMETER
;
1557 /* XXX: Handle other NLM_F_* flags in the future. */
1560 * This operation should be setting up the dump state. If there's any
1561 * previous state, clear it up so as to set it up afresh.
1563 FreeUserDumpState(instance
);
1565 return InitUserDumpState(instance
, msgIn
);
1570 * --------------------------------------------------------------------------
1571 * Utility function to map the output buffer in an IRP. The buffer is assumed
1572 * to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
1573 * --------------------------------------------------------------------------
1576 MapIrpOutputBuffer(PIRP irp
,
1577 UINT32 bufferLength
,
1578 UINT32 requiredLength
,
1583 ASSERT(bufferLength
);
1584 ASSERT(requiredLength
);
1585 if (!buffer
|| !irp
|| bufferLength
== 0 || requiredLength
== 0) {
1586 return STATUS_INVALID_PARAMETER
;
1589 if (bufferLength
< requiredLength
) {
1590 return STATUS_NDIS_INVALID_LENGTH
;
1592 if (irp
->MdlAddress
== NULL
) {
1593 return STATUS_INVALID_PARAMETER
;
1595 *buffer
= MmGetSystemAddressForMdlSafe(irp
->MdlAddress
,
1596 NormalPagePriority
);
1597 if (*buffer
== NULL
) {
1598 return STATUS_INSUFFICIENT_RESOURCES
;
1601 return STATUS_SUCCESS
;
1605 * --------------------------------------------------------------------------
1606 * Utility function to fill up information about the state of a port in a reply
1608 * --------------------------------------------------------------------------
1611 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1612 POVS_VPORT_EVENT_ENTRY eventEntry
,
1617 OVS_MESSAGE msgOutTmp
;
1620 ASSERT(NlBufAt(nlBuf
, 0, 0) != 0 && nlBuf
->bufRemLen
>= sizeof msgOutTmp
);
1622 msgOutTmp
.nlMsg
.nlmsgType
= OVS_WIN_NL_VPORT_FAMILY_ID
;
1623 msgOutTmp
.nlMsg
.nlmsgFlags
= 0; /* XXX: ? */
1625 /* driver intiated messages should have zerp seq number*/
1626 msgOutTmp
.nlMsg
.nlmsgSeq
= 0;
1627 msgOutTmp
.nlMsg
.nlmsgPid
= usrParamsCtx
->ovsInstance
->pid
;
1629 msgOutTmp
.genlMsg
.version
= nlVportFamilyOps
.version
;
1630 msgOutTmp
.genlMsg
.reserved
= 0;
1632 /* we don't have netdev yet, treat link up/down a adding/removing a port*/
1633 if (eventEntry
->type
& (OVS_EVENT_LINK_UP
| OVS_EVENT_CONNECT
)) {
1634 msgOutTmp
.genlMsg
.cmd
= OVS_VPORT_CMD_NEW
;
1635 } else if (eventEntry
->type
&
1636 (OVS_EVENT_LINK_DOWN
| OVS_EVENT_DISCONNECT
)) {
1637 msgOutTmp
.genlMsg
.cmd
= OVS_VPORT_CMD_DEL
;
1640 return STATUS_UNSUCCESSFUL
;
1642 msgOutTmp
.ovsHdr
.dp_ifindex
= gOvsSwitchContext
->dpNo
;
1644 ok
= NlMsgPutHead(nlBuf
, (PCHAR
)&msgOutTmp
, sizeof msgOutTmp
);
1646 status
= STATUS_INVALID_BUFFER_SIZE
;
1650 ok
= NlMsgPutTailU32(nlBuf
, OVS_VPORT_ATTR_PORT_NO
, eventEntry
->portNo
) &&
1651 NlMsgPutTailU32(nlBuf
, OVS_VPORT_ATTR_TYPE
, eventEntry
->ovsType
) &&
1652 NlMsgPutTailU32(nlBuf
, OVS_VPORT_ATTR_UPCALL_PID
,
1653 eventEntry
->upcallPid
) &&
1654 NlMsgPutTailString(nlBuf
, OVS_VPORT_ATTR_NAME
, eventEntry
->ovsName
);
1656 status
= STATUS_INVALID_BUFFER_SIZE
;
1660 /* XXXX Should we add the port stats attributes?*/
1661 nlMsg
= (PNL_MSG_HDR
)NlBufAt(nlBuf
, 0, 0);
1662 nlMsg
->nlmsgLen
= NlBufSize(nlBuf
);
1663 status
= STATUS_SUCCESS
;
1671 * --------------------------------------------------------------------------
1672 * Handler for reading events from the driver event queue. This handler is
1673 * executed when user modes issues a socket receive on a socket assocaited
1674 * with the MC group for events.
1675 * XXX user mode should read multiple events in one system call
1676 * --------------------------------------------------------------------------
1679 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1682 POVS_MESSAGE msgOut
= (POVS_MESSAGE
)usrParamsCtx
->outputBuffer
;
1683 POVS_OPEN_INSTANCE instance
=
1684 (POVS_OPEN_INSTANCE
)usrParamsCtx
->ovsInstance
;
1688 ASSERT(usrParamsCtx
->devOp
== OVS_READ_DEV_OP
);
1690 /* Should never read events with a dump socket */
1691 ASSERT(instance
->dumpState
.ovsMsg
== NULL
);
1693 /* Must have an event queue */
1694 ASSERT(instance
->eventQueue
!= NULL
);
1696 /* Output buffer has been validated while validating read dev op. */
1697 ASSERT(msgOut
!= NULL
&& usrParamsCtx
->outputLength
>= sizeof *msgOut
);
1699 if (instance
->protocol
== NETLINK_NETFILTER
) {
1700 if (!instance
->mcastMask
) {
1701 status
= STATUS_SUCCESS
;
1706 OVS_CT_EVENT_ENTRY ctEventEntry
;
1707 status
= OvsRemoveCtEventEntry(usrParamsCtx
->ovsInstance
,
1710 if (status
!= STATUS_SUCCESS
) {
1711 /* If there were not elements, read should return no data. */
1712 status
= STATUS_SUCCESS
;
1717 /* Driver intiated messages should have zero seq number */
1718 status
= OvsCreateNlMsgFromCtEntry(&ctEventEntry
.entry
,
1719 usrParamsCtx
->outputBuffer
,
1720 usrParamsCtx
->outputLength
,
1722 0, /* No input msg */
1723 usrParamsCtx
->ovsInstance
->pid
,
1725 gOvsSwitchContext
->dpNo
);
1726 if (status
== NDIS_STATUS_SUCCESS
) {
1727 *replyLen
= msgOut
->nlMsg
.nlmsgLen
;
1729 } else if (instance
->protocol
== NETLINK_GENERIC
) {
1731 usrParamsCtx
->outputBuffer
,
1732 usrParamsCtx
->outputLength
);
1734 OVS_VPORT_EVENT_ENTRY eventEntry
;
1735 /* remove vport event entry from the vport event queue */
1736 status
= OvsRemoveVportEventEntry(usrParamsCtx
->ovsInstance
,
1738 if (status
!= STATUS_SUCCESS
) {
1739 /* If there were not elements, read should return no data. */
1740 status
= STATUS_SUCCESS
;
1745 status
= OvsPortFillInfo(usrParamsCtx
, &eventEntry
, &nlBuf
);
1746 if (status
== NDIS_STATUS_SUCCESS
) {
1747 *replyLen
= NlBufSize(&nlBuf
);
1750 status
= STATUS_INVALID_PARAMETER
;
1758 * --------------------------------------------------------------------------
1759 * Command Handler for 'OVS_CTRL_CMD_SOCK_PROP'.
1761 * Handler to set and verify socket properties between userspace and kernel.
1763 * Protocol is passed down by the userspace. It refers to the NETLINK family
1764 * and could be of different types (NETLINK_GENERIC/NETLINK_NETFILTER etc.,)
1765 * This function parses out the protocol and adds it to the open instance.
1767 * PID is generated by the kernel and is set in userspace after querying the
1768 * kernel for it. This function does not modify PID set in the kernel,
1769 * instead it verifies if it was sent down correctly.
1771 * XXX -This method can be modified to handle all Socket properties thereby
1772 * eliminating the use of OVS_IOCTL_GET_PID
1774 * --------------------------------------------------------------------------
1777 OvsSockPropCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1780 static const NL_POLICY ovsSocketPolicy
[] = {
1781 [OVS_NL_ATTR_SOCK_PROTO
] = { .type
= NL_A_U32
, .optional
= TRUE
},
1782 [OVS_NL_ATTR_SOCK_PID
] = { .type
= NL_A_U32
, .optional
= TRUE
}
1784 PNL_ATTR attrs
[ARRAY_SIZE(ovsSocketPolicy
)];
1786 if (usrParamsCtx
->outputLength
< sizeof(OVS_MESSAGE
)) {
1787 return STATUS_NDIS_INVALID_LENGTH
;
1791 POVS_MESSAGE msgIn
= (POVS_MESSAGE
)usrParamsCtx
->inputBuffer
;
1792 POVS_MESSAGE msgOut
= (POVS_MESSAGE
)usrParamsCtx
->outputBuffer
;
1793 POVS_OPEN_INSTANCE instance
= (POVS_OPEN_INSTANCE
)usrParamsCtx
->ovsInstance
;
1798 /* Parse the input */
1799 if (!NlAttrParse((PNL_MSG_HDR
)msgIn
,
1800 NLMSG_HDRLEN
+ GENL_HDRLEN
+ OVS_HDRLEN
,
1801 NlMsgAttrsLen((PNL_MSG_HDR
)msgIn
),
1803 ARRAY_SIZE(ovsSocketPolicy
),
1805 ARRAY_SIZE(attrs
))) {
1806 return STATUS_INVALID_PARAMETER
;
1809 /* Set the Protocol if it was passed down */
1810 if (attrs
[OVS_NL_ATTR_SOCK_PROTO
]) {
1811 protocol
= NlAttrGetU32(attrs
[OVS_NL_ATTR_SOCK_PROTO
]);
1813 instance
->protocol
= protocol
;
1817 /* Verify if the PID sent down matches the kernel */
1818 if (attrs
[OVS_NL_ATTR_SOCK_PID
]) {
1819 pid
= NlAttrGetU32(attrs
[OVS_NL_ATTR_SOCK_PID
]);
1820 if (pid
!= instance
->pid
) {
1821 OVS_LOG_ERROR("Received invalid pid:%d expected:%d",
1822 pid
, instance
->pid
);
1823 return STATUS_INVALID_PARAMETER
;
1827 /* Prepare the output */
1828 NlBufInit(&nlBuf
, usrParamsCtx
->outputBuffer
, usrParamsCtx
->outputLength
);
1829 if(!NlFillOvsMsg(&nlBuf
, msgIn
->nlMsg
.nlmsgType
, NLM_F_MULTI
,
1830 msgIn
->nlMsg
.nlmsgSeq
, msgIn
->nlMsg
.nlmsgPid
,
1831 msgIn
->genlMsg
.cmd
, msgIn
->genlMsg
.version
,
1832 gOvsSwitchContext
->dpNo
)){
1833 return STATUS_INVALID_BUFFER_SIZE
;
1836 if (!NlMsgPutTailU32(&nlBuf
, OVS_NL_ATTR_SOCK_PID
,
1838 return STATUS_INVALID_BUFFER_SIZE
;
1841 if (!NlMsgPutTailU32(&nlBuf
, OVS_NL_ATTR_SOCK_PROTO
,
1842 instance
->protocol
)) {
1843 return STATUS_INVALID_BUFFER_SIZE
;
1846 nlMsg
= (PNL_MSG_HDR
)NlBufAt(&nlBuf
, 0, 0);
1847 nlMsg
->nlmsgLen
= NlBufSize(&nlBuf
);
1848 *replyLen
= msgOut
->nlMsg
.nlmsgLen
;
1850 return STATUS_SUCCESS
;