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.
31 #define OVS_DBG_MOD OVS_DBG_VPORT
34 #define VPORT_NIC_ENTER(_nic) \
35 OVS_LOG_TRACE("Enter: PortId: %x, NicIndex: %d", _nic->PortId, \
38 #define VPORT_NIC_EXIT(_nic) \
39 OVS_LOG_TRACE("Exit: PortId: %x, NicIndex: %d", _nic->PortId, \
42 #define VPORT_PORT_ENTER(_port) \
43 OVS_LOG_TRACE("Enter: PortId: %x", _port->PortId)
45 #define VPORT_PORT_EXIT(_port) \
46 OVS_LOG_TRACE("Exit: PortId: %x", _port->PortId)
48 #define OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC 100
50 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
51 extern PNDIS_SPIN_LOCK gOvsCtrlLock;
53 static VOID OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
54 PNDIS_SWITCH_PORT_PARAMETERS portParam);
55 static VOID OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
56 POVS_VPORT_ENTRY vport, PNDIS_SWITCH_NIC_PARAMETERS nicParam);
57 static VOID OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVPort,
58 POVS_VPORT_ENTRY virtExtVport, UINT32 nicIndex);
59 static __inline VOID OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext,
61 static NTSTATUS OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
62 POVS_VPORT_EXT_INFO extInfo);
63 static NTSTATUS CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
68 static POVS_VPORT_ENTRY OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
69 PWSTR wsName, SIZE_T wstrSize);
70 static NDIS_STATUS InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
71 POVS_VPORT_ENTRY vport,
75 * Functions implemented in relaton to NDIS port manipulation.
78 HvCreatePort(POVS_SWITCH_CONTEXT switchContext,
79 PNDIS_SWITCH_PORT_PARAMETERS portParam)
81 POVS_VPORT_ENTRY vport;
82 LOCK_STATE_EX lockState;
83 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
84 BOOLEAN newPort = FALSE;
86 VPORT_PORT_ENTER(portParam);
88 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
89 /* Lookup by port ID. */
90 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
91 portParam->PortId, 0);
93 OVS_LOG_ERROR("Port add failed due to duplicate port name, "
94 "port Id: %u", portParam->PortId);
95 status = STATUS_DATA_NOT_ACCEPTED;
96 goto create_port_done;
100 * Lookup by port name to see if this port with this name had been added
101 * (and deleted) previously.
103 vport = OvsFindVportByHvNameW(gOvsSwitchContext,
104 portParam->PortFriendlyName.String,
105 portParam->PortFriendlyName.Length);
106 if (vport && vport->isPresentOnHv == FALSE) {
107 OVS_LOG_ERROR("Port add failed since a port already exists on "
108 "the specified port Id: %u, ovsName: %s",
109 portParam->PortId, vport->ovsName);
110 status = STATUS_DATA_NOT_ACCEPTED;
111 goto create_port_done;
115 ASSERT(vport->isPresentOnHv);
116 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
119 * It should be possible to simply just mark this port as "not deleted"
120 * given that the port Id and the name are the same and also provided
121 * that the other properties that we cache have not changed.
123 if (vport->portType != portParam->PortType) {
124 OVS_LOG_INFO("Port add failed due to PortType change, port Id: %u"
125 " old: %u, new: %u", portParam->PortId,
126 vport->portType, portParam->PortType);
127 status = STATUS_DATA_NOT_ACCEPTED;
128 goto create_port_done;
130 vport->isPresentOnHv = FALSE;
132 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
134 status = NDIS_STATUS_RESOURCES;
135 goto create_port_done;
139 OvsInitVportWithPortParam(vport, portParam);
140 InitHvVportCommon(switchContext, vport, newPort);
143 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
144 VPORT_PORT_EXIT(portParam);
150 * Function updating the port properties
153 HvUpdatePort(POVS_SWITCH_CONTEXT switchContext,
154 PNDIS_SWITCH_PORT_PARAMETERS portParam)
156 POVS_VPORT_ENTRY vport;
157 LOCK_STATE_EX lockState;
158 OVS_VPORT_STATE ovsState;
159 NDIS_SWITCH_NIC_STATE nicState;
161 VPORT_PORT_ENTER(portParam);
163 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
164 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
165 portParam->PortId, 0);
167 * Update properties only for NETDEV ports for supprting PS script
168 * We don't allow changing the names of the internal or external ports
170 if (vport == NULL || (( vport->portType != NdisSwitchPortTypeSynthetic) &&
171 ( vport->portType != NdisSwitchPortTypeEmulated))) {
172 goto update_port_done;
175 /* Store the nic and the OVS states as Nic Create won't be called */
176 ovsState = vport->ovsState;
177 nicState = vport->nicState;
180 * Currently only the port friendly name is being updated
181 * Make sure that no other properties are changed
183 ASSERT(portParam->PortId == vport->portId);
184 ASSERT(portParam->PortState == vport->portState);
185 ASSERT(portParam->PortType == vport->portType);
188 * Call the set parameters function the handle all properties
189 * change in a single place in case future version supports change of
192 OvsInitVportWithPortParam(vport, portParam);
193 /* Retore the nic and OVS states */
194 vport->nicState = nicState;
195 vport->ovsState = ovsState;
198 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
199 VPORT_PORT_EXIT(portParam);
201 /* Must always return success */
202 return NDIS_STATUS_SUCCESS;
206 HvTeardownPort(POVS_SWITCH_CONTEXT switchContext,
207 PNDIS_SWITCH_PORT_PARAMETERS portParam)
209 POVS_VPORT_ENTRY vport;
210 LOCK_STATE_EX lockState;
212 VPORT_PORT_ENTER(portParam);
214 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
215 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
216 portParam->PortId, 0);
218 /* add assertion here */
219 vport->portState = NdisSwitchPortStateTeardown;
220 vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
222 OVS_LOG_WARN("Vport not present.");
224 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
226 VPORT_PORT_EXIT(portParam);
231 HvDeletePort(POVS_SWITCH_CONTEXT switchContext,
232 PNDIS_SWITCH_PORT_PARAMETERS portParams)
234 POVS_VPORT_ENTRY vport;
235 LOCK_STATE_EX lockState;
237 VPORT_PORT_ENTER(portParams);
239 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
240 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
241 portParams->PortId, 0);
244 * XXX: we can only destroy and remove the port if its datapath port
245 * counterpart was deleted. If the datapath port counterpart is present,
246 * we only mark the vport for deletion, so that a netlink command vport
247 * delete will delete the vport.
250 OvsRemoveAndDeleteVport(switchContext, vport, TRUE, FALSE, NULL);
252 OVS_LOG_WARN("Vport not present.");
254 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
256 VPORT_PORT_EXIT(portParams);
261 * Functions implemented in relaton to NDIS NIC manipulation.
264 HvCreateNic(POVS_SWITCH_CONTEXT switchContext,
265 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
267 POVS_VPORT_ENTRY vport;
270 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
272 LOCK_STATE_EX lockState;
274 VPORT_NIC_ENTER(nicParam);
276 /* Wait for lists to be initialized. */
277 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
279 if (!switchContext->isActivated) {
280 OVS_LOG_WARN("Switch is not activated yet.");
281 /* Veto the creation of nic */
282 status = NDIS_STATUS_NOT_SUPPORTED;
286 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
287 vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId, 0);
289 OVS_LOG_ERROR("Create NIC without Switch Port,"
290 " PortId: %x, NicIndex: %d",
291 nicParam->PortId, nicParam->NicIndex);
292 status = NDIS_STATUS_INVALID_PARAMETER;
296 if (nicParam->NicType == NdisSwitchNicTypeExternal &&
297 nicParam->NicIndex != 0) {
298 POVS_VPORT_ENTRY virtExtVport =
299 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
301 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
303 status = NDIS_STATUS_RESOURCES;
306 OvsInitPhysNicVport(vport, virtExtVport, nicParam->NicIndex);
307 status = InitHvVportCommon(switchContext, vport, TRUE);
308 if (status != NDIS_STATUS_SUCCESS) {
309 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
313 OvsInitVportWithNicParam(switchContext, vport, nicParam);
314 portNo = vport->portNo;
315 if (vport->ovsState == OVS_STATE_CONNECTED) {
316 event = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
317 } else if (vport->ovsState == OVS_STATE_NIC_CREATED) {
318 event = OVS_EVENT_CONNECT;
322 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
323 if (portNo != OVS_DPPORT_NUMBER_INVALID && event) {
324 OvsPostEvent(portNo, event);
328 VPORT_NIC_EXIT(nicParam);
329 OVS_LOG_TRACE("Exit: status %8x.\n", status);
335 /* Mark already created NIC as connected. */
337 HvConnectNic(POVS_SWITCH_CONTEXT switchContext,
338 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
340 LOCK_STATE_EX lockState;
341 POVS_VPORT_ENTRY vport;
344 VPORT_NIC_ENTER(nicParam);
346 /* Wait for lists to be initialized. */
347 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
349 if (!switchContext->isActivated) {
350 OVS_LOG_WARN("Switch is not activated yet.");
354 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
355 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
360 OVS_LOG_WARN("Vport not present.");
361 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
366 vport->ovsState = OVS_STATE_CONNECTED;
367 vport->nicState = NdisSwitchNicStateConnected;
368 portNo = vport->portNo;
370 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
372 /* XXX only if portNo != INVALID or always? */
373 OvsPostEvent(portNo, OVS_EVENT_LINK_UP);
375 if (nicParam->NicType == NdisSwitchNicTypeInternal) {
376 OvsInternalAdapterUp(portNo, &nicParam->NetCfgInstanceId);
380 VPORT_NIC_EXIT(nicParam);
384 HvUpdateNic(POVS_SWITCH_CONTEXT switchContext,
385 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
387 POVS_VPORT_ENTRY vport;
388 LOCK_STATE_EX lockState;
390 UINT32 status = 0, portNo = 0;
392 VPORT_NIC_ENTER(nicParam);
394 /* Wait for lists to be initialized. */
395 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
397 if (!switchContext->isActivated) {
398 OVS_LOG_WARN("Switch is not activated yet.");
399 goto update_nic_done;
402 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
403 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
407 OVS_LOG_WARN("Vport search failed.");
408 goto update_nic_done;
410 switch (nicParam->NicType) {
411 case NdisSwitchNicTypeExternal:
412 case NdisSwitchNicTypeInternal:
413 RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
416 case NdisSwitchNicTypeSynthetic:
417 case NdisSwitchNicTypeEmulated:
418 if (!RtlEqualMemory(vport->vmMacAddress, nicParam->VMMacAddress,
419 sizeof (vport->vmMacAddress))) {
420 status |= OVS_EVENT_MAC_CHANGE;
421 RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
422 sizeof (vport->vmMacAddress));
428 if (!RtlEqualMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
429 sizeof (vport->permMacAddress))) {
430 RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
431 sizeof (vport->permMacAddress));
432 status |= OVS_EVENT_MAC_CHANGE;
434 if (!RtlEqualMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
435 sizeof (vport->currMacAddress))) {
436 RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
437 sizeof (vport->currMacAddress));
438 status |= OVS_EVENT_MAC_CHANGE;
441 if (vport->mtu != nicParam->MTU) {
442 vport->mtu = nicParam->MTU;
443 status |= OVS_EVENT_MTU_CHANGE;
445 vport->numaNodeId = nicParam->NumaNodeId;
446 portNo = vport->portNo;
448 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
449 if (status && portNo) {
450 OvsPostEvent(portNo, status);
453 VPORT_NIC_EXIT(nicParam);
458 HvDisconnectNic(POVS_SWITCH_CONTEXT switchContext,
459 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
461 POVS_VPORT_ENTRY vport;
463 LOCK_STATE_EX lockState;
464 BOOLEAN isInternalPort = FALSE;
466 VPORT_NIC_ENTER(nicParam);
468 /* Wait for lists to be initialized. */
469 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
471 if (!switchContext->isActivated) {
472 OVS_LOG_WARN("Switch is not activated yet.");
476 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
477 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
482 OVS_LOG_WARN("Vport not present.");
483 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
487 vport->nicState = NdisSwitchNicStateDisconnected;
488 vport->ovsState = OVS_STATE_NIC_CREATED;
489 portNo = vport->portNo;
491 if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
492 isInternalPort = TRUE;
495 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
497 /* XXX if portNo != INVALID or always? */
498 OvsPostEvent(portNo, OVS_EVENT_LINK_DOWN);
500 if (isInternalPort) {
501 OvsInternalAdapterDown();
505 VPORT_NIC_EXIT(nicParam);
510 HvDeleteNic(POVS_SWITCH_CONTEXT switchContext,
511 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
513 LOCK_STATE_EX lockState;
514 POVS_VPORT_ENTRY vport;
517 VPORT_NIC_ENTER(nicParam);
518 /* Wait for lists to be initialized. */
519 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
521 if (!switchContext->isActivated) {
522 OVS_LOG_WARN("Switch is not activated yet.");
526 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
527 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
532 OVS_LOG_WARN("Vport not present.");
533 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
537 portNo = vport->portNo;
538 if (vport->portType == NdisSwitchPortTypeExternal &&
539 vport->nicIndex != 0) {
540 OvsRemoveAndDeleteVport(switchContext, vport, TRUE, FALSE, NULL);
542 vport->nicState = NdisSwitchNicStateUnknown;
543 vport->ovsState = OVS_STATE_PORT_CREATED;
545 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
546 /* XXX if portNo != INVALID or always? */
547 OvsPostEvent(portNo, OVS_EVENT_DISCONNECT);
550 VPORT_NIC_EXIT(nicParam);
555 * OVS Vport related functionality.
558 OvsFindVportByPortNo(POVS_SWITCH_CONTEXT switchContext,
561 POVS_VPORT_ENTRY vport;
562 PLIST_ENTRY head, link;
563 UINT32 hash = OvsJhashBytes((const VOID *)&portNo, sizeof(portNo),
565 head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
566 LIST_FORALL(head, link) {
567 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
568 if (vport->portNo == portNo) {
577 OvsFindVportByOvsName(POVS_SWITCH_CONTEXT switchContext,
580 POVS_VPORT_ENTRY vport;
581 PLIST_ENTRY head, link;
583 SIZE_T length = strlen(name) + 1;
585 hash = OvsJhashBytes((const VOID *)name, length, OVS_HASH_BASIS);
586 head = &(switchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK]);
588 LIST_FORALL(head, link) {
589 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, ovsNameLink);
590 if (!strcmp(name, vport->ovsName)) {
598 /* OvsFindVportByHvName: "name" is assumed to be null-terminated */
600 OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
601 PWSTR wsName, SIZE_T wstrSize)
603 POVS_VPORT_ENTRY vport = NULL;
604 PLIST_ENTRY head, link;
607 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
608 head = &(switchContext->portIdHashArray[i]);
609 LIST_FORALL(head, link) {
610 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
613 * NOTE about portFriendlyName:
614 * If the string is NULL-terminated, the Length member does not
615 * include the terminating NULL character.
617 if (vport->portFriendlyName.Length == wstrSize &&
618 RtlEqualMemory(wsName, vport->portFriendlyName.String,
619 vport->portFriendlyName.Length)) {
628 * Look in the list of ports that were added from the Hyper-V switch and
632 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
633 head = &(switchContext->portNoHashArray[i]);
634 LIST_FORALL(head, link) {
635 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
636 if (vport->portFriendlyName.Length == wstrSize &&
637 RtlEqualMemory(wsName, vport->portFriendlyName.String,
638 vport->portFriendlyName.Length)) {
652 OvsFindVportByHvNameA(POVS_SWITCH_CONTEXT switchContext,
655 POVS_VPORT_ENTRY vport = NULL;
656 /* 'portFriendlyName' is not NUL-terminated. */
657 SIZE_T length = strlen(name);
658 SIZE_T wstrSize = length * sizeof(WCHAR);
661 PWSTR wsName = OvsAllocateMemoryWithTag(wstrSize, OVS_VPORT_POOL_TAG);
665 for (i = 0; i < length; i++) {
668 vport = OvsFindVportByHvNameW(switchContext, wsName, wstrSize);
669 OvsFreeMemoryWithTag(wsName, OVS_VPORT_POOL_TAG);
674 OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT switchContext,
675 NDIS_SWITCH_PORT_ID portId,
676 NDIS_SWITCH_NIC_INDEX index)
678 if (switchContext->virtualExternalVport &&
679 portId == switchContext->virtualExternalPortId &&
680 index == switchContext->virtualExternalVport->nicIndex) {
681 return (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
682 } else if (switchContext->internalVport &&
683 portId == switchContext->internalPortId &&
684 index == switchContext->internalVport->nicIndex) {
685 return (POVS_VPORT_ENTRY)switchContext->internalVport;
687 PLIST_ENTRY head, link;
688 POVS_VPORT_ENTRY vport;
690 hash = OvsJhashWords((UINT32 *)&portId, 1, OVS_HASH_BASIS);
691 head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
692 LIST_FORALL(head, link) {
693 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
694 if (portId == vport->portId && index == vport->nicIndex) {
703 OvsAllocateVport(VOID)
705 POVS_VPORT_ENTRY vport;
706 vport = (POVS_VPORT_ENTRY)OvsAllocateMemoryWithTag(
707 sizeof(OVS_VPORT_ENTRY), OVS_VPORT_POOL_TAG);
711 RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY));
712 vport->ovsState = OVS_STATE_UNKNOWN;
713 vport->isPresentOnHv = FALSE;
714 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
716 InitializeListHead(&vport->ovsNameLink);
717 InitializeListHead(&vport->portIdLink);
718 InitializeListHead(&vport->portNoLink);
724 OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
725 PNDIS_SWITCH_PORT_PARAMETERS portParam)
727 vport->portType = portParam->PortType;
728 vport->portState = portParam->PortState;
729 vport->portId = portParam->PortId;
730 vport->nicState = NdisSwitchNicStateUnknown;
731 vport->isExternal = FALSE;
732 vport->isBridgeInternal = FALSE;
734 switch (vport->portType) {
735 case NdisSwitchPortTypeExternal:
736 vport->isExternal = TRUE;
737 vport->ovsType = OVS_VPORT_TYPE_NETDEV;
739 case NdisSwitchPortTypeInternal:
740 vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
742 case NdisSwitchPortTypeSynthetic:
743 case NdisSwitchPortTypeEmulated:
744 vport->ovsType = OVS_VPORT_TYPE_NETDEV;
747 RtlCopyMemory(&vport->hvPortName, &portParam->PortName,
748 sizeof (NDIS_SWITCH_PORT_NAME));
749 /* For external and internal ports, 'portFriendlyName' is overwritten
751 RtlCopyMemory(&vport->portFriendlyName, &portParam->PortFriendlyName,
752 sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
754 switch (vport->portState) {
755 case NdisSwitchPortStateCreated:
756 vport->ovsState = OVS_STATE_PORT_CREATED;
758 case NdisSwitchPortStateTeardown:
759 vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
761 case NdisSwitchPortStateDeleted:
762 vport->ovsState = OVS_STATE_PORT_DELETED;
769 OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
770 POVS_VPORT_ENTRY vport,
771 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
773 ASSERT(vport->portId == nicParam->PortId);
774 ASSERT(vport->ovsState == OVS_STATE_PORT_CREATED);
776 UNREFERENCED_PARAMETER(switchContext);
778 RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
779 sizeof (nicParam->PermanentMacAddress));
780 RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
781 sizeof (nicParam->CurrentMacAddress));
783 if (nicParam->NicType == NdisSwitchNicTypeSynthetic ||
784 nicParam->NicType == NdisSwitchNicTypeEmulated) {
785 RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
786 sizeof (nicParam->VMMacAddress));
787 RtlCopyMemory(&vport->vmName, &nicParam->VmName,
788 sizeof (nicParam->VmName));
790 RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
791 sizeof (nicParam->NetCfgInstanceId));
793 RtlCopyMemory(&vport->nicName, &nicParam->NicName,
794 sizeof (nicParam->NicName));
795 vport->mtu = nicParam->MTU;
796 vport->nicState = nicParam->NicState;
797 vport->nicIndex = nicParam->NicIndex;
798 vport->numaNodeId = nicParam->NumaNodeId;
800 switch (vport->nicState) {
801 case NdisSwitchNicStateCreated:
802 vport->ovsState = OVS_STATE_NIC_CREATED;
804 case NdisSwitchNicStateConnected:
805 vport->ovsState = OVS_STATE_CONNECTED;
807 case NdisSwitchNicStateDisconnected:
808 vport->ovsState = OVS_STATE_NIC_CREATED;
810 case NdisSwitchNicStateDeleted:
811 vport->ovsState = OVS_STATE_PORT_CREATED;
817 * --------------------------------------------------------------------------
818 * Copies the relevant NDIS port properties from a virtual (pseudo) external
819 * NIC to a physical (real) external NIC.
820 * --------------------------------------------------------------------------
823 OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVport,
824 POVS_VPORT_ENTRY virtExtVport,
827 physExtVport->portType = virtExtVport->portType;
828 physExtVport->portState = virtExtVport->portState;
829 physExtVport->portId = virtExtVport->portId;
830 physExtVport->nicState = NdisSwitchNicStateUnknown;
831 physExtVport->ovsType = OVS_VPORT_TYPE_NETDEV;
832 physExtVport->isExternal = TRUE;
833 physExtVport->isBridgeInternal = FALSE;
834 physExtVport->nicIndex = (NDIS_SWITCH_NIC_INDEX)physNicIndex;
836 RtlCopyMemory(&physExtVport->hvPortName, &virtExtVport->hvPortName,
837 sizeof (NDIS_SWITCH_PORT_NAME));
839 /* 'portFriendlyName' is overwritten later. */
840 RtlCopyMemory(&physExtVport->portFriendlyName,
841 &virtExtVport->portFriendlyName,
842 sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
844 physExtVport->ovsState = OVS_STATE_PORT_CREATED;
848 * --------------------------------------------------------------------------
849 * Initializes a tunnel vport.
850 * --------------------------------------------------------------------------
853 OvsInitTunnelVport(POVS_VPORT_ENTRY vport,
854 OVS_VPORT_TYPE ovsType,
857 NTSTATUS status = STATUS_SUCCESS;
859 vport->isBridgeInternal = FALSE;
860 vport->ovsType = ovsType;
861 vport->ovsState = OVS_STATE_PORT_CREATED;
863 case OVS_VPORT_TYPE_GRE:
865 case OVS_VPORT_TYPE_GRE64:
867 case OVS_VPORT_TYPE_VXLAN:
868 status = OvsInitVxlanTunnel(vport, dstPort);
877 * --------------------------------------------------------------------------
878 * Initializes a bridge internal vport ie. a port of type
879 * OVS_VPORT_TYPE_INTERNAL but not present on the Hyper-V switch.
880 * --------------------------------------------------------------------------
883 OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport)
885 vport->isBridgeInternal = TRUE;
886 vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
887 /* Mark the status to be connected, since there is no other initialization
889 vport->ovsState = OVS_STATE_CONNECTED;
890 return STATUS_SUCCESS;
894 * --------------------------------------------------------------------------
895 * For external vports 'portFriendlyName' provided by Hyper-V is over-written
896 * by synthetic names.
897 * --------------------------------------------------------------------------
900 AssignNicNameSpecial(POVS_VPORT_ENTRY vport)
904 if (vport->portType == NdisSwitchPortTypeExternal) {
905 if (vport->nicIndex == 0) {
906 ASSERT(vport->nicIndex == 0);
907 RtlStringCbPrintfW(vport->portFriendlyName.String,
909 L"%s.virtualAdapter", OVS_DPPORT_EXTERNAL_NAME_W);
911 RtlStringCbPrintfW(vport->portFriendlyName.String,
913 L"%s.%lu", OVS_DPPORT_EXTERNAL_NAME_W,
914 (UINT32)vport->nicIndex);
917 RtlStringCbPrintfW(vport->portFriendlyName.String,
919 L"%s", OVS_DPPORT_INTERNAL_NAME_W);
922 RtlStringCbLengthW(vport->portFriendlyName.String, IF_MAX_STRING_SIZE,
924 vport->portFriendlyName.Length = (USHORT)len;
929 * --------------------------------------------------------------------------
930 * Functionality common to any port on the Hyper-V switch. This function is not
931 * to be called for a port that is not on the Hyper-V switch.
933 * Inserts the port into 'portIdHashArray' and caches the pointer in the
934 * 'switchContext' if needed.
936 * For external NIC, assigns the name for the NIC.
937 * --------------------------------------------------------------------------
940 InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
941 POVS_VPORT_ENTRY vport,
946 switch (vport->portType) {
947 case NdisSwitchPortTypeExternal:
949 * Overwrite the 'portFriendlyName' of this external vport. The reason
950 * for having this in common code is to be able to call it from the NDIS
951 * Port callback as well as the NDIS NIC callback.
953 AssignNicNameSpecial(vport);
955 if (vport->nicIndex == 0) {
956 switchContext->virtualExternalPortId = vport->portId;
957 switchContext->virtualExternalVport = vport;
959 switchContext->numPhysicalNics++;
962 case NdisSwitchPortTypeInternal:
963 ASSERT(vport->isBridgeInternal == FALSE);
965 /* Overwrite the 'portFriendlyName' of the internal vport. */
966 AssignNicNameSpecial(vport);
967 switchContext->internalPortId = vport->portId;
968 switchContext->internalVport = vport;
970 case NdisSwitchPortTypeSynthetic:
971 case NdisSwitchPortTypeEmulated:
976 * It is important to not insert vport corresponding to virtual external
977 * port into the 'portIdHashArray' since the port should not be exposed to
980 if (vport->portType == NdisSwitchPortTypeExternal &&
981 vport->nicIndex == 0) {
982 return NDIS_STATUS_SUCCESS;
986 * NOTE: OvsJhashWords has portId as "1" word. This should be ok, even
987 * though sizeof(NDIS_SWITCH_PORT_ID) = 4, not 2, because the
988 * hyper-v switch seems to use only 2 bytes out of 4.
990 hash = OvsJhashWords(&vport->portId, 1, OVS_HASH_BASIS);
991 InsertHeadList(&switchContext->portIdHashArray[hash & OVS_VPORT_MASK],
994 switchContext->numHvVports++;
996 return NDIS_STATUS_SUCCESS;
1000 * --------------------------------------------------------------------------
1001 * Functionality common to any port added from OVS userspace.
1003 * Inserts the port into 'portIdHashArray', 'ovsPortNameHashArray' and caches
1004 * the pointer in the 'switchContext' if needed.
1005 * --------------------------------------------------------------------------
1008 InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
1009 POVS_VPORT_ENTRY vport)
1013 switch(vport->ovsType) {
1014 case OVS_VPORT_TYPE_VXLAN:
1015 ASSERT(switchContext->vxlanVport == NULL);
1016 switchContext->vxlanVport = vport;
1017 switchContext->numNonHvVports++;
1019 case OVS_VPORT_TYPE_INTERNAL:
1020 if (vport->isBridgeInternal) {
1021 switchContext->numNonHvVports++;
1028 * Insert the port into the hash array of ports: by port number and ovs
1029 * and ovs (datapath) port name.
1030 * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1031 * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1033 hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
1034 InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
1035 &vport->portNoLink);
1037 hash = OvsJhashBytes(vport->ovsName, strlen(vport->ovsName) + 1,
1040 &gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
1041 &vport->ovsNameLink);
1043 return STATUS_SUCCESS;
1048 * --------------------------------------------------------------------------
1049 * Provides functionality that is partly complementatry to
1050 * InitOvsVportCommon()/InitHvVportCommon().
1052 * 'hvDelete' indicates if caller is removing the vport as a result of the
1053 * port being removed on the Hyper-V switch.
1054 * 'ovsDelete' indicates if caller is removing the vport as a result of the
1055 * port being removed from OVS userspace.
1056 * --------------------------------------------------------------------------
1059 OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
1060 POVS_VPORT_ENTRY vport,
1063 BOOLEAN *vportDeallocated)
1065 BOOLEAN hvSwitchPort = FALSE;
1066 BOOLEAN deletedOnOvs = FALSE, deletedOnHv = FALSE;
1068 if (vportDeallocated) {
1069 *vportDeallocated = FALSE;
1072 if (vport->isExternal) {
1073 if (vport->nicIndex == 0) {
1074 ASSERT(switchContext->numPhysicalNics == 0);
1075 switchContext->virtualExternalPortId = 0;
1076 switchContext->virtualExternalVport = NULL;
1077 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1078 if (vportDeallocated) {
1079 *vportDeallocated = TRUE;
1083 ASSERT(switchContext->numPhysicalNics);
1084 switchContext->numPhysicalNics--;
1085 hvSwitchPort = TRUE;
1089 switch (vport->ovsType) {
1090 case OVS_VPORT_TYPE_INTERNAL:
1091 if (!vport->isBridgeInternal) {
1092 switchContext->internalPortId = 0;
1093 switchContext->internalVport = NULL;
1094 OvsInternalAdapterDown();
1095 hvSwitchPort = TRUE;
1098 case OVS_VPORT_TYPE_VXLAN:
1099 OvsCleanupVxlanTunnel(vport);
1100 switchContext->vxlanVport = NULL;
1102 case OVS_VPORT_TYPE_GRE:
1103 case OVS_VPORT_TYPE_GRE64:
1105 case OVS_VPORT_TYPE_NETDEV:
1106 hvSwitchPort = TRUE;
1112 * 'hvDelete' == TRUE indicates that the port should be removed from the
1113 * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
1114 * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
1116 * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
1118 if (vport->isPresentOnHv == TRUE) {
1121 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1122 deletedOnOvs = TRUE;
1125 if (hvDelete && !deletedOnHv) {
1126 vport->isPresentOnHv = TRUE;
1128 /* Remove the port from the relevant lists. */
1129 RemoveEntryList(&vport->portIdLink);
1130 InitializeListHead(&vport->portIdLink);
1133 if (ovsDelete && !deletedOnOvs) {
1134 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
1135 vport->ovsName[0] = '\0';
1137 /* Remove the port from the relevant lists. */
1138 RemoveEntryList(&vport->ovsNameLink);
1139 InitializeListHead(&vport->ovsNameLink);
1140 RemoveEntryList(&vport->portNoLink);
1141 InitializeListHead(&vport->portNoLink);
1142 deletedOnOvs = TRUE;
1146 * Deallocate the port if it has been deleted on the Hyper-V switch as well
1149 if (deletedOnHv && deletedOnOvs) {
1151 switchContext->numHvVports--;
1153 switchContext->numNonHvVports--;
1155 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1156 if (vportDeallocated) {
1157 *vportDeallocated = TRUE;
1163 OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
1165 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1167 PNDIS_SWITCH_PORT_PARAMETERS portParam;
1168 PNDIS_SWITCH_PORT_ARRAY portArray = NULL;
1169 POVS_VPORT_ENTRY vport;
1171 OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
1173 status = OvsGetPortsOnSwitch(switchContext, &portArray);
1174 if (status != NDIS_STATUS_SUCCESS) {
1178 for (arrIndex = 0; arrIndex < portArray->NumElements; arrIndex++) {
1179 portParam = NDIS_SWITCH_PORT_AT_ARRAY_INDEX(portArray, arrIndex);
1181 if (portParam->IsValidationPort) {
1185 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
1186 if (vport == NULL) {
1187 status = NDIS_STATUS_RESOURCES;
1190 OvsInitVportWithPortParam(vport, portParam);
1191 status = InitHvVportCommon(switchContext, vport, TRUE);
1192 if (status != NDIS_STATUS_SUCCESS) {
1193 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1199 if (status != NDIS_STATUS_SUCCESS) {
1200 OvsClearAllSwitchVports(switchContext);
1203 OvsFreeSwitchPortsArray(portArray);
1205 OVS_LOG_TRACE("Exit: status: %x", status);
1212 OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext)
1214 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1215 PNDIS_SWITCH_NIC_ARRAY nicArray = NULL;
1217 PNDIS_SWITCH_NIC_PARAMETERS nicParam;
1218 POVS_VPORT_ENTRY vport;
1220 OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
1222 * Now, get NIC list.
1224 status = OvsGetNicsOnSwitch(switchContext, &nicArray);
1225 if (status != NDIS_STATUS_SUCCESS) {
1228 for (arrIndex = 0; arrIndex < nicArray->NumElements; ++arrIndex) {
1230 nicParam = NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray, arrIndex);
1233 * XXX: Check if the port is configured with a VLAN. Disallow such a
1234 * configuration, since we don't support tag-in-tag.
1238 * XXX: Check if the port is connected to a VF. Disconnect the VF in
1242 if (nicParam->NicType == NdisSwitchNicTypeExternal &&
1243 nicParam->NicIndex != 0) {
1244 POVS_VPORT_ENTRY virtExtVport =
1245 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
1247 vport = OvsAllocateVport();
1249 OvsInitPhysNicVport(vport, virtExtVport,
1250 nicParam->NicIndex);
1251 status = InitHvVportCommon(switchContext, vport, TRUE);
1252 if (status != NDIS_STATUS_SUCCESS) {
1253 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1258 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
1260 nicParam->NicIndex);
1262 if (vport == NULL) {
1263 OVS_LOG_ERROR("Fail to allocate vport");
1266 OvsInitVportWithNicParam(switchContext, vport, nicParam);
1267 if (nicParam->NicType == NdisSwitchNicTypeInternal) {
1268 OvsInternalAdapterUp(vport->portNo, &nicParam->NetCfgInstanceId);
1273 OvsFreeSwitchNicsArray(nicArray);
1275 OVS_LOG_TRACE("Exit: status: %x", status);
1280 * --------------------------------------------------------------------------
1281 * Deletes ports added from the Hyper-V switch as well as OVS usersapce. The
1282 * function deletes ports in 'portIdHashArray'. This will delete most of the
1283 * ports that are in the 'portNoHashArray' as well. Any remaining ports
1284 * are deleted by walking the the 'portNoHashArray'.
1285 * --------------------------------------------------------------------------
1288 OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
1290 for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1291 PLIST_ENTRY head, link, next;
1293 head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
1294 LIST_FORALL_SAFE(head, link, next) {
1295 POVS_VPORT_ENTRY vport;
1296 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1297 OvsRemoveAndDeleteVport(switchContext, vport, TRUE, TRUE, NULL);
1301 * Remove 'virtualExternalVport' as well. This port is not part of the
1302 * 'portIdHashArray'.
1304 if (switchContext->virtualExternalVport) {
1305 OvsRemoveAndDeleteVport(switchContext,
1306 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE,
1310 for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1311 PLIST_ENTRY head, link, next;
1313 head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
1314 LIST_FORALL_SAFE(head, link, next) {
1315 POVS_VPORT_ENTRY vport;
1316 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1317 ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
1318 (vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
1319 vport->isBridgeInternal) || vport->isPresentOnHv == TRUE);
1320 OvsRemoveAndDeleteVport(switchContext, vport, TRUE, TRUE, NULL);
1324 ASSERT(switchContext->virtualExternalVport == NULL);
1325 ASSERT(switchContext->internalVport == NULL);
1326 ASSERT(switchContext->vxlanVport == NULL);
1331 OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
1336 UNICODE_STRING ustr;
1340 ustr.Buffer = wStr->String;
1341 ustr.Length = wStr->Length;
1342 ustr.MaximumLength = IF_MAX_STRING_SIZE;
1345 astr.MaximumLength = maxStrLen;
1348 size = RtlUnicodeStringToAnsiSize(&ustr);
1349 if (size > maxStrLen) {
1350 return STATUS_BUFFER_OVERFLOW;
1353 status = RtlUnicodeStringToAnsiString(&astr, &ustr, FALSE);
1355 ASSERT(status == STATUS_SUCCESS);
1356 if (status != STATUS_SUCCESS) {
1359 ASSERT(astr.Length <= maxStrLen);
1360 str[astr.Length] = 0;
1361 return STATUS_SUCCESS;
1365 * --------------------------------------------------------------------------
1366 * Utility function that populates a 'OVS_VPORT_EXT_INFO' structure for the
1369 * Assumes that 'gOvsCtrlLock' is held.
1370 * --------------------------------------------------------------------------
1373 OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
1374 POVS_VPORT_EXT_INFO extInfo)
1376 POVS_VPORT_ENTRY vport;
1378 LOCK_STATE_EX lockState;
1379 NTSTATUS status = STATUS_SUCCESS;
1380 BOOLEAN doConvert = FALSE;
1382 RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO));
1383 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1384 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1385 NDIS_RWL_AT_DISPATCH_LEVEL);
1386 if (vportGet->portNo == 0) {
1387 StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
1388 vport = OvsFindVportByHvNameA(gOvsSwitchContext, vportGet->name);
1389 if (vport != NULL) {
1390 /* If the port is not a Hyper-V port and it has been added earlier,
1391 * we'll find it in 'ovsPortNameHashArray'. */
1392 vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name);
1395 vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo);
1397 if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED &&
1398 vport->ovsState != OVS_STATE_NIC_CREATED)) {
1399 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1400 if (vportGet->portNo) {
1401 OVS_LOG_WARN("vport %u does not exist any more", vportGet->portNo);
1403 OVS_LOG_WARN("vport %s does not exist any more", vportGet->name);
1405 status = STATUS_DEVICE_DOES_NOT_EXIST;
1408 extInfo->dpNo = vportGet->dpNo;
1409 extInfo->portNo = vport->portNo;
1410 RtlCopyMemory(extInfo->macAddress, vport->currMacAddress,
1411 sizeof (vport->currMacAddress));
1412 RtlCopyMemory(extInfo->permMACAddress, vport->permMacAddress,
1413 sizeof (vport->permMacAddress));
1414 if (vport->ovsType == OVS_VPORT_TYPE_NETDEV) {
1415 RtlCopyMemory(extInfo->vmMACAddress, vport->vmMacAddress,
1416 sizeof (vport->vmMacAddress));
1418 extInfo->nicIndex = vport->nicIndex;
1419 extInfo->portId = vport->portId;
1420 extInfo->type = vport->ovsType;
1421 extInfo->mtu = vport->mtu;
1425 if (vport->ovsState == OVS_STATE_NIC_CREATED) {
1426 extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_DOWN;
1427 } else if (vport->ovsState == OVS_STATE_CONNECTED) {
1428 extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
1430 extInfo->status = OVS_EVENT_DISCONNECT;
1432 if (extInfo->type == OVS_VPORT_TYPE_NETDEV &&
1433 (vport->ovsState == OVS_STATE_NIC_CREATED ||
1434 vport->ovsState == OVS_STATE_CONNECTED)) {
1437 extInfo->vmUUID[0] = 0;
1438 extInfo->vifUUID[0] = 0;
1440 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1442 status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName,
1444 OVS_MAX_PORT_NAME_LENGTH);
1445 if (status != STATUS_SUCCESS) {
1446 OVS_LOG_INFO("Fail to convert NIC name.");
1447 extInfo->vmUUID[0] = 0;
1450 status = OvsConvertIfCountedStrToAnsiStr(&vport->vmName,
1452 OVS_MAX_VM_UUID_LEN);
1453 if (status != STATUS_SUCCESS) {
1454 OVS_LOG_INFO("Fail to convert VM name.");
1455 extInfo->vmUUID[0] = 0;
1458 status = OvsConvertIfCountedStrToAnsiStr(&vport->nicName,
1460 OVS_MAX_VIF_UUID_LEN);
1461 if (status != STATUS_SUCCESS) {
1462 OVS_LOG_INFO("Fail to convert nic UUID");
1463 extInfo->vifUUID[0] = 0;
1466 * for now ignore status
1468 status = STATUS_SUCCESS;
1476 * --------------------------------------------------------------------------
1477 * Command Handler for 'OVS_WIN_NETDEV_CMD_GET'.
1478 * --------------------------------------------------------------------------
1481 OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1484 NTSTATUS status = STATUS_SUCCESS;
1485 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1486 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1487 NL_ERROR nlError = NL_ERROR_SUCCESS;
1488 OVS_VPORT_GET vportGet;
1489 OVS_VPORT_EXT_INFO info;
1491 static const NL_POLICY ovsNetdevPolicy[] = {
1492 [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING,
1494 .maxLen = IFNAMSIZ },
1496 PNL_ATTR netdevAttrs[ARRAY_SIZE(ovsNetdevPolicy)];
1498 /* input buffer has been validated while validating transaction dev op. */
1499 ASSERT(usrParamsCtx->inputBuffer != NULL &&
1500 usrParamsCtx->inputLength > sizeof *msgIn);
1502 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1503 return STATUS_INVALID_BUFFER_SIZE;
1506 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1507 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1508 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1509 ovsNetdevPolicy, netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
1510 return STATUS_INVALID_PARAMETER;
1513 OvsAcquireCtrlLock();
1515 vportGet.portNo = 0;
1516 RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
1517 NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
1519 status = OvsGetExtInfoIoctl(&vportGet, &info);
1520 if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
1521 nlError = NL_ERROR_NODEV;
1522 OvsReleaseCtrlLock();
1526 status = CreateNetlinkMesgForNetdev(&info, msgIn,
1527 usrParamsCtx->outputBuffer, usrParamsCtx->outputLength,
1528 gOvsSwitchContext->dpNo);
1529 if (status == STATUS_SUCCESS) {
1530 *replyLen = msgOut->nlMsg.nlmsgLen;
1532 OvsReleaseCtrlLock();
1535 if (nlError != NL_ERROR_SUCCESS) {
1536 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1537 usrParamsCtx->outputBuffer;
1539 NlBuildErrorMsg(msgIn, msgError, nlError);
1540 *replyLen = msgError->nlMsg.nlmsgLen;
1543 return STATUS_SUCCESS;
1548 * --------------------------------------------------------------------------
1549 * Utility function to construct an OVS_MESSAGE for the specified vport. The
1550 * OVS_MESSAGE contains the output of a netdev command.
1551 * --------------------------------------------------------------------------
1554 CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
1563 UINT32 netdevFlags = 0;
1565 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1567 ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1568 msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1569 msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1572 return STATUS_INVALID_BUFFER_SIZE;
1575 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
1578 return STATUS_INVALID_BUFFER_SIZE;
1581 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
1583 return STATUS_INVALID_BUFFER_SIZE;
1586 ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
1589 return STATUS_INVALID_BUFFER_SIZE;
1592 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
1593 (PCHAR)info->macAddress, sizeof (info->macAddress));
1595 return STATUS_INVALID_BUFFER_SIZE;
1598 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
1600 return STATUS_INVALID_BUFFER_SIZE;
1603 if (info->status != OVS_EVENT_CONNECT) {
1604 netdevFlags = OVS_WIN_NETDEV_IFF_UP;
1606 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
1609 return STATUS_INVALID_BUFFER_SIZE;
1613 * XXX: add netdev_stats when we have the definition available in the
1617 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1618 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1620 return STATUS_SUCCESS;
1623 static __inline VOID
1624 OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec)
1626 while ((!switchContext->isActivated) &&
1627 (!switchContext->isActivateFailed)) {
1628 /* Wait for the switch to be active and
1629 * the list of ports in OVS to be initialized. */
1630 NdisMSleep(sleepMicroSec);
1635 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1642 OVS_VPORT_FULL_STATS vportStats;
1646 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1648 ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1649 msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1650 msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1653 return STATUS_INVALID_BUFFER_SIZE;
1656 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1658 return STATUS_INVALID_BUFFER_SIZE;
1661 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1663 return STATUS_INVALID_BUFFER_SIZE;
1666 ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1668 return STATUS_INVALID_BUFFER_SIZE;
1672 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1673 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1674 * it means we have an array of pids, instead of a single pid.
1675 * ATM we assume we have one pid only.
1678 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1681 return STATUS_INVALID_BUFFER_SIZE;
1685 vportStats.rxPackets = vport->stats.rxPackets;
1686 vportStats.rxBytes = vport->stats.rxBytes;
1687 vportStats.txPackets = vport->stats.txPackets;
1688 vportStats.txBytes = vport->stats.txBytes;
1689 vportStats.rxErrors = vport->errStats.rxErrors;
1690 vportStats.txErrors = vport->errStats.txErrors;
1691 vportStats.rxDropped = vport->errStats.rxDropped;
1692 vportStats.txDropped = vport->errStats.txDropped;
1694 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1696 sizeof(OVS_VPORT_FULL_STATS));
1698 return STATUS_INVALID_BUFFER_SIZE;
1702 * XXX: when vxlan udp dest port becomes configurable, we will also need
1703 * to add vport options
1706 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1707 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1709 return STATUS_SUCCESS;
1713 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1717 POVS_OPEN_INSTANCE instance =
1718 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1719 LOCK_STATE_EX lockState;
1720 UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1723 * XXX: this function shares some code with other dump command(s).
1724 * In the future, we will need to refactor the dump functions
1727 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1729 if (instance->dumpState.ovsMsg == NULL) {
1731 return STATUS_INVALID_DEVICE_STATE;
1734 /* Output buffer has been validated while validating read dev op. */
1735 ASSERT(usrParamsCtx->outputBuffer != NULL);
1737 msgIn = instance->dumpState.ovsMsg;
1739 OvsAcquireCtrlLock();
1742 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1743 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1744 * it means we have an array of pids, instead of a single pid.
1745 * ATM we assume we have one pid only.
1747 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1748 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1749 NDIS_RWL_AT_DISPATCH_LEVEL);
1751 if (gOvsSwitchContext->numHvVports > 0 ||
1752 gOvsSwitchContext->numNonHvVports > 0) {
1753 /* inBucket: the bucket, used for lookup */
1754 UINT32 inBucket = instance->dumpState.index[0];
1755 /* inIndex: index within the given bucket, used for lookup */
1756 UINT32 inIndex = instance->dumpState.index[1];
1757 /* the bucket to be used for the next dump operation */
1758 UINT32 outBucket = 0;
1759 /* the index within the outBucket to be used for the next dump */
1760 UINT32 outIndex = 0;
1762 for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1763 PLIST_ENTRY head, link;
1764 head = &(gOvsSwitchContext->portNoHashArray[i]);
1765 POVS_VPORT_ENTRY vport = NULL;
1768 LIST_FORALL(head, link) {
1771 * if one or more dumps were previously done on this same bucket,
1772 * inIndex will be > 0, so we'll need to reply with the
1773 * inIndex + 1 vport from the bucket.
1775 if (outIndex >= inIndex) {
1776 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1778 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
1779 OvsCreateMsgFromVport(vport, msgIn,
1780 usrParamsCtx->outputBuffer,
1781 usrParamsCtx->outputLength,
1782 gOvsSwitchContext->dpNo);
1795 * if no vport was found above, check the next bucket, beginning
1796 * with the first (i.e. index 0) elem from within that bucket
1803 /* XXX: what about NLMSG_DONE (as msg type)? */
1804 instance->dumpState.index[0] = outBucket;
1805 instance->dumpState.index[1] = outIndex;
1808 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1810 OvsReleaseCtrlLock();
1812 /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1813 if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1814 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1815 *replyLen = msgOut->nlMsg.nlmsgLen;
1818 * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1822 /* Free up the dump state, since there's no more data to continue. */
1823 FreeUserDumpState(instance);
1826 return STATUS_SUCCESS;
1830 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1833 NTSTATUS status = STATUS_SUCCESS;
1834 LOCK_STATE_EX lockState;
1836 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1837 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1838 POVS_VPORT_ENTRY vport = NULL;
1839 NL_ERROR nlError = NL_ERROR_SUCCESS;
1840 PCHAR portName = NULL;
1841 UINT32 portNameLen = 0;
1842 UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
1844 static const NL_POLICY ovsVportPolicy[] = {
1845 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1846 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1851 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1853 /* input buffer has been validated while validating write dev op. */
1854 ASSERT(usrParamsCtx->inputBuffer != NULL);
1856 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1857 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1858 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1859 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1860 return STATUS_INVALID_PARAMETER;
1863 /* Output buffer has been validated while validating transact dev op. */
1864 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1866 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1867 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1868 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1869 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1871 /* the port name is expected to be null-terminated */
1872 ASSERT(portName[portNameLen - 1] == '\0');
1874 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1875 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1876 portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1878 vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
1880 nlError = NL_ERROR_INVAL;
1881 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1886 nlError = NL_ERROR_NODEV;
1887 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1891 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1892 usrParamsCtx->outputLength,
1893 gOvsSwitchContext->dpNo);
1894 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1896 *replyLen = msgOut->nlMsg.nlmsgLen;
1899 if (nlError != NL_ERROR_SUCCESS) {
1900 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1901 usrParamsCtx->outputBuffer;
1903 NlBuildErrorMsg(msgIn, msgError, nlError);
1904 *replyLen = msgError->nlMsg.nlmsgLen;
1907 return STATUS_SUCCESS;
1911 * --------------------------------------------------------------------------
1912 * Command Handler for 'OVS_VPORT_CMD_NEW'.
1914 * The function handles the initial call to setup the dump state, as well as
1915 * subsequent calls to continue dumping data.
1916 * --------------------------------------------------------------------------
1919 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1924 switch (usrParamsCtx->devOp) {
1925 case OVS_WRITE_DEV_OP:
1926 return OvsSetupDumpStart(usrParamsCtx);
1928 case OVS_READ_DEV_OP:
1929 return OvsGetVportDumpNext(usrParamsCtx, replyLen);
1931 case OVS_TRANSACTION_DEV_OP:
1932 return OvsGetVport(usrParamsCtx, replyLen);
1935 return STATUS_INVALID_DEVICE_REQUEST;
1941 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
1943 /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
1944 for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
1945 POVS_VPORT_ENTRY vport;
1947 vport = OvsFindVportByPortNo(switchContext, i);
1953 return OVS_DPPORT_NUMBER_INVALID;
1957 * --------------------------------------------------------------------------
1958 * Command Handler for 'OVS_VPORT_CMD_NEW'.
1959 * --------------------------------------------------------------------------
1962 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1965 NDIS_STATUS status = STATUS_SUCCESS;
1966 LOCK_STATE_EX lockState;
1968 NL_ERROR nlError = NL_ERROR_SUCCESS;
1969 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1970 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1971 POVS_VPORT_ENTRY vport = NULL;
1975 BOOLEAN isBridgeInternal = FALSE;
1976 BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE;
1977 BOOLEAN addInternalPortAsNetdev = FALSE;
1979 static const NL_POLICY ovsVportPolicy[] = {
1980 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1981 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
1982 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
1984 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
1985 .optional = FALSE },
1986 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
1989 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1991 /* input buffer has been validated while validating write dev op. */
1992 ASSERT(usrParamsCtx->inputBuffer != NULL);
1994 /* Output buffer has been validated while validating transact dev op. */
1995 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1997 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1998 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1999 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2000 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2001 return STATUS_INVALID_PARAMETER;
2004 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2005 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2006 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2008 /* we are expecting null terminated strings to be passed */
2009 ASSERT(portName[portNameLen - 1] == '\0');
2011 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2013 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2015 nlError = NL_ERROR_EXIST;
2019 if (portName && portType == OVS_VPORT_TYPE_NETDEV &&
2020 !strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
2021 addInternalPortAsNetdev = TRUE;
2024 if (portName && portType == OVS_VPORT_TYPE_INTERNAL &&
2025 strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
2026 isBridgeInternal = TRUE;
2029 if (portType == OVS_VPORT_TYPE_INTERNAL && !isBridgeInternal) {
2030 vport = gOvsSwitchContext->internalVport;
2031 } else if (portType == OVS_VPORT_TYPE_NETDEV) {
2032 /* External ports can also be looked up like VIF ports. */
2033 vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
2035 ASSERT(OvsIsTunnelVportType(portType) ||
2036 (portType == OVS_VPORT_TYPE_INTERNAL && isBridgeInternal));
2037 ASSERT(OvsGetTunnelVport(gOvsSwitchContext, portType) == NULL ||
2038 !OvsIsTunnelVportType(portType));
2040 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
2041 if (vport == NULL) {
2042 nlError = NL_ERROR_NOMEM;
2045 vportAllocated = TRUE;
2047 if (OvsIsTunnelVportType(portType)) {
2048 status = OvsInitTunnelVport(vport, portType, VXLAN_UDP_PORT);
2049 nlError = NlMapStatusToNlErr(status);
2051 OvsInitBridgeInternalVport(vport);
2053 vportInitialized = TRUE;
2055 if (nlError == NL_ERROR_SUCCESS) {
2056 vport->ovsState = OVS_STATE_CONNECTED;
2057 vport->nicState = NdisSwitchNicStateConnected;
2060 * Allow the vport to be deleted, because there is no
2061 * corresponding hyper-v switch part.
2063 vport->isPresentOnHv = TRUE;
2068 nlError = NL_ERROR_INVAL;
2071 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2072 nlError = NL_ERROR_EXIST;
2076 /* Initialize the vport with OVS specific properties. */
2077 if (addInternalPortAsNetdev != TRUE) {
2078 vport->ovsType = portType;
2080 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2082 * XXX: when we implement the limit for ovs port number to be
2083 * MAXUINT16, we'll need to check the port number received from the
2086 vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2088 vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
2089 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2090 nlError = NL_ERROR_NOMEM;
2095 /* The ovs port name must be uninitialized. */
2096 ASSERT(vport->ovsName[0] == '\0');
2097 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2099 RtlCopyMemory(vport->ovsName, portName, portNameLen);
2100 /* if we don't have options, then vport->portOptions will be NULL */
2101 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2104 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2105 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2106 * it means we have an array of pids, instead of a single pid.
2107 * ATM we assume we have one pid only.
2109 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2111 status = InitOvsVportCommon(gOvsSwitchContext, vport);
2112 ASSERT(status == STATUS_SUCCESS);
2114 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2115 usrParamsCtx->outputLength,
2116 gOvsSwitchContext->dpNo);
2118 *replyLen = msgOut->nlMsg.nlmsgLen;
2121 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2123 if (nlError != NL_ERROR_SUCCESS) {
2124 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2125 usrParamsCtx->outputBuffer;
2127 if (vport && vportAllocated == TRUE) {
2128 if (vportInitialized == TRUE) {
2129 if (OvsIsTunnelVportType(portType)) {
2130 OvsCleanupVxlanTunnel(vport);
2133 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2136 NlBuildErrorMsg(msgIn, msgError, nlError);
2137 *replyLen = msgError->nlMsg.nlmsgLen;
2140 return STATUS_SUCCESS;
2145 * --------------------------------------------------------------------------
2146 * Command Handler for 'OVS_VPORT_CMD_SET'.
2147 * --------------------------------------------------------------------------
2150 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2153 NDIS_STATUS status = STATUS_SUCCESS;
2154 LOCK_STATE_EX lockState;
2156 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2157 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2158 POVS_VPORT_ENTRY vport = NULL;
2159 NL_ERROR nlError = NL_ERROR_SUCCESS;
2161 static const NL_POLICY ovsVportPolicy[] = {
2162 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2163 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
2164 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2166 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2168 [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
2169 .minLen = sizeof(OVS_VPORT_FULL_STATS),
2170 .maxLen = sizeof(OVS_VPORT_FULL_STATS),
2172 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2174 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2176 ASSERT(usrParamsCtx->inputBuffer != NULL);
2178 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2179 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2180 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2181 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2182 return STATUS_INVALID_PARAMETER;
2185 /* Output buffer has been validated while validating transact dev op. */
2186 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2188 OvsAcquireCtrlLock();
2190 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2191 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2192 PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2194 UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2196 /* the port name is expected to be null-terminated */
2197 ASSERT(portName[portNameLen - 1] == '\0');
2199 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2200 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2201 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2202 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2206 nlError = NL_ERROR_NODEV;
2211 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2212 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2213 * it means we have an array of pids, instead of a single pid.
2214 * Currently, we support only one pid.
2216 if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
2217 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2220 if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
2221 OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2222 if (type != vport->ovsType) {
2223 nlError = NL_ERROR_INVAL;
2228 if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2229 OVS_LOG_ERROR("Vport options not supported");
2230 nlError = NL_ERROR_NOTSUPP;
2234 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2235 usrParamsCtx->outputLength,
2236 gOvsSwitchContext->dpNo);
2238 *replyLen = msgOut->nlMsg.nlmsgLen;
2241 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2242 OvsReleaseCtrlLock();
2244 if (nlError != NL_ERROR_SUCCESS) {
2245 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2246 usrParamsCtx->outputBuffer;
2248 NlBuildErrorMsg(msgIn, msgError, nlError);
2249 *replyLen = msgError->nlMsg.nlmsgLen;
2252 return STATUS_SUCCESS;
2256 * --------------------------------------------------------------------------
2257 * Command Handler for 'OVS_VPORT_CMD_DEL'.
2258 * --------------------------------------------------------------------------
2261 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2264 NDIS_STATUS status = STATUS_SUCCESS;
2265 LOCK_STATE_EX lockState;
2267 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2268 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2269 POVS_VPORT_ENTRY vport = NULL;
2270 NL_ERROR nlError = NL_ERROR_SUCCESS;
2271 PSTR portName = NULL;
2272 UINT32 portNameLen = 0;
2274 static const NL_POLICY ovsVportPolicy[] = {
2275 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2276 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2279 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2281 ASSERT(usrParamsCtx->inputBuffer != NULL);
2283 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2284 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2285 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2286 ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2287 return STATUS_INVALID_PARAMETER;
2290 /* Output buffer has been validated while validating transact dev op. */
2291 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2293 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2294 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2295 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2296 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2298 /* the port name is expected to be null-terminated */
2299 ASSERT(portName[portNameLen - 1] == '\0');
2301 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2303 else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2304 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2305 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2309 nlError = NL_ERROR_NODEV;
2313 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2314 usrParamsCtx->outputLength,
2315 gOvsSwitchContext->dpNo);
2318 * Mark the port as deleted from OVS userspace. If the port does not exist
2319 * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
2321 OvsRemoveAndDeleteVport(gOvsSwitchContext, vport, FALSE, TRUE, NULL);
2323 *replyLen = msgOut->nlMsg.nlmsgLen;
2326 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2328 if (nlError != NL_ERROR_SUCCESS) {
2329 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2330 usrParamsCtx->outputBuffer;
2332 NlBuildErrorMsg(msgIn, msgError, nlError);
2333 *replyLen = msgError->nlMsg.nlmsgLen;
2336 return STATUS_SUCCESS;