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,
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
194 * We don't allow changing the names of the internal or external ports
196 if (vport == NULL || (( vport->portType != NdisSwitchPortTypeSynthetic) &&
197 ( vport->portType != NdisSwitchPortTypeEmulated))) {
198 goto update_port_done;
201 /* Store the nic and the OVS states as Nic Create won't be called */
202 ovsState = vport->ovsState;
203 nicState = vport->nicState;
206 * Currently only the port friendly name is being updated
207 * Make sure that no other properties are changed
209 ASSERT(portParam->PortId == vport->portId);
210 ASSERT(portParam->PortState == vport->portState);
211 ASSERT(portParam->PortType == vport->portType);
214 * Call the set parameters function the handle all properties
215 * change in a single place in case future version supports change of
218 OvsInitVportWithPortParam(vport, portParam);
219 /* Retore the nic and OVS states */
220 vport->nicState = nicState;
221 vport->ovsState = ovsState;
224 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
225 VPORT_PORT_EXIT(portParam);
227 /* Must always return success */
228 return NDIS_STATUS_SUCCESS;
232 HvTeardownPort(POVS_SWITCH_CONTEXT switchContext,
233 PNDIS_SWITCH_PORT_PARAMETERS portParam)
235 POVS_VPORT_ENTRY vport;
236 LOCK_STATE_EX lockState;
238 VPORT_PORT_ENTER(portParam);
240 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
241 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
242 portParam->PortId, 0);
244 /* add assertion here */
245 vport->portState = NdisSwitchPortStateTeardown;
246 vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
248 OVS_LOG_WARN("Vport not present.");
250 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
252 VPORT_PORT_EXIT(portParam);
257 HvDeletePort(POVS_SWITCH_CONTEXT switchContext,
258 PNDIS_SWITCH_PORT_PARAMETERS portParams)
260 POVS_VPORT_ENTRY vport;
261 LOCK_STATE_EX lockState;
263 VPORT_PORT_ENTER(portParams);
265 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
266 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
267 portParams->PortId, 0);
270 * XXX: we can only destroy and remove the port if its datapath port
271 * counterpart was deleted. If the datapath port counterpart is present,
272 * we only mark the vport for deletion, so that a netlink command vport
273 * delete will delete the vport.
276 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
278 OVS_LOG_WARN("Vport not present.");
280 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
282 VPORT_PORT_EXIT(portParams);
287 * Functions implemented in relaton to NDIS NIC manipulation.
290 HvCreateNic(POVS_SWITCH_CONTEXT switchContext,
291 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
293 POVS_VPORT_ENTRY vport;
296 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
298 LOCK_STATE_EX lockState;
300 VPORT_NIC_ENTER(nicParam);
302 /* Wait for lists to be initialized. */
303 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
305 if (!switchContext->isActivated) {
306 OVS_LOG_WARN("Switch is not activated yet.");
307 /* Veto the creation of nic */
308 status = NDIS_STATUS_NOT_SUPPORTED;
312 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
313 vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId, 0);
315 OVS_LOG_ERROR("Create NIC without Switch Port,"
316 " PortId: %x, NicIndex: %d",
317 nicParam->PortId, nicParam->NicIndex);
318 status = NDIS_STATUS_INVALID_PARAMETER;
322 if (nicParam->NicType == NdisSwitchNicTypeExternal &&
323 nicParam->NicIndex != 0) {
324 POVS_VPORT_ENTRY virtExtVport =
325 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
327 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
329 status = NDIS_STATUS_RESOURCES;
332 OvsInitPhysNicVport(vport, virtExtVport, nicParam->NicIndex);
333 status = InitHvVportCommon(switchContext, vport, TRUE);
334 if (status != NDIS_STATUS_SUCCESS) {
335 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
339 OvsInitVportWithNicParam(switchContext, vport, nicParam);
340 portNo = vport->portNo;
341 if (vport->ovsState == OVS_STATE_CONNECTED) {
342 event = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
343 } else if (vport->ovsState == OVS_STATE_NIC_CREATED) {
344 event = OVS_EVENT_CONNECT;
348 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
349 if (portNo != OVS_DPPORT_NUMBER_INVALID && event) {
350 OvsPostEvent(portNo, event);
354 VPORT_NIC_EXIT(nicParam);
355 OVS_LOG_TRACE("Exit: status %8x.\n", status);
361 /* Mark already created NIC as connected. */
363 HvConnectNic(POVS_SWITCH_CONTEXT switchContext,
364 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
366 LOCK_STATE_EX lockState;
367 POVS_VPORT_ENTRY vport;
370 VPORT_NIC_ENTER(nicParam);
372 /* Wait for lists to be initialized. */
373 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
375 if (!switchContext->isActivated) {
376 OVS_LOG_WARN("Switch is not activated yet.");
380 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
381 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
386 OVS_LOG_WARN("Vport not present.");
387 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
392 vport->ovsState = OVS_STATE_CONNECTED;
393 vport->nicState = NdisSwitchNicStateConnected;
394 portNo = vport->portNo;
396 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
398 /* XXX only if portNo != INVALID or always? */
399 OvsPostEvent(portNo, OVS_EVENT_LINK_UP);
401 if (nicParam->NicType == NdisSwitchNicTypeInternal) {
402 OvsInternalAdapterUp(portNo, &nicParam->NetCfgInstanceId);
406 VPORT_NIC_EXIT(nicParam);
410 HvUpdateNic(POVS_SWITCH_CONTEXT switchContext,
411 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
413 POVS_VPORT_ENTRY vport;
414 LOCK_STATE_EX lockState;
416 UINT32 status = 0, portNo = 0;
418 VPORT_NIC_ENTER(nicParam);
420 /* Wait for lists to be initialized. */
421 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
423 if (!switchContext->isActivated) {
424 OVS_LOG_WARN("Switch is not activated yet.");
425 goto update_nic_done;
428 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
429 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
433 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
434 OVS_LOG_WARN("Vport search failed.");
435 goto update_nic_done;
437 switch (nicParam->NicType) {
438 case NdisSwitchNicTypeExternal:
439 case NdisSwitchNicTypeInternal:
440 RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
443 case NdisSwitchNicTypeSynthetic:
444 case NdisSwitchNicTypeEmulated:
445 if (!RtlEqualMemory(vport->vmMacAddress, nicParam->VMMacAddress,
446 sizeof (vport->vmMacAddress))) {
447 status |= OVS_EVENT_MAC_CHANGE;
448 RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
449 sizeof (vport->vmMacAddress));
455 if (!RtlEqualMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
456 sizeof (vport->permMacAddress))) {
457 RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
458 sizeof (vport->permMacAddress));
459 status |= OVS_EVENT_MAC_CHANGE;
461 if (!RtlEqualMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
462 sizeof (vport->currMacAddress))) {
463 RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
464 sizeof (vport->currMacAddress));
465 status |= OVS_EVENT_MAC_CHANGE;
468 if (vport->mtu != nicParam->MTU) {
469 vport->mtu = nicParam->MTU;
470 status |= OVS_EVENT_MTU_CHANGE;
472 vport->numaNodeId = nicParam->NumaNodeId;
473 portNo = vport->portNo;
475 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
476 if (status && portNo) {
477 OvsPostEvent(portNo, status);
480 VPORT_NIC_EXIT(nicParam);
485 HvDisconnectNic(POVS_SWITCH_CONTEXT switchContext,
486 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
488 POVS_VPORT_ENTRY vport;
490 LOCK_STATE_EX lockState;
491 BOOLEAN isInternalPort = FALSE;
493 VPORT_NIC_ENTER(nicParam);
495 /* Wait for lists to be initialized. */
496 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
498 if (!switchContext->isActivated) {
499 OVS_LOG_WARN("Switch is not activated yet.");
503 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
504 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
509 OVS_LOG_WARN("Vport not present.");
510 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
514 vport->nicState = NdisSwitchNicStateDisconnected;
515 vport->ovsState = OVS_STATE_NIC_CREATED;
516 portNo = vport->portNo;
518 if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
519 isInternalPort = TRUE;
522 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
524 /* XXX if portNo != INVALID or always? */
525 OvsPostEvent(portNo, OVS_EVENT_LINK_DOWN);
527 if (isInternalPort) {
528 OvsInternalAdapterDown();
532 VPORT_NIC_EXIT(nicParam);
537 HvDeleteNic(POVS_SWITCH_CONTEXT switchContext,
538 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
540 LOCK_STATE_EX lockState;
541 POVS_VPORT_ENTRY vport;
544 VPORT_NIC_ENTER(nicParam);
545 /* Wait for lists to be initialized. */
546 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
548 if (!switchContext->isActivated) {
549 OVS_LOG_WARN("Switch is not activated yet.");
553 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
554 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
559 OVS_LOG_WARN("Vport not present.");
560 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
564 vport->nicState = NdisSwitchNicStateUnknown;
565 vport->ovsState = OVS_STATE_PORT_CREATED;
567 portNo = vport->portNo;
568 if (vport->portType == NdisSwitchPortTypeExternal &&
569 vport->nicIndex != 0) {
570 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
573 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
574 /* XXX if portNo != INVALID or always? */
575 OvsPostEvent(portNo, OVS_EVENT_DISCONNECT);
578 VPORT_NIC_EXIT(nicParam);
583 * OVS Vport related functionality.
586 OvsFindVportByPortNo(POVS_SWITCH_CONTEXT switchContext,
589 POVS_VPORT_ENTRY vport;
590 PLIST_ENTRY head, link;
591 UINT32 hash = OvsJhashBytes((const VOID *)&portNo, sizeof(portNo),
593 head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
594 LIST_FORALL(head, link) {
595 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
596 if (vport->portNo == portNo) {
605 OvsFindTunnelVportByDstPort(POVS_SWITCH_CONTEXT switchContext,
607 OVS_VPORT_TYPE ovsPortType)
609 POVS_VPORT_ENTRY vport;
610 PLIST_ENTRY head, link;
611 UINT32 hash = OvsJhashBytes((const VOID *)&dstPort, sizeof(dstPort),
613 head = &(switchContext->tunnelVportsArray[hash & OVS_VPORT_MASK]);
614 LIST_FORALL(head, link) {
615 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, tunnelVportLink);
616 if (GetPortFromPriv(vport) == dstPort &&
617 vport->ovsType == ovsPortType) {
626 OvsFindVportByOvsName(POVS_SWITCH_CONTEXT switchContext,
629 POVS_VPORT_ENTRY vport;
630 PLIST_ENTRY head, link;
632 SIZE_T length = strlen(name) + 1;
634 hash = OvsJhashBytes((const VOID *)name, length, OVS_HASH_BASIS);
635 head = &(switchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK]);
637 LIST_FORALL(head, link) {
638 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, ovsNameLink);
639 if (!strcmp(name, vport->ovsName)) {
647 /* OvsFindVportByHvName: "name" is assumed to be null-terminated */
649 OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
650 PWSTR wsName, SIZE_T wstrSize)
652 POVS_VPORT_ENTRY vport = NULL;
653 PLIST_ENTRY head, link;
656 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
657 head = &(switchContext->portIdHashArray[i]);
658 LIST_FORALL(head, link) {
659 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
662 * NOTE about portFriendlyName:
663 * If the string is NULL-terminated, the Length member does not
664 * include the terminating NULL character.
666 if (vport->portFriendlyName.Length == wstrSize &&
667 RtlEqualMemory(wsName, vport->portFriendlyName.String,
668 vport->portFriendlyName.Length)) {
677 * Look in the list of ports that were added from the Hyper-V switch and
681 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
682 head = &(switchContext->portNoHashArray[i]);
683 LIST_FORALL(head, link) {
684 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
685 if (vport->portFriendlyName.Length == wstrSize &&
686 RtlEqualMemory(wsName, vport->portFriendlyName.String,
687 vport->portFriendlyName.Length)) {
701 OvsFindVportByHvNameA(POVS_SWITCH_CONTEXT switchContext,
704 POVS_VPORT_ENTRY vport = NULL;
705 /* 'portFriendlyName' is not NUL-terminated. */
706 SIZE_T length = strlen(name);
707 SIZE_T wstrSize = length * sizeof(WCHAR);
710 PWSTR wsName = OvsAllocateMemoryWithTag(wstrSize, OVS_VPORT_POOL_TAG);
714 for (i = 0; i < length; i++) {
717 vport = OvsFindVportByHvNameW(switchContext, wsName, wstrSize);
718 OvsFreeMemoryWithTag(wsName, OVS_VPORT_POOL_TAG);
723 OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT switchContext,
724 NDIS_SWITCH_PORT_ID portId,
725 NDIS_SWITCH_NIC_INDEX index)
727 if (switchContext->virtualExternalVport &&
728 portId == switchContext->virtualExternalPortId &&
729 index == switchContext->virtualExternalVport->nicIndex) {
730 return (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
731 } else if (switchContext->internalVport &&
732 portId == switchContext->internalPortId &&
733 index == switchContext->internalVport->nicIndex) {
734 return (POVS_VPORT_ENTRY)switchContext->internalVport;
736 PLIST_ENTRY head, link;
737 POVS_VPORT_ENTRY vport;
739 hash = OvsJhashWords((UINT32 *)&portId, 1, OVS_HASH_BASIS);
740 head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
741 LIST_FORALL(head, link) {
742 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
743 if (portId == vport->portId && index == vport->nicIndex) {
752 OvsAllocateVport(VOID)
754 POVS_VPORT_ENTRY vport;
755 vport = (POVS_VPORT_ENTRY)OvsAllocateMemoryWithTag(
756 sizeof(OVS_VPORT_ENTRY), OVS_VPORT_POOL_TAG);
760 RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY));
761 vport->ovsState = OVS_STATE_UNKNOWN;
762 vport->isAbsentOnHv = FALSE;
763 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
765 InitializeListHead(&vport->ovsNameLink);
766 InitializeListHead(&vport->portIdLink);
767 InitializeListHead(&vport->portNoLink);
773 OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
774 PNDIS_SWITCH_PORT_PARAMETERS portParam)
776 vport->portType = portParam->PortType;
777 vport->portState = portParam->PortState;
778 vport->portId = portParam->PortId;
779 vport->nicState = NdisSwitchNicStateUnknown;
780 vport->isExternal = FALSE;
781 vport->isBridgeInternal = FALSE;
783 switch (vport->portType) {
784 case NdisSwitchPortTypeExternal:
785 vport->isExternal = TRUE;
786 vport->ovsType = OVS_VPORT_TYPE_NETDEV;
788 case NdisSwitchPortTypeInternal:
789 vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
791 case NdisSwitchPortTypeSynthetic:
792 case NdisSwitchPortTypeEmulated:
793 vport->ovsType = OVS_VPORT_TYPE_NETDEV;
796 RtlCopyMemory(&vport->hvPortName, &portParam->PortName,
797 sizeof (NDIS_SWITCH_PORT_NAME));
798 /* For external and internal ports, 'portFriendlyName' is overwritten
800 RtlCopyMemory(&vport->portFriendlyName, &portParam->PortFriendlyName,
801 sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
803 switch (vport->portState) {
804 case NdisSwitchPortStateCreated:
805 vport->ovsState = OVS_STATE_PORT_CREATED;
807 case NdisSwitchPortStateTeardown:
808 vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
810 case NdisSwitchPortStateDeleted:
811 vport->ovsState = OVS_STATE_PORT_DELETED;
818 OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
819 POVS_VPORT_ENTRY vport,
820 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
822 ASSERT(vport->portId == nicParam->PortId);
823 ASSERT(vport->ovsState == OVS_STATE_PORT_CREATED);
825 UNREFERENCED_PARAMETER(switchContext);
827 RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
828 sizeof (nicParam->PermanentMacAddress));
829 RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
830 sizeof (nicParam->CurrentMacAddress));
832 if (nicParam->NicType == NdisSwitchNicTypeSynthetic ||
833 nicParam->NicType == NdisSwitchNicTypeEmulated) {
834 RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
835 sizeof (nicParam->VMMacAddress));
836 RtlCopyMemory(&vport->vmName, &nicParam->VmName,
837 sizeof (nicParam->VmName));
839 RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
840 sizeof (nicParam->NetCfgInstanceId));
842 RtlCopyMemory(&vport->nicName, &nicParam->NicName,
843 sizeof (nicParam->NicName));
844 vport->mtu = nicParam->MTU;
845 vport->nicState = nicParam->NicState;
846 vport->nicIndex = nicParam->NicIndex;
847 vport->numaNodeId = nicParam->NumaNodeId;
849 switch (vport->nicState) {
850 case NdisSwitchNicStateCreated:
851 vport->ovsState = OVS_STATE_NIC_CREATED;
853 case NdisSwitchNicStateConnected:
854 vport->ovsState = OVS_STATE_CONNECTED;
856 case NdisSwitchNicStateDisconnected:
857 vport->ovsState = OVS_STATE_NIC_CREATED;
859 case NdisSwitchNicStateDeleted:
860 vport->ovsState = OVS_STATE_PORT_CREATED;
866 * --------------------------------------------------------------------------
867 * Copies the relevant NDIS port properties from a virtual (pseudo) external
868 * NIC to a physical (real) external NIC.
869 * --------------------------------------------------------------------------
872 OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVport,
873 POVS_VPORT_ENTRY virtExtVport,
876 physExtVport->portType = virtExtVport->portType;
877 physExtVport->portState = virtExtVport->portState;
878 physExtVport->portId = virtExtVport->portId;
879 physExtVport->nicState = NdisSwitchNicStateUnknown;
880 physExtVport->ovsType = OVS_VPORT_TYPE_NETDEV;
881 physExtVport->isExternal = TRUE;
882 physExtVport->isBridgeInternal = FALSE;
883 physExtVport->nicIndex = (NDIS_SWITCH_NIC_INDEX)physNicIndex;
885 RtlCopyMemory(&physExtVport->hvPortName, &virtExtVport->hvPortName,
886 sizeof (NDIS_SWITCH_PORT_NAME));
888 /* 'portFriendlyName' is overwritten later. */
889 RtlCopyMemory(&physExtVport->portFriendlyName,
890 &virtExtVport->portFriendlyName,
891 sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
893 physExtVport->ovsState = OVS_STATE_PORT_CREATED;
897 * --------------------------------------------------------------------------
898 * Initializes a tunnel vport.
899 * --------------------------------------------------------------------------
902 OvsInitTunnelVport(PVOID userContext,
903 POVS_VPORT_ENTRY vport,
904 OVS_VPORT_TYPE ovsType,
907 NTSTATUS status = STATUS_SUCCESS;
908 POVS_USER_PARAMS_CONTEXT usrParamsCtx =
909 (POVS_USER_PARAMS_CONTEXT)userContext;
911 vport->isBridgeInternal = FALSE;
912 vport->ovsType = ovsType;
913 vport->ovsState = OVS_STATE_PORT_CREATED;
915 case OVS_VPORT_TYPE_GRE:
917 case OVS_VPORT_TYPE_GRE64:
919 case OVS_VPORT_TYPE_VXLAN:
921 POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
923 tunnelContext = OvsAllocateMemoryWithTag(sizeof(*tunnelContext),
925 if (tunnelContext == NULL) {
926 status = STATUS_INSUFFICIENT_RESOURCES;
929 tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
930 tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
931 tunnelContext->outputLength = usrParamsCtx->outputLength;
932 tunnelContext->vport = vport;
934 status = OvsInitVxlanTunnel(usrParamsCtx->irp,
937 OvsTunnelVportPendingInit,
938 (PVOID)tunnelContext);
939 if (status != STATUS_PENDING) {
940 OvsFreeMemoryWithTag(tunnelContext, OVS_VPORT_POOL_TAG);
941 tunnelContext = NULL;
945 case OVS_VPORT_TYPE_STT:
946 status = OvsInitSttTunnel(vport, dstPort);
955 * --------------------------------------------------------------------------
956 * Initializes a bridge internal vport ie. a port of type
957 * OVS_VPORT_TYPE_INTERNAL but not present on the Hyper-V switch.
958 * --------------------------------------------------------------------------
961 OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport)
963 vport->isBridgeInternal = TRUE;
964 vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
965 /* Mark the status to be connected, since there is no other initialization
967 vport->ovsState = OVS_STATE_CONNECTED;
968 return STATUS_SUCCESS;
972 * --------------------------------------------------------------------------
973 * For external vports 'portFriendlyName' provided by Hyper-V is over-written
974 * by synthetic names.
975 * --------------------------------------------------------------------------
978 AssignNicNameSpecial(POVS_VPORT_ENTRY vport)
982 if (vport->portType == NdisSwitchPortTypeExternal) {
983 if (vport->nicIndex == 0) {
984 ASSERT(vport->nicIndex == 0);
985 RtlStringCbPrintfW(vport->portFriendlyName.String,
987 L"%s.virtualAdapter", OVS_DPPORT_EXTERNAL_NAME_W);
989 RtlStringCbPrintfW(vport->portFriendlyName.String,
991 L"%s.%lu", OVS_DPPORT_EXTERNAL_NAME_W,
992 (UINT32)vport->nicIndex);
995 RtlStringCbPrintfW(vport->portFriendlyName.String,
997 L"%s", OVS_DPPORT_INTERNAL_NAME_W);
1000 RtlStringCbLengthW(vport->portFriendlyName.String, IF_MAX_STRING_SIZE,
1002 vport->portFriendlyName.Length = (USHORT)len;
1007 * --------------------------------------------------------------------------
1008 * Functionality common to any port on the Hyper-V switch. This function is not
1009 * to be called for a port that is not on the Hyper-V switch.
1011 * Inserts the port into 'portIdHashArray' and caches the pointer in the
1012 * 'switchContext' if needed.
1014 * For external NIC, assigns the name for the NIC.
1015 * --------------------------------------------------------------------------
1018 InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
1019 POVS_VPORT_ENTRY vport,
1024 switch (vport->portType) {
1025 case NdisSwitchPortTypeExternal:
1027 * Overwrite the 'portFriendlyName' of this external vport. The reason
1028 * for having this in common code is to be able to call it from the NDIS
1029 * Port callback as well as the NDIS NIC callback.
1031 AssignNicNameSpecial(vport);
1033 if (vport->nicIndex == 0) {
1034 switchContext->virtualExternalPortId = vport->portId;
1035 switchContext->virtualExternalVport = vport;
1037 switchContext->numPhysicalNics++;
1040 case NdisSwitchPortTypeInternal:
1041 ASSERT(vport->isBridgeInternal == FALSE);
1043 /* Overwrite the 'portFriendlyName' of the internal vport. */
1044 AssignNicNameSpecial(vport);
1045 switchContext->internalPortId = vport->portId;
1046 switchContext->internalVport = vport;
1048 case NdisSwitchPortTypeSynthetic:
1049 case NdisSwitchPortTypeEmulated:
1054 * It is important to not insert vport corresponding to virtual external
1055 * port into the 'portIdHashArray' since the port should not be exposed to
1058 if (vport->portType == NdisSwitchPortTypeExternal &&
1059 vport->nicIndex == 0) {
1060 return NDIS_STATUS_SUCCESS;
1064 * NOTE: OvsJhashWords has portId as "1" word. This should be ok, even
1065 * though sizeof(NDIS_SWITCH_PORT_ID) = 4, not 2, because the
1066 * hyper-v switch seems to use only 2 bytes out of 4.
1068 hash = OvsJhashWords(&vport->portId, 1, OVS_HASH_BASIS);
1069 InsertHeadList(&switchContext->portIdHashArray[hash & OVS_VPORT_MASK],
1070 &vport->portIdLink);
1072 switchContext->numHvVports++;
1074 return NDIS_STATUS_SUCCESS;
1078 * --------------------------------------------------------------------------
1079 * Functionality common to any port added from OVS userspace.
1081 * Inserts the port into 'portNoHashArray', 'ovsPortNameHashArray' and in
1082 * 'tunnelVportsArray' if appropriate.
1083 * --------------------------------------------------------------------------
1086 InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
1087 POVS_VPORT_ENTRY vport)
1091 switch(vport->ovsType) {
1092 case OVS_VPORT_TYPE_VXLAN:
1093 case OVS_VPORT_TYPE_STT:
1095 UINT16 dstPort = GetPortFromPriv(vport);
1096 hash = OvsJhashBytes(&dstPort,
1100 &gOvsSwitchContext->tunnelVportsArray[hash & OVS_VPORT_MASK],
1101 &vport->tunnelVportLink);
1102 switchContext->numNonHvVports++;
1105 case OVS_VPORT_TYPE_INTERNAL:
1106 if (vport->isBridgeInternal) {
1107 switchContext->numNonHvVports++;
1114 * Insert the port into the hash array of ports: by port number and ovs
1115 * and ovs (datapath) port name.
1116 * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1117 * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1119 hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
1120 InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
1121 &vport->portNoLink);
1123 hash = OvsJhashBytes(vport->ovsName, strlen(vport->ovsName) + 1,
1126 &gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
1127 &vport->ovsNameLink);
1129 return STATUS_SUCCESS;
1134 * --------------------------------------------------------------------------
1135 * Provides functionality that is partly complementatry to
1136 * InitOvsVportCommon()/InitHvVportCommon().
1138 * 'hvDelete' indicates if caller is removing the vport as a result of the
1139 * port being removed on the Hyper-V switch.
1140 * 'ovsDelete' indicates if caller is removing the vport as a result of the
1141 * port being removed from OVS userspace.
1142 * --------------------------------------------------------------------------
1145 OvsRemoveAndDeleteVport(PVOID usrParamsContext,
1146 POVS_SWITCH_CONTEXT switchContext,
1147 POVS_VPORT_ENTRY vport,
1151 POVS_USER_PARAMS_CONTEXT usrParamsCtx =
1152 (POVS_USER_PARAMS_CONTEXT)usrParamsContext;
1153 BOOLEAN hvSwitchPort = FALSE;
1154 BOOLEAN deletedOnOvs = FALSE;
1155 BOOLEAN deletedOnHv = FALSE;
1157 switch (vport->ovsType) {
1158 case OVS_VPORT_TYPE_INTERNAL:
1159 if (!vport->isBridgeInternal) {
1160 if (hvDelete && vport->isAbsentOnHv == FALSE) {
1161 switchContext->internalPortId = 0;
1162 switchContext->internalVport = NULL;
1163 OvsInternalAdapterDown();
1165 hvSwitchPort = TRUE;
1168 case OVS_VPORT_TYPE_VXLAN:
1171 status = OvsRemoveTunnelVport(usrParamsCtx, switchContext, vport,
1172 hvDelete, ovsDelete);
1173 if (status != STATUS_SUCCESS) {
1177 case OVS_VPORT_TYPE_STT:
1178 OvsCleanupSttTunnel(vport);
1180 case OVS_VPORT_TYPE_GRE:
1181 case OVS_VPORT_TYPE_GRE64:
1183 case OVS_VPORT_TYPE_NETDEV:
1184 if (vport->isExternal) {
1185 if (vport->nicIndex == 0) {
1186 /* Such a vport is not part of any of the hash tables, since it
1187 * is not exposed to userspace. See Vport.h for explanation. */
1188 ASSERT(hvDelete == TRUE);
1189 ASSERT(switchContext->numPhysicalNics == 0);
1190 switchContext->virtualExternalPortId = 0;
1191 switchContext->virtualExternalVport = NULL;
1192 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1193 return STATUS_SUCCESS;
1196 hvSwitchPort = TRUE;
1202 * 'hvDelete' == TRUE indicates that the port should be removed from the
1203 * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
1204 * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
1206 * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
1208 if (vport->isAbsentOnHv == TRUE) {
1211 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1212 deletedOnOvs = TRUE;
1215 if (hvDelete && !deletedOnHv) {
1216 vport->isAbsentOnHv = TRUE;
1218 if (vport->isExternal) {
1219 ASSERT(vport->nicIndex != 0);
1220 ASSERT(switchContext->numPhysicalNics);
1221 switchContext->numPhysicalNics--;
1224 /* Remove the port from the relevant lists. */
1225 RemoveEntryList(&vport->portIdLink);
1226 InitializeListHead(&vport->portIdLink);
1229 if (ovsDelete && !deletedOnOvs) {
1230 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
1231 vport->ovsName[0] = '\0';
1233 /* Remove the port from the relevant lists. */
1234 RemoveEntryList(&vport->ovsNameLink);
1235 InitializeListHead(&vport->ovsNameLink);
1236 RemoveEntryList(&vport->portNoLink);
1237 InitializeListHead(&vport->portNoLink);
1238 if (OVS_VPORT_TYPE_VXLAN == vport->ovsType ||
1239 OVS_VPORT_TYPE_STT == vport->ovsType) {
1240 RemoveEntryList(&vport->tunnelVportLink);
1241 InitializeListHead(&vport->tunnelVportLink);
1244 deletedOnOvs = TRUE;
1248 * Deallocate the port if it has been deleted on the Hyper-V switch as well
1251 if (deletedOnHv && deletedOnOvs) {
1253 switchContext->numHvVports--;
1255 switchContext->numNonHvVports--;
1257 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1260 return STATUS_SUCCESS;
1264 OvsRemoveTunnelVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1265 POVS_SWITCH_CONTEXT switchContext,
1266 POVS_VPORT_ENTRY vport,
1270 POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
1273 tunnelContext = OvsAllocateMemory(sizeof(*tunnelContext));
1274 if (tunnelContext == NULL) {
1275 return STATUS_INSUFFICIENT_RESOURCES;
1277 RtlZeroMemory(tunnelContext, sizeof(*tunnelContext));
1279 tunnelContext->switchContext = switchContext;
1280 tunnelContext->hvSwitchPort = FALSE;
1281 tunnelContext->hvDelete = hvDelete;
1282 tunnelContext->ovsDelete = ovsDelete;
1283 tunnelContext->vport = vport;
1286 tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
1287 tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
1288 tunnelContext->outputLength = usrParamsCtx->outputLength;
1289 irp = usrParamsCtx->irp;
1292 return OvsCleanupVxlanTunnel(irp, vport, OvsTunnelVportPendingRemove,
1299 OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
1301 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1303 PNDIS_SWITCH_PORT_PARAMETERS portParam;
1304 PNDIS_SWITCH_PORT_ARRAY portArray = NULL;
1305 POVS_VPORT_ENTRY vport;
1307 OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
1309 status = OvsGetPortsOnSwitch(switchContext, &portArray);
1310 if (status != NDIS_STATUS_SUCCESS) {
1314 for (arrIndex = 0; arrIndex < portArray->NumElements; arrIndex++) {
1315 portParam = NDIS_SWITCH_PORT_AT_ARRAY_INDEX(portArray, arrIndex);
1317 if (portParam->IsValidationPort) {
1321 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
1322 if (vport == NULL) {
1323 status = NDIS_STATUS_RESOURCES;
1326 OvsInitVportWithPortParam(vport, portParam);
1327 status = InitHvVportCommon(switchContext, vport, TRUE);
1328 if (status != NDIS_STATUS_SUCCESS) {
1329 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1335 if (status != NDIS_STATUS_SUCCESS) {
1336 OvsClearAllSwitchVports(switchContext);
1339 OvsFreeSwitchPortsArray(portArray);
1341 OVS_LOG_TRACE("Exit: status: %x", status);
1348 OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext)
1350 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1351 PNDIS_SWITCH_NIC_ARRAY nicArray = NULL;
1353 PNDIS_SWITCH_NIC_PARAMETERS nicParam;
1354 POVS_VPORT_ENTRY vport;
1356 OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
1358 * Now, get NIC list.
1360 status = OvsGetNicsOnSwitch(switchContext, &nicArray);
1361 if (status != NDIS_STATUS_SUCCESS) {
1364 for (arrIndex = 0; arrIndex < nicArray->NumElements; ++arrIndex) {
1366 nicParam = NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray, arrIndex);
1369 * XXX: Check if the port is configured with a VLAN. Disallow such a
1370 * configuration, since we don't support tag-in-tag.
1374 * XXX: Check if the port is connected to a VF. Disconnect the VF in
1378 if (nicParam->NicType == NdisSwitchNicTypeExternal &&
1379 nicParam->NicIndex != 0) {
1380 POVS_VPORT_ENTRY virtExtVport =
1381 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
1383 vport = OvsAllocateVport();
1385 OvsInitPhysNicVport(vport, virtExtVport,
1386 nicParam->NicIndex);
1387 status = InitHvVportCommon(switchContext, vport, TRUE);
1388 if (status != NDIS_STATUS_SUCCESS) {
1389 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1394 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
1396 nicParam->NicIndex);
1398 if (vport == NULL) {
1399 OVS_LOG_ERROR("Fail to allocate vport");
1402 OvsInitVportWithNicParam(switchContext, vport, nicParam);
1403 if (nicParam->NicType == NdisSwitchNicTypeInternal) {
1404 OvsInternalAdapterUp(vport->portNo, &nicParam->NetCfgInstanceId);
1409 OvsFreeSwitchNicsArray(nicArray);
1411 OVS_LOG_TRACE("Exit: status: %x", status);
1416 * --------------------------------------------------------------------------
1417 * Deletes ports added from the Hyper-V switch as well as OVS usersapce. The
1418 * function deletes ports in 'portIdHashArray'. This will delete most of the
1419 * ports that are in the 'portNoHashArray' as well. Any remaining ports
1420 * are deleted by walking the the 'portNoHashArray'.
1421 * --------------------------------------------------------------------------
1424 OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
1426 for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1427 PLIST_ENTRY head, link, next;
1429 head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
1430 LIST_FORALL_SAFE(head, link, next) {
1431 POVS_VPORT_ENTRY vport;
1432 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1433 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
1438 * Remove 'virtualExternalVport' as well. This port is not part of the
1439 * 'portIdHashArray'.
1441 if (switchContext->virtualExternalVport) {
1442 OvsRemoveAndDeleteVport(NULL, switchContext,
1443 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE);
1447 for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1448 PLIST_ENTRY head, link, next;
1449 head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
1450 LIST_FORALL_SAFE(head, link, next) {
1451 POVS_VPORT_ENTRY vport;
1452 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1453 ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
1454 (vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
1455 vport->isBridgeInternal) || vport->isAbsentOnHv == TRUE);
1456 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
1460 ASSERT(switchContext->virtualExternalVport == NULL);
1461 ASSERT(switchContext->internalVport == NULL);
1466 OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
1471 UNICODE_STRING ustr;
1475 ustr.Buffer = wStr->String;
1476 ustr.Length = wStr->Length;
1477 ustr.MaximumLength = IF_MAX_STRING_SIZE;
1480 astr.MaximumLength = maxStrLen;
1483 size = RtlUnicodeStringToAnsiSize(&ustr);
1484 if (size > maxStrLen) {
1485 return STATUS_BUFFER_OVERFLOW;
1488 status = RtlUnicodeStringToAnsiString(&astr, &ustr, FALSE);
1490 ASSERT(status == STATUS_SUCCESS);
1491 if (status != STATUS_SUCCESS) {
1494 ASSERT(astr.Length <= maxStrLen);
1495 str[astr.Length] = 0;
1496 return STATUS_SUCCESS;
1500 * --------------------------------------------------------------------------
1501 * Utility function that populates a 'OVS_VPORT_EXT_INFO' structure for the
1503 * --------------------------------------------------------------------------
1506 OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
1507 POVS_VPORT_EXT_INFO extInfo)
1509 POVS_VPORT_ENTRY vport;
1511 LOCK_STATE_EX lockState;
1512 NTSTATUS status = STATUS_SUCCESS;
1513 BOOLEAN doConvert = FALSE;
1515 RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO));
1516 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1517 if (vportGet->portNo == 0) {
1518 StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
1519 vport = OvsFindVportByHvNameA(gOvsSwitchContext, vportGet->name);
1520 if (vport != NULL) {
1521 /* If the port is not a Hyper-V port and it has been added earlier,
1522 * we'll find it in 'ovsPortNameHashArray'. */
1523 vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name);
1526 vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo);
1528 if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED &&
1529 vport->ovsState != OVS_STATE_NIC_CREATED)) {
1530 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1531 if (vportGet->portNo) {
1532 OVS_LOG_WARN("vport %u does not exist any more", vportGet->portNo);
1534 OVS_LOG_WARN("vport %s does not exist any more", vportGet->name);
1536 status = STATUS_DEVICE_DOES_NOT_EXIST;
1539 extInfo->dpNo = vportGet->dpNo;
1540 extInfo->portNo = vport->portNo;
1541 RtlCopyMemory(extInfo->macAddress, vport->currMacAddress,
1542 sizeof (vport->currMacAddress));
1543 RtlCopyMemory(extInfo->permMACAddress, vport->permMacAddress,
1544 sizeof (vport->permMacAddress));
1545 if (vport->ovsType == OVS_VPORT_TYPE_NETDEV) {
1546 RtlCopyMemory(extInfo->vmMACAddress, vport->vmMacAddress,
1547 sizeof (vport->vmMacAddress));
1549 extInfo->nicIndex = vport->nicIndex;
1550 extInfo->portId = vport->portId;
1551 extInfo->type = vport->ovsType;
1552 extInfo->mtu = vport->mtu;
1556 if (vport->ovsState == OVS_STATE_NIC_CREATED) {
1557 extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_DOWN;
1558 } else if (vport->ovsState == OVS_STATE_CONNECTED) {
1559 extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
1561 extInfo->status = OVS_EVENT_DISCONNECT;
1563 if (extInfo->type == OVS_VPORT_TYPE_NETDEV &&
1564 (vport->ovsState == OVS_STATE_NIC_CREATED ||
1565 vport->ovsState == OVS_STATE_CONNECTED)) {
1568 extInfo->vmUUID[0] = 0;
1569 extInfo->vifUUID[0] = 0;
1571 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1573 status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName,
1575 OVS_MAX_PORT_NAME_LENGTH);
1576 if (status != STATUS_SUCCESS) {
1577 OVS_LOG_INFO("Fail to convert NIC name.");
1578 extInfo->vmUUID[0] = 0;
1581 status = OvsConvertIfCountedStrToAnsiStr(&vport->vmName,
1583 OVS_MAX_VM_UUID_LEN);
1584 if (status != STATUS_SUCCESS) {
1585 OVS_LOG_INFO("Fail to convert VM name.");
1586 extInfo->vmUUID[0] = 0;
1589 status = OvsConvertIfCountedStrToAnsiStr(&vport->nicName,
1591 OVS_MAX_VIF_UUID_LEN);
1592 if (status != STATUS_SUCCESS) {
1593 OVS_LOG_INFO("Fail to convert nic UUID");
1594 extInfo->vifUUID[0] = 0;
1597 * for now ignore status
1599 status = STATUS_SUCCESS;
1607 * --------------------------------------------------------------------------
1608 * Command Handler for 'OVS_WIN_NETDEV_CMD_GET'.
1609 * --------------------------------------------------------------------------
1612 OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1615 NTSTATUS status = STATUS_SUCCESS;
1616 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1617 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1618 NL_ERROR nlError = NL_ERROR_SUCCESS;
1619 OVS_VPORT_GET vportGet;
1620 OVS_VPORT_EXT_INFO info;
1622 static const NL_POLICY ovsNetdevPolicy[] = {
1623 [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING,
1625 .maxLen = IFNAMSIZ },
1627 PNL_ATTR netdevAttrs[ARRAY_SIZE(ovsNetdevPolicy)];
1629 /* input buffer has been validated while validating transaction dev op. */
1630 ASSERT(usrParamsCtx->inputBuffer != NULL &&
1631 usrParamsCtx->inputLength > sizeof *msgIn);
1633 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1634 return STATUS_INVALID_BUFFER_SIZE;
1637 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1638 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1639 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1640 ovsNetdevPolicy, netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
1641 return STATUS_INVALID_PARAMETER;
1644 vportGet.portNo = 0;
1645 RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
1646 NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
1648 status = OvsGetExtInfoIoctl(&vportGet, &info);
1649 if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
1650 nlError = NL_ERROR_NODEV;
1654 status = CreateNetlinkMesgForNetdev(&info, msgIn,
1655 usrParamsCtx->outputBuffer, usrParamsCtx->outputLength,
1656 gOvsSwitchContext->dpNo);
1657 if (status == STATUS_SUCCESS) {
1658 *replyLen = msgOut->nlMsg.nlmsgLen;
1662 if (nlError != NL_ERROR_SUCCESS) {
1663 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1664 usrParamsCtx->outputBuffer;
1666 NlBuildErrorMsg(msgIn, msgError, nlError);
1667 *replyLen = msgError->nlMsg.nlmsgLen;
1670 return STATUS_SUCCESS;
1675 * --------------------------------------------------------------------------
1676 * Utility function to construct an OVS_MESSAGE for the specified vport. The
1677 * OVS_MESSAGE contains the output of a netdev command.
1678 * --------------------------------------------------------------------------
1681 CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
1690 UINT32 netdevFlags = 0;
1692 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1694 ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1695 msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1696 msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1699 return STATUS_INVALID_BUFFER_SIZE;
1702 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
1705 return STATUS_INVALID_BUFFER_SIZE;
1708 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
1710 return STATUS_INVALID_BUFFER_SIZE;
1713 ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
1716 return STATUS_INVALID_BUFFER_SIZE;
1719 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
1720 (PCHAR)info->macAddress, sizeof (info->macAddress));
1722 return STATUS_INVALID_BUFFER_SIZE;
1725 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
1727 return STATUS_INVALID_BUFFER_SIZE;
1730 if (info->status != OVS_EVENT_CONNECT) {
1731 netdevFlags = OVS_WIN_NETDEV_IFF_UP;
1733 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
1736 return STATUS_INVALID_BUFFER_SIZE;
1740 * XXX: add netdev_stats when we have the definition available in the
1744 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1745 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1747 return STATUS_SUCCESS;
1750 static __inline VOID
1751 OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec)
1753 while ((!switchContext->isActivated) &&
1754 (!switchContext->isActivateFailed)) {
1755 /* Wait for the switch to be active and
1756 * the list of ports in OVS to be initialized. */
1757 NdisMSleep(sleepMicroSec);
1762 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1769 OVS_VPORT_FULL_STATS vportStats;
1773 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1775 ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1776 msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1777 msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1780 return STATUS_INVALID_BUFFER_SIZE;
1783 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1785 return STATUS_INVALID_BUFFER_SIZE;
1788 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1790 return STATUS_INVALID_BUFFER_SIZE;
1793 ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1795 return STATUS_INVALID_BUFFER_SIZE;
1799 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1800 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1801 * it means we have an array of pids, instead of a single pid.
1802 * ATM we assume we have one pid only.
1805 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1808 return STATUS_INVALID_BUFFER_SIZE;
1812 vportStats.rxPackets = vport->stats.rxPackets;
1813 vportStats.rxBytes = vport->stats.rxBytes;
1814 vportStats.txPackets = vport->stats.txPackets;
1815 vportStats.txBytes = vport->stats.txBytes;
1816 vportStats.rxErrors = vport->errStats.rxErrors;
1817 vportStats.txErrors = vport->errStats.txErrors;
1818 vportStats.rxDropped = vport->errStats.rxDropped;
1819 vportStats.txDropped = vport->errStats.txDropped;
1821 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1823 sizeof(OVS_VPORT_FULL_STATS));
1825 return STATUS_INVALID_BUFFER_SIZE;
1829 * XXX: when vxlan udp dest port becomes configurable, we will also need
1830 * to add vport options
1833 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1834 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1836 return STATUS_SUCCESS;
1840 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1844 POVS_OPEN_INSTANCE instance =
1845 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1846 LOCK_STATE_EX lockState;
1847 UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1850 * XXX: this function shares some code with other dump command(s).
1851 * In the future, we will need to refactor the dump functions
1854 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1856 if (instance->dumpState.ovsMsg == NULL) {
1858 return STATUS_INVALID_DEVICE_STATE;
1861 /* Output buffer has been validated while validating read dev op. */
1862 ASSERT(usrParamsCtx->outputBuffer != NULL);
1864 msgIn = instance->dumpState.ovsMsg;
1867 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1868 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1869 * it means we have an array of pids, instead of a single pid.
1870 * ATM we assume we have one pid only.
1872 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1874 if (gOvsSwitchContext->numHvVports > 0 ||
1875 gOvsSwitchContext->numNonHvVports > 0) {
1876 /* inBucket: the bucket, used for lookup */
1877 UINT32 inBucket = instance->dumpState.index[0];
1878 /* inIndex: index within the given bucket, used for lookup */
1879 UINT32 inIndex = instance->dumpState.index[1];
1880 /* the bucket to be used for the next dump operation */
1881 UINT32 outBucket = 0;
1882 /* the index within the outBucket to be used for the next dump */
1883 UINT32 outIndex = 0;
1885 for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1886 PLIST_ENTRY head, link;
1887 head = &(gOvsSwitchContext->portNoHashArray[i]);
1888 POVS_VPORT_ENTRY vport = NULL;
1891 LIST_FORALL(head, link) {
1894 * if one or more dumps were previously done on this same bucket,
1895 * inIndex will be > 0, so we'll need to reply with the
1896 * inIndex + 1 vport from the bucket.
1898 if (outIndex >= inIndex) {
1899 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1901 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
1902 OvsCreateMsgFromVport(vport, msgIn,
1903 usrParamsCtx->outputBuffer,
1904 usrParamsCtx->outputLength,
1905 gOvsSwitchContext->dpNo);
1918 * if no vport was found above, check the next bucket, beginning
1919 * with the first (i.e. index 0) elem from within that bucket
1926 /* XXX: what about NLMSG_DONE (as msg type)? */
1927 instance->dumpState.index[0] = outBucket;
1928 instance->dumpState.index[1] = outIndex;
1931 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1933 /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1934 if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1935 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1936 *replyLen = msgOut->nlMsg.nlmsgLen;
1939 * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1943 /* Free up the dump state, since there's no more data to continue. */
1944 FreeUserDumpState(instance);
1947 return STATUS_SUCCESS;
1951 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1954 NTSTATUS status = STATUS_SUCCESS;
1955 LOCK_STATE_EX lockState;
1957 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1958 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1959 POVS_VPORT_ENTRY vport = NULL;
1960 NL_ERROR nlError = NL_ERROR_SUCCESS;
1961 PCHAR portName = NULL;
1962 UINT32 portNameLen = 0;
1963 UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
1965 static const NL_POLICY ovsVportPolicy[] = {
1966 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1967 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1972 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1974 /* input buffer has been validated while validating write dev op. */
1975 ASSERT(usrParamsCtx->inputBuffer != NULL);
1977 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1978 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1979 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1980 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1981 return STATUS_INVALID_PARAMETER;
1984 /* Output buffer has been validated while validating transact dev op. */
1985 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1987 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1988 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1989 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1990 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1992 /* the port name is expected to be null-terminated */
1993 ASSERT(portName[portNameLen - 1] == '\0');
1995 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1996 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1997 portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1999 vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
2001 nlError = NL_ERROR_INVAL;
2002 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2007 nlError = NL_ERROR_NODEV;
2008 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2012 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2013 usrParamsCtx->outputLength,
2014 gOvsSwitchContext->dpNo);
2015 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2017 *replyLen = msgOut->nlMsg.nlmsgLen;
2020 if (nlError != NL_ERROR_SUCCESS) {
2021 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2022 usrParamsCtx->outputBuffer;
2024 NlBuildErrorMsg(msgIn, msgError, nlError);
2025 *replyLen = msgError->nlMsg.nlmsgLen;
2028 return STATUS_SUCCESS;
2032 * --------------------------------------------------------------------------
2033 * Command Handler for 'OVS_VPORT_CMD_GET'.
2035 * The function handles the initial call to setup the dump state, as well as
2036 * subsequent calls to continue dumping data.
2037 * --------------------------------------------------------------------------
2040 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2045 switch (usrParamsCtx->devOp) {
2046 case OVS_WRITE_DEV_OP:
2047 return OvsSetupDumpStart(usrParamsCtx);
2049 case OVS_READ_DEV_OP:
2050 return OvsGetVportDumpNext(usrParamsCtx, replyLen);
2052 case OVS_TRANSACTION_DEV_OP:
2053 return OvsGetVport(usrParamsCtx, replyLen);
2056 return STATUS_INVALID_DEVICE_REQUEST;
2062 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
2064 /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
2065 for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
2066 POVS_VPORT_ENTRY vport;
2068 vport = OvsFindVportByPortNo(switchContext, i);
2074 return OVS_DPPORT_NUMBER_INVALID;
2078 * --------------------------------------------------------------------------
2079 * Command Handler for 'OVS_VPORT_CMD_NEW'.
2080 * --------------------------------------------------------------------------
2083 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2086 NDIS_STATUS status = STATUS_SUCCESS;
2087 LOCK_STATE_EX lockState;
2089 NL_ERROR nlError = NL_ERROR_SUCCESS;
2090 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2091 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2092 POVS_VPORT_ENTRY vport = NULL;
2096 BOOLEAN isBridgeInternal = FALSE;
2097 BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE;
2098 BOOLEAN addInternalPortAsNetdev = FALSE;
2100 static const NL_POLICY ovsVportPolicy[] = {
2101 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2102 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2103 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2105 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2106 .optional = FALSE },
2107 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2110 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2112 /* input buffer has been validated while validating write dev op. */
2113 ASSERT(usrParamsCtx->inputBuffer != NULL);
2115 /* Output buffer has been validated while validating transact dev op. */
2116 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2118 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2119 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2120 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2121 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2122 return STATUS_INVALID_PARAMETER;
2125 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2126 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2127 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2129 /* we are expecting null terminated strings to be passed */
2130 ASSERT(portName[portNameLen - 1] == '\0');
2132 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2134 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2136 nlError = NL_ERROR_EXIST;
2140 if (portName && portType == OVS_VPORT_TYPE_NETDEV &&
2141 !strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
2142 addInternalPortAsNetdev = TRUE;
2145 if (portName && portType == OVS_VPORT_TYPE_INTERNAL &&
2146 strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
2147 isBridgeInternal = TRUE;
2150 if (portType == OVS_VPORT_TYPE_INTERNAL && !isBridgeInternal) {
2151 vport = gOvsSwitchContext->internalVport;
2152 } else if (portType == OVS_VPORT_TYPE_NETDEV) {
2153 /* External ports can also be looked up like VIF ports. */
2154 vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
2156 ASSERT(OvsIsTunnelVportType(portType) ||
2157 (portType == OVS_VPORT_TYPE_INTERNAL && isBridgeInternal));
2159 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
2160 if (vport == NULL) {
2161 nlError = NL_ERROR_NOMEM;
2164 vportAllocated = TRUE;
2166 if (OvsIsTunnelVportType(portType)) {
2167 UINT16 transportPortDest = 0;
2170 case OVS_VPORT_TYPE_VXLAN:
2171 transportPortDest = VXLAN_UDP_PORT;
2173 case OVS_VPORT_TYPE_STT:
2174 transportPortDest = STT_TCP_PORT;
2180 PNL_ATTR attr = NlAttrFindNested(vportAttrs[OVS_VPORT_ATTR_OPTIONS],
2181 OVS_TUNNEL_ATTR_DST_PORT);
2183 transportPortDest = NlAttrGetU16(attr);
2186 status = OvsInitTunnelVport(usrParamsCtx,
2191 nlError = NlMapStatusToNlErr(status);
2193 OvsInitBridgeInternalVport(vport);
2196 vportInitialized = TRUE;
2198 if (nlError == NL_ERROR_SUCCESS) {
2199 vport->ovsState = OVS_STATE_CONNECTED;
2200 vport->nicState = NdisSwitchNicStateConnected;
2203 * Allow the vport to be deleted, because there is no
2204 * corresponding hyper-v switch part.
2206 vport->isAbsentOnHv = TRUE;
2213 nlError = NL_ERROR_INVAL;
2216 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2217 nlError = NL_ERROR_EXIST;
2221 /* Initialize the vport with OVS specific properties. */
2222 if (addInternalPortAsNetdev != TRUE) {
2223 vport->ovsType = portType;
2225 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2227 * XXX: when we implement the limit for ovs port number to be
2228 * MAXUINT16, we'll need to check the port number received from the
2231 vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2233 vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
2234 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2235 nlError = NL_ERROR_NOMEM;
2240 /* The ovs port name must be uninitialized. */
2241 ASSERT(vport->ovsName[0] == '\0');
2242 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2244 RtlCopyMemory(vport->ovsName, portName, portNameLen);
2245 /* if we don't have options, then vport->portOptions will be NULL */
2246 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2249 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2250 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2251 * it means we have an array of pids, instead of a single pid.
2252 * ATM we assume we have one pid only.
2254 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2256 status = InitOvsVportCommon(gOvsSwitchContext, vport);
2257 ASSERT(status == STATUS_SUCCESS);
2259 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2260 usrParamsCtx->outputLength,
2261 gOvsSwitchContext->dpNo);
2263 *replyLen = msgOut->nlMsg.nlmsgLen;
2266 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2268 if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2269 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2270 usrParamsCtx->outputBuffer;
2272 if (vport && vportAllocated == TRUE) {
2273 if (vportInitialized == TRUE) {
2274 if (OvsIsTunnelVportType(portType)) {
2275 switch (vport->ovsType) {
2276 case OVS_VPORT_TYPE_VXLAN:
2277 OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2279 case OVS_VPORT_TYPE_STT:
2280 OvsCleanupSttTunnel(vport);;
2283 ASSERT(!"Invalid tunnel port type");
2287 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2290 NlBuildErrorMsg(msgIn, msgError, nlError);
2291 *replyLen = msgError->nlMsg.nlmsgLen;
2294 return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2299 * --------------------------------------------------------------------------
2300 * Command Handler for 'OVS_VPORT_CMD_SET'.
2301 * --------------------------------------------------------------------------
2304 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2307 NDIS_STATUS status = STATUS_SUCCESS;
2308 LOCK_STATE_EX lockState;
2310 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2311 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2312 POVS_VPORT_ENTRY vport = NULL;
2313 NL_ERROR nlError = NL_ERROR_SUCCESS;
2315 static const NL_POLICY ovsVportPolicy[] = {
2316 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2317 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
2318 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2320 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2322 [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
2323 .minLen = sizeof(OVS_VPORT_FULL_STATS),
2324 .maxLen = sizeof(OVS_VPORT_FULL_STATS),
2326 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2328 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2330 ASSERT(usrParamsCtx->inputBuffer != NULL);
2332 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2333 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2334 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2335 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2336 return STATUS_INVALID_PARAMETER;
2339 /* Output buffer has been validated while validating transact dev op. */
2340 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2342 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2343 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2344 PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2346 UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2348 /* the port name is expected to be null-terminated */
2349 ASSERT(portName[portNameLen - 1] == '\0');
2351 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2352 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2353 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2354 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2358 nlError = NL_ERROR_NODEV;
2363 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2364 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2365 * it means we have an array of pids, instead of a single pid.
2366 * Currently, we support only one pid.
2368 if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
2369 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2372 if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
2373 OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2374 if (type != vport->ovsType) {
2375 nlError = NL_ERROR_INVAL;
2380 if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2381 OVS_LOG_ERROR("Vport options not supported");
2382 nlError = NL_ERROR_NOTSUPP;
2386 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2387 usrParamsCtx->outputLength,
2388 gOvsSwitchContext->dpNo);
2390 *replyLen = msgOut->nlMsg.nlmsgLen;
2393 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2395 if (nlError != NL_ERROR_SUCCESS) {
2396 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2397 usrParamsCtx->outputBuffer;
2399 NlBuildErrorMsg(msgIn, msgError, nlError);
2400 *replyLen = msgError->nlMsg.nlmsgLen;
2403 return STATUS_SUCCESS;
2407 * --------------------------------------------------------------------------
2408 * Command Handler for 'OVS_VPORT_CMD_DEL'.
2409 * --------------------------------------------------------------------------
2412 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2415 NDIS_STATUS status = STATUS_SUCCESS;
2416 LOCK_STATE_EX lockState;
2418 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2419 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2420 POVS_VPORT_ENTRY vport = NULL;
2421 NL_ERROR nlError = NL_ERROR_SUCCESS;
2422 PSTR portName = NULL;
2423 UINT32 portNameLen = 0;
2425 static const NL_POLICY ovsVportPolicy[] = {
2426 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2427 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2430 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2432 ASSERT(usrParamsCtx->inputBuffer != NULL);
2434 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2435 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2436 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2437 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2438 return STATUS_INVALID_PARAMETER;
2441 /* Output buffer has been validated while validating transact dev op. */
2442 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2444 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2445 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2446 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2447 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2449 /* the port name is expected to be null-terminated */
2450 ASSERT(portName[portNameLen - 1] == '\0');
2452 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2454 else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2455 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2456 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2460 nlError = NL_ERROR_NODEV;
2464 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2465 usrParamsCtx->outputLength,
2466 gOvsSwitchContext->dpNo);
2468 *replyLen = msgOut->nlMsg.nlmsgLen;
2471 * Mark the port as deleted from OVS userspace. If the port does not exist
2472 * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
2474 status = OvsRemoveAndDeleteVport(usrParamsCtx,
2480 nlError = NlMapStatusToNlErr(status);
2484 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2486 if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2487 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2488 usrParamsCtx->outputBuffer;
2490 NlBuildErrorMsg(msgIn, msgError, nlError);
2491 *replyLen = msgError->nlMsg.nlmsgLen;
2494 return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2498 OvsTunnelVportPendingRemove(PVOID context,
2502 POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2503 (POVS_TUNFLT_INIT_CONTEXT) context;
2504 POVS_SWITCH_CONTEXT switchContext = tunnelContext->switchContext;
2505 POVS_VPORT_ENTRY vport = tunnelContext->vport;
2506 POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2507 POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2508 NL_ERROR nlError = NlMapStatusToNlErr(status);
2509 LOCK_STATE_EX lockState;
2511 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
2513 if (msgIn && msgOut) {
2514 /* Check the received status to reply to the caller. */
2515 if (STATUS_SUCCESS == status) {
2516 OvsCreateMsgFromVport(vport,
2519 tunnelContext->outputLength,
2520 switchContext->dpNo);
2522 *replyLen = msgOut->nlMsg.nlmsgLen;
2524 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)msgOut;
2526 NlBuildErrorMsg(msgIn, msgError, nlError);
2527 *replyLen = msgError->nlMsg.nlmsgLen;
2531 ASSERT(vport->isAbsentOnHv == TRUE);
2532 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
2534 /* Remove the port from the relevant lists. */
2535 switchContext->numNonHvVports--;
2536 RemoveEntryList(&vport->ovsNameLink);
2537 RemoveEntryList(&vport->portNoLink);
2538 RemoveEntryList(&vport->tunnelVportLink);
2541 OvsFreeMemoryWithTag(vport->priv, OVS_VXLAN_POOL_TAG);
2545 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2547 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
2551 OvsTunnelVportPendingInit(PVOID context,
2555 POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2556 (POVS_TUNFLT_INIT_CONTEXT) context;
2557 POVS_VPORT_ENTRY vport = tunnelContext->vport;
2558 POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2559 POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2561 ULONG portNameLen = 0;
2562 UINT32 portType = 0;
2563 NL_ERROR nlError = NL_ERROR_SUCCESS;
2564 BOOLEAN error = TRUE;
2567 if (!NT_SUCCESS(status)) {
2568 nlError = NlMapStatusToNlErr(status);
2572 static const NL_POLICY ovsVportPolicy[] = {
2573 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2574 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2575 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2576 .optional = FALSE },
2577 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2578 .optional = FALSE },
2579 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2582 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2584 /* input buffer has been validated while validating write dev op. */
2585 ASSERT(msgIn != NULL);
2587 /* Output buffer has been validated while validating transact dev op. */
2588 ASSERT(msgOut != NULL && tunnelContext->outputLength >= sizeof *msgOut);
2590 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2591 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2592 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2593 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2594 nlError = NL_ERROR_INVAL;
2598 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2599 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2600 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2602 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2603 nlError = NL_ERROR_EXIST;
2607 vport->ovsState = OVS_STATE_CONNECTED;
2608 vport->nicState = NdisSwitchNicStateConnected;
2611 * Allow the vport to be deleted, because there is no
2612 * corresponding hyper-v switch part.
2614 vport->isAbsentOnHv = TRUE;
2616 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2618 * XXX: when we implement the limit for OVS port number to be
2619 * MAXUINT16, we'll need to check the port number received from the
2623 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2626 OvsComputeVportNo(gOvsSwitchContext);
2627 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2628 nlError = NL_ERROR_NOMEM;
2633 /* The ovs port name must be uninitialized. */
2634 ASSERT(vport->ovsName[0] == '\0');
2635 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2637 RtlCopyMemory(vport->ovsName, portName, portNameLen);
2638 /* if we don't have options, then vport->portOptions will be NULL */
2639 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2642 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2643 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2644 * it means we have an array of pids, instead of a single pid.
2645 * ATM we assume we have one pid only.
2648 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2650 status = InitOvsVportCommon(gOvsSwitchContext, vport);
2651 ASSERT(status == STATUS_SUCCESS);
2653 OvsCreateMsgFromVport(vport,
2656 tunnelContext->outputLength,
2657 gOvsSwitchContext->dpNo);
2659 *replyLen = msgOut->nlMsg.nlmsgLen;
2665 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) msgOut;
2667 OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2668 OvsFreeMemory(vport);
2670 NlBuildErrorMsg(msgIn, msgError, nlError);
2671 *replyLen = msgError->nlMsg.nlmsgLen;