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 (nicParam->NicType == NdisSwitchNicTypeInternal ||
331 (nicParam->NicType == NdisSwitchNicTypeExternal &&
332 nicParam->NicIndex != 0)) {
333 GetNICAlias(&nicParam->NetCfgInstanceId, &portFriendlyName);
336 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
338 * There can be one or more NICs for the external port. We create a vport
339 * structure for each such NIC, and each NIC inherits a lot of properties
340 * from the parent external port.
342 if (nicParam->NicType == NdisSwitchNicTypeExternal &&
343 nicParam->NicIndex != 0) {
344 POVS_VPORT_ENTRY virtExtVport =
345 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
347 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
351 NDIS_SWITCH_PORT_PARAMETERS portParam;
352 /* Find by interface name */
353 WCHAR interfaceName[IF_MAX_STRING_SIZE] = { 0 };
354 NET_LUID interfaceLuid = { 0 };
356 status = ConvertInterfaceGuidToLuid(&nicParam->NetCfgInstanceId,
358 if (status == STATUS_SUCCESS) {
359 status = ConvertInterfaceLuidToAlias(&interfaceLuid,
361 IF_MAX_STRING_SIZE + 1);
362 if (status == STATUS_SUCCESS) {
363 RtlStringCbLengthW(interfaceName,
366 vport = OvsFindVportByHvNameW(switchContext,
372 OvsCopyPortParamsFromVport(virtExtVport, &portParam);
373 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
374 status = HvCreatePort(switchContext, &portParam,
376 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
377 if (status != NDIS_STATUS_SUCCESS) {
383 vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId,
386 OVS_LOG_ERROR("Create NIC without Switch Port,"
387 " PortId: %x, NicIndex: %d",
388 nicParam->PortId, nicParam->NicIndex);
389 status = NDIS_STATUS_INVALID_PARAMETER;
392 OvsInitVportWithNicParam(switchContext, vport, nicParam);
393 if (nicParam->NicType == NdisSwitchNicTypeInternal ||
394 (nicParam->NicType == NdisSwitchNicTypeExternal &&
395 nicParam->NicIndex != 0)) {
396 RtlCopyMemory(&vport->portFriendlyName, &portFriendlyName,
397 sizeof portFriendlyName);
401 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
404 VPORT_NIC_EXIT(nicParam);
405 OVS_LOG_TRACE("Exit: status %8x.\n", status);
411 * --------------------------------------------------------------------------
412 * Function to process connection event of a NIC on the Hyper-V switch.
413 * --------------------------------------------------------------------------
416 HvConnectNic(POVS_SWITCH_CONTEXT switchContext,
417 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
419 LOCK_STATE_EX lockState;
420 POVS_VPORT_ENTRY vport;
423 VPORT_NIC_ENTER(nicParam);
425 /* Wait for lists to be initialized. */
426 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
428 if (!switchContext->isActivated) {
429 OVS_LOG_WARN("Switch is not activated yet.");
433 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
434 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
439 OVS_LOG_WARN("Vport not present.");
440 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
445 vport->ovsState = OVS_STATE_CONNECTED;
446 vport->nicState = NdisSwitchNicStateConnected;
447 portNo = vport->portNo;
449 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
451 if (nicParam->NicType == NdisSwitchNicTypeInternal) {
452 OvsInternalAdapterUp(&nicParam->NetCfgInstanceId);
456 VPORT_NIC_EXIT(nicParam);
461 * --------------------------------------------------------------------------
462 * Function to process updates to a NIC on the Hyper-V switch.
463 * --------------------------------------------------------------------------
466 HvUpdateNic(POVS_SWITCH_CONTEXT switchContext,
467 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
469 POVS_VPORT_ENTRY vport;
470 LOCK_STATE_EX lockState;
472 IF_COUNTED_STRING portFriendlyName = {0};
474 VPORT_NIC_ENTER(nicParam);
476 /* Wait for lists to be initialized. */
477 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
479 if (!switchContext->isActivated) {
480 OVS_LOG_WARN("Switch is not activated yet.");
481 goto update_nic_done;
484 /* GetNICAlias() must be called outside of a lock. */
485 if (nicParam->NicType == NdisSwitchNicTypeInternal ||
486 (nicParam->NicType == NdisSwitchNicTypeExternal &&
487 nicParam->NicIndex != 0)) {
488 GetNICAlias(&nicParam->NetCfgInstanceId, &portFriendlyName);
491 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
492 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
496 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
497 OVS_LOG_WARN("Vport search failed.");
498 goto update_nic_done;
500 switch (nicParam->NicType) {
501 case NdisSwitchNicTypeExternal:
502 case NdisSwitchNicTypeInternal:
503 RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
505 RtlCopyMemory(&vport->portFriendlyName, &portFriendlyName,
506 sizeof portFriendlyName);
508 case NdisSwitchNicTypeSynthetic:
509 case NdisSwitchNicTypeEmulated:
510 if (!RtlEqualMemory(vport->vmMacAddress, nicParam->VMMacAddress,
511 sizeof (vport->vmMacAddress))) {
512 event |= OVS_EVENT_MAC_CHANGE;
513 RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
514 sizeof (vport->vmMacAddress));
520 if (!RtlEqualMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
521 sizeof (vport->permMacAddress))) {
522 RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
523 sizeof (vport->permMacAddress));
524 event |= OVS_EVENT_MAC_CHANGE;
526 if (!RtlEqualMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
527 sizeof (vport->currMacAddress))) {
528 RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
529 sizeof (vport->currMacAddress));
530 event |= OVS_EVENT_MAC_CHANGE;
533 if (vport->mtu != nicParam->MTU) {
534 vport->mtu = nicParam->MTU;
535 event |= OVS_EVENT_MTU_CHANGE;
537 vport->numaNodeId = nicParam->NumaNodeId;
539 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
542 * XXX: Not sure what kind of event to post here. DPIF is not interested in
543 * changes to MAC address. Netdev-windows might be intrested, though.
544 * That said, if the name chagnes, not clear what kind of event to be
545 * posted. We might have to delete the vport, and have userspace recreate
550 VPORT_NIC_EXIT(nicParam);
554 * --------------------------------------------------------------------------
555 * Function to process disconnect event of a NIC on the Hyper-V switch.
556 * --------------------------------------------------------------------------
559 HvDisconnectNic(POVS_SWITCH_CONTEXT switchContext,
560 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
562 POVS_VPORT_ENTRY vport;
563 LOCK_STATE_EX lockState;
564 BOOLEAN isInternalPort = FALSE;
565 OVS_EVENT_ENTRY event;
567 VPORT_NIC_ENTER(nicParam);
569 /* Wait for lists to be initialized. */
570 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
572 if (!switchContext->isActivated) {
573 OVS_LOG_WARN("Switch is not activated yet.");
577 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
578 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
583 OVS_LOG_WARN("Vport not present.");
584 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
588 vport->nicState = NdisSwitchNicStateDisconnected;
589 vport->ovsState = OVS_STATE_NIC_CREATED;
591 if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
592 isInternalPort = TRUE;
595 event.portNo = vport->portNo;
596 event.ovsType = vport->ovsType;
597 event.upcallPid = vport->upcallPid;
598 RtlCopyMemory(&event.ovsName, &vport->ovsName, sizeof event.ovsName);
599 event.type = OVS_EVENT_LINK_DOWN;
601 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
604 * Delete the port from the hash tables accessible to userspace. After this
605 * point, userspace should not be able to access this port.
607 OvsRemoveAndDeleteVport(NULL, switchContext, vport, FALSE, TRUE);
608 OvsPostEvent(&event);
610 if (isInternalPort) {
611 OvsInternalAdapterDown();
615 VPORT_NIC_EXIT(nicParam);
619 * --------------------------------------------------------------------------
620 * Function to process delete event of a NIC on the Hyper-V switch.
621 * --------------------------------------------------------------------------
624 HvDeleteNic(POVS_SWITCH_CONTEXT switchContext,
625 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
627 LOCK_STATE_EX lockState;
628 POVS_VPORT_ENTRY vport;
630 VPORT_NIC_ENTER(nicParam);
631 /* Wait for lists to be initialized. */
632 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
634 if (!switchContext->isActivated) {
635 OVS_LOG_WARN("Switch is not activated yet.");
639 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
640 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
645 OVS_LOG_WARN("Vport not present.");
646 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
650 vport->nicState = NdisSwitchNicStateUnknown;
651 vport->ovsState = OVS_STATE_PORT_CREATED;
653 if (vport->portType == NdisSwitchPortTypeExternal &&
654 vport->nicIndex != 0) {
655 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
658 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
661 VPORT_NIC_EXIT(nicParam);
665 * OVS Vport related functionality.
668 OvsFindVportByPortNo(POVS_SWITCH_CONTEXT switchContext,
671 POVS_VPORT_ENTRY vport;
672 PLIST_ENTRY head, link;
673 UINT32 hash = OvsJhashBytes((const VOID *)&portNo, sizeof(portNo),
675 head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
676 LIST_FORALL(head, link) {
677 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
678 if (vport->portNo == portNo) {
687 OvsFindTunnelVportByDstPort(POVS_SWITCH_CONTEXT switchContext,
689 OVS_VPORT_TYPE ovsPortType)
691 POVS_VPORT_ENTRY vport;
692 PLIST_ENTRY head, link;
693 UINT32 hash = OvsJhashBytes((const VOID *)&dstPort, sizeof(dstPort),
695 head = &(switchContext->tunnelVportsArray[hash & OVS_VPORT_MASK]);
696 LIST_FORALL(head, link) {
697 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, tunnelVportLink);
698 if (GetPortFromPriv(vport) == dstPort &&
699 vport->ovsType == ovsPortType) {
708 OvsFindVportByOvsName(POVS_SWITCH_CONTEXT switchContext,
711 POVS_VPORT_ENTRY vport;
712 PLIST_ENTRY head, link;
714 SIZE_T length = strlen(name) + 1;
716 hash = OvsJhashBytes((const VOID *)name, length, OVS_HASH_BASIS);
717 head = &(switchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK]);
719 LIST_FORALL(head, link) {
720 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, ovsNameLink);
721 if (!strcmp(name, vport->ovsName)) {
729 /* OvsFindVportByHvName: "name" is assumed to be null-terminated */
731 OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
732 PWSTR wsName, SIZE_T wstrSize)
734 POVS_VPORT_ENTRY vport = NULL;
735 PLIST_ENTRY head, link;
738 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
739 head = &(switchContext->portIdHashArray[i]);
740 LIST_FORALL(head, link) {
741 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
744 * NOTE about portFriendlyName:
745 * If the string is NULL-terminated, the Length member does not
746 * include the terminating NULL character.
748 if (vport->portFriendlyName.Length == wstrSize &&
749 RtlEqualMemory(wsName, vport->portFriendlyName.String,
750 vport->portFriendlyName.Length)) {
759 * Look in the list of ports that were added from the Hyper-V switch and
763 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
764 head = &(switchContext->portNoHashArray[i]);
765 LIST_FORALL(head, link) {
766 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
767 if (vport->portFriendlyName.Length == wstrSize &&
768 RtlEqualMemory(wsName, vport->portFriendlyName.String,
769 vport->portFriendlyName.Length)) {
783 OvsFindVportByHvNameA(POVS_SWITCH_CONTEXT switchContext,
786 POVS_VPORT_ENTRY vport = NULL;
787 /* 'portFriendlyName' is not NUL-terminated. */
788 SIZE_T length = strlen(name);
789 SIZE_T wstrSize = length * sizeof(WCHAR);
792 PWSTR wsName = OvsAllocateMemoryWithTag(wstrSize, OVS_VPORT_POOL_TAG);
796 for (i = 0; i < length; i++) {
799 vport = OvsFindVportByHvNameW(switchContext, wsName, wstrSize);
800 OvsFreeMemoryWithTag(wsName, OVS_VPORT_POOL_TAG);
805 OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT switchContext,
806 NDIS_SWITCH_PORT_ID portId,
807 NDIS_SWITCH_NIC_INDEX index)
809 if (switchContext->virtualExternalVport &&
810 portId == switchContext->virtualExternalPortId &&
811 index == switchContext->virtualExternalVport->nicIndex) {
812 return (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
813 } else if (switchContext->internalVport &&
814 portId == switchContext->internalPortId &&
815 index == switchContext->internalVport->nicIndex) {
816 return (POVS_VPORT_ENTRY)switchContext->internalVport;
818 PLIST_ENTRY head, link;
819 POVS_VPORT_ENTRY vport;
821 hash = OvsJhashWords((UINT32 *)&portId, 1, OVS_HASH_BASIS);
822 head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
823 LIST_FORALL(head, link) {
824 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
825 if (portId == vport->portId && index == vport->nicIndex) {
834 OvsAllocateVport(VOID)
836 POVS_VPORT_ENTRY vport;
837 vport = (POVS_VPORT_ENTRY)OvsAllocateMemoryWithTag(
838 sizeof(OVS_VPORT_ENTRY), OVS_VPORT_POOL_TAG);
842 RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY));
843 vport->ovsState = OVS_STATE_UNKNOWN;
844 vport->isAbsentOnHv = FALSE;
845 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
847 InitializeListHead(&vport->ovsNameLink);
848 InitializeListHead(&vport->portIdLink);
849 InitializeListHead(&vport->portNoLink);
855 OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
856 PNDIS_SWITCH_PORT_PARAMETERS portParam)
858 vport->portType = portParam->PortType;
859 vport->portState = portParam->PortState;
860 vport->portId = portParam->PortId;
861 vport->nicState = NdisSwitchNicStateUnknown;
862 vport->isExternal = FALSE;
863 vport->isBridgeInternal = FALSE;
865 switch (vport->portType) {
866 case NdisSwitchPortTypeExternal:
867 vport->isExternal = TRUE;
868 vport->ovsType = OVS_VPORT_TYPE_NETDEV;
870 case NdisSwitchPortTypeInternal:
871 vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
873 case NdisSwitchPortTypeSynthetic:
874 case NdisSwitchPortTypeEmulated:
875 vport->ovsType = OVS_VPORT_TYPE_NETDEV;
878 RtlCopyMemory(&vport->hvPortName, &portParam->PortName,
879 sizeof (NDIS_SWITCH_PORT_NAME));
880 /* For external and internal ports, 'portFriendlyName' is overwritten
882 RtlCopyMemory(&vport->portFriendlyName, &portParam->PortFriendlyName,
883 sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
885 switch (vport->portState) {
886 case NdisSwitchPortStateCreated:
887 vport->ovsState = OVS_STATE_PORT_CREATED;
889 case NdisSwitchPortStateTeardown:
890 vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
892 case NdisSwitchPortStateDeleted:
893 vport->ovsState = OVS_STATE_PORT_DELETED;
900 OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
901 POVS_VPORT_ENTRY vport,
902 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
904 ASSERT(vport->portId == nicParam->PortId);
905 ASSERT(vport->ovsState == OVS_STATE_PORT_CREATED);
907 UNREFERENCED_PARAMETER(switchContext);
909 RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
910 sizeof (nicParam->PermanentMacAddress));
911 RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
912 sizeof (nicParam->CurrentMacAddress));
914 if (nicParam->NicType == NdisSwitchNicTypeSynthetic ||
915 nicParam->NicType == NdisSwitchNicTypeEmulated) {
916 RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
917 sizeof (nicParam->VMMacAddress));
918 RtlCopyMemory(&vport->vmName, &nicParam->VmName,
919 sizeof (nicParam->VmName));
921 RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
922 sizeof (nicParam->NetCfgInstanceId));
924 RtlCopyMemory(&vport->nicName, &nicParam->NicName,
925 sizeof (nicParam->NicName));
926 vport->mtu = nicParam->MTU;
927 vport->nicState = nicParam->NicState;
928 vport->nicIndex = nicParam->NicIndex;
929 vport->numaNodeId = nicParam->NumaNodeId;
931 switch (vport->nicState) {
932 case NdisSwitchNicStateCreated:
933 vport->ovsState = OVS_STATE_NIC_CREATED;
935 case NdisSwitchNicStateConnected:
936 vport->ovsState = OVS_STATE_CONNECTED;
938 case NdisSwitchNicStateDisconnected:
939 vport->ovsState = OVS_STATE_NIC_CREATED;
941 case NdisSwitchNicStateDeleted:
942 vport->ovsState = OVS_STATE_PORT_CREATED;
948 * --------------------------------------------------------------------------
949 * Populates 'portParam' based on 'vport'.
950 * --------------------------------------------------------------------------
953 OvsCopyPortParamsFromVport(POVS_VPORT_ENTRY vport,
954 PNDIS_SWITCH_PORT_PARAMETERS portParam)
956 portParam->Flags = 0;
957 portParam->PortId = vport->portId;
958 RtlCopyMemory(&portParam->PortName, &vport->hvPortName,
959 sizeof (NDIS_SWITCH_PORT_NAME));
960 RtlCopyMemory(&portParam->PortFriendlyName,
961 &vport->portFriendlyName,
962 sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
963 portParam->PortType = vport->portType;
964 portParam->IsValidationPort = FALSE;
965 portParam->PortState = vport->portState;
969 * --------------------------------------------------------------------------
970 * Initializes a tunnel vport.
971 * --------------------------------------------------------------------------
974 OvsInitTunnelVport(PVOID userContext,
975 POVS_VPORT_ENTRY vport,
976 OVS_VPORT_TYPE ovsType,
979 NTSTATUS status = STATUS_SUCCESS;
980 POVS_USER_PARAMS_CONTEXT usrParamsCtx =
981 (POVS_USER_PARAMS_CONTEXT)userContext;
983 vport->isBridgeInternal = FALSE;
984 vport->ovsType = ovsType;
985 vport->ovsState = OVS_STATE_PORT_CREATED;
987 case OVS_VPORT_TYPE_GRE:
989 case OVS_VPORT_TYPE_VXLAN:
991 POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
993 tunnelContext = OvsAllocateMemoryWithTag(sizeof(*tunnelContext),
995 if (tunnelContext == NULL) {
996 status = STATUS_INSUFFICIENT_RESOURCES;
999 tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
1000 tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
1001 tunnelContext->outputLength = usrParamsCtx->outputLength;
1002 tunnelContext->vport = vport;
1004 status = OvsInitVxlanTunnel(usrParamsCtx->irp,
1007 OvsTunnelVportPendingInit,
1008 (PVOID)tunnelContext);
1009 if (status != STATUS_PENDING) {
1010 OvsFreeMemoryWithTag(tunnelContext, OVS_VPORT_POOL_TAG);
1011 tunnelContext = NULL;
1015 case OVS_VPORT_TYPE_STT:
1016 status = OvsInitSttTunnel(vport, dstPort);
1025 * --------------------------------------------------------------------------
1026 * Initializes a bridge internal vport ie. a port of type
1027 * OVS_VPORT_TYPE_INTERNAL but not present on the Hyper-V switch.
1028 * --------------------------------------------------------------------------
1031 OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport)
1033 vport->isBridgeInternal = TRUE;
1034 vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
1035 /* Mark the status to be connected, since there is no other initialization
1037 vport->ovsState = OVS_STATE_CONNECTED;
1038 return STATUS_SUCCESS;
1042 * --------------------------------------------------------------------------
1043 * For external and internal vports 'portFriendlyName' parameter, provided by
1044 * Hyper-V, is overwritten with the interface alias name.
1045 * --------------------------------------------------------------------------
1048 GetNICAlias(GUID *netCfgInstanceId,
1049 IF_COUNTED_STRING *portFriendlyName)
1051 NTSTATUS status = STATUS_SUCCESS;
1052 WCHAR interfaceName[IF_MAX_STRING_SIZE] = { 0 };
1053 NET_LUID interfaceLuid = { 0 };
1056 status = ConvertInterfaceGuidToLuid(netCfgInstanceId,
1058 if (status == STATUS_SUCCESS) {
1060 * Must be called from PASSIVE_LEVEL. Resulted in a
1061 * STATUS_INVALID_DEVICE_REQUEST if not.
1063 status = ConvertInterfaceLuidToAlias(&interfaceLuid, interfaceName,
1064 IF_MAX_STRING_SIZE + 1);
1065 if (status == STATUS_SUCCESS) {
1066 RtlStringCbPrintfW(portFriendlyName->String,
1067 IF_MAX_STRING_SIZE, L"%s", interfaceName);
1068 RtlStringCbLengthW(portFriendlyName->String, IF_MAX_STRING_SIZE,
1070 portFriendlyName->Length = (USHORT)len;
1072 OVS_LOG_ERROR("Fail to convert interface LUID to alias, status: %x",
1076 OVS_LOG_ERROR("Fail to convert interface GUID to LUID, status: %x",
1085 * --------------------------------------------------------------------------
1086 * Functionality common to any port on the Hyper-V switch. This function is not
1087 * to be called for a port that is not on the Hyper-V switch.
1089 * Inserts the port into 'portIdHashArray' and caches the pointer in the
1090 * 'switchContext' if needed.
1091 * --------------------------------------------------------------------------
1094 UpdateSwitchCtxWithVport(POVS_SWITCH_CONTEXT switchContext,
1095 POVS_VPORT_ENTRY vport,
1100 switch (vport->portType) {
1101 case NdisSwitchPortTypeExternal:
1102 if (vport->nicIndex == 0) {
1103 switchContext->virtualExternalPortId = vport->portId;
1104 switchContext->virtualExternalVport = vport;
1106 switchContext->numPhysicalNics++;
1109 case NdisSwitchPortTypeInternal:
1110 ASSERT(vport->isBridgeInternal == FALSE);
1111 switchContext->internalPortId = vport->portId;
1112 switchContext->internalVport = vport;
1114 case NdisSwitchPortTypeSynthetic:
1115 case NdisSwitchPortTypeEmulated:
1120 * It is important to not insert vport corresponding to virtual external
1121 * port into the 'portIdHashArray' since the port should not be exposed to
1124 if (vport->portType == NdisSwitchPortTypeExternal &&
1125 vport->nicIndex == 0) {
1130 * NOTE: OvsJhashWords has portId as "1" word. This should be ok, even
1131 * though sizeof(NDIS_SWITCH_PORT_ID) = 4, not 2, because the
1132 * hyper-v switch seems to use only 2 bytes out of 4.
1134 hash = OvsJhashWords(&vport->portId, 1, OVS_HASH_BASIS);
1135 InsertHeadList(&switchContext->portIdHashArray[hash & OVS_VPORT_MASK],
1136 &vport->portIdLink);
1138 switchContext->numHvVports++;
1144 * --------------------------------------------------------------------------
1145 * Functionality common to any port added from OVS userspace.
1147 * Inserts the port into 'portNoHashArray', 'ovsPortNameHashArray' and in
1148 * 'tunnelVportsArray' if appropriate.
1149 * --------------------------------------------------------------------------
1152 InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
1153 POVS_VPORT_ENTRY vport)
1157 switch(vport->ovsType) {
1158 case OVS_VPORT_TYPE_VXLAN:
1159 case OVS_VPORT_TYPE_STT:
1161 UINT16 dstPort = GetPortFromPriv(vport);
1162 hash = OvsJhashBytes(&dstPort,
1166 &gOvsSwitchContext->tunnelVportsArray[hash & OVS_VPORT_MASK],
1167 &vport->tunnelVportLink);
1168 switchContext->numNonHvVports++;
1171 case OVS_VPORT_TYPE_INTERNAL:
1172 if (vport->isBridgeInternal) {
1173 switchContext->numNonHvVports++;
1180 * Insert the port into the hash array of ports: by port number and ovs
1181 * and ovs (datapath) port name.
1182 * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1183 * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1185 hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
1186 InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
1187 &vport->portNoLink);
1189 hash = OvsJhashBytes(vport->ovsName, strlen(vport->ovsName) + 1,
1192 &gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
1193 &vport->ovsNameLink);
1195 return STATUS_SUCCESS;
1200 * --------------------------------------------------------------------------
1201 * Provides functionality that is partly complementatry to
1202 * InitOvsVportCommon()/UpdateSwitchCtxWithVport().
1204 * 'hvDelete' indicates if caller is removing the vport as a result of the
1205 * port being removed on the Hyper-V switch.
1206 * 'ovsDelete' indicates if caller is removing the vport as a result of the
1207 * port being removed from OVS userspace.
1208 * --------------------------------------------------------------------------
1211 OvsRemoveAndDeleteVport(PVOID usrParamsContext,
1212 POVS_SWITCH_CONTEXT switchContext,
1213 POVS_VPORT_ENTRY vport,
1217 POVS_USER_PARAMS_CONTEXT usrParamsCtx =
1218 (POVS_USER_PARAMS_CONTEXT)usrParamsContext;
1219 BOOLEAN hvSwitchPort = FALSE;
1220 BOOLEAN deletedOnOvs = FALSE;
1221 BOOLEAN deletedOnHv = FALSE;
1223 switch (vport->ovsType) {
1224 case OVS_VPORT_TYPE_INTERNAL:
1225 if (!vport->isBridgeInternal) {
1226 if (hvDelete && vport->isAbsentOnHv == FALSE) {
1227 switchContext->internalPortId = 0;
1228 switchContext->internalVport = NULL;
1229 OvsInternalAdapterDown();
1231 hvSwitchPort = TRUE;
1234 case OVS_VPORT_TYPE_VXLAN:
1237 status = OvsRemoveTunnelVport(usrParamsCtx, switchContext, vport,
1238 hvDelete, ovsDelete);
1239 if (status != STATUS_SUCCESS) {
1243 case OVS_VPORT_TYPE_STT:
1244 OvsCleanupSttTunnel(vport);
1246 case OVS_VPORT_TYPE_GRE:
1248 case OVS_VPORT_TYPE_NETDEV:
1249 if (vport->isExternal) {
1250 if (vport->nicIndex == 0) {
1251 /* Such a vport is not part of any of the hash tables, since it
1252 * is not exposed to userspace. See Vport.h for explanation. */
1253 ASSERT(hvDelete == TRUE);
1254 ASSERT(switchContext->numPhysicalNics == 0);
1255 switchContext->virtualExternalPortId = 0;
1256 switchContext->virtualExternalVport = NULL;
1257 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1258 return STATUS_SUCCESS;
1261 hvSwitchPort = TRUE;
1267 * 'hvDelete' == TRUE indicates that the port should be removed from the
1268 * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
1269 * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
1271 * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
1273 if (vport->isAbsentOnHv == TRUE) {
1276 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1277 deletedOnOvs = TRUE;
1280 if (hvDelete && !deletedOnHv) {
1281 vport->isAbsentOnHv = TRUE;
1283 if (vport->isExternal) {
1284 ASSERT(vport->nicIndex != 0);
1285 ASSERT(switchContext->numPhysicalNics);
1286 switchContext->numPhysicalNics--;
1289 /* Remove the port from the relevant lists. */
1290 RemoveEntryList(&vport->portIdLink);
1291 InitializeListHead(&vport->portIdLink);
1294 if (ovsDelete && !deletedOnOvs) {
1295 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
1296 vport->ovsName[0] = '\0';
1298 /* Remove the port from the relevant lists. */
1299 RemoveEntryList(&vport->ovsNameLink);
1300 InitializeListHead(&vport->ovsNameLink);
1301 RemoveEntryList(&vport->portNoLink);
1302 InitializeListHead(&vport->portNoLink);
1303 if (OVS_VPORT_TYPE_VXLAN == vport->ovsType ||
1304 OVS_VPORT_TYPE_STT == vport->ovsType) {
1305 RemoveEntryList(&vport->tunnelVportLink);
1306 InitializeListHead(&vport->tunnelVportLink);
1309 deletedOnOvs = TRUE;
1313 * Deallocate the port if it has been deleted on the Hyper-V switch as well
1316 if (deletedOnHv && deletedOnOvs) {
1318 switchContext->numHvVports--;
1320 switchContext->numNonHvVports--;
1322 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1325 return STATUS_SUCCESS;
1329 OvsRemoveTunnelVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1330 POVS_SWITCH_CONTEXT switchContext,
1331 POVS_VPORT_ENTRY vport,
1335 POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
1338 tunnelContext = OvsAllocateMemoryWithTag(sizeof(*tunnelContext),
1339 OVS_VPORT_POOL_TAG);
1340 if (tunnelContext == NULL) {
1341 return STATUS_INSUFFICIENT_RESOURCES;
1343 RtlZeroMemory(tunnelContext, sizeof(*tunnelContext));
1345 tunnelContext->switchContext = switchContext;
1346 tunnelContext->hvSwitchPort = FALSE;
1347 tunnelContext->hvDelete = hvDelete;
1348 tunnelContext->ovsDelete = ovsDelete;
1349 tunnelContext->vport = vport;
1352 tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
1353 tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
1354 tunnelContext->outputLength = usrParamsCtx->outputLength;
1355 irp = usrParamsCtx->irp;
1358 return OvsCleanupVxlanTunnel(irp, vport, OvsTunnelVportPendingRemove,
1363 * --------------------------------------------------------------------------
1364 * Enumerates the ports on the Hyper-V switch.
1365 * --------------------------------------------------------------------------
1368 OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
1370 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1372 PNDIS_SWITCH_PORT_PARAMETERS portParam;
1373 PNDIS_SWITCH_PORT_ARRAY portArray = NULL;
1375 OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
1377 status = OvsGetPortsOnSwitch(switchContext, &portArray);
1378 if (status != NDIS_STATUS_SUCCESS) {
1382 for (arrIndex = 0; arrIndex < portArray->NumElements; arrIndex++) {
1383 portParam = NDIS_SWITCH_PORT_AT_ARRAY_INDEX(portArray, arrIndex);
1385 if (portParam->IsValidationPort) {
1389 status = HvCreatePort(switchContext, portParam, 0);
1390 if (status != STATUS_SUCCESS && status != STATUS_DATA_NOT_ACCEPTED) {
1396 if (status != NDIS_STATUS_SUCCESS) {
1397 OvsClearAllSwitchVports(switchContext);
1400 OvsFreeSwitchPortsArray(portArray);
1402 OVS_LOG_TRACE("Exit: status: %x", status);
1408 * --------------------------------------------------------------------------
1409 * Enumerates the NICs on the Hyper-V switch.
1410 * --------------------------------------------------------------------------
1413 OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext)
1415 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1416 PNDIS_SWITCH_NIC_ARRAY nicArray = NULL;
1418 PNDIS_SWITCH_NIC_PARAMETERS nicParam;
1420 OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
1422 * Now, get NIC list.
1424 status = OvsGetNicsOnSwitch(switchContext, &nicArray);
1425 if (status != NDIS_STATUS_SUCCESS) {
1428 for (arrIndex = 0; arrIndex < nicArray->NumElements; ++arrIndex) {
1429 nicParam = NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray, arrIndex);
1432 * XXX: Check if the port is configured with a VLAN. Disallow such a
1433 * configuration, since we don't support tag-in-tag.
1434 * XXX: Check if the port is connected to a VF. Disconnect the VF in
1438 status = HvCreateNic(switchContext, nicParam);
1439 if (status == NDIS_STATUS_SUCCESS) {
1440 HvConnectNic(switchContext, nicParam);
1445 OvsFreeSwitchNicsArray(nicArray);
1447 OVS_LOG_TRACE("Exit: status: %x", status);
1452 * --------------------------------------------------------------------------
1453 * Deletes ports added from the Hyper-V switch as well as OVS usersapce. The
1454 * function deletes ports in 'portIdHashArray'. This will delete most of the
1455 * ports that are in the 'portNoHashArray' as well. Any remaining ports
1456 * are deleted by walking the 'portNoHashArray'.
1457 * --------------------------------------------------------------------------
1460 OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
1462 for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1463 PLIST_ENTRY head, link, next;
1465 head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
1466 LIST_FORALL_SAFE(head, link, next) {
1467 POVS_VPORT_ENTRY vport;
1468 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1469 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
1474 * Remove 'virtualExternalVport' as well. This port is not part of the
1475 * 'portIdHashArray'.
1477 if (switchContext->virtualExternalVport) {
1478 OvsRemoveAndDeleteVport(NULL, switchContext,
1479 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE);
1483 for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1484 PLIST_ENTRY head, link, next;
1485 head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
1486 LIST_FORALL_SAFE(head, link, next) {
1487 POVS_VPORT_ENTRY vport;
1488 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1489 ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
1490 (vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
1491 vport->isBridgeInternal) || vport->isAbsentOnHv == TRUE);
1492 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
1496 ASSERT(switchContext->virtualExternalVport == NULL);
1497 ASSERT(switchContext->internalVport == NULL);
1502 OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
1507 UNICODE_STRING ustr;
1511 ustr.Buffer = wStr->String;
1512 ustr.Length = wStr->Length;
1513 ustr.MaximumLength = IF_MAX_STRING_SIZE;
1516 astr.MaximumLength = maxStrLen;
1519 size = RtlUnicodeStringToAnsiSize(&ustr);
1520 if (size > maxStrLen) {
1521 return STATUS_BUFFER_OVERFLOW;
1524 status = RtlUnicodeStringToAnsiString(&astr, &ustr, FALSE);
1526 ASSERT(status == STATUS_SUCCESS);
1527 if (status != STATUS_SUCCESS) {
1530 ASSERT(astr.Length <= maxStrLen);
1531 str[astr.Length] = 0;
1532 return STATUS_SUCCESS;
1536 * --------------------------------------------------------------------------
1537 * Utility function that populates a 'OVS_VPORT_EXT_INFO' structure for the
1539 * --------------------------------------------------------------------------
1542 OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
1543 POVS_VPORT_EXT_INFO extInfo)
1545 POVS_VPORT_ENTRY vport;
1547 LOCK_STATE_EX lockState;
1548 NTSTATUS status = STATUS_SUCCESS;
1549 BOOLEAN doConvert = FALSE;
1551 RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO));
1552 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1553 if (vportGet->portNo == 0) {
1554 StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
1555 vport = OvsFindVportByHvNameA(gOvsSwitchContext, vportGet->name);
1556 if (vport == NULL) {
1557 /* If the port is not a Hyper-V port and it has been added earlier,
1558 * we'll find it in 'ovsPortNameHashArray'. */
1559 vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name);
1562 vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo);
1564 if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED &&
1565 vport->ovsState != OVS_STATE_NIC_CREATED)) {
1566 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1567 if (vportGet->portNo) {
1568 OVS_LOG_WARN("vport %u does not exist any more", vportGet->portNo);
1570 OVS_LOG_WARN("vport %s does not exist any more", vportGet->name);
1572 status = STATUS_DEVICE_DOES_NOT_EXIST;
1575 extInfo->dpNo = vportGet->dpNo;
1576 extInfo->portNo = vport->portNo;
1577 RtlCopyMemory(extInfo->macAddress, vport->currMacAddress,
1578 sizeof (vport->currMacAddress));
1579 RtlCopyMemory(extInfo->permMACAddress, vport->permMacAddress,
1580 sizeof (vport->permMacAddress));
1581 if (vport->ovsType == OVS_VPORT_TYPE_NETDEV) {
1582 RtlCopyMemory(extInfo->vmMACAddress, vport->vmMacAddress,
1583 sizeof (vport->vmMacAddress));
1585 extInfo->nicIndex = vport->nicIndex;
1586 extInfo->portId = vport->portId;
1587 extInfo->type = vport->ovsType;
1588 extInfo->mtu = vport->mtu;
1592 if (vport->ovsState == OVS_STATE_NIC_CREATED) {
1593 extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_DOWN;
1594 } else if (vport->ovsState == OVS_STATE_CONNECTED) {
1595 extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
1597 extInfo->status = OVS_EVENT_DISCONNECT;
1599 if (extInfo->type == OVS_VPORT_TYPE_NETDEV &&
1600 (vport->ovsState == OVS_STATE_NIC_CREATED ||
1601 vport->ovsState == OVS_STATE_CONNECTED)) {
1604 extInfo->vmUUID[0] = 0;
1605 extInfo->vifUUID[0] = 0;
1607 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1609 status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName,
1611 OVS_MAX_PORT_NAME_LENGTH);
1612 if (status != STATUS_SUCCESS) {
1613 OVS_LOG_INFO("Fail to convert NIC name.");
1614 extInfo->vmUUID[0] = 0;
1617 status = OvsConvertIfCountedStrToAnsiStr(&vport->vmName,
1619 OVS_MAX_VM_UUID_LEN);
1620 if (status != STATUS_SUCCESS) {
1621 OVS_LOG_INFO("Fail to convert VM name.");
1622 extInfo->vmUUID[0] = 0;
1625 status = OvsConvertIfCountedStrToAnsiStr(&vport->nicName,
1627 OVS_MAX_VIF_UUID_LEN);
1628 if (status != STATUS_SUCCESS) {
1629 OVS_LOG_INFO("Fail to convert nic UUID");
1630 extInfo->vifUUID[0] = 0;
1633 * for now ignore status
1635 status = STATUS_SUCCESS;
1643 * --------------------------------------------------------------------------
1644 * Command Handler for 'OVS_WIN_NETDEV_CMD_GET'.
1645 * --------------------------------------------------------------------------
1648 OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1651 NTSTATUS status = STATUS_SUCCESS;
1652 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1653 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1654 NL_ERROR nlError = NL_ERROR_SUCCESS;
1655 OVS_VPORT_GET vportGet;
1656 OVS_VPORT_EXT_INFO info;
1658 static const NL_POLICY ovsNetdevPolicy[] = {
1659 [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING,
1661 .maxLen = IFNAMSIZ },
1663 PNL_ATTR netdevAttrs[ARRAY_SIZE(ovsNetdevPolicy)];
1665 /* input buffer has been validated while validating transaction dev op. */
1666 ASSERT(usrParamsCtx->inputBuffer != NULL &&
1667 usrParamsCtx->inputLength > sizeof *msgIn);
1669 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1670 return STATUS_INVALID_BUFFER_SIZE;
1673 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1674 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1675 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1676 ovsNetdevPolicy, ARRAY_SIZE(ovsNetdevPolicy),
1677 netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
1678 return STATUS_INVALID_PARAMETER;
1681 vportGet.portNo = 0;
1682 RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
1683 NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
1685 status = OvsGetExtInfoIoctl(&vportGet, &info);
1686 if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
1687 nlError = NL_ERROR_NODEV;
1691 status = CreateNetlinkMesgForNetdev(&info, msgIn,
1692 usrParamsCtx->outputBuffer, usrParamsCtx->outputLength,
1693 gOvsSwitchContext->dpNo);
1694 if (status == STATUS_SUCCESS) {
1695 *replyLen = msgOut->nlMsg.nlmsgLen;
1699 if (nlError != NL_ERROR_SUCCESS) {
1700 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1701 usrParamsCtx->outputBuffer;
1703 NlBuildErrorMsg(msgIn, msgError, nlError);
1704 *replyLen = msgError->nlMsg.nlmsgLen;
1707 return STATUS_SUCCESS;
1712 * --------------------------------------------------------------------------
1713 * Utility function to construct an OVS_MESSAGE for the specified vport. The
1714 * OVS_MESSAGE contains the output of a netdev command.
1715 * --------------------------------------------------------------------------
1718 CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
1727 UINT32 netdevFlags = 0;
1729 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1731 ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1732 msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1733 msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1736 return STATUS_INVALID_BUFFER_SIZE;
1739 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
1742 return STATUS_INVALID_BUFFER_SIZE;
1745 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
1747 return STATUS_INVALID_BUFFER_SIZE;
1750 ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
1753 return STATUS_INVALID_BUFFER_SIZE;
1756 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
1757 (PCHAR)info->macAddress, sizeof (info->macAddress));
1759 return STATUS_INVALID_BUFFER_SIZE;
1762 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
1764 return STATUS_INVALID_BUFFER_SIZE;
1767 if (info->status != OVS_EVENT_CONNECT) {
1768 netdevFlags = OVS_WIN_NETDEV_IFF_UP;
1770 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
1773 return STATUS_INVALID_BUFFER_SIZE;
1777 * XXX: add netdev_stats when we have the definition available in the
1781 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1782 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1784 return STATUS_SUCCESS;
1787 static __inline VOID
1788 OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec)
1790 while ((!switchContext->isActivated) &&
1791 (!switchContext->isActivateFailed)) {
1792 /* Wait for the switch to be active and
1793 * the list of ports in OVS to be initialized. */
1794 NdisMSleep(sleepMicroSec);
1799 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1806 OVS_VPORT_FULL_STATS vportStats;
1810 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1812 ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1813 msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1814 msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1817 return STATUS_INVALID_BUFFER_SIZE;
1820 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1822 return STATUS_INVALID_BUFFER_SIZE;
1825 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1827 return STATUS_INVALID_BUFFER_SIZE;
1830 ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1832 return STATUS_INVALID_BUFFER_SIZE;
1836 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1837 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1838 * it means we have an array of pids, instead of a single pid.
1839 * ATM we assume we have one pid only.
1842 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1845 return STATUS_INVALID_BUFFER_SIZE;
1849 vportStats.rxPackets = vport->stats.rxPackets;
1850 vportStats.rxBytes = vport->stats.rxBytes;
1851 vportStats.txPackets = vport->stats.txPackets;
1852 vportStats.txBytes = vport->stats.txBytes;
1853 vportStats.rxErrors = vport->errStats.rxErrors;
1854 vportStats.txErrors = vport->errStats.txErrors;
1855 vportStats.rxDropped = vport->errStats.rxDropped;
1856 vportStats.txDropped = vport->errStats.txDropped;
1858 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1860 sizeof(OVS_VPORT_FULL_STATS));
1862 return STATUS_INVALID_BUFFER_SIZE;
1866 * XXX: when vxlan udp dest port becomes configurable, we will also need
1867 * to add vport options
1870 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1871 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1873 return STATUS_SUCCESS;
1877 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1881 POVS_OPEN_INSTANCE instance =
1882 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1883 LOCK_STATE_EX lockState;
1884 UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1887 * XXX: this function shares some code with other dump command(s).
1888 * In the future, we will need to refactor the dump functions
1891 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1893 if (instance->dumpState.ovsMsg == NULL) {
1895 return STATUS_INVALID_DEVICE_STATE;
1898 /* Output buffer has been validated while validating read dev op. */
1899 ASSERT(usrParamsCtx->outputBuffer != NULL);
1901 msgIn = instance->dumpState.ovsMsg;
1904 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1905 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1906 * it means we have an array of pids, instead of a single pid.
1907 * ATM we assume we have one pid only.
1909 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1911 if (gOvsSwitchContext->numHvVports > 0 ||
1912 gOvsSwitchContext->numNonHvVports > 0) {
1913 /* inBucket: the bucket, used for lookup */
1914 UINT32 inBucket = instance->dumpState.index[0];
1915 /* inIndex: index within the given bucket, used for lookup */
1916 UINT32 inIndex = instance->dumpState.index[1];
1917 /* the bucket to be used for the next dump operation */
1918 UINT32 outBucket = 0;
1919 /* the index within the outBucket to be used for the next dump */
1920 UINT32 outIndex = 0;
1922 for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1923 PLIST_ENTRY head, link;
1924 head = &(gOvsSwitchContext->portNoHashArray[i]);
1925 POVS_VPORT_ENTRY vport = NULL;
1928 LIST_FORALL(head, link) {
1931 * if one or more dumps were previously done on this same bucket,
1932 * inIndex will be > 0, so we'll need to reply with the
1933 * inIndex + 1 vport from the bucket.
1935 if (outIndex >= inIndex) {
1936 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1938 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
1939 OvsCreateMsgFromVport(vport, msgIn,
1940 usrParamsCtx->outputBuffer,
1941 usrParamsCtx->outputLength,
1942 gOvsSwitchContext->dpNo);
1955 * if no vport was found above, check the next bucket, beginning
1956 * with the first (i.e. index 0) elem from within that bucket
1963 /* XXX: what about NLMSG_DONE (as msg type)? */
1964 instance->dumpState.index[0] = outBucket;
1965 instance->dumpState.index[1] = outIndex;
1968 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1970 /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1971 if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1972 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1973 *replyLen = msgOut->nlMsg.nlmsgLen;
1976 * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1980 /* Free up the dump state, since there's no more data to continue. */
1981 FreeUserDumpState(instance);
1984 return STATUS_SUCCESS;
1988 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1991 NTSTATUS status = STATUS_SUCCESS;
1992 LOCK_STATE_EX lockState;
1994 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1995 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1996 POVS_VPORT_ENTRY vport = NULL;
1997 NL_ERROR nlError = NL_ERROR_SUCCESS;
1998 PCHAR portName = NULL;
1999 UINT32 portNameLen = 0;
2000 UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
2002 static const NL_POLICY ovsVportPolicy[] = {
2003 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2004 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
2009 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2011 /* input buffer has been validated while validating write dev op. */
2012 ASSERT(usrParamsCtx->inputBuffer != NULL);
2014 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2015 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2016 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2017 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2018 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2019 return STATUS_INVALID_PARAMETER;
2022 /* Output buffer has been validated while validating transact dev op. */
2023 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2025 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
2026 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2027 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2028 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2030 /* the port name is expected to be null-terminated */
2031 ASSERT(portName[portNameLen - 1] == '\0');
2033 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2034 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2035 portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2037 vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
2039 nlError = NL_ERROR_INVAL;
2040 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2045 nlError = NL_ERROR_NODEV;
2046 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2050 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2051 usrParamsCtx->outputLength,
2052 gOvsSwitchContext->dpNo);
2053 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2055 *replyLen = msgOut->nlMsg.nlmsgLen;
2058 if (nlError != NL_ERROR_SUCCESS) {
2059 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2060 usrParamsCtx->outputBuffer;
2062 NlBuildErrorMsg(msgIn, msgError, nlError);
2063 *replyLen = msgError->nlMsg.nlmsgLen;
2066 return STATUS_SUCCESS;
2070 * --------------------------------------------------------------------------
2071 * Command Handler for 'OVS_VPORT_CMD_GET'.
2073 * The function handles the initial call to setup the dump state, as well as
2074 * subsequent calls to continue dumping data.
2075 * --------------------------------------------------------------------------
2078 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2083 switch (usrParamsCtx->devOp) {
2084 case OVS_WRITE_DEV_OP:
2085 return OvsSetupDumpStart(usrParamsCtx);
2087 case OVS_READ_DEV_OP:
2088 return OvsGetVportDumpNext(usrParamsCtx, replyLen);
2090 case OVS_TRANSACTION_DEV_OP:
2091 return OvsGetVport(usrParamsCtx, replyLen);
2094 return STATUS_INVALID_DEVICE_REQUEST;
2100 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
2102 /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
2103 for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
2104 POVS_VPORT_ENTRY vport;
2106 vport = OvsFindVportByPortNo(switchContext, i);
2112 return OVS_DPPORT_NUMBER_INVALID;
2116 * --------------------------------------------------------------------------
2117 * Command Handler for 'OVS_VPORT_CMD_NEW'.
2118 * --------------------------------------------------------------------------
2121 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2124 NDIS_STATUS status = STATUS_SUCCESS;
2125 LOCK_STATE_EX lockState;
2127 NL_ERROR nlError = NL_ERROR_SUCCESS;
2128 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2129 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2130 POVS_VPORT_ENTRY vport = NULL;
2134 BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE;
2136 static const NL_POLICY ovsVportPolicy[] = {
2137 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2138 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2139 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2141 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2142 .optional = FALSE },
2143 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2146 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2148 /* input buffer has been validated while validating write dev op. */
2149 ASSERT(usrParamsCtx->inputBuffer != NULL);
2151 /* Output buffer has been validated while validating transact dev op. */
2152 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2154 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2155 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2156 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2157 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2158 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2159 return STATUS_INVALID_PARAMETER;
2162 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2163 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2164 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2166 /* we are expecting null terminated strings to be passed */
2167 ASSERT(portName[portNameLen - 1] == '\0');
2169 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2171 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2173 nlError = NL_ERROR_EXIST;
2177 if (portType == OVS_VPORT_TYPE_NETDEV) {
2178 /* External ports can also be looked up like VIF ports. */
2179 vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
2181 ASSERT(OvsIsTunnelVportType(portType) ||
2182 portType == OVS_VPORT_TYPE_INTERNAL);
2184 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
2185 if (vport == NULL) {
2186 nlError = NL_ERROR_NOMEM;
2189 vportAllocated = TRUE;
2191 if (OvsIsTunnelVportType(portType)) {
2192 UINT16 transportPortDest = 0;
2195 case OVS_VPORT_TYPE_VXLAN:
2196 transportPortDest = VXLAN_UDP_PORT;
2198 case OVS_VPORT_TYPE_STT:
2199 transportPortDest = STT_TCP_PORT;
2202 nlError = NL_ERROR_INVAL;
2206 if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2207 PNL_ATTR attr = NlAttrFindNested(vportAttrs[OVS_VPORT_ATTR_OPTIONS],
2208 OVS_TUNNEL_ATTR_DST_PORT);
2210 transportPortDest = NlAttrGetU16(attr);
2214 status = OvsInitTunnelVport(usrParamsCtx,
2219 nlError = NlMapStatusToNlErr(status);
2221 OvsInitBridgeInternalVport(vport);
2224 vportInitialized = TRUE;
2226 if (nlError == NL_ERROR_SUCCESS) {
2227 vport->ovsState = OVS_STATE_CONNECTED;
2228 vport->nicState = NdisSwitchNicStateConnected;
2231 * Allow the vport to be deleted, because there is no
2232 * corresponding hyper-v switch part.
2234 vport->isAbsentOnHv = TRUE;
2241 nlError = NL_ERROR_INVAL;
2244 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2245 nlError = NL_ERROR_EXIST;
2249 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2251 * XXX: when we implement the limit for ovs port number to be
2252 * MAXUINT16, we'll need to check the port number received from the
2255 vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2257 vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
2258 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2259 nlError = NL_ERROR_NOMEM;
2264 /* The ovs port name must be uninitialized. */
2265 ASSERT(vport->ovsName[0] == '\0');
2266 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2268 RtlCopyMemory(vport->ovsName, portName, portNameLen);
2269 /* if we don't have options, then vport->portOptions will be NULL */
2270 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2273 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2274 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2275 * it means we have an array of pids, instead of a single pid.
2276 * ATM we assume we have one pid only.
2278 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2280 status = InitOvsVportCommon(gOvsSwitchContext, vport);
2281 ASSERT(status == STATUS_SUCCESS);
2283 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2284 usrParamsCtx->outputLength,
2285 gOvsSwitchContext->dpNo);
2287 *replyLen = msgOut->nlMsg.nlmsgLen;
2290 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2292 if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2293 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2294 usrParamsCtx->outputBuffer;
2296 if (vport && vportAllocated == TRUE) {
2297 if (vportInitialized == TRUE) {
2298 if (OvsIsTunnelVportType(portType)) {
2299 switch (vport->ovsType) {
2300 case OVS_VPORT_TYPE_VXLAN:
2301 OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2303 case OVS_VPORT_TYPE_STT:
2304 OvsCleanupSttTunnel(vport);
2307 ASSERT(!"Invalid tunnel port type");
2311 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2314 NlBuildErrorMsg(msgIn, msgError, nlError);
2315 *replyLen = msgError->nlMsg.nlmsgLen;
2318 return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2323 * --------------------------------------------------------------------------
2324 * Command Handler for 'OVS_VPORT_CMD_SET'.
2325 * --------------------------------------------------------------------------
2328 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2331 NDIS_STATUS status = STATUS_SUCCESS;
2332 LOCK_STATE_EX lockState;
2334 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2335 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2336 POVS_VPORT_ENTRY vport = NULL;
2337 NL_ERROR nlError = NL_ERROR_SUCCESS;
2339 static const NL_POLICY ovsVportPolicy[] = {
2340 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2341 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
2342 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2344 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2346 [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
2347 .minLen = sizeof(OVS_VPORT_FULL_STATS),
2348 .maxLen = sizeof(OVS_VPORT_FULL_STATS),
2350 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2352 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2354 ASSERT(usrParamsCtx->inputBuffer != NULL);
2356 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2357 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2358 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2359 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2360 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2361 return STATUS_INVALID_PARAMETER;
2364 /* Output buffer has been validated while validating transact dev op. */
2365 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2367 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2368 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2369 PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2371 UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2373 /* the port name is expected to be null-terminated */
2374 ASSERT(portName[portNameLen - 1] == '\0');
2376 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2377 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2378 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2379 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2383 nlError = NL_ERROR_NODEV;
2388 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2389 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2390 * it means we have an array of pids, instead of a single pid.
2391 * Currently, we support only one pid.
2393 if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
2394 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2397 if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
2398 OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2399 if (type != vport->ovsType) {
2400 nlError = NL_ERROR_INVAL;
2405 if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2406 OVS_LOG_ERROR("Vport options not supported");
2407 nlError = NL_ERROR_NOTSUPP;
2411 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2412 usrParamsCtx->outputLength,
2413 gOvsSwitchContext->dpNo);
2415 *replyLen = msgOut->nlMsg.nlmsgLen;
2418 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2420 if (nlError != NL_ERROR_SUCCESS) {
2421 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2422 usrParamsCtx->outputBuffer;
2424 NlBuildErrorMsg(msgIn, msgError, nlError);
2425 *replyLen = msgError->nlMsg.nlmsgLen;
2428 return STATUS_SUCCESS;
2432 * --------------------------------------------------------------------------
2433 * Command Handler for 'OVS_VPORT_CMD_DEL'.
2434 * --------------------------------------------------------------------------
2437 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2440 NDIS_STATUS status = STATUS_SUCCESS;
2441 LOCK_STATE_EX lockState;
2443 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2444 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2445 POVS_VPORT_ENTRY vport = NULL;
2446 NL_ERROR nlError = NL_ERROR_SUCCESS;
2447 PSTR portName = NULL;
2448 UINT32 portNameLen = 0;
2450 static const NL_POLICY ovsVportPolicy[] = {
2451 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2452 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2455 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2457 ASSERT(usrParamsCtx->inputBuffer != NULL);
2459 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2460 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2461 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2462 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2463 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2464 return STATUS_INVALID_PARAMETER;
2467 /* Output buffer has been validated while validating transact dev op. */
2468 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2470 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2471 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2472 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2473 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2475 /* the port name is expected to be null-terminated */
2476 ASSERT(portName[portNameLen - 1] == '\0');
2478 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2480 else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2481 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2482 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2486 nlError = NL_ERROR_NODEV;
2490 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2491 usrParamsCtx->outputLength,
2492 gOvsSwitchContext->dpNo);
2494 *replyLen = msgOut->nlMsg.nlmsgLen;
2497 * Mark the port as deleted from OVS userspace. If the port does not exist
2498 * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
2500 status = OvsRemoveAndDeleteVport(usrParamsCtx,
2506 nlError = NlMapStatusToNlErr(status);
2510 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2512 if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2513 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2514 usrParamsCtx->outputBuffer;
2516 NlBuildErrorMsg(msgIn, msgError, nlError);
2517 *replyLen = msgError->nlMsg.nlmsgLen;
2520 return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2524 OvsTunnelVportPendingRemove(PVOID context,
2528 POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2529 (POVS_TUNFLT_INIT_CONTEXT) context;
2530 POVS_SWITCH_CONTEXT switchContext = tunnelContext->switchContext;
2531 POVS_VPORT_ENTRY vport = tunnelContext->vport;
2532 POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2533 POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2534 NL_ERROR nlError = NlMapStatusToNlErr(status);
2535 LOCK_STATE_EX lockState;
2537 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
2539 if (msgIn && msgOut) {
2540 /* Check the received status to reply to the caller. */
2541 if (STATUS_SUCCESS == status) {
2542 OvsCreateMsgFromVport(vport,
2545 tunnelContext->outputLength,
2546 switchContext->dpNo);
2548 *replyLen = msgOut->nlMsg.nlmsgLen;
2550 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)msgOut;
2552 NlBuildErrorMsg(msgIn, msgError, nlError);
2553 *replyLen = msgError->nlMsg.nlmsgLen;
2557 ASSERT(vport->isAbsentOnHv == TRUE);
2558 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
2560 /* Remove the port from the relevant lists. */
2561 switchContext->numNonHvVports--;
2562 RemoveEntryList(&vport->ovsNameLink);
2563 RemoveEntryList(&vport->portNoLink);
2564 RemoveEntryList(&vport->tunnelVportLink);
2567 OvsFreeMemoryWithTag(vport->priv, OVS_VXLAN_POOL_TAG);
2571 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2573 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
2577 OvsTunnelVportPendingInit(PVOID context,
2581 POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2582 (POVS_TUNFLT_INIT_CONTEXT) context;
2583 POVS_VPORT_ENTRY vport = tunnelContext->vport;
2584 POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2585 POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2587 ULONG portNameLen = 0;
2588 UINT32 portType = 0;
2589 NL_ERROR nlError = NL_ERROR_SUCCESS;
2590 BOOLEAN error = TRUE;
2593 if (!NT_SUCCESS(status)) {
2594 nlError = NlMapStatusToNlErr(status);
2598 static const NL_POLICY ovsVportPolicy[] = {
2599 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2600 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2601 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2602 .optional = FALSE },
2603 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2604 .optional = FALSE },
2605 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2608 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2610 /* input buffer has been validated while validating write dev op. */
2611 ASSERT(msgIn != NULL);
2613 /* Output buffer has been validated while validating transact dev op. */
2614 ASSERT(msgOut != NULL && tunnelContext->outputLength >= sizeof *msgOut);
2616 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2617 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2618 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2619 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2620 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2621 nlError = NL_ERROR_INVAL;
2625 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2626 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2627 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2629 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2630 nlError = NL_ERROR_EXIST;
2634 vport->ovsState = OVS_STATE_CONNECTED;
2635 vport->nicState = NdisSwitchNicStateConnected;
2638 * Allow the vport to be deleted, because there is no
2639 * corresponding hyper-v switch part.
2641 vport->isAbsentOnHv = TRUE;
2643 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2645 * XXX: when we implement the limit for OVS port number to be
2646 * MAXUINT16, we'll need to check the port number received from the
2650 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2653 OvsComputeVportNo(gOvsSwitchContext);
2654 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2655 nlError = NL_ERROR_NOMEM;
2660 /* The ovs port name must be uninitialized. */
2661 ASSERT(vport->ovsName[0] == '\0');
2662 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2664 RtlCopyMemory(vport->ovsName, portName, portNameLen);
2665 /* if we don't have options, then vport->portOptions will be NULL */
2666 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2669 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2670 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2671 * it means we have an array of pids, instead of a single pid.
2672 * ATM we assume we have one pid only.
2675 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2677 status = InitOvsVportCommon(gOvsSwitchContext, vport);
2678 ASSERT(status == STATUS_SUCCESS);
2680 OvsCreateMsgFromVport(vport,
2683 tunnelContext->outputLength,
2684 gOvsSwitchContext->dpNo);
2686 *replyLen = msgOut->nlMsg.nlmsgLen;
2692 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) msgOut;
2694 OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2695 OvsFreeMemory(vport);
2697 NlBuildErrorMsg(msgIn, msgError, nlError);
2698 *replyLen = msgError->nlMsg.nlmsgLen;