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 OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVPort,
72 POVS_VPORT_ENTRY virtExtVport, UINT32 nicIndex);
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 NDIS_STATUS InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
85 POVS_VPORT_ENTRY vport,
87 static NTSTATUS OvsRemoveTunnelVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
88 POVS_SWITCH_CONTEXT switchContext,
89 POVS_VPORT_ENTRY vport,
92 static VOID OvsTunnelVportPendingInit(PVOID context,
95 static VOID OvsTunnelVportPendingRemove(PVOID context,
98 static VOID AssignNicNameSpecial(POVS_VPORT_ENTRY vport);
101 * Functions implemented in relaton to NDIS port manipulation.
104 HvCreatePort(POVS_SWITCH_CONTEXT switchContext,
105 PNDIS_SWITCH_PORT_PARAMETERS portParam)
107 POVS_VPORT_ENTRY vport;
108 LOCK_STATE_EX lockState;
109 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
110 BOOLEAN newPort = FALSE;
112 VPORT_PORT_ENTER(portParam);
114 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
115 /* Lookup by port ID. */
116 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
117 portParam->PortId, 0);
119 OVS_LOG_ERROR("Port add failed due to duplicate port name, "
120 "port Id: %u", portParam->PortId);
121 status = STATUS_DATA_NOT_ACCEPTED;
122 goto create_port_done;
126 * Lookup by port name to see if this port with this name had been added
127 * (and deleted) previously.
129 vport = OvsFindVportByHvNameW(gOvsSwitchContext,
130 portParam->PortFriendlyName.String,
131 portParam->PortFriendlyName.Length);
132 if (vport && vport->isAbsentOnHv == FALSE) {
133 OVS_LOG_ERROR("Port add failed since a port already exists on "
134 "the specified port Id: %u, ovsName: %s",
135 portParam->PortId, vport->ovsName);
136 status = STATUS_DATA_NOT_ACCEPTED;
137 goto create_port_done;
141 ASSERT(vport->isAbsentOnHv);
142 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
145 * It should be possible to simply just mark this port as "not deleted"
146 * given that the port Id and the name are the same and also provided
147 * that the other properties that we cache have not changed.
149 if (vport->portType != portParam->PortType) {
150 OVS_LOG_INFO("Port add failed due to PortType change, port Id: %u"
151 " old: %u, new: %u", portParam->PortId,
152 vport->portType, portParam->PortType);
153 status = STATUS_DATA_NOT_ACCEPTED;
154 goto create_port_done;
156 vport->isAbsentOnHv = FALSE;
158 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
160 status = NDIS_STATUS_RESOURCES;
161 goto create_port_done;
165 OvsInitVportWithPortParam(vport, portParam);
166 InitHvVportCommon(switchContext, vport, newPort);
169 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
170 VPORT_PORT_EXIT(portParam);
176 * Function updating the port properties
179 HvUpdatePort(POVS_SWITCH_CONTEXT switchContext,
180 PNDIS_SWITCH_PORT_PARAMETERS portParam)
182 POVS_VPORT_ENTRY vport;
183 LOCK_STATE_EX lockState;
184 OVS_VPORT_STATE ovsState;
185 NDIS_SWITCH_NIC_STATE nicState;
187 VPORT_PORT_ENTER(portParam);
189 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
190 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
191 portParam->PortId, 0);
193 * Update properties only for NETDEV ports for supprting PS script
196 goto update_port_done;
199 /* Store the nic and the OVS states as Nic Create won't be called */
200 ovsState = vport->ovsState;
201 nicState = vport->nicState;
204 * Currently only the port friendly name is being updated
205 * Make sure that no other properties are changed
207 ASSERT(portParam->PortId == vport->portId);
208 ASSERT(portParam->PortState == vport->portState);
209 ASSERT(portParam->PortType == vport->portType);
212 * Call the set parameters function the handle all properties
213 * change in a single place in case future version supports change of
216 OvsInitVportWithPortParam(vport, portParam);
217 /* Retore the nic and OVS states */
218 vport->nicState = nicState;
219 vport->ovsState = ovsState;
222 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
223 VPORT_PORT_EXIT(portParam);
225 /* Must always return success */
226 return NDIS_STATUS_SUCCESS;
230 HvTeardownPort(POVS_SWITCH_CONTEXT switchContext,
231 PNDIS_SWITCH_PORT_PARAMETERS portParam)
233 POVS_VPORT_ENTRY vport;
234 LOCK_STATE_EX lockState;
236 VPORT_PORT_ENTER(portParam);
238 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
239 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
240 portParam->PortId, 0);
242 /* add assertion here */
243 vport->portState = NdisSwitchPortStateTeardown;
244 vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
246 OVS_LOG_WARN("Vport not present.");
248 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
250 VPORT_PORT_EXIT(portParam);
255 HvDeletePort(POVS_SWITCH_CONTEXT switchContext,
256 PNDIS_SWITCH_PORT_PARAMETERS portParams)
258 POVS_VPORT_ENTRY vport;
259 LOCK_STATE_EX lockState;
261 VPORT_PORT_ENTER(portParams);
263 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
264 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
265 portParams->PortId, 0);
268 * XXX: we can only destroy and remove the port if its datapath port
269 * counterpart was deleted. If the datapath port counterpart is present,
270 * we only mark the vport for deletion, so that a netlink command vport
271 * delete will delete the vport.
274 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
276 OVS_LOG_WARN("Vport not present.");
278 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
280 VPORT_PORT_EXIT(portParams);
285 * Functions implemented in relaton to NDIS NIC manipulation.
288 HvCreateNic(POVS_SWITCH_CONTEXT switchContext,
289 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
291 POVS_VPORT_ENTRY vport;
294 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
296 LOCK_STATE_EX lockState;
298 VPORT_NIC_ENTER(nicParam);
300 /* Wait for lists to be initialized. */
301 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
303 if (!switchContext->isActivated) {
304 OVS_LOG_WARN("Switch is not activated yet.");
305 /* Veto the creation of nic */
306 status = NDIS_STATUS_NOT_SUPPORTED;
310 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
311 vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId, 0);
313 OVS_LOG_ERROR("Create NIC without Switch Port,"
314 " PortId: %x, NicIndex: %d",
315 nicParam->PortId, nicParam->NicIndex);
316 status = NDIS_STATUS_INVALID_PARAMETER;
320 if (nicParam->NicType == NdisSwitchNicTypeExternal &&
321 nicParam->NicIndex != 0) {
322 POVS_VPORT_ENTRY virtExtVport =
323 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
325 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
327 status = NDIS_STATUS_RESOURCES;
330 OvsInitPhysNicVport(vport, virtExtVport, nicParam->NicIndex);
331 status = InitHvVportCommon(switchContext, vport, TRUE);
332 if (status != NDIS_STATUS_SUCCESS) {
333 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
337 OvsInitVportWithNicParam(switchContext, vport, nicParam);
338 portNo = vport->portNo;
339 if (vport->ovsState == OVS_STATE_CONNECTED) {
340 event = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
341 } else if (vport->ovsState == OVS_STATE_NIC_CREATED) {
342 event = OVS_EVENT_CONNECT;
346 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
347 if (portNo != OVS_DPPORT_NUMBER_INVALID && event) {
348 OvsPostEvent(portNo, event);
352 VPORT_NIC_EXIT(nicParam);
353 OVS_LOG_TRACE("Exit: status %8x.\n", status);
359 /* Mark already created NIC as connected. */
361 HvConnectNic(POVS_SWITCH_CONTEXT switchContext,
362 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
364 LOCK_STATE_EX lockState;
365 POVS_VPORT_ENTRY vport;
368 VPORT_NIC_ENTER(nicParam);
370 /* Wait for lists to be initialized. */
371 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
373 if (!switchContext->isActivated) {
374 OVS_LOG_WARN("Switch is not activated yet.");
378 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
379 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
384 OVS_LOG_WARN("Vport not present.");
385 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
390 vport->ovsState = OVS_STATE_CONNECTED;
391 vport->nicState = NdisSwitchNicStateConnected;
392 portNo = vport->portNo;
394 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
396 /* XXX only if portNo != INVALID or always? */
397 OvsPostEvent(portNo, OVS_EVENT_LINK_UP);
399 if (nicParam->NicType == NdisSwitchNicTypeInternal) {
400 OvsInternalAdapterUp(portNo, &nicParam->NetCfgInstanceId);
404 VPORT_NIC_EXIT(nicParam);
408 HvUpdateNic(POVS_SWITCH_CONTEXT switchContext,
409 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
411 POVS_VPORT_ENTRY vport;
412 LOCK_STATE_EX lockState;
414 UINT32 status = 0, portNo = 0;
416 VPORT_NIC_ENTER(nicParam);
418 /* Wait for lists to be initialized. */
419 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
421 if (!switchContext->isActivated) {
422 OVS_LOG_WARN("Switch is not activated yet.");
423 goto update_nic_done;
426 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
427 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
431 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
432 OVS_LOG_WARN("Vport search failed.");
433 goto update_nic_done;
435 switch (nicParam->NicType) {
436 case NdisSwitchNicTypeExternal:
437 case NdisSwitchNicTypeInternal:
438 RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
440 AssignNicNameSpecial(vport);
442 case NdisSwitchNicTypeSynthetic:
443 case NdisSwitchNicTypeEmulated:
444 if (!RtlEqualMemory(vport->vmMacAddress, nicParam->VMMacAddress,
445 sizeof (vport->vmMacAddress))) {
446 status |= OVS_EVENT_MAC_CHANGE;
447 RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
448 sizeof (vport->vmMacAddress));
454 if (!RtlEqualMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
455 sizeof (vport->permMacAddress))) {
456 RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
457 sizeof (vport->permMacAddress));
458 status |= OVS_EVENT_MAC_CHANGE;
460 if (!RtlEqualMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
461 sizeof (vport->currMacAddress))) {
462 RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
463 sizeof (vport->currMacAddress));
464 status |= OVS_EVENT_MAC_CHANGE;
467 if (vport->mtu != nicParam->MTU) {
468 vport->mtu = nicParam->MTU;
469 status |= OVS_EVENT_MTU_CHANGE;
471 vport->numaNodeId = nicParam->NumaNodeId;
472 portNo = vport->portNo;
474 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
475 if (status && portNo) {
476 OvsPostEvent(portNo, status);
479 VPORT_NIC_EXIT(nicParam);
484 HvDisconnectNic(POVS_SWITCH_CONTEXT switchContext,
485 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
487 POVS_VPORT_ENTRY vport;
489 LOCK_STATE_EX lockState;
490 BOOLEAN isInternalPort = FALSE;
492 VPORT_NIC_ENTER(nicParam);
494 /* Wait for lists to be initialized. */
495 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
497 if (!switchContext->isActivated) {
498 OVS_LOG_WARN("Switch is not activated yet.");
502 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
503 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
508 OVS_LOG_WARN("Vport not present.");
509 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
513 vport->nicState = NdisSwitchNicStateDisconnected;
514 vport->ovsState = OVS_STATE_NIC_CREATED;
515 portNo = vport->portNo;
517 if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
518 isInternalPort = TRUE;
521 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
523 /* XXX if portNo != INVALID or always? */
524 OvsPostEvent(portNo, OVS_EVENT_LINK_DOWN);
526 if (isInternalPort) {
527 OvsInternalAdapterDown();
531 VPORT_NIC_EXIT(nicParam);
536 HvDeleteNic(POVS_SWITCH_CONTEXT switchContext,
537 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
539 LOCK_STATE_EX lockState;
540 POVS_VPORT_ENTRY vport;
543 VPORT_NIC_ENTER(nicParam);
544 /* Wait for lists to be initialized. */
545 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
547 if (!switchContext->isActivated) {
548 OVS_LOG_WARN("Switch is not activated yet.");
552 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
553 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
558 OVS_LOG_WARN("Vport not present.");
559 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
563 vport->nicState = NdisSwitchNicStateUnknown;
564 vport->ovsState = OVS_STATE_PORT_CREATED;
566 portNo = vport->portNo;
567 if (vport->portType == NdisSwitchPortTypeExternal &&
568 vport->nicIndex != 0) {
569 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
572 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
573 /* XXX if portNo != INVALID or always? */
574 OvsPostEvent(portNo, OVS_EVENT_DISCONNECT);
577 VPORT_NIC_EXIT(nicParam);
582 * OVS Vport related functionality.
585 OvsFindVportByPortNo(POVS_SWITCH_CONTEXT switchContext,
588 POVS_VPORT_ENTRY vport;
589 PLIST_ENTRY head, link;
590 UINT32 hash = OvsJhashBytes((const VOID *)&portNo, sizeof(portNo),
592 head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
593 LIST_FORALL(head, link) {
594 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
595 if (vport->portNo == portNo) {
604 OvsFindTunnelVportByDstPort(POVS_SWITCH_CONTEXT switchContext,
606 OVS_VPORT_TYPE ovsPortType)
608 POVS_VPORT_ENTRY vport;
609 PLIST_ENTRY head, link;
610 UINT32 hash = OvsJhashBytes((const VOID *)&dstPort, sizeof(dstPort),
612 head = &(switchContext->tunnelVportsArray[hash & OVS_VPORT_MASK]);
613 LIST_FORALL(head, link) {
614 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, tunnelVportLink);
615 if (GetPortFromPriv(vport) == dstPort &&
616 vport->ovsType == ovsPortType) {
625 OvsFindVportByOvsName(POVS_SWITCH_CONTEXT switchContext,
628 POVS_VPORT_ENTRY vport;
629 PLIST_ENTRY head, link;
631 SIZE_T length = strlen(name) + 1;
633 hash = OvsJhashBytes((const VOID *)name, length, OVS_HASH_BASIS);
634 head = &(switchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK]);
636 LIST_FORALL(head, link) {
637 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, ovsNameLink);
638 if (!strcmp(name, vport->ovsName)) {
646 /* OvsFindVportByHvName: "name" is assumed to be null-terminated */
648 OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
649 PWSTR wsName, SIZE_T wstrSize)
651 POVS_VPORT_ENTRY vport = NULL;
652 PLIST_ENTRY head, link;
655 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
656 head = &(switchContext->portIdHashArray[i]);
657 LIST_FORALL(head, link) {
658 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
661 * NOTE about portFriendlyName:
662 * If the string is NULL-terminated, the Length member does not
663 * include the terminating NULL character.
665 if (vport->portFriendlyName.Length == wstrSize &&
666 RtlEqualMemory(wsName, vport->portFriendlyName.String,
667 vport->portFriendlyName.Length)) {
676 * Look in the list of ports that were added from the Hyper-V switch and
680 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
681 head = &(switchContext->portNoHashArray[i]);
682 LIST_FORALL(head, link) {
683 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
684 if (vport->portFriendlyName.Length == wstrSize &&
685 RtlEqualMemory(wsName, vport->portFriendlyName.String,
686 vport->portFriendlyName.Length)) {
700 OvsFindVportByHvNameA(POVS_SWITCH_CONTEXT switchContext,
703 POVS_VPORT_ENTRY vport = NULL;
704 /* 'portFriendlyName' is not NUL-terminated. */
705 SIZE_T length = strlen(name);
706 SIZE_T wstrSize = length * sizeof(WCHAR);
709 PWSTR wsName = OvsAllocateMemoryWithTag(wstrSize, OVS_VPORT_POOL_TAG);
713 for (i = 0; i < length; i++) {
716 vport = OvsFindVportByHvNameW(switchContext, wsName, wstrSize);
717 OvsFreeMemoryWithTag(wsName, OVS_VPORT_POOL_TAG);
722 OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT switchContext,
723 NDIS_SWITCH_PORT_ID portId,
724 NDIS_SWITCH_NIC_INDEX index)
726 if (switchContext->virtualExternalVport &&
727 portId == switchContext->virtualExternalPortId &&
728 index == switchContext->virtualExternalVport->nicIndex) {
729 return (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
730 } else if (switchContext->internalVport &&
731 portId == switchContext->internalPortId &&
732 index == switchContext->internalVport->nicIndex) {
733 return (POVS_VPORT_ENTRY)switchContext->internalVport;
735 PLIST_ENTRY head, link;
736 POVS_VPORT_ENTRY vport;
738 hash = OvsJhashWords((UINT32 *)&portId, 1, OVS_HASH_BASIS);
739 head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
740 LIST_FORALL(head, link) {
741 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
742 if (portId == vport->portId && index == vport->nicIndex) {
751 OvsAllocateVport(VOID)
753 POVS_VPORT_ENTRY vport;
754 vport = (POVS_VPORT_ENTRY)OvsAllocateMemoryWithTag(
755 sizeof(OVS_VPORT_ENTRY), OVS_VPORT_POOL_TAG);
759 RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY));
760 vport->ovsState = OVS_STATE_UNKNOWN;
761 vport->isAbsentOnHv = FALSE;
762 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
764 InitializeListHead(&vport->ovsNameLink);
765 InitializeListHead(&vport->portIdLink);
766 InitializeListHead(&vport->portNoLink);
772 OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
773 PNDIS_SWITCH_PORT_PARAMETERS portParam)
775 vport->portType = portParam->PortType;
776 vport->portState = portParam->PortState;
777 vport->portId = portParam->PortId;
778 vport->nicState = NdisSwitchNicStateUnknown;
779 vport->isExternal = FALSE;
780 vport->isBridgeInternal = FALSE;
782 switch (vport->portType) {
783 case NdisSwitchPortTypeExternal:
784 vport->isExternal = TRUE;
785 vport->ovsType = OVS_VPORT_TYPE_NETDEV;
787 case NdisSwitchPortTypeInternal:
788 vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
790 case NdisSwitchPortTypeSynthetic:
791 case NdisSwitchPortTypeEmulated:
792 vport->ovsType = OVS_VPORT_TYPE_NETDEV;
795 RtlCopyMemory(&vport->hvPortName, &portParam->PortName,
796 sizeof (NDIS_SWITCH_PORT_NAME));
797 /* For external and internal ports, 'portFriendlyName' is overwritten
799 RtlCopyMemory(&vport->portFriendlyName, &portParam->PortFriendlyName,
800 sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
802 switch (vport->portState) {
803 case NdisSwitchPortStateCreated:
804 vport->ovsState = OVS_STATE_PORT_CREATED;
806 case NdisSwitchPortStateTeardown:
807 vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
809 case NdisSwitchPortStateDeleted:
810 vport->ovsState = OVS_STATE_PORT_DELETED;
817 OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
818 POVS_VPORT_ENTRY vport,
819 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
821 ASSERT(vport->portId == nicParam->PortId);
822 ASSERT(vport->ovsState == OVS_STATE_PORT_CREATED);
824 UNREFERENCED_PARAMETER(switchContext);
826 RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
827 sizeof (nicParam->PermanentMacAddress));
828 RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
829 sizeof (nicParam->CurrentMacAddress));
831 if (nicParam->NicType == NdisSwitchNicTypeSynthetic ||
832 nicParam->NicType == NdisSwitchNicTypeEmulated) {
833 RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
834 sizeof (nicParam->VMMacAddress));
835 RtlCopyMemory(&vport->vmName, &nicParam->VmName,
836 sizeof (nicParam->VmName));
838 RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
839 sizeof (nicParam->NetCfgInstanceId));
841 RtlCopyMemory(&vport->nicName, &nicParam->NicName,
842 sizeof (nicParam->NicName));
843 vport->mtu = nicParam->MTU;
844 vport->nicState = nicParam->NicState;
845 vport->nicIndex = nicParam->NicIndex;
846 vport->numaNodeId = nicParam->NumaNodeId;
848 switch (vport->nicState) {
849 case NdisSwitchNicStateCreated:
850 vport->ovsState = OVS_STATE_NIC_CREATED;
852 case NdisSwitchNicStateConnected:
853 vport->ovsState = OVS_STATE_CONNECTED;
855 case NdisSwitchNicStateDisconnected:
856 vport->ovsState = OVS_STATE_NIC_CREATED;
858 case NdisSwitchNicStateDeleted:
859 vport->ovsState = OVS_STATE_PORT_CREATED;
865 * --------------------------------------------------------------------------
866 * Copies the relevant NDIS port properties from a virtual (pseudo) external
867 * NIC to a physical (real) external NIC.
868 * --------------------------------------------------------------------------
871 OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVport,
872 POVS_VPORT_ENTRY virtExtVport,
875 physExtVport->portType = virtExtVport->portType;
876 physExtVport->portState = virtExtVport->portState;
877 physExtVport->portId = virtExtVport->portId;
878 physExtVport->nicState = NdisSwitchNicStateUnknown;
879 physExtVport->ovsType = OVS_VPORT_TYPE_NETDEV;
880 physExtVport->isExternal = TRUE;
881 physExtVport->isBridgeInternal = FALSE;
882 physExtVport->nicIndex = (NDIS_SWITCH_NIC_INDEX)physNicIndex;
884 RtlCopyMemory(&physExtVport->hvPortName, &virtExtVport->hvPortName,
885 sizeof (NDIS_SWITCH_PORT_NAME));
887 /* 'portFriendlyName' is overwritten later. */
888 RtlCopyMemory(&physExtVport->portFriendlyName,
889 &virtExtVport->portFriendlyName,
890 sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
892 physExtVport->ovsState = OVS_STATE_PORT_CREATED;
896 * --------------------------------------------------------------------------
897 * Initializes a tunnel vport.
898 * --------------------------------------------------------------------------
901 OvsInitTunnelVport(PVOID userContext,
902 POVS_VPORT_ENTRY vport,
903 OVS_VPORT_TYPE ovsType,
906 NTSTATUS status = STATUS_SUCCESS;
907 POVS_USER_PARAMS_CONTEXT usrParamsCtx =
908 (POVS_USER_PARAMS_CONTEXT)userContext;
910 vport->isBridgeInternal = FALSE;
911 vport->ovsType = ovsType;
912 vport->ovsState = OVS_STATE_PORT_CREATED;
914 case OVS_VPORT_TYPE_GRE:
916 case OVS_VPORT_TYPE_VXLAN:
918 POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
920 tunnelContext = OvsAllocateMemoryWithTag(sizeof(*tunnelContext),
922 if (tunnelContext == NULL) {
923 status = STATUS_INSUFFICIENT_RESOURCES;
926 tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
927 tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
928 tunnelContext->outputLength = usrParamsCtx->outputLength;
929 tunnelContext->vport = vport;
931 status = OvsInitVxlanTunnel(usrParamsCtx->irp,
934 OvsTunnelVportPendingInit,
935 (PVOID)tunnelContext);
936 if (status != STATUS_PENDING) {
937 OvsFreeMemoryWithTag(tunnelContext, OVS_VPORT_POOL_TAG);
938 tunnelContext = NULL;
942 case OVS_VPORT_TYPE_STT:
943 status = OvsInitSttTunnel(vport, dstPort);
952 * --------------------------------------------------------------------------
953 * Initializes a bridge internal vport ie. a port of type
954 * OVS_VPORT_TYPE_INTERNAL but not present on the Hyper-V switch.
955 * --------------------------------------------------------------------------
958 OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport)
960 vport->isBridgeInternal = TRUE;
961 vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
962 /* Mark the status to be connected, since there is no other initialization
964 vport->ovsState = OVS_STATE_CONNECTED;
965 return STATUS_SUCCESS;
969 * --------------------------------------------------------------------------
970 * For external and internal vports 'portFriendlyName' parameter, provided by
971 * Hyper-V, is overwritten with the interface alias name.
972 * --------------------------------------------------------------------------
975 AssignNicNameSpecial(POVS_VPORT_ENTRY vport)
977 NTSTATUS status = STATUS_SUCCESS;
978 WCHAR interfaceName[IF_MAX_STRING_SIZE] = { 0 };
979 NET_LUID interfaceLuid = { 0 };
982 ASSERT(vport->portType == NdisSwitchPortTypeExternal ||
983 vport->portType == NdisSwitchPortTypeInternal);
985 status = ConvertInterfaceGuidToLuid(&vport->netCfgInstanceId,
987 if (status == STATUS_SUCCESS) {
988 status = ConvertInterfaceLuidToAlias(&interfaceLuid, interfaceName,
989 IF_MAX_STRING_SIZE + 1);
990 if (status == STATUS_SUCCESS) {
991 if (vport->portType == NdisSwitchPortTypeExternal &&
992 vport->nicIndex == 0) {
993 RtlStringCbPrintfW(vport->portFriendlyName.String, IF_MAX_STRING_SIZE,
994 L"%s.virtualAdapter", interfaceName);
996 RtlStringCbPrintfW(vport->portFriendlyName.String,
997 IF_MAX_STRING_SIZE, L"%s", interfaceName);
1000 RtlStringCbLengthW(vport->portFriendlyName.String, IF_MAX_STRING_SIZE,
1002 vport->portFriendlyName.Length = (USHORT)len;
1004 OVS_LOG_INFO("Fail to convert interface LUID to alias, status: %x",
1008 OVS_LOG_INFO("Fail to convert interface GUID to LUID, status: %x",
1015 * --------------------------------------------------------------------------
1016 * Functionality common to any port on the Hyper-V switch. This function is not
1017 * to be called for a port that is not on the Hyper-V switch.
1019 * Inserts the port into 'portIdHashArray' and caches the pointer in the
1020 * 'switchContext' if needed.
1022 * For external NIC, assigns the name for the NIC.
1023 * --------------------------------------------------------------------------
1026 InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
1027 POVS_VPORT_ENTRY vport,
1032 switch (vport->portType) {
1033 case NdisSwitchPortTypeExternal:
1035 * Overwrite the 'portFriendlyName' of this external vport. The reason
1036 * for having this in common code is to be able to call it from the NDIS
1037 * Port callback as well as the NDIS NIC callback.
1039 AssignNicNameSpecial(vport);
1041 if (vport->nicIndex == 0) {
1042 switchContext->virtualExternalPortId = vport->portId;
1043 switchContext->virtualExternalVport = vport;
1045 switchContext->numPhysicalNics++;
1048 case NdisSwitchPortTypeInternal:
1049 ASSERT(vport->isBridgeInternal == FALSE);
1051 /* Overwrite the 'portFriendlyName' of the internal vport. */
1052 AssignNicNameSpecial(vport);
1053 switchContext->internalPortId = vport->portId;
1054 switchContext->internalVport = vport;
1056 case NdisSwitchPortTypeSynthetic:
1057 case NdisSwitchPortTypeEmulated:
1062 * It is important to not insert vport corresponding to virtual external
1063 * port into the 'portIdHashArray' since the port should not be exposed to
1066 if (vport->portType == NdisSwitchPortTypeExternal &&
1067 vport->nicIndex == 0) {
1068 return NDIS_STATUS_SUCCESS;
1072 * NOTE: OvsJhashWords has portId as "1" word. This should be ok, even
1073 * though sizeof(NDIS_SWITCH_PORT_ID) = 4, not 2, because the
1074 * hyper-v switch seems to use only 2 bytes out of 4.
1076 hash = OvsJhashWords(&vport->portId, 1, OVS_HASH_BASIS);
1077 InsertHeadList(&switchContext->portIdHashArray[hash & OVS_VPORT_MASK],
1078 &vport->portIdLink);
1080 switchContext->numHvVports++;
1082 return NDIS_STATUS_SUCCESS;
1086 * --------------------------------------------------------------------------
1087 * Functionality common to any port added from OVS userspace.
1089 * Inserts the port into 'portNoHashArray', 'ovsPortNameHashArray' and in
1090 * 'tunnelVportsArray' if appropriate.
1091 * --------------------------------------------------------------------------
1094 InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
1095 POVS_VPORT_ENTRY vport)
1099 switch(vport->ovsType) {
1100 case OVS_VPORT_TYPE_VXLAN:
1101 case OVS_VPORT_TYPE_STT:
1103 UINT16 dstPort = GetPortFromPriv(vport);
1104 hash = OvsJhashBytes(&dstPort,
1108 &gOvsSwitchContext->tunnelVportsArray[hash & OVS_VPORT_MASK],
1109 &vport->tunnelVportLink);
1110 switchContext->numNonHvVports++;
1113 case OVS_VPORT_TYPE_INTERNAL:
1114 if (vport->isBridgeInternal) {
1115 switchContext->numNonHvVports++;
1122 * Insert the port into the hash array of ports: by port number and ovs
1123 * and ovs (datapath) port name.
1124 * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1125 * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1127 hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
1128 InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
1129 &vport->portNoLink);
1131 hash = OvsJhashBytes(vport->ovsName, strlen(vport->ovsName) + 1,
1134 &gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
1135 &vport->ovsNameLink);
1137 return STATUS_SUCCESS;
1142 * --------------------------------------------------------------------------
1143 * Provides functionality that is partly complementatry to
1144 * InitOvsVportCommon()/InitHvVportCommon().
1146 * 'hvDelete' indicates if caller is removing the vport as a result of the
1147 * port being removed on the Hyper-V switch.
1148 * 'ovsDelete' indicates if caller is removing the vport as a result of the
1149 * port being removed from OVS userspace.
1150 * --------------------------------------------------------------------------
1153 OvsRemoveAndDeleteVport(PVOID usrParamsContext,
1154 POVS_SWITCH_CONTEXT switchContext,
1155 POVS_VPORT_ENTRY vport,
1159 POVS_USER_PARAMS_CONTEXT usrParamsCtx =
1160 (POVS_USER_PARAMS_CONTEXT)usrParamsContext;
1161 BOOLEAN hvSwitchPort = FALSE;
1162 BOOLEAN deletedOnOvs = FALSE;
1163 BOOLEAN deletedOnHv = FALSE;
1165 switch (vport->ovsType) {
1166 case OVS_VPORT_TYPE_INTERNAL:
1167 if (!vport->isBridgeInternal) {
1168 if (hvDelete && vport->isAbsentOnHv == FALSE) {
1169 switchContext->internalPortId = 0;
1170 switchContext->internalVport = NULL;
1171 OvsInternalAdapterDown();
1173 hvSwitchPort = TRUE;
1176 case OVS_VPORT_TYPE_VXLAN:
1179 status = OvsRemoveTunnelVport(usrParamsCtx, switchContext, vport,
1180 hvDelete, ovsDelete);
1181 if (status != STATUS_SUCCESS) {
1185 case OVS_VPORT_TYPE_STT:
1186 OvsCleanupSttTunnel(vport);
1188 case OVS_VPORT_TYPE_GRE:
1190 case OVS_VPORT_TYPE_NETDEV:
1191 if (vport->isExternal) {
1192 if (vport->nicIndex == 0) {
1193 /* Such a vport is not part of any of the hash tables, since it
1194 * is not exposed to userspace. See Vport.h for explanation. */
1195 ASSERT(hvDelete == TRUE);
1196 ASSERT(switchContext->numPhysicalNics == 0);
1197 switchContext->virtualExternalPortId = 0;
1198 switchContext->virtualExternalVport = NULL;
1199 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1200 return STATUS_SUCCESS;
1203 hvSwitchPort = TRUE;
1209 * 'hvDelete' == TRUE indicates that the port should be removed from the
1210 * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
1211 * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
1213 * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
1215 if (vport->isAbsentOnHv == TRUE) {
1218 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1219 deletedOnOvs = TRUE;
1222 if (hvDelete && !deletedOnHv) {
1223 vport->isAbsentOnHv = TRUE;
1225 if (vport->isExternal) {
1226 ASSERT(vport->nicIndex != 0);
1227 ASSERT(switchContext->numPhysicalNics);
1228 switchContext->numPhysicalNics--;
1231 /* Remove the port from the relevant lists. */
1232 RemoveEntryList(&vport->portIdLink);
1233 InitializeListHead(&vport->portIdLink);
1236 if (ovsDelete && !deletedOnOvs) {
1237 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
1238 vport->ovsName[0] = '\0';
1240 /* Remove the port from the relevant lists. */
1241 RemoveEntryList(&vport->ovsNameLink);
1242 InitializeListHead(&vport->ovsNameLink);
1243 RemoveEntryList(&vport->portNoLink);
1244 InitializeListHead(&vport->portNoLink);
1245 if (OVS_VPORT_TYPE_VXLAN == vport->ovsType ||
1246 OVS_VPORT_TYPE_STT == vport->ovsType) {
1247 RemoveEntryList(&vport->tunnelVportLink);
1248 InitializeListHead(&vport->tunnelVportLink);
1251 deletedOnOvs = TRUE;
1255 * Deallocate the port if it has been deleted on the Hyper-V switch as well
1258 if (deletedOnHv && deletedOnOvs) {
1260 switchContext->numHvVports--;
1262 switchContext->numNonHvVports--;
1264 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1267 return STATUS_SUCCESS;
1271 OvsRemoveTunnelVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1272 POVS_SWITCH_CONTEXT switchContext,
1273 POVS_VPORT_ENTRY vport,
1277 POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
1280 tunnelContext = OvsAllocateMemoryWithTag(sizeof(*tunnelContext),
1281 OVS_VPORT_POOL_TAG);
1282 if (tunnelContext == NULL) {
1283 return STATUS_INSUFFICIENT_RESOURCES;
1285 RtlZeroMemory(tunnelContext, sizeof(*tunnelContext));
1287 tunnelContext->switchContext = switchContext;
1288 tunnelContext->hvSwitchPort = FALSE;
1289 tunnelContext->hvDelete = hvDelete;
1290 tunnelContext->ovsDelete = ovsDelete;
1291 tunnelContext->vport = vport;
1294 tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
1295 tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
1296 tunnelContext->outputLength = usrParamsCtx->outputLength;
1297 irp = usrParamsCtx->irp;
1300 return OvsCleanupVxlanTunnel(irp, vport, OvsTunnelVportPendingRemove,
1307 OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
1309 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1311 PNDIS_SWITCH_PORT_PARAMETERS portParam;
1312 PNDIS_SWITCH_PORT_ARRAY portArray = NULL;
1313 POVS_VPORT_ENTRY vport;
1315 OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
1317 status = OvsGetPortsOnSwitch(switchContext, &portArray);
1318 if (status != NDIS_STATUS_SUCCESS) {
1322 for (arrIndex = 0; arrIndex < portArray->NumElements; arrIndex++) {
1323 portParam = NDIS_SWITCH_PORT_AT_ARRAY_INDEX(portArray, arrIndex);
1325 if (portParam->IsValidationPort) {
1329 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
1330 if (vport == NULL) {
1331 status = NDIS_STATUS_RESOURCES;
1334 OvsInitVportWithPortParam(vport, portParam);
1335 status = InitHvVportCommon(switchContext, vport, TRUE);
1336 if (status != NDIS_STATUS_SUCCESS) {
1337 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1343 if (status != NDIS_STATUS_SUCCESS) {
1344 OvsClearAllSwitchVports(switchContext);
1347 OvsFreeSwitchPortsArray(portArray);
1349 OVS_LOG_TRACE("Exit: status: %x", status);
1356 OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext)
1358 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1359 PNDIS_SWITCH_NIC_ARRAY nicArray = NULL;
1361 PNDIS_SWITCH_NIC_PARAMETERS nicParam;
1362 POVS_VPORT_ENTRY vport;
1364 OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
1366 * Now, get NIC list.
1368 status = OvsGetNicsOnSwitch(switchContext, &nicArray);
1369 if (status != NDIS_STATUS_SUCCESS) {
1372 for (arrIndex = 0; arrIndex < nicArray->NumElements; ++arrIndex) {
1374 nicParam = NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray, arrIndex);
1377 * XXX: Check if the port is configured with a VLAN. Disallow such a
1378 * configuration, since we don't support tag-in-tag.
1382 * XXX: Check if the port is connected to a VF. Disconnect the VF in
1386 if (nicParam->NicType == NdisSwitchNicTypeExternal &&
1387 nicParam->NicIndex != 0) {
1388 POVS_VPORT_ENTRY virtExtVport =
1389 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
1391 vport = OvsAllocateVport();
1393 OvsInitPhysNicVport(vport, virtExtVport,
1394 nicParam->NicIndex);
1395 OvsInitVportWithNicParam(switchContext, vport, nicParam);
1396 status = InitHvVportCommon(switchContext, vport, TRUE);
1397 if (status != NDIS_STATUS_SUCCESS) {
1398 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1402 OVS_LOG_ERROR("Fail to allocate vport.");
1406 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
1408 nicParam->NicIndex);
1409 if (vport == NULL) {
1411 "Could not found vport with portId: %d and nicIndex: %d.",
1412 nicParam->PortId, nicParam->NicIndex);
1415 OvsInitVportWithNicParam(switchContext, vport, nicParam);
1418 if (nicParam->NicType == NdisSwitchNicTypeInternal) {
1420 * Overwrite the 'portFriendlyName' of the internal vport.
1422 * The call to AssignNicNameSpecial() is needed here, because the
1423 * necessary 'netCfgInstanceId' of the vport is available.
1424 * On port creation the latter information is missing and the
1425 * 'portFriendlyName' of the vport fails to be overwritten with the
1426 * correct information.
1428 AssignNicNameSpecial(vport);
1429 OvsInternalAdapterUp(vport->portNo, &nicParam->NetCfgInstanceId);
1434 OvsFreeSwitchNicsArray(nicArray);
1436 OVS_LOG_TRACE("Exit: status: %x", status);
1441 * --------------------------------------------------------------------------
1442 * Deletes ports added from the Hyper-V switch as well as OVS usersapce. The
1443 * function deletes ports in 'portIdHashArray'. This will delete most of the
1444 * ports that are in the 'portNoHashArray' as well. Any remaining ports
1445 * are deleted by walking the 'portNoHashArray'.
1446 * --------------------------------------------------------------------------
1449 OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
1451 for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1452 PLIST_ENTRY head, link, next;
1454 head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
1455 LIST_FORALL_SAFE(head, link, next) {
1456 POVS_VPORT_ENTRY vport;
1457 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1458 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
1463 * Remove 'virtualExternalVport' as well. This port is not part of the
1464 * 'portIdHashArray'.
1466 if (switchContext->virtualExternalVport) {
1467 OvsRemoveAndDeleteVport(NULL, switchContext,
1468 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE);
1472 for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1473 PLIST_ENTRY head, link, next;
1474 head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
1475 LIST_FORALL_SAFE(head, link, next) {
1476 POVS_VPORT_ENTRY vport;
1477 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1478 ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
1479 (vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
1480 vport->isBridgeInternal) || vport->isAbsentOnHv == TRUE);
1481 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
1485 ASSERT(switchContext->virtualExternalVport == NULL);
1486 ASSERT(switchContext->internalVport == NULL);
1491 OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
1496 UNICODE_STRING ustr;
1500 ustr.Buffer = wStr->String;
1501 ustr.Length = wStr->Length;
1502 ustr.MaximumLength = IF_MAX_STRING_SIZE;
1505 astr.MaximumLength = maxStrLen;
1508 size = RtlUnicodeStringToAnsiSize(&ustr);
1509 if (size > maxStrLen) {
1510 return STATUS_BUFFER_OVERFLOW;
1513 status = RtlUnicodeStringToAnsiString(&astr, &ustr, FALSE);
1515 ASSERT(status == STATUS_SUCCESS);
1516 if (status != STATUS_SUCCESS) {
1519 ASSERT(astr.Length <= maxStrLen);
1520 str[astr.Length] = 0;
1521 return STATUS_SUCCESS;
1525 * --------------------------------------------------------------------------
1526 * Utility function that populates a 'OVS_VPORT_EXT_INFO' structure for the
1528 * --------------------------------------------------------------------------
1531 OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
1532 POVS_VPORT_EXT_INFO extInfo)
1534 POVS_VPORT_ENTRY vport;
1536 LOCK_STATE_EX lockState;
1537 NTSTATUS status = STATUS_SUCCESS;
1538 BOOLEAN doConvert = FALSE;
1540 RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO));
1541 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1542 if (vportGet->portNo == 0) {
1543 StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
1544 vport = OvsFindVportByHvNameA(gOvsSwitchContext, vportGet->name);
1545 if (vport != NULL) {
1546 /* If the port is not a Hyper-V port and it has been added earlier,
1547 * we'll find it in 'ovsPortNameHashArray'. */
1548 vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name);
1551 vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo);
1553 if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED &&
1554 vport->ovsState != OVS_STATE_NIC_CREATED)) {
1555 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1556 if (vportGet->portNo) {
1557 OVS_LOG_WARN("vport %u does not exist any more", vportGet->portNo);
1559 OVS_LOG_WARN("vport %s does not exist any more", vportGet->name);
1561 status = STATUS_DEVICE_DOES_NOT_EXIST;
1564 extInfo->dpNo = vportGet->dpNo;
1565 extInfo->portNo = vport->portNo;
1566 RtlCopyMemory(extInfo->macAddress, vport->currMacAddress,
1567 sizeof (vport->currMacAddress));
1568 RtlCopyMemory(extInfo->permMACAddress, vport->permMacAddress,
1569 sizeof (vport->permMacAddress));
1570 if (vport->ovsType == OVS_VPORT_TYPE_NETDEV) {
1571 RtlCopyMemory(extInfo->vmMACAddress, vport->vmMacAddress,
1572 sizeof (vport->vmMacAddress));
1574 extInfo->nicIndex = vport->nicIndex;
1575 extInfo->portId = vport->portId;
1576 extInfo->type = vport->ovsType;
1577 extInfo->mtu = vport->mtu;
1581 if (vport->ovsState == OVS_STATE_NIC_CREATED) {
1582 extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_DOWN;
1583 } else if (vport->ovsState == OVS_STATE_CONNECTED) {
1584 extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
1586 extInfo->status = OVS_EVENT_DISCONNECT;
1588 if (extInfo->type == OVS_VPORT_TYPE_NETDEV &&
1589 (vport->ovsState == OVS_STATE_NIC_CREATED ||
1590 vport->ovsState == OVS_STATE_CONNECTED)) {
1593 extInfo->vmUUID[0] = 0;
1594 extInfo->vifUUID[0] = 0;
1596 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1598 status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName,
1600 OVS_MAX_PORT_NAME_LENGTH);
1601 if (status != STATUS_SUCCESS) {
1602 OVS_LOG_INFO("Fail to convert NIC name.");
1603 extInfo->vmUUID[0] = 0;
1606 status = OvsConvertIfCountedStrToAnsiStr(&vport->vmName,
1608 OVS_MAX_VM_UUID_LEN);
1609 if (status != STATUS_SUCCESS) {
1610 OVS_LOG_INFO("Fail to convert VM name.");
1611 extInfo->vmUUID[0] = 0;
1614 status = OvsConvertIfCountedStrToAnsiStr(&vport->nicName,
1616 OVS_MAX_VIF_UUID_LEN);
1617 if (status != STATUS_SUCCESS) {
1618 OVS_LOG_INFO("Fail to convert nic UUID");
1619 extInfo->vifUUID[0] = 0;
1622 * for now ignore status
1624 status = STATUS_SUCCESS;
1632 * --------------------------------------------------------------------------
1633 * Command Handler for 'OVS_WIN_NETDEV_CMD_GET'.
1634 * --------------------------------------------------------------------------
1637 OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1640 NTSTATUS status = STATUS_SUCCESS;
1641 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1642 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1643 NL_ERROR nlError = NL_ERROR_SUCCESS;
1644 OVS_VPORT_GET vportGet;
1645 OVS_VPORT_EXT_INFO info;
1647 static const NL_POLICY ovsNetdevPolicy[] = {
1648 [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING,
1650 .maxLen = IFNAMSIZ },
1652 PNL_ATTR netdevAttrs[ARRAY_SIZE(ovsNetdevPolicy)];
1654 /* input buffer has been validated while validating transaction dev op. */
1655 ASSERT(usrParamsCtx->inputBuffer != NULL &&
1656 usrParamsCtx->inputLength > sizeof *msgIn);
1658 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1659 return STATUS_INVALID_BUFFER_SIZE;
1662 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1663 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1664 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1665 ovsNetdevPolicy, ARRAY_SIZE(ovsNetdevPolicy),
1666 netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
1667 return STATUS_INVALID_PARAMETER;
1670 vportGet.portNo = 0;
1671 RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
1672 NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
1674 status = OvsGetExtInfoIoctl(&vportGet, &info);
1675 if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
1676 nlError = NL_ERROR_NODEV;
1680 status = CreateNetlinkMesgForNetdev(&info, msgIn,
1681 usrParamsCtx->outputBuffer, usrParamsCtx->outputLength,
1682 gOvsSwitchContext->dpNo);
1683 if (status == STATUS_SUCCESS) {
1684 *replyLen = msgOut->nlMsg.nlmsgLen;
1688 if (nlError != NL_ERROR_SUCCESS) {
1689 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1690 usrParamsCtx->outputBuffer;
1692 NlBuildErrorMsg(msgIn, msgError, nlError);
1693 *replyLen = msgError->nlMsg.nlmsgLen;
1696 return STATUS_SUCCESS;
1701 * --------------------------------------------------------------------------
1702 * Utility function to construct an OVS_MESSAGE for the specified vport. The
1703 * OVS_MESSAGE contains the output of a netdev command.
1704 * --------------------------------------------------------------------------
1707 CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
1716 UINT32 netdevFlags = 0;
1718 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1720 ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1721 msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1722 msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1725 return STATUS_INVALID_BUFFER_SIZE;
1728 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
1731 return STATUS_INVALID_BUFFER_SIZE;
1734 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
1736 return STATUS_INVALID_BUFFER_SIZE;
1739 ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
1742 return STATUS_INVALID_BUFFER_SIZE;
1745 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
1746 (PCHAR)info->macAddress, sizeof (info->macAddress));
1748 return STATUS_INVALID_BUFFER_SIZE;
1751 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
1753 return STATUS_INVALID_BUFFER_SIZE;
1756 if (info->status != OVS_EVENT_CONNECT) {
1757 netdevFlags = OVS_WIN_NETDEV_IFF_UP;
1759 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
1762 return STATUS_INVALID_BUFFER_SIZE;
1766 * XXX: add netdev_stats when we have the definition available in the
1770 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1771 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1773 return STATUS_SUCCESS;
1776 static __inline VOID
1777 OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec)
1779 while ((!switchContext->isActivated) &&
1780 (!switchContext->isActivateFailed)) {
1781 /* Wait for the switch to be active and
1782 * the list of ports in OVS to be initialized. */
1783 NdisMSleep(sleepMicroSec);
1788 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1795 OVS_VPORT_FULL_STATS vportStats;
1799 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1801 ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1802 msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1803 msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1806 return STATUS_INVALID_BUFFER_SIZE;
1809 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1811 return STATUS_INVALID_BUFFER_SIZE;
1814 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1816 return STATUS_INVALID_BUFFER_SIZE;
1819 ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1821 return STATUS_INVALID_BUFFER_SIZE;
1825 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1826 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1827 * it means we have an array of pids, instead of a single pid.
1828 * ATM we assume we have one pid only.
1831 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1834 return STATUS_INVALID_BUFFER_SIZE;
1838 vportStats.rxPackets = vport->stats.rxPackets;
1839 vportStats.rxBytes = vport->stats.rxBytes;
1840 vportStats.txPackets = vport->stats.txPackets;
1841 vportStats.txBytes = vport->stats.txBytes;
1842 vportStats.rxErrors = vport->errStats.rxErrors;
1843 vportStats.txErrors = vport->errStats.txErrors;
1844 vportStats.rxDropped = vport->errStats.rxDropped;
1845 vportStats.txDropped = vport->errStats.txDropped;
1847 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1849 sizeof(OVS_VPORT_FULL_STATS));
1851 return STATUS_INVALID_BUFFER_SIZE;
1855 * XXX: when vxlan udp dest port becomes configurable, we will also need
1856 * to add vport options
1859 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1860 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1862 return STATUS_SUCCESS;
1866 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1870 POVS_OPEN_INSTANCE instance =
1871 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1872 LOCK_STATE_EX lockState;
1873 UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1876 * XXX: this function shares some code with other dump command(s).
1877 * In the future, we will need to refactor the dump functions
1880 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1882 if (instance->dumpState.ovsMsg == NULL) {
1884 return STATUS_INVALID_DEVICE_STATE;
1887 /* Output buffer has been validated while validating read dev op. */
1888 ASSERT(usrParamsCtx->outputBuffer != NULL);
1890 msgIn = instance->dumpState.ovsMsg;
1893 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1894 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1895 * it means we have an array of pids, instead of a single pid.
1896 * ATM we assume we have one pid only.
1898 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1900 if (gOvsSwitchContext->numHvVports > 0 ||
1901 gOvsSwitchContext->numNonHvVports > 0) {
1902 /* inBucket: the bucket, used for lookup */
1903 UINT32 inBucket = instance->dumpState.index[0];
1904 /* inIndex: index within the given bucket, used for lookup */
1905 UINT32 inIndex = instance->dumpState.index[1];
1906 /* the bucket to be used for the next dump operation */
1907 UINT32 outBucket = 0;
1908 /* the index within the outBucket to be used for the next dump */
1909 UINT32 outIndex = 0;
1911 for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1912 PLIST_ENTRY head, link;
1913 head = &(gOvsSwitchContext->portNoHashArray[i]);
1914 POVS_VPORT_ENTRY vport = NULL;
1917 LIST_FORALL(head, link) {
1920 * if one or more dumps were previously done on this same bucket,
1921 * inIndex will be > 0, so we'll need to reply with the
1922 * inIndex + 1 vport from the bucket.
1924 if (outIndex >= inIndex) {
1925 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1927 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
1928 OvsCreateMsgFromVport(vport, msgIn,
1929 usrParamsCtx->outputBuffer,
1930 usrParamsCtx->outputLength,
1931 gOvsSwitchContext->dpNo);
1944 * if no vport was found above, check the next bucket, beginning
1945 * with the first (i.e. index 0) elem from within that bucket
1952 /* XXX: what about NLMSG_DONE (as msg type)? */
1953 instance->dumpState.index[0] = outBucket;
1954 instance->dumpState.index[1] = outIndex;
1957 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1959 /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1960 if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1961 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1962 *replyLen = msgOut->nlMsg.nlmsgLen;
1965 * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1969 /* Free up the dump state, since there's no more data to continue. */
1970 FreeUserDumpState(instance);
1973 return STATUS_SUCCESS;
1977 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1980 NTSTATUS status = STATUS_SUCCESS;
1981 LOCK_STATE_EX lockState;
1983 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1984 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1985 POVS_VPORT_ENTRY vport = NULL;
1986 NL_ERROR nlError = NL_ERROR_SUCCESS;
1987 PCHAR portName = NULL;
1988 UINT32 portNameLen = 0;
1989 UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
1991 static const NL_POLICY ovsVportPolicy[] = {
1992 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1993 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1998 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2000 /* input buffer has been validated while validating write dev op. */
2001 ASSERT(usrParamsCtx->inputBuffer != NULL);
2003 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2004 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2005 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2006 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2007 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2008 return STATUS_INVALID_PARAMETER;
2011 /* Output buffer has been validated while validating transact dev op. */
2012 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2014 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
2015 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2016 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2017 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2019 /* the port name is expected to be null-terminated */
2020 ASSERT(portName[portNameLen - 1] == '\0');
2022 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2023 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2024 portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2026 vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
2028 nlError = NL_ERROR_INVAL;
2029 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2034 nlError = NL_ERROR_NODEV;
2035 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2039 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2040 usrParamsCtx->outputLength,
2041 gOvsSwitchContext->dpNo);
2042 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2044 *replyLen = msgOut->nlMsg.nlmsgLen;
2047 if (nlError != NL_ERROR_SUCCESS) {
2048 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2049 usrParamsCtx->outputBuffer;
2051 NlBuildErrorMsg(msgIn, msgError, nlError);
2052 *replyLen = msgError->nlMsg.nlmsgLen;
2055 return STATUS_SUCCESS;
2059 * --------------------------------------------------------------------------
2060 * Command Handler for 'OVS_VPORT_CMD_GET'.
2062 * The function handles the initial call to setup the dump state, as well as
2063 * subsequent calls to continue dumping data.
2064 * --------------------------------------------------------------------------
2067 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2072 switch (usrParamsCtx->devOp) {
2073 case OVS_WRITE_DEV_OP:
2074 return OvsSetupDumpStart(usrParamsCtx);
2076 case OVS_READ_DEV_OP:
2077 return OvsGetVportDumpNext(usrParamsCtx, replyLen);
2079 case OVS_TRANSACTION_DEV_OP:
2080 return OvsGetVport(usrParamsCtx, replyLen);
2083 return STATUS_INVALID_DEVICE_REQUEST;
2089 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
2091 /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
2092 for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
2093 POVS_VPORT_ENTRY vport;
2095 vport = OvsFindVportByPortNo(switchContext, i);
2101 return OVS_DPPORT_NUMBER_INVALID;
2105 * --------------------------------------------------------------------------
2106 * Command Handler for 'OVS_VPORT_CMD_NEW'.
2107 * --------------------------------------------------------------------------
2110 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2113 NDIS_STATUS status = STATUS_SUCCESS;
2114 LOCK_STATE_EX lockState;
2116 NL_ERROR nlError = NL_ERROR_SUCCESS;
2117 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2118 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2119 POVS_VPORT_ENTRY vport = NULL;
2123 BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE;
2125 static const NL_POLICY ovsVportPolicy[] = {
2126 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2127 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2128 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2130 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2131 .optional = FALSE },
2132 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2135 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2137 /* input buffer has been validated while validating write dev op. */
2138 ASSERT(usrParamsCtx->inputBuffer != NULL);
2140 /* Output buffer has been validated while validating transact dev op. */
2141 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2143 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2144 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2145 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2146 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2147 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2148 return STATUS_INVALID_PARAMETER;
2151 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2152 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2153 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2155 /* we are expecting null terminated strings to be passed */
2156 ASSERT(portName[portNameLen - 1] == '\0');
2158 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2160 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2162 nlError = NL_ERROR_EXIST;
2166 if (portType == OVS_VPORT_TYPE_NETDEV) {
2167 /* External ports can also be looked up like VIF ports. */
2168 vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
2170 ASSERT(OvsIsTunnelVportType(portType) ||
2171 portType == OVS_VPORT_TYPE_INTERNAL);
2173 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
2174 if (vport == NULL) {
2175 nlError = NL_ERROR_NOMEM;
2178 vportAllocated = TRUE;
2180 if (OvsIsTunnelVportType(portType)) {
2181 UINT16 transportPortDest = 0;
2184 case OVS_VPORT_TYPE_VXLAN:
2185 transportPortDest = VXLAN_UDP_PORT;
2187 case OVS_VPORT_TYPE_STT:
2188 transportPortDest = STT_TCP_PORT;
2194 if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2195 PNL_ATTR attr = NlAttrFindNested(vportAttrs[OVS_VPORT_ATTR_OPTIONS],
2196 OVS_TUNNEL_ATTR_DST_PORT);
2198 transportPortDest = NlAttrGetU16(attr);
2202 status = OvsInitTunnelVport(usrParamsCtx,
2207 nlError = NlMapStatusToNlErr(status);
2209 OvsInitBridgeInternalVport(vport);
2212 vportInitialized = TRUE;
2214 if (nlError == NL_ERROR_SUCCESS) {
2215 vport->ovsState = OVS_STATE_CONNECTED;
2216 vport->nicState = NdisSwitchNicStateConnected;
2219 * Allow the vport to be deleted, because there is no
2220 * corresponding hyper-v switch part.
2222 vport->isAbsentOnHv = TRUE;
2229 nlError = NL_ERROR_INVAL;
2232 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2233 nlError = NL_ERROR_EXIST;
2237 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2239 * XXX: when we implement the limit for ovs port number to be
2240 * MAXUINT16, we'll need to check the port number received from the
2243 vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2245 vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
2246 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2247 nlError = NL_ERROR_NOMEM;
2252 /* The ovs port name must be uninitialized. */
2253 ASSERT(vport->ovsName[0] == '\0');
2254 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2256 RtlCopyMemory(vport->ovsName, portName, portNameLen);
2257 /* if we don't have options, then vport->portOptions will be NULL */
2258 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2261 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2262 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2263 * it means we have an array of pids, instead of a single pid.
2264 * ATM we assume we have one pid only.
2266 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2268 status = InitOvsVportCommon(gOvsSwitchContext, vport);
2269 ASSERT(status == STATUS_SUCCESS);
2271 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2272 usrParamsCtx->outputLength,
2273 gOvsSwitchContext->dpNo);
2275 *replyLen = msgOut->nlMsg.nlmsgLen;
2278 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2280 if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2281 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2282 usrParamsCtx->outputBuffer;
2284 if (vport && vportAllocated == TRUE) {
2285 if (vportInitialized == TRUE) {
2286 if (OvsIsTunnelVportType(portType)) {
2287 switch (vport->ovsType) {
2288 case OVS_VPORT_TYPE_VXLAN:
2289 OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2291 case OVS_VPORT_TYPE_STT:
2292 OvsCleanupSttTunnel(vport);
2295 ASSERT(!"Invalid tunnel port type");
2299 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2302 NlBuildErrorMsg(msgIn, msgError, nlError);
2303 *replyLen = msgError->nlMsg.nlmsgLen;
2306 return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2311 * --------------------------------------------------------------------------
2312 * Command Handler for 'OVS_VPORT_CMD_SET'.
2313 * --------------------------------------------------------------------------
2316 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2319 NDIS_STATUS status = STATUS_SUCCESS;
2320 LOCK_STATE_EX lockState;
2322 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2323 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2324 POVS_VPORT_ENTRY vport = NULL;
2325 NL_ERROR nlError = NL_ERROR_SUCCESS;
2327 static const NL_POLICY ovsVportPolicy[] = {
2328 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2329 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
2330 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2332 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2334 [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
2335 .minLen = sizeof(OVS_VPORT_FULL_STATS),
2336 .maxLen = sizeof(OVS_VPORT_FULL_STATS),
2338 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2340 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2342 ASSERT(usrParamsCtx->inputBuffer != NULL);
2344 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2345 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2346 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2347 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2348 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2349 return STATUS_INVALID_PARAMETER;
2352 /* Output buffer has been validated while validating transact dev op. */
2353 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2355 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2356 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2357 PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2359 UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2361 /* the port name is expected to be null-terminated */
2362 ASSERT(portName[portNameLen - 1] == '\0');
2364 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2365 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2366 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2367 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2371 nlError = NL_ERROR_NODEV;
2376 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2377 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2378 * it means we have an array of pids, instead of a single pid.
2379 * Currently, we support only one pid.
2381 if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
2382 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2385 if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
2386 OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2387 if (type != vport->ovsType) {
2388 nlError = NL_ERROR_INVAL;
2393 if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2394 OVS_LOG_ERROR("Vport options not supported");
2395 nlError = NL_ERROR_NOTSUPP;
2399 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2400 usrParamsCtx->outputLength,
2401 gOvsSwitchContext->dpNo);
2403 *replyLen = msgOut->nlMsg.nlmsgLen;
2406 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2408 if (nlError != NL_ERROR_SUCCESS) {
2409 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2410 usrParamsCtx->outputBuffer;
2412 NlBuildErrorMsg(msgIn, msgError, nlError);
2413 *replyLen = msgError->nlMsg.nlmsgLen;
2416 return STATUS_SUCCESS;
2420 * --------------------------------------------------------------------------
2421 * Command Handler for 'OVS_VPORT_CMD_DEL'.
2422 * --------------------------------------------------------------------------
2425 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2428 NDIS_STATUS status = STATUS_SUCCESS;
2429 LOCK_STATE_EX lockState;
2431 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2432 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2433 POVS_VPORT_ENTRY vport = NULL;
2434 NL_ERROR nlError = NL_ERROR_SUCCESS;
2435 PSTR portName = NULL;
2436 UINT32 portNameLen = 0;
2438 static const NL_POLICY ovsVportPolicy[] = {
2439 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2440 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2443 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2445 ASSERT(usrParamsCtx->inputBuffer != NULL);
2447 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2448 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2449 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2450 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2451 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2452 return STATUS_INVALID_PARAMETER;
2455 /* Output buffer has been validated while validating transact dev op. */
2456 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2458 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2459 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2460 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2461 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2463 /* the port name is expected to be null-terminated */
2464 ASSERT(portName[portNameLen - 1] == '\0');
2466 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2468 else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2469 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2470 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2474 nlError = NL_ERROR_NODEV;
2478 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2479 usrParamsCtx->outputLength,
2480 gOvsSwitchContext->dpNo);
2482 *replyLen = msgOut->nlMsg.nlmsgLen;
2485 * Mark the port as deleted from OVS userspace. If the port does not exist
2486 * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
2488 status = OvsRemoveAndDeleteVport(usrParamsCtx,
2494 nlError = NlMapStatusToNlErr(status);
2498 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2500 if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2501 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2502 usrParamsCtx->outputBuffer;
2504 NlBuildErrorMsg(msgIn, msgError, nlError);
2505 *replyLen = msgError->nlMsg.nlmsgLen;
2508 return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2512 OvsTunnelVportPendingRemove(PVOID context,
2516 POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2517 (POVS_TUNFLT_INIT_CONTEXT) context;
2518 POVS_SWITCH_CONTEXT switchContext = tunnelContext->switchContext;
2519 POVS_VPORT_ENTRY vport = tunnelContext->vport;
2520 POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2521 POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2522 NL_ERROR nlError = NlMapStatusToNlErr(status);
2523 LOCK_STATE_EX lockState;
2525 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
2527 if (msgIn && msgOut) {
2528 /* Check the received status to reply to the caller. */
2529 if (STATUS_SUCCESS == status) {
2530 OvsCreateMsgFromVport(vport,
2533 tunnelContext->outputLength,
2534 switchContext->dpNo);
2536 *replyLen = msgOut->nlMsg.nlmsgLen;
2538 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)msgOut;
2540 NlBuildErrorMsg(msgIn, msgError, nlError);
2541 *replyLen = msgError->nlMsg.nlmsgLen;
2545 ASSERT(vport->isAbsentOnHv == TRUE);
2546 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
2548 /* Remove the port from the relevant lists. */
2549 switchContext->numNonHvVports--;
2550 RemoveEntryList(&vport->ovsNameLink);
2551 RemoveEntryList(&vport->portNoLink);
2552 RemoveEntryList(&vport->tunnelVportLink);
2555 OvsFreeMemoryWithTag(vport->priv, OVS_VXLAN_POOL_TAG);
2559 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2561 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
2565 OvsTunnelVportPendingInit(PVOID context,
2569 POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2570 (POVS_TUNFLT_INIT_CONTEXT) context;
2571 POVS_VPORT_ENTRY vport = tunnelContext->vport;
2572 POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2573 POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2575 ULONG portNameLen = 0;
2576 UINT32 portType = 0;
2577 NL_ERROR nlError = NL_ERROR_SUCCESS;
2578 BOOLEAN error = TRUE;
2581 if (!NT_SUCCESS(status)) {
2582 nlError = NlMapStatusToNlErr(status);
2586 static const NL_POLICY ovsVportPolicy[] = {
2587 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2588 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2589 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2590 .optional = FALSE },
2591 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2592 .optional = FALSE },
2593 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2596 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2598 /* input buffer has been validated while validating write dev op. */
2599 ASSERT(msgIn != NULL);
2601 /* Output buffer has been validated while validating transact dev op. */
2602 ASSERT(msgOut != NULL && tunnelContext->outputLength >= sizeof *msgOut);
2604 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2605 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2606 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2607 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2608 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2609 nlError = NL_ERROR_INVAL;
2613 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2614 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2615 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2617 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2618 nlError = NL_ERROR_EXIST;
2622 vport->ovsState = OVS_STATE_CONNECTED;
2623 vport->nicState = NdisSwitchNicStateConnected;
2626 * Allow the vport to be deleted, because there is no
2627 * corresponding hyper-v switch part.
2629 vport->isAbsentOnHv = TRUE;
2631 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2633 * XXX: when we implement the limit for OVS port number to be
2634 * MAXUINT16, we'll need to check the port number received from the
2638 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2641 OvsComputeVportNo(gOvsSwitchContext);
2642 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2643 nlError = NL_ERROR_NOMEM;
2648 /* The ovs port name must be uninitialized. */
2649 ASSERT(vport->ovsName[0] == '\0');
2650 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2652 RtlCopyMemory(vport->ovsName, portName, portNameLen);
2653 /* if we don't have options, then vport->portOptions will be NULL */
2654 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2657 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2658 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2659 * it means we have an array of pids, instead of a single pid.
2660 * ATM we assume we have one pid only.
2663 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2665 status = InitOvsVportCommon(gOvsSwitchContext, vport);
2666 ASSERT(status == STATUS_SUCCESS);
2668 OvsCreateMsgFromVport(vport,
2671 tunnelContext->outputLength,
2672 gOvsSwitchContext->dpNo);
2674 *replyLen = msgOut->nlMsg.nlmsgLen;
2680 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) msgOut;
2682 OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2683 OvsFreeMemory(vport);
2685 NlBuildErrorMsg(msgIn, msgError, nlError);
2686 *replyLen = msgError->nlMsg.nlmsgLen;