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.
32 #define OVS_DBG_MOD OVS_DBG_VPORT
35 #define VPORT_NIC_ENTER(_nic) \
36 OVS_LOG_TRACE("Enter: PortId: %x, NicIndex: %d", _nic->PortId, \
39 #define VPORT_NIC_EXIT(_nic) \
40 OVS_LOG_TRACE("Exit: PortId: %x, NicIndex: %d", _nic->PortId, \
43 #define VPORT_PORT_ENTER(_port) \
44 OVS_LOG_TRACE("Enter: PortId: %x", _port->PortId)
46 #define VPORT_PORT_EXIT(_port) \
47 OVS_LOG_TRACE("Exit: PortId: %x", _port->PortId)
49 #define OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC 100
51 /* Context structure used to pass back and forth information to the tunnel
53 typedef struct _OVS_TUNFLT_INIT_CONTEXT {
54 POVS_SWITCH_CONTEXT switchContext;
58 POVS_VPORT_ENTRY vport;
62 } OVS_TUNFLT_INIT_CONTEXT, *POVS_TUNFLT_INIT_CONTEXT;
65 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
67 static VOID OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
68 PNDIS_SWITCH_PORT_PARAMETERS portParam);
69 static VOID OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
70 POVS_VPORT_ENTRY vport, PNDIS_SWITCH_NIC_PARAMETERS nicParam);
71 static VOID OvsCopyPortParamsFromVport(POVS_VPORT_ENTRY vport,
72 PNDIS_SWITCH_PORT_PARAMETERS portParam);
73 static __inline VOID OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext,
75 static NTSTATUS OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
76 POVS_VPORT_EXT_INFO extInfo);
77 static NTSTATUS CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
82 static POVS_VPORT_ENTRY OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
83 PWSTR wsName, SIZE_T wstrSize);
84 static VOID UpdateSwitchCtxWithVport(POVS_SWITCH_CONTEXT switchContext,
85 POVS_VPORT_ENTRY vport, BOOLEAN newPort);
86 static NTSTATUS OvsRemoveTunnelVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
87 POVS_SWITCH_CONTEXT switchContext,
88 POVS_VPORT_ENTRY vport,
91 static VOID OvsTunnelVportPendingInit(PVOID context,
94 static VOID OvsTunnelVportPendingRemove(PVOID context,
97 static NTSTATUS GetNICAlias(GUID *netCfgInstanceId,
98 IF_COUNTED_STRING *portFriendlyName);
101 * --------------------------------------------------------------------------
102 * Creates a Vport entry for a Hyper-V switch port. 'nicIndex' is typically
103 * associated with a NIC than a port. We use it here for the special case
104 * where we need to create a Vport for an external NIC with NicIndex > 0.
105 * --------------------------------------------------------------------------
108 HvCreatePort(POVS_SWITCH_CONTEXT switchContext,
109 PNDIS_SWITCH_PORT_PARAMETERS portParam,
110 NDIS_SWITCH_NIC_INDEX nicIndex)
112 POVS_VPORT_ENTRY vport;
113 LOCK_STATE_EX lockState;
114 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
115 BOOLEAN newPort = FALSE;
117 VPORT_PORT_ENTER(portParam);
119 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
120 /* Lookup by port ID. */
121 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
122 portParam->PortId, nicIndex);
124 OVS_LOG_ERROR("Port add failed due to duplicate port name, "
125 "port Id: %u", portParam->PortId);
126 status = STATUS_DATA_NOT_ACCEPTED;
127 goto create_port_done;
131 * Lookup by port name to see if this port with this name had been added
132 * (and deleted) previously.
134 vport = OvsFindVportByHvNameW(gOvsSwitchContext,
135 portParam->PortFriendlyName.String,
136 portParam->PortFriendlyName.Length);
137 if (vport && vport->isAbsentOnHv == FALSE) {
138 OVS_LOG_ERROR("Port add failed since a port already exists on "
139 "the specified port Id: %u, ovsName: %s",
140 portParam->PortId, vport->ovsName);
141 status = STATUS_DATA_NOT_ACCEPTED;
142 goto create_port_done;
146 ASSERT(vport->isAbsentOnHv);
147 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
150 * It should be possible to simply just mark this port as "not deleted"
151 * given that the port Id and the name are the same and also provided
152 * that the other properties that we cache have not changed.
154 if (vport->portType != portParam->PortType) {
155 OVS_LOG_INFO("Port add failed due to PortType change, port Id: %u"
156 " old: %u, new: %u", portParam->PortId,
157 vport->portType, portParam->PortType);
158 status = STATUS_DATA_NOT_ACCEPTED;
159 goto create_port_done;
161 vport->isAbsentOnHv = FALSE;
163 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
165 status = NDIS_STATUS_RESOURCES;
166 goto create_port_done;
170 OvsInitVportWithPortParam(vport, portParam);
171 vport->nicIndex = nicIndex;
172 UpdateSwitchCtxWithVport(switchContext, vport, newPort);
175 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
176 VPORT_PORT_EXIT(portParam);
182 * --------------------------------------------------------------------------
183 * Function to process updates to a port on the Hyper-Vs witch.
184 * --------------------------------------------------------------------------
187 HvUpdatePort(POVS_SWITCH_CONTEXT switchContext,
188 PNDIS_SWITCH_PORT_PARAMETERS portParam)
190 POVS_VPORT_ENTRY vport;
191 LOCK_STATE_EX lockState;
192 OVS_VPORT_STATE ovsState;
193 NDIS_SWITCH_NIC_STATE nicState;
195 VPORT_PORT_ENTER(portParam);
197 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
198 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
199 portParam->PortId, 0);
201 * Update properties only for NETDEV ports for supprting PS script
204 goto update_port_done;
207 /* Store the nic and the OVS states as Nic Create won't be called */
208 ovsState = vport->ovsState;
209 nicState = vport->nicState;
212 * Currently only the port friendly name is being updated
213 * Make sure that no other properties are changed
215 ASSERT(portParam->PortId == vport->portId);
216 ASSERT(portParam->PortState == vport->portState);
217 ASSERT(portParam->PortType == vport->portType);
220 * Call the set parameters function the handle all properties
221 * change in a single place in case future version supports change of
224 OvsInitVportWithPortParam(vport, portParam);
225 /* Retore the nic and OVS states */
226 vport->nicState = nicState;
227 vport->ovsState = ovsState;
230 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
231 VPORT_PORT_EXIT(portParam);
233 /* Must always return success */
234 return NDIS_STATUS_SUCCESS;
239 * --------------------------------------------------------------------------
240 * Function to process teardown of a port on the Hyper-V switch.
241 * --------------------------------------------------------------------------
244 HvTeardownPort(POVS_SWITCH_CONTEXT switchContext,
245 PNDIS_SWITCH_PORT_PARAMETERS portParam)
247 POVS_VPORT_ENTRY vport;
248 LOCK_STATE_EX lockState;
250 VPORT_PORT_ENTER(portParam);
252 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
253 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
254 portParam->PortId, 0);
256 /* add assertion here */
257 vport->portState = NdisSwitchPortStateTeardown;
258 vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
260 OVS_LOG_WARN("Vport not present.");
262 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
264 VPORT_PORT_EXIT(portParam);
268 * --------------------------------------------------------------------------
269 * Function to process deletion of a port on the Hyper-V switch.
270 * --------------------------------------------------------------------------
273 HvDeletePort(POVS_SWITCH_CONTEXT switchContext,
274 PNDIS_SWITCH_PORT_PARAMETERS portParams)
276 POVS_VPORT_ENTRY vport;
277 LOCK_STATE_EX lockState;
279 VPORT_PORT_ENTER(portParams);
281 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
282 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
283 portParams->PortId, 0);
286 * XXX: we can only destroy and remove the port if its datapath port
287 * counterpart was deleted. If the datapath port counterpart is present,
288 * we only mark the vport for deletion, so that a netlink command vport
289 * delete will delete the vport.
292 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
294 OVS_LOG_WARN("Vport not present.");
296 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
298 VPORT_PORT_EXIT(portParams);
303 * --------------------------------------------------------------------------
304 * Function to process addition of a NIC connection on the Hyper-V switch.
305 * XXX: Posting an event to DPIF is incorrect here. However, it might be useful
306 * to post an event to netdev-windows.c.
307 * --------------------------------------------------------------------------
310 HvCreateNic(POVS_SWITCH_CONTEXT switchContext,
311 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
313 POVS_VPORT_ENTRY vport;
314 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
315 IF_COUNTED_STRING portFriendlyName = {0};
316 LOCK_STATE_EX lockState;
318 VPORT_NIC_ENTER(nicParam);
320 /* Wait for lists to be initialized. */
321 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
323 if (!switchContext->isActivated) {
324 OVS_LOG_WARN("Switch is not activated yet.");
325 /* Veto the creation of nic */
326 status = NDIS_STATUS_NOT_SUPPORTED;
330 if (OvsIsInternalNIC(nicParam->NicType) ||
331 OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) {
332 GetNICAlias(&nicParam->NetCfgInstanceId, &portFriendlyName);
335 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
337 * There can be one or more NICs for the external port. We create a vport
338 * structure for each such NIC, and each NIC inherits a lot of properties
339 * from the parent external port.
341 if (OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) {
342 NDIS_SWITCH_PORT_PARAMETERS portParam;
343 POVS_VPORT_ENTRY virtExtVport =
344 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
346 ASSERT(virtExtVport);
347 ASSERT(OvsFindVportByPortIdAndNicIndex(switchContext,
349 nicParam->NicIndex) == NULL);
350 OvsCopyPortParamsFromVport(virtExtVport, &portParam);
351 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
352 status = HvCreatePort(switchContext, &portParam,
354 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
355 if (status != NDIS_STATUS_SUCCESS) {
360 vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId,
363 OVS_LOG_ERROR("Create NIC without Switch Port,"
364 " PortId: %x, NicIndex: %d",
365 nicParam->PortId, nicParam->NicIndex);
366 status = NDIS_STATUS_INVALID_PARAMETER;
369 OvsInitVportWithNicParam(switchContext, vport, nicParam);
370 if (OvsIsInternalNIC(nicParam->NicType) ||
371 OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) {
372 RtlCopyMemory(&vport->portFriendlyName, &portFriendlyName,
373 sizeof portFriendlyName);
377 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
380 VPORT_NIC_EXIT(nicParam);
381 OVS_LOG_TRACE("Exit: status %8x.\n", status);
387 * --------------------------------------------------------------------------
388 * Function to process connection event of a NIC on the Hyper-V switch.
389 * --------------------------------------------------------------------------
392 HvConnectNic(POVS_SWITCH_CONTEXT switchContext,
393 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
395 LOCK_STATE_EX lockState;
396 POVS_VPORT_ENTRY vport;
399 VPORT_NIC_ENTER(nicParam);
401 /* Wait for lists to be initialized. */
402 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
404 if (!switchContext->isActivated) {
405 OVS_LOG_WARN("Switch is not activated yet.");
409 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
410 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
415 OVS_LOG_WARN("Vport not present.");
416 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
421 vport->ovsState = OVS_STATE_CONNECTED;
422 vport->nicState = NdisSwitchNicStateConnected;
423 portNo = vport->portNo;
425 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
427 if (nicParam->NicType == NdisSwitchNicTypeInternal) {
428 OvsInternalAdapterUp(&nicParam->NetCfgInstanceId);
432 VPORT_NIC_EXIT(nicParam);
437 * --------------------------------------------------------------------------
438 * Function to process updates to a NIC on the Hyper-V switch.
439 * --------------------------------------------------------------------------
442 HvUpdateNic(POVS_SWITCH_CONTEXT switchContext,
443 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
445 POVS_VPORT_ENTRY vport;
446 LOCK_STATE_EX lockState;
448 IF_COUNTED_STRING portFriendlyName = {0};
449 BOOLEAN nameChanged = FALSE;
450 BOOLEAN aliasLookup = FALSE;
452 VPORT_NIC_ENTER(nicParam);
454 /* Wait for lists to be initialized. */
455 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
457 if (!switchContext->isActivated) {
458 OVS_LOG_WARN("Switch is not activated yet.");
459 goto update_nic_done;
462 /* GetNICAlias() must be called outside of a lock. */
463 if (nicParam->NicType == NdisSwitchNicTypeInternal ||
464 OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) {
465 GetNICAlias(&nicParam->NetCfgInstanceId, &portFriendlyName);
469 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
470 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
474 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
475 OVS_LOG_WARN("Vport search failed.");
476 goto update_nic_done;
478 switch (nicParam->NicType) {
479 case NdisSwitchNicTypeExternal:
480 case NdisSwitchNicTypeInternal:
481 RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
484 if (RtlCompareMemory(&vport->portFriendlyName,
485 &portFriendlyName, vport->portFriendlyName.Length) !=
486 vport->portFriendlyName.Length) {
487 RtlCopyMemory(&vport->portFriendlyName, &portFriendlyName,
488 sizeof portFriendlyName);
493 case NdisSwitchNicTypeSynthetic:
494 case NdisSwitchNicTypeEmulated:
495 if (!RtlEqualMemory(vport->vmMacAddress, nicParam->VMMacAddress,
496 sizeof (vport->vmMacAddress))) {
497 event |= OVS_EVENT_MAC_CHANGE;
498 RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
499 sizeof (vport->vmMacAddress));
505 if (!RtlEqualMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
506 sizeof (vport->permMacAddress))) {
507 RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
508 sizeof (vport->permMacAddress));
509 event |= OVS_EVENT_MAC_CHANGE;
511 if (!RtlEqualMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
512 sizeof (vport->currMacAddress))) {
513 RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
514 sizeof (vport->currMacAddress));
515 event |= OVS_EVENT_MAC_CHANGE;
518 if (vport->mtu != nicParam->MTU) {
519 vport->mtu = nicParam->MTU;
520 event |= OVS_EVENT_MTU_CHANGE;
522 vport->numaNodeId = nicParam->NumaNodeId;
525 OVS_EVENT_ENTRY event;
526 event.portNo = vport->portNo;
527 event.ovsType = vport->ovsType;
528 event.upcallPid = vport->upcallPid;
529 RtlCopyMemory(&event.ovsName, &vport->ovsName, sizeof event.ovsName);
530 event.type = OVS_EVENT_LINK_DOWN;
531 OvsRemoveAndDeleteVport(NULL, switchContext, vport, FALSE, TRUE);
532 OvsPostEvent(&event);
535 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
538 * XXX: Not sure what kind of event to post here. DPIF is not interested in
539 * changes to MAC address. Netdev-windows might be intrested, though.
540 * That said, if the name chagnes, not clear what kind of event to be
541 * posted. We might have to delete the vport, and have userspace recreate
546 VPORT_NIC_EXIT(nicParam);
550 * --------------------------------------------------------------------------
551 * Function to process disconnect event of a NIC on the Hyper-V switch.
552 * --------------------------------------------------------------------------
555 HvDisconnectNic(POVS_SWITCH_CONTEXT switchContext,
556 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
558 POVS_VPORT_ENTRY vport;
559 LOCK_STATE_EX lockState;
560 BOOLEAN isInternalPort = FALSE;
561 OVS_EVENT_ENTRY event;
563 VPORT_NIC_ENTER(nicParam);
565 /* Wait for lists to be initialized. */
566 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
568 if (!switchContext->isActivated) {
569 OVS_LOG_WARN("Switch is not activated yet.");
573 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
574 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
579 OVS_LOG_WARN("Vport not present.");
580 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
584 vport->nicState = NdisSwitchNicStateDisconnected;
585 vport->ovsState = OVS_STATE_NIC_CREATED;
587 if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
588 isInternalPort = TRUE;
591 event.portNo = vport->portNo;
592 event.ovsType = vport->ovsType;
593 event.upcallPid = vport->upcallPid;
594 RtlCopyMemory(&event.ovsName, &vport->ovsName, sizeof event.ovsName);
595 event.type = OVS_EVENT_LINK_DOWN;
598 * Delete the port from the hash tables accessible to userspace. After this
599 * point, userspace should not be able to access this port.
601 if (OvsIsRealExternalVport(vport)) {
602 OvsRemoveAndDeleteVport(NULL, switchContext, vport, FALSE, TRUE);
603 OvsPostEvent(&event);
605 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
607 if (isInternalPort) {
608 OvsInternalAdapterDown();
612 VPORT_NIC_EXIT(nicParam);
616 * --------------------------------------------------------------------------
617 * Function to process delete event of a NIC on the Hyper-V switch.
618 * --------------------------------------------------------------------------
621 HvDeleteNic(POVS_SWITCH_CONTEXT switchContext,
622 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
624 LOCK_STATE_EX lockState;
625 POVS_VPORT_ENTRY vport;
627 VPORT_NIC_ENTER(nicParam);
628 /* Wait for lists to be initialized. */
629 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
631 if (!switchContext->isActivated) {
632 OVS_LOG_WARN("Switch is not activated yet.");
636 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
637 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
642 OVS_LOG_WARN("Vport not present.");
643 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
647 vport->nicState = NdisSwitchNicStateUnknown;
648 vport->ovsState = OVS_STATE_PORT_CREATED;
650 if (OvsIsRealExternalVport(vport)) {
651 /* This vport was created in HvCreateNic(). */
652 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
655 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
658 VPORT_NIC_EXIT(nicParam);
662 * OVS Vport related functionality.
665 OvsFindVportByPortNo(POVS_SWITCH_CONTEXT switchContext,
668 POVS_VPORT_ENTRY vport;
669 PLIST_ENTRY head, link;
670 UINT32 hash = OvsJhashBytes((const VOID *)&portNo, sizeof(portNo),
672 head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
673 LIST_FORALL(head, link) {
674 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
675 if (vport->portNo == portNo) {
684 OvsFindTunnelVportByDstPort(POVS_SWITCH_CONTEXT switchContext,
686 OVS_VPORT_TYPE ovsPortType)
688 POVS_VPORT_ENTRY vport;
689 PLIST_ENTRY head, link;
690 UINT32 hash = OvsJhashBytes((const VOID *)&dstPort, sizeof(dstPort),
692 head = &(switchContext->tunnelVportsArray[hash & OVS_VPORT_MASK]);
693 LIST_FORALL(head, link) {
694 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, tunnelVportLink);
695 if (GetPortFromPriv(vport) == dstPort &&
696 vport->ovsType == ovsPortType) {
705 OvsFindVportByOvsName(POVS_SWITCH_CONTEXT switchContext,
708 POVS_VPORT_ENTRY vport;
709 PLIST_ENTRY head, link;
711 SIZE_T length = strlen(name) + 1;
713 hash = OvsJhashBytes((const VOID *)name, length, OVS_HASH_BASIS);
714 head = &(switchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK]);
716 LIST_FORALL(head, link) {
717 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, ovsNameLink);
718 if (!strcmp(name, vport->ovsName)) {
726 /* OvsFindVportByHvName: "name" is assumed to be null-terminated */
728 OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
729 PWSTR wsName, SIZE_T wstrSize)
731 POVS_VPORT_ENTRY vport = NULL;
732 PLIST_ENTRY head, link;
735 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
736 head = &(switchContext->portIdHashArray[i]);
737 LIST_FORALL(head, link) {
738 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
741 * NOTE about portFriendlyName:
742 * If the string is NULL-terminated, the Length member does not
743 * include the terminating NULL character.
745 if (vport->portFriendlyName.Length == wstrSize &&
746 RtlEqualMemory(wsName, vport->portFriendlyName.String,
747 vport->portFriendlyName.Length)) {
756 * Look in the list of ports that were added from the Hyper-V switch and
760 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
761 head = &(switchContext->portNoHashArray[i]);
762 LIST_FORALL(head, link) {
763 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
764 if (vport->portFriendlyName.Length == wstrSize &&
765 RtlEqualMemory(wsName, vport->portFriendlyName.String,
766 vport->portFriendlyName.Length)) {
780 OvsFindVportByHvNameA(POVS_SWITCH_CONTEXT switchContext,
783 POVS_VPORT_ENTRY vport = NULL;
784 /* 'portFriendlyName' is not NUL-terminated. */
785 SIZE_T length = strlen(name);
786 SIZE_T wstrSize = length * sizeof(WCHAR);
789 PWSTR wsName = OvsAllocateMemoryWithTag(wstrSize, OVS_VPORT_POOL_TAG);
793 for (i = 0; i < length; i++) {
796 vport = OvsFindVportByHvNameW(switchContext, wsName, wstrSize);
797 OvsFreeMemoryWithTag(wsName, OVS_VPORT_POOL_TAG);
802 OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT switchContext,
803 NDIS_SWITCH_PORT_ID portId,
804 NDIS_SWITCH_NIC_INDEX index)
806 if (switchContext->virtualExternalVport &&
807 portId == switchContext->virtualExternalPortId &&
808 index == switchContext->virtualExternalVport->nicIndex) {
809 return (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
810 } else if (switchContext->internalVport &&
811 portId == switchContext->internalPortId &&
812 index == switchContext->internalVport->nicIndex) {
813 return (POVS_VPORT_ENTRY)switchContext->internalVport;
815 PLIST_ENTRY head, link;
816 POVS_VPORT_ENTRY vport;
818 hash = OvsJhashWords((UINT32 *)&portId, 1, OVS_HASH_BASIS);
819 head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
820 LIST_FORALL(head, link) {
821 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
822 if (portId == vport->portId && index == vport->nicIndex) {
831 OvsAllocateVport(VOID)
833 POVS_VPORT_ENTRY vport;
834 vport = (POVS_VPORT_ENTRY)OvsAllocateMemoryWithTag(
835 sizeof(OVS_VPORT_ENTRY), OVS_VPORT_POOL_TAG);
839 RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY));
840 vport->ovsState = OVS_STATE_UNKNOWN;
841 vport->isAbsentOnHv = FALSE;
842 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
844 InitializeListHead(&vport->ovsNameLink);
845 InitializeListHead(&vport->portIdLink);
846 InitializeListHead(&vport->portNoLink);
852 OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
853 PNDIS_SWITCH_PORT_PARAMETERS portParam)
855 vport->portType = portParam->PortType;
856 vport->portState = portParam->PortState;
857 vport->portId = portParam->PortId;
858 vport->nicState = NdisSwitchNicStateUnknown;
859 vport->isExternal = FALSE;
860 vport->isBridgeInternal = FALSE;
862 switch (vport->portType) {
863 case NdisSwitchPortTypeExternal:
864 vport->isExternal = TRUE;
865 vport->ovsType = OVS_VPORT_TYPE_NETDEV;
867 case NdisSwitchPortTypeInternal:
868 vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
870 case NdisSwitchPortTypeSynthetic:
871 case NdisSwitchPortTypeEmulated:
872 vport->ovsType = OVS_VPORT_TYPE_NETDEV;
875 RtlCopyMemory(&vport->hvPortName, &portParam->PortName,
876 sizeof (NDIS_SWITCH_PORT_NAME));
877 /* For external and internal ports, 'portFriendlyName' is overwritten
879 RtlCopyMemory(&vport->portFriendlyName, &portParam->PortFriendlyName,
880 sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
882 switch (vport->portState) {
883 case NdisSwitchPortStateCreated:
884 vport->ovsState = OVS_STATE_PORT_CREATED;
886 case NdisSwitchPortStateTeardown:
887 vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
889 case NdisSwitchPortStateDeleted:
890 vport->ovsState = OVS_STATE_PORT_DELETED;
897 OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
898 POVS_VPORT_ENTRY vport,
899 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
901 ASSERT(vport->portId == nicParam->PortId);
902 ASSERT(vport->ovsState == OVS_STATE_PORT_CREATED);
904 UNREFERENCED_PARAMETER(switchContext);
906 RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
907 sizeof (nicParam->PermanentMacAddress));
908 RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
909 sizeof (nicParam->CurrentMacAddress));
911 if (nicParam->NicType == NdisSwitchNicTypeSynthetic ||
912 nicParam->NicType == NdisSwitchNicTypeEmulated) {
913 RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
914 sizeof (nicParam->VMMacAddress));
915 RtlCopyMemory(&vport->vmName, &nicParam->VmName,
916 sizeof (nicParam->VmName));
918 RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
919 sizeof (nicParam->NetCfgInstanceId));
921 RtlCopyMemory(&vport->nicName, &nicParam->NicName,
922 sizeof (nicParam->NicName));
923 vport->mtu = nicParam->MTU;
924 vport->nicState = nicParam->NicState;
925 vport->nicIndex = nicParam->NicIndex;
926 vport->nicType = nicParam->NicType;
927 vport->numaNodeId = nicParam->NumaNodeId;
929 switch (vport->nicState) {
930 case NdisSwitchNicStateCreated:
931 vport->ovsState = OVS_STATE_NIC_CREATED;
933 case NdisSwitchNicStateConnected:
934 vport->ovsState = OVS_STATE_CONNECTED;
936 case NdisSwitchNicStateDisconnected:
937 vport->ovsState = OVS_STATE_NIC_CREATED;
939 case NdisSwitchNicStateDeleted:
940 vport->ovsState = OVS_STATE_PORT_CREATED;
946 * --------------------------------------------------------------------------
947 * Populates 'portParam' based on 'vport'.
948 * --------------------------------------------------------------------------
951 OvsCopyPortParamsFromVport(POVS_VPORT_ENTRY vport,
952 PNDIS_SWITCH_PORT_PARAMETERS portParam)
954 portParam->Flags = 0;
955 portParam->PortId = vport->portId;
956 RtlCopyMemory(&portParam->PortName, &vport->hvPortName,
957 sizeof (NDIS_SWITCH_PORT_NAME));
958 RtlCopyMemory(&portParam->PortFriendlyName,
959 &vport->portFriendlyName,
960 sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
961 portParam->PortType = vport->portType;
962 portParam->IsValidationPort = FALSE;
963 portParam->PortState = vport->portState;
967 * --------------------------------------------------------------------------
968 * Initializes a tunnel vport.
969 * --------------------------------------------------------------------------
972 OvsInitTunnelVport(PVOID userContext,
973 POVS_VPORT_ENTRY vport,
974 OVS_VPORT_TYPE ovsType,
977 NTSTATUS status = STATUS_SUCCESS;
978 POVS_USER_PARAMS_CONTEXT usrParamsCtx =
979 (POVS_USER_PARAMS_CONTEXT)userContext;
981 vport->isBridgeInternal = FALSE;
982 vport->ovsType = ovsType;
983 vport->ovsState = OVS_STATE_PORT_CREATED;
985 case OVS_VPORT_TYPE_GRE:
987 case OVS_VPORT_TYPE_VXLAN:
989 POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
991 tunnelContext = OvsAllocateMemoryWithTag(sizeof(*tunnelContext),
993 if (tunnelContext == NULL) {
994 status = STATUS_INSUFFICIENT_RESOURCES;
997 tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
998 tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
999 tunnelContext->outputLength = usrParamsCtx->outputLength;
1000 tunnelContext->vport = vport;
1002 status = OvsInitVxlanTunnel(usrParamsCtx->irp,
1005 OvsTunnelVportPendingInit,
1006 (PVOID)tunnelContext);
1007 if (status != STATUS_PENDING) {
1008 OvsFreeMemoryWithTag(tunnelContext, OVS_VPORT_POOL_TAG);
1009 tunnelContext = NULL;
1013 case OVS_VPORT_TYPE_STT:
1014 status = OvsInitSttTunnel(vport, dstPort);
1023 * --------------------------------------------------------------------------
1024 * Initializes a bridge internal vport ie. a port of type
1025 * OVS_VPORT_TYPE_INTERNAL but not present on the Hyper-V switch.
1026 * --------------------------------------------------------------------------
1029 OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport)
1031 vport->isBridgeInternal = TRUE;
1032 vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
1033 /* Mark the status to be connected, since there is no other initialization
1035 vport->ovsState = OVS_STATE_CONNECTED;
1036 return STATUS_SUCCESS;
1040 * --------------------------------------------------------------------------
1041 * For external and internal vports 'portFriendlyName' parameter, provided by
1042 * Hyper-V, is overwritten with the interface alias name.
1043 * --------------------------------------------------------------------------
1046 GetNICAlias(GUID *netCfgInstanceId,
1047 IF_COUNTED_STRING *portFriendlyName)
1049 NTSTATUS status = STATUS_SUCCESS;
1050 WCHAR interfaceName[IF_MAX_STRING_SIZE] = { 0 };
1051 NET_LUID interfaceLuid = { 0 };
1054 status = ConvertInterfaceGuidToLuid(netCfgInstanceId,
1056 if (status == STATUS_SUCCESS) {
1058 * Must be called from PASSIVE_LEVEL. Resulted in a
1059 * STATUS_INVALID_DEVICE_REQUEST if not.
1061 status = ConvertInterfaceLuidToAlias(&interfaceLuid, interfaceName,
1062 IF_MAX_STRING_SIZE + 1);
1063 if (status == STATUS_SUCCESS) {
1064 RtlStringCbPrintfW(portFriendlyName->String,
1065 IF_MAX_STRING_SIZE, L"%s", interfaceName);
1066 RtlStringCbLengthW(portFriendlyName->String, IF_MAX_STRING_SIZE,
1068 portFriendlyName->Length = (USHORT)len;
1070 OVS_LOG_ERROR("Fail to convert interface LUID to alias, status: %x",
1074 OVS_LOG_ERROR("Fail to convert interface GUID to LUID, status: %x",
1083 * --------------------------------------------------------------------------
1084 * Functionality common to any port on the Hyper-V switch. This function is not
1085 * to be called for a port that is not on the Hyper-V switch.
1087 * Inserts the port into 'portIdHashArray' and caches the pointer in the
1088 * 'switchContext' if needed.
1089 * --------------------------------------------------------------------------
1092 UpdateSwitchCtxWithVport(POVS_SWITCH_CONTEXT switchContext,
1093 POVS_VPORT_ENTRY vport,
1098 switch (vport->portType) {
1099 case NdisSwitchPortTypeExternal:
1100 if (vport->nicIndex == 0) {
1101 switchContext->virtualExternalPortId = vport->portId;
1102 switchContext->virtualExternalVport = vport;
1104 switchContext->numPhysicalNics++;
1107 case NdisSwitchPortTypeInternal:
1108 ASSERT(vport->isBridgeInternal == FALSE);
1109 switchContext->internalPortId = vport->portId;
1110 switchContext->internalVport = vport;
1112 case NdisSwitchPortTypeSynthetic:
1113 case NdisSwitchPortTypeEmulated:
1118 * It is important to not insert vport corresponding to virtual external
1119 * port into the 'portIdHashArray' since the port should not be exposed to
1122 if (vport->portType == NdisSwitchPortTypeExternal &&
1123 vport->nicIndex == 0) {
1128 * NOTE: OvsJhashWords has portId as "1" word. This should be ok, even
1129 * though sizeof(NDIS_SWITCH_PORT_ID) = 4, not 2, because the
1130 * hyper-v switch seems to use only 2 bytes out of 4.
1132 hash = OvsJhashWords(&vport->portId, 1, OVS_HASH_BASIS);
1133 InsertHeadList(&switchContext->portIdHashArray[hash & OVS_VPORT_MASK],
1134 &vport->portIdLink);
1136 switchContext->numHvVports++;
1142 * --------------------------------------------------------------------------
1143 * Functionality common to any port added from OVS userspace.
1145 * Inserts the port into 'portNoHashArray', 'ovsPortNameHashArray' and in
1146 * 'tunnelVportsArray' if appropriate.
1147 * --------------------------------------------------------------------------
1150 InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
1151 POVS_VPORT_ENTRY vport)
1155 switch(vport->ovsType) {
1156 case OVS_VPORT_TYPE_VXLAN:
1157 case OVS_VPORT_TYPE_STT:
1159 UINT16 dstPort = GetPortFromPriv(vport);
1160 hash = OvsJhashBytes(&dstPort,
1164 &gOvsSwitchContext->tunnelVportsArray[hash & OVS_VPORT_MASK],
1165 &vport->tunnelVportLink);
1166 switchContext->numNonHvVports++;
1169 case OVS_VPORT_TYPE_INTERNAL:
1170 if (vport->isBridgeInternal) {
1171 switchContext->numNonHvVports++;
1178 * Insert the port into the hash array of ports: by port number and ovs
1179 * and ovs (datapath) port name.
1180 * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1181 * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1183 hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
1184 InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
1185 &vport->portNoLink);
1187 hash = OvsJhashBytes(vport->ovsName, strlen(vport->ovsName) + 1,
1190 &gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
1191 &vport->ovsNameLink);
1193 return STATUS_SUCCESS;
1198 * --------------------------------------------------------------------------
1199 * Provides functionality that is partly complementatry to
1200 * InitOvsVportCommon()/UpdateSwitchCtxWithVport().
1202 * 'hvDelete' indicates if caller is removing the vport as a result of the
1203 * port being removed on the Hyper-V switch.
1204 * 'ovsDelete' indicates if caller is removing the vport as a result of the
1205 * port being removed from OVS userspace.
1206 * --------------------------------------------------------------------------
1209 OvsRemoveAndDeleteVport(PVOID usrParamsContext,
1210 POVS_SWITCH_CONTEXT switchContext,
1211 POVS_VPORT_ENTRY vport,
1215 POVS_USER_PARAMS_CONTEXT usrParamsCtx =
1216 (POVS_USER_PARAMS_CONTEXT)usrParamsContext;
1217 BOOLEAN hvSwitchPort = FALSE;
1218 BOOLEAN deletedOnOvs = FALSE;
1219 BOOLEAN deletedOnHv = FALSE;
1221 switch (vport->ovsType) {
1222 case OVS_VPORT_TYPE_INTERNAL:
1223 if (!vport->isBridgeInternal) {
1224 if (hvDelete && vport->isAbsentOnHv == FALSE) {
1225 switchContext->internalPortId = 0;
1226 switchContext->internalVport = NULL;
1227 OvsInternalAdapterDown();
1229 hvSwitchPort = TRUE;
1232 case OVS_VPORT_TYPE_VXLAN:
1235 status = OvsRemoveTunnelVport(usrParamsCtx, switchContext, vport,
1236 hvDelete, ovsDelete);
1237 if (status != STATUS_SUCCESS) {
1241 case OVS_VPORT_TYPE_STT:
1242 OvsCleanupSttTunnel(vport);
1244 case OVS_VPORT_TYPE_GRE:
1246 case OVS_VPORT_TYPE_NETDEV:
1247 if (vport->isExternal) {
1248 if (vport->nicIndex == 0) {
1249 /* Such a vport is not part of any of the hash tables, since it
1250 * is not exposed to userspace. See Vport.h for explanation. */
1251 ASSERT(hvDelete == TRUE);
1252 ASSERT(switchContext->numPhysicalNics == 0);
1253 switchContext->virtualExternalPortId = 0;
1254 switchContext->virtualExternalVport = NULL;
1255 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1256 return STATUS_SUCCESS;
1259 hvSwitchPort = TRUE;
1265 * 'hvDelete' == TRUE indicates that the port should be removed from the
1266 * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
1267 * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
1269 * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
1271 if (vport->isAbsentOnHv == TRUE) {
1274 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1275 deletedOnOvs = TRUE;
1278 if (hvDelete && !deletedOnHv) {
1279 vport->isAbsentOnHv = TRUE;
1281 if (vport->isExternal) {
1282 ASSERT(vport->nicIndex != 0);
1283 ASSERT(switchContext->numPhysicalNics);
1284 switchContext->numPhysicalNics--;
1287 /* Remove the port from the relevant lists. */
1288 RemoveEntryList(&vport->portIdLink);
1289 InitializeListHead(&vport->portIdLink);
1292 if (ovsDelete && !deletedOnOvs) {
1293 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
1294 vport->ovsName[0] = '\0';
1296 /* Remove the port from the relevant lists. */
1297 RemoveEntryList(&vport->ovsNameLink);
1298 InitializeListHead(&vport->ovsNameLink);
1299 RemoveEntryList(&vport->portNoLink);
1300 InitializeListHead(&vport->portNoLink);
1301 if (OVS_VPORT_TYPE_VXLAN == vport->ovsType ||
1302 OVS_VPORT_TYPE_STT == vport->ovsType) {
1303 RemoveEntryList(&vport->tunnelVportLink);
1304 InitializeListHead(&vport->tunnelVportLink);
1307 deletedOnOvs = TRUE;
1311 * Deallocate the port if it has been deleted on the Hyper-V switch as well
1314 if (deletedOnHv && deletedOnOvs) {
1316 switchContext->numHvVports--;
1318 switchContext->numNonHvVports--;
1320 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1323 return STATUS_SUCCESS;
1327 OvsRemoveTunnelVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1328 POVS_SWITCH_CONTEXT switchContext,
1329 POVS_VPORT_ENTRY vport,
1333 POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
1336 tunnelContext = OvsAllocateMemoryWithTag(sizeof(*tunnelContext),
1337 OVS_VPORT_POOL_TAG);
1338 if (tunnelContext == NULL) {
1339 return STATUS_INSUFFICIENT_RESOURCES;
1341 RtlZeroMemory(tunnelContext, sizeof(*tunnelContext));
1343 tunnelContext->switchContext = switchContext;
1344 tunnelContext->hvSwitchPort = FALSE;
1345 tunnelContext->hvDelete = hvDelete;
1346 tunnelContext->ovsDelete = ovsDelete;
1347 tunnelContext->vport = vport;
1350 tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
1351 tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
1352 tunnelContext->outputLength = usrParamsCtx->outputLength;
1353 irp = usrParamsCtx->irp;
1356 return OvsCleanupVxlanTunnel(irp, vport, OvsTunnelVportPendingRemove,
1361 * --------------------------------------------------------------------------
1362 * Enumerates the ports on the Hyper-V switch.
1363 * --------------------------------------------------------------------------
1366 OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
1368 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1370 PNDIS_SWITCH_PORT_PARAMETERS portParam;
1371 PNDIS_SWITCH_PORT_ARRAY portArray = NULL;
1373 OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
1375 status = OvsGetPortsOnSwitch(switchContext, &portArray);
1376 if (status != NDIS_STATUS_SUCCESS) {
1380 for (arrIndex = 0; arrIndex < portArray->NumElements; arrIndex++) {
1381 portParam = NDIS_SWITCH_PORT_AT_ARRAY_INDEX(portArray, arrIndex);
1383 if (portParam->IsValidationPort) {
1387 status = HvCreatePort(switchContext, portParam, 0);
1388 if (status != STATUS_SUCCESS && status != STATUS_DATA_NOT_ACCEPTED) {
1394 if (status != NDIS_STATUS_SUCCESS) {
1395 OvsClearAllSwitchVports(switchContext);
1398 OvsFreeSwitchPortsArray(portArray);
1400 OVS_LOG_TRACE("Exit: status: %x", status);
1406 * --------------------------------------------------------------------------
1407 * Enumerates the NICs on the Hyper-V switch.
1408 * --------------------------------------------------------------------------
1411 OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext)
1413 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1414 PNDIS_SWITCH_NIC_ARRAY nicArray = NULL;
1416 PNDIS_SWITCH_NIC_PARAMETERS nicParam;
1418 OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
1420 * Now, get NIC list.
1422 status = OvsGetNicsOnSwitch(switchContext, &nicArray);
1423 if (status != NDIS_STATUS_SUCCESS) {
1426 for (arrIndex = 0; arrIndex < nicArray->NumElements; ++arrIndex) {
1427 nicParam = NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray, arrIndex);
1430 * XXX: Check if the port is configured with a VLAN. Disallow such a
1431 * configuration, since we don't support tag-in-tag.
1432 * XXX: Check if the port is connected to a VF. Disconnect the VF in
1436 status = HvCreateNic(switchContext, nicParam);
1437 if (status == NDIS_STATUS_SUCCESS) {
1438 HvConnectNic(switchContext, nicParam);
1443 OvsFreeSwitchNicsArray(nicArray);
1445 OVS_LOG_TRACE("Exit: status: %x", status);
1450 * --------------------------------------------------------------------------
1451 * Deletes ports added from the Hyper-V switch as well as OVS usersapce. The
1452 * function deletes ports in 'portIdHashArray'. This will delete most of the
1453 * ports that are in the 'portNoHashArray' as well. Any remaining ports
1454 * are deleted by walking the 'portNoHashArray'.
1455 * --------------------------------------------------------------------------
1458 OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
1460 for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1461 PLIST_ENTRY head, link, next;
1463 head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
1464 LIST_FORALL_SAFE(head, link, next) {
1465 POVS_VPORT_ENTRY vport;
1466 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1467 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
1472 * Remove 'virtualExternalVport' as well. This port is not part of the
1473 * 'portIdHashArray'.
1475 if (switchContext->virtualExternalVport) {
1476 OvsRemoveAndDeleteVport(NULL, switchContext,
1477 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE);
1481 for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1482 PLIST_ENTRY head, link, next;
1483 head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
1484 LIST_FORALL_SAFE(head, link, next) {
1485 POVS_VPORT_ENTRY vport;
1486 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1487 ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
1488 (vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
1489 vport->isBridgeInternal) || vport->isAbsentOnHv == TRUE);
1490 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
1494 ASSERT(switchContext->virtualExternalVport == NULL);
1495 ASSERT(switchContext->internalVport == NULL);
1500 OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
1505 UNICODE_STRING ustr;
1509 ustr.Buffer = wStr->String;
1510 ustr.Length = wStr->Length;
1511 ustr.MaximumLength = IF_MAX_STRING_SIZE;
1514 astr.MaximumLength = maxStrLen;
1517 size = RtlUnicodeStringToAnsiSize(&ustr);
1518 if (size > maxStrLen) {
1519 return STATUS_BUFFER_OVERFLOW;
1522 status = RtlUnicodeStringToAnsiString(&astr, &ustr, FALSE);
1524 ASSERT(status == STATUS_SUCCESS);
1525 if (status != STATUS_SUCCESS) {
1528 ASSERT(astr.Length <= maxStrLen);
1529 str[astr.Length] = 0;
1530 return STATUS_SUCCESS;
1534 * --------------------------------------------------------------------------
1535 * Utility function that populates a 'OVS_VPORT_EXT_INFO' structure for the
1537 * --------------------------------------------------------------------------
1540 OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
1541 POVS_VPORT_EXT_INFO extInfo)
1543 POVS_VPORT_ENTRY vport;
1545 LOCK_STATE_EX lockState;
1546 NTSTATUS status = STATUS_SUCCESS;
1547 BOOLEAN doConvert = FALSE;
1549 RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO));
1550 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1551 if (vportGet->portNo == 0) {
1552 StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
1553 vport = OvsFindVportByHvNameA(gOvsSwitchContext, vportGet->name);
1554 if (vport == NULL) {
1555 /* If the port is not a Hyper-V port and it has been added earlier,
1556 * we'll find it in 'ovsPortNameHashArray'. */
1557 vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name);
1560 vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo);
1562 if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED &&
1563 vport->ovsState != OVS_STATE_NIC_CREATED)) {
1564 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1565 if (vportGet->portNo) {
1566 OVS_LOG_WARN("vport %u does not exist any more", vportGet->portNo);
1568 OVS_LOG_WARN("vport %s does not exist any more", vportGet->name);
1570 status = STATUS_DEVICE_DOES_NOT_EXIST;
1573 extInfo->dpNo = vportGet->dpNo;
1574 extInfo->portNo = vport->portNo;
1575 RtlCopyMemory(extInfo->macAddress, vport->currMacAddress,
1576 sizeof (vport->currMacAddress));
1577 RtlCopyMemory(extInfo->permMACAddress, vport->permMacAddress,
1578 sizeof (vport->permMacAddress));
1579 if (vport->ovsType == OVS_VPORT_TYPE_NETDEV) {
1580 RtlCopyMemory(extInfo->vmMACAddress, vport->vmMacAddress,
1581 sizeof (vport->vmMacAddress));
1583 extInfo->nicIndex = vport->nicIndex;
1584 extInfo->portId = vport->portId;
1585 extInfo->type = vport->ovsType;
1586 extInfo->mtu = vport->mtu;
1590 if (vport->ovsState == OVS_STATE_NIC_CREATED) {
1591 extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_DOWN;
1592 } else if (vport->ovsState == OVS_STATE_CONNECTED) {
1593 extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
1595 extInfo->status = OVS_EVENT_DISCONNECT;
1597 if (extInfo->type == OVS_VPORT_TYPE_NETDEV &&
1598 (vport->ovsState == OVS_STATE_NIC_CREATED ||
1599 vport->ovsState == OVS_STATE_CONNECTED)) {
1602 extInfo->vmUUID[0] = 0;
1603 extInfo->vifUUID[0] = 0;
1605 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1607 status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName,
1609 OVS_MAX_PORT_NAME_LENGTH);
1610 if (status != STATUS_SUCCESS) {
1611 OVS_LOG_INFO("Fail to convert NIC name.");
1612 extInfo->vmUUID[0] = 0;
1615 status = OvsConvertIfCountedStrToAnsiStr(&vport->vmName,
1617 OVS_MAX_VM_UUID_LEN);
1618 if (status != STATUS_SUCCESS) {
1619 OVS_LOG_INFO("Fail to convert VM name.");
1620 extInfo->vmUUID[0] = 0;
1623 status = OvsConvertIfCountedStrToAnsiStr(&vport->nicName,
1625 OVS_MAX_VIF_UUID_LEN);
1626 if (status != STATUS_SUCCESS) {
1627 OVS_LOG_INFO("Fail to convert nic UUID");
1628 extInfo->vifUUID[0] = 0;
1631 * for now ignore status
1633 status = STATUS_SUCCESS;
1641 * --------------------------------------------------------------------------
1642 * Command Handler for 'OVS_WIN_NETDEV_CMD_GET'.
1643 * --------------------------------------------------------------------------
1646 OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1649 NTSTATUS status = STATUS_SUCCESS;
1650 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1651 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1652 NL_ERROR nlError = NL_ERROR_SUCCESS;
1653 OVS_VPORT_GET vportGet;
1654 OVS_VPORT_EXT_INFO info;
1656 static const NL_POLICY ovsNetdevPolicy[] = {
1657 [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING,
1659 .maxLen = IFNAMSIZ },
1661 PNL_ATTR netdevAttrs[ARRAY_SIZE(ovsNetdevPolicy)];
1663 /* input buffer has been validated while validating transaction dev op. */
1664 ASSERT(usrParamsCtx->inputBuffer != NULL &&
1665 usrParamsCtx->inputLength > sizeof *msgIn);
1667 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1668 return STATUS_INVALID_BUFFER_SIZE;
1671 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1672 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1673 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1674 ovsNetdevPolicy, ARRAY_SIZE(ovsNetdevPolicy),
1675 netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
1676 return STATUS_INVALID_PARAMETER;
1679 vportGet.portNo = 0;
1680 RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
1681 NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
1683 status = OvsGetExtInfoIoctl(&vportGet, &info);
1684 if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
1685 nlError = NL_ERROR_NODEV;
1689 status = CreateNetlinkMesgForNetdev(&info, msgIn,
1690 usrParamsCtx->outputBuffer, usrParamsCtx->outputLength,
1691 gOvsSwitchContext->dpNo);
1692 if (status == STATUS_SUCCESS) {
1693 *replyLen = msgOut->nlMsg.nlmsgLen;
1697 if (nlError != NL_ERROR_SUCCESS) {
1698 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1699 usrParamsCtx->outputBuffer;
1701 NlBuildErrorMsg(msgIn, msgError, nlError);
1702 *replyLen = msgError->nlMsg.nlmsgLen;
1705 return STATUS_SUCCESS;
1710 * --------------------------------------------------------------------------
1711 * Utility function to construct an OVS_MESSAGE for the specified vport. The
1712 * OVS_MESSAGE contains the output of a netdev command.
1713 * --------------------------------------------------------------------------
1716 CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
1725 UINT32 netdevFlags = 0;
1727 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1729 ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1730 msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1731 msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1734 return STATUS_INVALID_BUFFER_SIZE;
1737 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
1740 return STATUS_INVALID_BUFFER_SIZE;
1743 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
1745 return STATUS_INVALID_BUFFER_SIZE;
1748 ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
1751 return STATUS_INVALID_BUFFER_SIZE;
1754 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
1755 (PCHAR)info->macAddress, sizeof (info->macAddress));
1757 return STATUS_INVALID_BUFFER_SIZE;
1760 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
1762 return STATUS_INVALID_BUFFER_SIZE;
1765 if (info->status != OVS_EVENT_CONNECT) {
1766 netdevFlags = OVS_WIN_NETDEV_IFF_UP;
1768 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
1771 return STATUS_INVALID_BUFFER_SIZE;
1775 * XXX: add netdev_stats when we have the definition available in the
1779 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1780 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1782 return STATUS_SUCCESS;
1785 static __inline VOID
1786 OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec)
1788 while ((!switchContext->isActivated) &&
1789 (!switchContext->isActivateFailed)) {
1790 /* Wait for the switch to be active and
1791 * the list of ports in OVS to be initialized. */
1792 NdisMSleep(sleepMicroSec);
1797 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1804 OVS_VPORT_FULL_STATS vportStats;
1808 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1810 ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1811 msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1812 msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1815 return STATUS_INVALID_BUFFER_SIZE;
1818 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1820 return STATUS_INVALID_BUFFER_SIZE;
1823 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1825 return STATUS_INVALID_BUFFER_SIZE;
1828 ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1830 return STATUS_INVALID_BUFFER_SIZE;
1834 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1835 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1836 * it means we have an array of pids, instead of a single pid.
1837 * ATM we assume we have one pid only.
1840 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1843 return STATUS_INVALID_BUFFER_SIZE;
1847 vportStats.rxPackets = vport->stats.rxPackets;
1848 vportStats.rxBytes = vport->stats.rxBytes;
1849 vportStats.txPackets = vport->stats.txPackets;
1850 vportStats.txBytes = vport->stats.txBytes;
1851 vportStats.rxErrors = vport->errStats.rxErrors;
1852 vportStats.txErrors = vport->errStats.txErrors;
1853 vportStats.rxDropped = vport->errStats.rxDropped;
1854 vportStats.txDropped = vport->errStats.txDropped;
1856 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1858 sizeof(OVS_VPORT_FULL_STATS));
1860 return STATUS_INVALID_BUFFER_SIZE;
1864 * XXX: when vxlan udp dest port becomes configurable, we will also need
1865 * to add vport options
1868 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1869 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1871 return STATUS_SUCCESS;
1875 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1879 POVS_OPEN_INSTANCE instance =
1880 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1881 LOCK_STATE_EX lockState;
1882 UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1885 * XXX: this function shares some code with other dump command(s).
1886 * In the future, we will need to refactor the dump functions
1889 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1891 if (instance->dumpState.ovsMsg == NULL) {
1893 return STATUS_INVALID_DEVICE_STATE;
1896 /* Output buffer has been validated while validating read dev op. */
1897 ASSERT(usrParamsCtx->outputBuffer != NULL);
1899 msgIn = instance->dumpState.ovsMsg;
1902 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1903 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1904 * it means we have an array of pids, instead of a single pid.
1905 * ATM we assume we have one pid only.
1907 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1909 if (gOvsSwitchContext->numHvVports > 0 ||
1910 gOvsSwitchContext->numNonHvVports > 0) {
1911 /* inBucket: the bucket, used for lookup */
1912 UINT32 inBucket = instance->dumpState.index[0];
1913 /* inIndex: index within the given bucket, used for lookup */
1914 UINT32 inIndex = instance->dumpState.index[1];
1915 /* the bucket to be used for the next dump operation */
1916 UINT32 outBucket = 0;
1917 /* the index within the outBucket to be used for the next dump */
1918 UINT32 outIndex = 0;
1920 for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1921 PLIST_ENTRY head, link;
1922 head = &(gOvsSwitchContext->portNoHashArray[i]);
1923 POVS_VPORT_ENTRY vport = NULL;
1926 LIST_FORALL(head, link) {
1929 * if one or more dumps were previously done on this same bucket,
1930 * inIndex will be > 0, so we'll need to reply with the
1931 * inIndex + 1 vport from the bucket.
1933 if (outIndex >= inIndex) {
1934 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1936 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
1937 OvsCreateMsgFromVport(vport, msgIn,
1938 usrParamsCtx->outputBuffer,
1939 usrParamsCtx->outputLength,
1940 gOvsSwitchContext->dpNo);
1953 * if no vport was found above, check the next bucket, beginning
1954 * with the first (i.e. index 0) elem from within that bucket
1961 /* XXX: what about NLMSG_DONE (as msg type)? */
1962 instance->dumpState.index[0] = outBucket;
1963 instance->dumpState.index[1] = outIndex;
1966 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1968 /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1969 if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1970 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1971 *replyLen = msgOut->nlMsg.nlmsgLen;
1974 * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1978 /* Free up the dump state, since there's no more data to continue. */
1979 FreeUserDumpState(instance);
1982 return STATUS_SUCCESS;
1986 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1989 NTSTATUS status = STATUS_SUCCESS;
1990 LOCK_STATE_EX lockState;
1992 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1993 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1994 POVS_VPORT_ENTRY vport = NULL;
1995 NL_ERROR nlError = NL_ERROR_SUCCESS;
1996 PCHAR portName = NULL;
1997 UINT32 portNameLen = 0;
1998 UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
2000 static const NL_POLICY ovsVportPolicy[] = {
2001 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2002 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
2007 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2009 /* input buffer has been validated while validating write dev op. */
2010 ASSERT(usrParamsCtx->inputBuffer != NULL);
2012 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2013 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2014 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2015 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2016 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2017 return STATUS_INVALID_PARAMETER;
2020 /* Output buffer has been validated while validating transact dev op. */
2021 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2023 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
2024 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2025 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2026 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2028 /* the port name is expected to be null-terminated */
2029 ASSERT(portName[portNameLen - 1] == '\0');
2031 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2032 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2033 portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2035 vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
2037 nlError = NL_ERROR_INVAL;
2038 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2043 nlError = NL_ERROR_NODEV;
2044 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2048 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2049 usrParamsCtx->outputLength,
2050 gOvsSwitchContext->dpNo);
2051 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2053 *replyLen = msgOut->nlMsg.nlmsgLen;
2056 if (nlError != NL_ERROR_SUCCESS) {
2057 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2058 usrParamsCtx->outputBuffer;
2060 NlBuildErrorMsg(msgIn, msgError, nlError);
2061 *replyLen = msgError->nlMsg.nlmsgLen;
2064 return STATUS_SUCCESS;
2068 * --------------------------------------------------------------------------
2069 * Command Handler for 'OVS_VPORT_CMD_GET'.
2071 * The function handles the initial call to setup the dump state, as well as
2072 * subsequent calls to continue dumping data.
2073 * --------------------------------------------------------------------------
2076 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2081 switch (usrParamsCtx->devOp) {
2082 case OVS_WRITE_DEV_OP:
2083 return OvsSetupDumpStart(usrParamsCtx);
2085 case OVS_READ_DEV_OP:
2086 return OvsGetVportDumpNext(usrParamsCtx, replyLen);
2088 case OVS_TRANSACTION_DEV_OP:
2089 return OvsGetVport(usrParamsCtx, replyLen);
2092 return STATUS_INVALID_DEVICE_REQUEST;
2098 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
2100 /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
2101 for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
2102 POVS_VPORT_ENTRY vport;
2104 vport = OvsFindVportByPortNo(switchContext, i);
2110 return OVS_DPPORT_NUMBER_INVALID;
2114 * --------------------------------------------------------------------------
2115 * Command Handler for 'OVS_VPORT_CMD_NEW'.
2116 * --------------------------------------------------------------------------
2119 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2122 NDIS_STATUS status = STATUS_SUCCESS;
2123 LOCK_STATE_EX lockState;
2125 NL_ERROR nlError = NL_ERROR_SUCCESS;
2126 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2127 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2128 POVS_VPORT_ENTRY vport = NULL;
2132 BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE;
2134 static const NL_POLICY ovsVportPolicy[] = {
2135 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2136 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2137 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2139 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2140 .optional = FALSE },
2141 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2144 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2146 /* input buffer has been validated while validating write dev op. */
2147 ASSERT(usrParamsCtx->inputBuffer != NULL);
2149 /* Output buffer has been validated while validating transact dev op. */
2150 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2152 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2153 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2154 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2155 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2156 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2157 return STATUS_INVALID_PARAMETER;
2160 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2161 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2162 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2164 /* we are expecting null terminated strings to be passed */
2165 ASSERT(portName[portNameLen - 1] == '\0');
2167 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2169 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2171 nlError = NL_ERROR_EXIST;
2175 if (portType == OVS_VPORT_TYPE_NETDEV) {
2176 /* External ports can also be looked up like VIF ports. */
2177 vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
2179 ASSERT(OvsIsTunnelVportType(portType) ||
2180 portType == OVS_VPORT_TYPE_INTERNAL);
2182 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
2183 if (vport == NULL) {
2184 nlError = NL_ERROR_NOMEM;
2187 vportAllocated = TRUE;
2189 if (OvsIsTunnelVportType(portType)) {
2190 UINT16 transportPortDest = 0;
2193 case OVS_VPORT_TYPE_VXLAN:
2194 transportPortDest = VXLAN_UDP_PORT;
2196 case OVS_VPORT_TYPE_STT:
2197 transportPortDest = STT_TCP_PORT;
2200 nlError = NL_ERROR_INVAL;
2204 if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2205 PNL_ATTR attr = NlAttrFindNested(vportAttrs[OVS_VPORT_ATTR_OPTIONS],
2206 OVS_TUNNEL_ATTR_DST_PORT);
2208 transportPortDest = NlAttrGetU16(attr);
2212 status = OvsInitTunnelVport(usrParamsCtx,
2217 nlError = NlMapStatusToNlErr(status);
2219 OvsInitBridgeInternalVport(vport);
2222 vportInitialized = TRUE;
2224 if (nlError == NL_ERROR_SUCCESS) {
2225 vport->ovsState = OVS_STATE_CONNECTED;
2226 vport->nicState = NdisSwitchNicStateConnected;
2229 * Allow the vport to be deleted, because there is no
2230 * corresponding hyper-v switch part.
2232 vport->isAbsentOnHv = TRUE;
2239 nlError = NL_ERROR_INVAL;
2242 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2243 nlError = NL_ERROR_EXIST;
2247 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2249 * XXX: when we implement the limit for ovs port number to be
2250 * MAXUINT16, we'll need to check the port number received from the
2253 vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2255 vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
2256 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2257 nlError = NL_ERROR_NOMEM;
2262 /* The ovs port name must be uninitialized. */
2263 ASSERT(vport->ovsName[0] == '\0');
2264 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2266 RtlCopyMemory(vport->ovsName, portName, portNameLen);
2267 /* if we don't have options, then vport->portOptions will be NULL */
2268 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2271 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2272 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2273 * it means we have an array of pids, instead of a single pid.
2274 * ATM we assume we have one pid only.
2276 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2278 status = InitOvsVportCommon(gOvsSwitchContext, vport);
2279 ASSERT(status == STATUS_SUCCESS);
2281 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2282 usrParamsCtx->outputLength,
2283 gOvsSwitchContext->dpNo);
2285 *replyLen = msgOut->nlMsg.nlmsgLen;
2288 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2290 if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2291 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2292 usrParamsCtx->outputBuffer;
2294 if (vport && vportAllocated == TRUE) {
2295 if (vportInitialized == TRUE) {
2296 if (OvsIsTunnelVportType(portType)) {
2297 switch (vport->ovsType) {
2298 case OVS_VPORT_TYPE_VXLAN:
2299 OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2301 case OVS_VPORT_TYPE_STT:
2302 OvsCleanupSttTunnel(vport);
2305 ASSERT(!"Invalid tunnel port type");
2309 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2312 NlBuildErrorMsg(msgIn, msgError, nlError);
2313 *replyLen = msgError->nlMsg.nlmsgLen;
2316 return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2321 * --------------------------------------------------------------------------
2322 * Command Handler for 'OVS_VPORT_CMD_SET'.
2323 * --------------------------------------------------------------------------
2326 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2329 NDIS_STATUS status = STATUS_SUCCESS;
2330 LOCK_STATE_EX lockState;
2332 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2333 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2334 POVS_VPORT_ENTRY vport = NULL;
2335 NL_ERROR nlError = NL_ERROR_SUCCESS;
2337 static const NL_POLICY ovsVportPolicy[] = {
2338 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2339 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
2340 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2342 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2344 [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
2345 .minLen = sizeof(OVS_VPORT_FULL_STATS),
2346 .maxLen = sizeof(OVS_VPORT_FULL_STATS),
2348 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2350 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2352 ASSERT(usrParamsCtx->inputBuffer != NULL);
2354 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2355 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2356 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2357 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2358 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2359 return STATUS_INVALID_PARAMETER;
2362 /* Output buffer has been validated while validating transact dev op. */
2363 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2365 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2366 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2367 PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2369 UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2371 /* the port name is expected to be null-terminated */
2372 ASSERT(portName[portNameLen - 1] == '\0');
2374 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2375 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2376 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2377 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2381 nlError = NL_ERROR_NODEV;
2386 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2387 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2388 * it means we have an array of pids, instead of a single pid.
2389 * Currently, we support only one pid.
2391 if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
2392 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2395 if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
2396 OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2397 if (type != vport->ovsType) {
2398 nlError = NL_ERROR_INVAL;
2403 if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2404 OVS_LOG_ERROR("Vport options not supported");
2405 nlError = NL_ERROR_NOTSUPP;
2409 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2410 usrParamsCtx->outputLength,
2411 gOvsSwitchContext->dpNo);
2413 *replyLen = msgOut->nlMsg.nlmsgLen;
2416 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2418 if (nlError != NL_ERROR_SUCCESS) {
2419 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2420 usrParamsCtx->outputBuffer;
2422 NlBuildErrorMsg(msgIn, msgError, nlError);
2423 *replyLen = msgError->nlMsg.nlmsgLen;
2426 return STATUS_SUCCESS;
2430 * --------------------------------------------------------------------------
2431 * Command Handler for 'OVS_VPORT_CMD_DEL'.
2432 * --------------------------------------------------------------------------
2435 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2438 NDIS_STATUS status = STATUS_SUCCESS;
2439 LOCK_STATE_EX lockState;
2441 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2442 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2443 POVS_VPORT_ENTRY vport = NULL;
2444 NL_ERROR nlError = NL_ERROR_SUCCESS;
2445 PSTR portName = NULL;
2446 UINT32 portNameLen = 0;
2448 static const NL_POLICY ovsVportPolicy[] = {
2449 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2450 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2453 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2455 ASSERT(usrParamsCtx->inputBuffer != NULL);
2457 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2458 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2459 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2460 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2461 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2462 return STATUS_INVALID_PARAMETER;
2465 /* Output buffer has been validated while validating transact dev op. */
2466 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2468 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2469 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2470 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2471 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2473 /* the port name is expected to be null-terminated */
2474 ASSERT(portName[portNameLen - 1] == '\0');
2476 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2478 else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2479 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2480 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2484 nlError = NL_ERROR_NODEV;
2488 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2489 usrParamsCtx->outputLength,
2490 gOvsSwitchContext->dpNo);
2492 *replyLen = msgOut->nlMsg.nlmsgLen;
2495 * Mark the port as deleted from OVS userspace. If the port does not exist
2496 * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
2498 status = OvsRemoveAndDeleteVport(usrParamsCtx,
2504 nlError = NlMapStatusToNlErr(status);
2508 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2510 if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2511 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2512 usrParamsCtx->outputBuffer;
2514 NlBuildErrorMsg(msgIn, msgError, nlError);
2515 *replyLen = msgError->nlMsg.nlmsgLen;
2518 return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2522 OvsTunnelVportPendingRemove(PVOID context,
2526 POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2527 (POVS_TUNFLT_INIT_CONTEXT) context;
2528 POVS_SWITCH_CONTEXT switchContext = tunnelContext->switchContext;
2529 POVS_VPORT_ENTRY vport = tunnelContext->vport;
2530 POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2531 POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2532 NL_ERROR nlError = NlMapStatusToNlErr(status);
2533 LOCK_STATE_EX lockState;
2535 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
2537 if (msgIn && msgOut) {
2538 /* Check the received status to reply to the caller. */
2539 if (STATUS_SUCCESS == status) {
2540 OvsCreateMsgFromVport(vport,
2543 tunnelContext->outputLength,
2544 switchContext->dpNo);
2546 *replyLen = msgOut->nlMsg.nlmsgLen;
2548 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)msgOut;
2550 NlBuildErrorMsg(msgIn, msgError, nlError);
2551 *replyLen = msgError->nlMsg.nlmsgLen;
2555 ASSERT(vport->isAbsentOnHv == TRUE);
2556 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
2558 /* Remove the port from the relevant lists. */
2559 switchContext->numNonHvVports--;
2560 RemoveEntryList(&vport->ovsNameLink);
2561 RemoveEntryList(&vport->portNoLink);
2562 RemoveEntryList(&vport->tunnelVportLink);
2565 OvsFreeMemoryWithTag(vport->priv, OVS_VXLAN_POOL_TAG);
2569 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2571 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
2575 OvsTunnelVportPendingInit(PVOID context,
2579 POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2580 (POVS_TUNFLT_INIT_CONTEXT) context;
2581 POVS_VPORT_ENTRY vport = tunnelContext->vport;
2582 POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2583 POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2585 ULONG portNameLen = 0;
2586 UINT32 portType = 0;
2587 NL_ERROR nlError = NL_ERROR_SUCCESS;
2588 BOOLEAN error = TRUE;
2591 if (!NT_SUCCESS(status)) {
2592 nlError = NlMapStatusToNlErr(status);
2596 static const NL_POLICY ovsVportPolicy[] = {
2597 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2598 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2599 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2600 .optional = FALSE },
2601 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2602 .optional = FALSE },
2603 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2606 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2608 /* input buffer has been validated while validating write dev op. */
2609 ASSERT(msgIn != NULL);
2611 /* Output buffer has been validated while validating transact dev op. */
2612 ASSERT(msgOut != NULL && tunnelContext->outputLength >= sizeof *msgOut);
2614 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2615 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2616 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2617 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2618 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2619 nlError = NL_ERROR_INVAL;
2623 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2624 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2625 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2627 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2628 nlError = NL_ERROR_EXIST;
2632 vport->ovsState = OVS_STATE_CONNECTED;
2633 vport->nicState = NdisSwitchNicStateConnected;
2636 * Allow the vport to be deleted, because there is no
2637 * corresponding hyper-v switch part.
2639 vport->isAbsentOnHv = TRUE;
2641 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2643 * XXX: when we implement the limit for OVS port number to be
2644 * MAXUINT16, we'll need to check the port number received from the
2648 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2651 OvsComputeVportNo(gOvsSwitchContext);
2652 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2653 nlError = NL_ERROR_NOMEM;
2658 /* The ovs port name must be uninitialized. */
2659 ASSERT(vport->ovsName[0] == '\0');
2660 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2662 RtlCopyMemory(vport->ovsName, portName, portNameLen);
2663 /* if we don't have options, then vport->portOptions will be NULL */
2664 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2667 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2668 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2669 * it means we have an array of pids, instead of a single pid.
2670 * ATM we assume we have one pid only.
2673 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2675 status = InitOvsVportCommon(gOvsSwitchContext, vport);
2676 ASSERT(status == STATUS_SUCCESS);
2678 OvsCreateMsgFromVport(vport,
2681 tunnelContext->outputLength,
2682 gOvsSwitchContext->dpNo);
2684 *replyLen = msgOut->nlMsg.nlmsgLen;
2690 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) msgOut;
2692 OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2693 OvsFreeMemory(vport);
2695 NlBuildErrorMsg(msgIn, msgError, nlError);
2696 *replyLen = msgError->nlMsg.nlmsgLen;