2 * Copyright (c) 2014 VMware, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
32 #define OVS_DBG_MOD OVS_DBG_VPORT
35 #define VPORT_NIC_ENTER(_nic) \
36 OVS_LOG_TRACE("Enter: PortId: %x, NicIndex: %d", _nic->PortId, \
39 #define VPORT_NIC_EXIT(_nic) \
40 OVS_LOG_TRACE("Exit: PortId: %x, NicIndex: %d", _nic->PortId, \
43 #define VPORT_PORT_ENTER(_port) \
44 OVS_LOG_TRACE("Enter: PortId: %x", _port->PortId)
46 #define VPORT_PORT_EXIT(_port) \
47 OVS_LOG_TRACE("Exit: PortId: %x", _port->PortId)
49 #define OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC 100
51 /* Context structure used to pass back and forth information to the tunnel
53 typedef struct _OVS_TUNFLT_INIT_CONTEXT {
54 POVS_SWITCH_CONTEXT switchContext;
58 POVS_VPORT_ENTRY vport;
62 } OVS_TUNFLT_INIT_CONTEXT, *POVS_TUNFLT_INIT_CONTEXT;
65 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
67 static VOID OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
68 PNDIS_SWITCH_PORT_PARAMETERS portParam);
69 static VOID OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
70 POVS_VPORT_ENTRY vport, PNDIS_SWITCH_NIC_PARAMETERS nicParam);
71 static VOID OvsCopyPortParamsFromVport(POVS_VPORT_ENTRY vport,
72 PNDIS_SWITCH_PORT_PARAMETERS portParam);
73 static __inline VOID OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext,
75 static NTSTATUS OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
76 POVS_VPORT_EXT_INFO extInfo);
77 static NTSTATUS CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
82 static POVS_VPORT_ENTRY OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
83 PWSTR wsName, SIZE_T wstrSize);
84 static VOID UpdateSwitchCtxWithVport(POVS_SWITCH_CONTEXT switchContext,
85 POVS_VPORT_ENTRY vport, BOOLEAN newPort);
86 static NTSTATUS OvsRemoveTunnelVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
87 POVS_SWITCH_CONTEXT switchContext,
88 POVS_VPORT_ENTRY vport,
91 static VOID OvsTunnelVportPendingInit(PVOID context,
94 static VOID OvsTunnelVportPendingRemove(PVOID context,
97 static VOID AssignNicNameSpecial(POVS_VPORT_ENTRY vport);
100 * --------------------------------------------------------------------------
101 * Creates a Vport entry for a Hyper-V switch port. 'nicIndex' is typically
102 * associated with a NIC than a port. We use it here for the special case
103 * where we need to create a Vport for an external NIC with NicIndex > 0.
104 * --------------------------------------------------------------------------
107 HvCreatePort(POVS_SWITCH_CONTEXT switchContext,
108 PNDIS_SWITCH_PORT_PARAMETERS portParam,
109 NDIS_SWITCH_NIC_INDEX nicIndex)
111 POVS_VPORT_ENTRY vport;
112 LOCK_STATE_EX lockState;
113 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
114 BOOLEAN newPort = FALSE;
116 VPORT_PORT_ENTER(portParam);
118 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
119 /* Lookup by port ID. */
120 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
121 portParam->PortId, nicIndex);
123 OVS_LOG_ERROR("Port add failed due to duplicate port name, "
124 "port Id: %u", portParam->PortId);
125 status = STATUS_DATA_NOT_ACCEPTED;
126 goto create_port_done;
130 * Lookup by port name to see if this port with this name had been added
131 * (and deleted) previously.
133 vport = OvsFindVportByHvNameW(gOvsSwitchContext,
134 portParam->PortFriendlyName.String,
135 portParam->PortFriendlyName.Length);
136 if (vport && vport->isAbsentOnHv == FALSE) {
137 OVS_LOG_ERROR("Port add failed since a port already exists on "
138 "the specified port Id: %u, ovsName: %s",
139 portParam->PortId, vport->ovsName);
140 status = STATUS_DATA_NOT_ACCEPTED;
141 goto create_port_done;
145 ASSERT(vport->isAbsentOnHv);
146 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
149 * It should be possible to simply just mark this port as "not deleted"
150 * given that the port Id and the name are the same and also provided
151 * that the other properties that we cache have not changed.
153 if (vport->portType != portParam->PortType) {
154 OVS_LOG_INFO("Port add failed due to PortType change, port Id: %u"
155 " old: %u, new: %u", portParam->PortId,
156 vport->portType, portParam->PortType);
157 status = STATUS_DATA_NOT_ACCEPTED;
158 goto create_port_done;
160 vport->isAbsentOnHv = FALSE;
162 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
164 status = NDIS_STATUS_RESOURCES;
165 goto create_port_done;
169 OvsInitVportWithPortParam(vport, portParam);
170 vport->nicIndex = nicIndex;
171 UpdateSwitchCtxWithVport(switchContext, vport, newPort);
174 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
175 VPORT_PORT_EXIT(portParam);
181 * --------------------------------------------------------------------------
182 * Function to process updates to a port on the Hyper-Vs witch.
183 * --------------------------------------------------------------------------
186 HvUpdatePort(POVS_SWITCH_CONTEXT switchContext,
187 PNDIS_SWITCH_PORT_PARAMETERS portParam)
189 POVS_VPORT_ENTRY vport;
190 LOCK_STATE_EX lockState;
191 OVS_VPORT_STATE ovsState;
192 NDIS_SWITCH_NIC_STATE nicState;
194 VPORT_PORT_ENTER(portParam);
196 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
197 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
198 portParam->PortId, 0);
200 * Update properties only for NETDEV ports for supprting PS script
203 goto update_port_done;
206 /* Store the nic and the OVS states as Nic Create won't be called */
207 ovsState = vport->ovsState;
208 nicState = vport->nicState;
211 * Currently only the port friendly name is being updated
212 * Make sure that no other properties are changed
214 ASSERT(portParam->PortId == vport->portId);
215 ASSERT(portParam->PortState == vport->portState);
216 ASSERT(portParam->PortType == vport->portType);
219 * Call the set parameters function the handle all properties
220 * change in a single place in case future version supports change of
223 OvsInitVportWithPortParam(vport, portParam);
224 /* Retore the nic and OVS states */
225 vport->nicState = nicState;
226 vport->ovsState = ovsState;
229 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
230 VPORT_PORT_EXIT(portParam);
232 /* Must always return success */
233 return NDIS_STATUS_SUCCESS;
238 * --------------------------------------------------------------------------
239 * Function to process teardown of a port on the Hyper-V switch.
240 * --------------------------------------------------------------------------
243 HvTeardownPort(POVS_SWITCH_CONTEXT switchContext,
244 PNDIS_SWITCH_PORT_PARAMETERS portParam)
246 POVS_VPORT_ENTRY vport;
247 LOCK_STATE_EX lockState;
249 VPORT_PORT_ENTER(portParam);
251 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
252 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
253 portParam->PortId, 0);
255 /* add assertion here */
256 vport->portState = NdisSwitchPortStateTeardown;
257 vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
259 OVS_LOG_WARN("Vport not present.");
261 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
263 VPORT_PORT_EXIT(portParam);
267 * --------------------------------------------------------------------------
268 * Function to process deletion of a port on the Hyper-V switch.
269 * --------------------------------------------------------------------------
272 HvDeletePort(POVS_SWITCH_CONTEXT switchContext,
273 PNDIS_SWITCH_PORT_PARAMETERS portParams)
275 POVS_VPORT_ENTRY vport;
276 LOCK_STATE_EX lockState;
278 VPORT_PORT_ENTER(portParams);
280 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
281 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
282 portParams->PortId, 0);
285 * XXX: we can only destroy and remove the port if its datapath port
286 * counterpart was deleted. If the datapath port counterpart is present,
287 * we only mark the vport for deletion, so that a netlink command vport
288 * delete will delete the vport.
291 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
293 OVS_LOG_WARN("Vport not present.");
295 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
297 VPORT_PORT_EXIT(portParams);
302 * --------------------------------------------------------------------------
303 * Function to process addition of a NIC connection on the Hyper-V switch.
304 * XXX: Posting an event to DPIF is incorrect here. However, it might be useful
305 * to post an event to netdev-windows.c.
306 * --------------------------------------------------------------------------
309 HvCreateNic(POVS_SWITCH_CONTEXT switchContext,
310 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
312 POVS_VPORT_ENTRY vport;
313 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
315 LOCK_STATE_EX lockState;
317 VPORT_NIC_ENTER(nicParam);
319 /* Wait for lists to be initialized. */
320 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
322 if (!switchContext->isActivated) {
323 OVS_LOG_WARN("Switch is not activated yet.");
324 /* Veto the creation of nic */
325 status = NDIS_STATUS_NOT_SUPPORTED;
329 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
331 * There can be one or more NICs for the external port. We create a vport
332 * structure for each such NIC, and each NIC inherits a lot of properties
333 * from the parent external port.
335 if (nicParam->NicType == NdisSwitchNicTypeExternal &&
336 nicParam->NicIndex != 0) {
337 POVS_VPORT_ENTRY virtExtVport =
338 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
340 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
344 NDIS_SWITCH_PORT_PARAMETERS portParam;
345 /* Find by interface name */
346 WCHAR interfaceName[IF_MAX_STRING_SIZE] = { 0 };
347 NET_LUID interfaceLuid = { 0 };
349 status = ConvertInterfaceGuidToLuid(&nicParam->NetCfgInstanceId,
351 if (status == STATUS_SUCCESS) {
352 status = ConvertInterfaceLuidToAlias(&interfaceLuid,
354 IF_MAX_STRING_SIZE + 1);
355 if (status == STATUS_SUCCESS) {
356 RtlStringCbLengthW(interfaceName,
359 vport = OvsFindVportByHvNameW(switchContext,
365 OvsCopyPortParamsFromVport(virtExtVport, &portParam);
366 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
367 status = HvCreatePort(switchContext, &portParam,
369 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
370 if (status != NDIS_STATUS_SUCCESS) {
376 vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId,
379 OVS_LOG_ERROR("Create NIC without Switch Port,"
380 " PortId: %x, NicIndex: %d",
381 nicParam->PortId, nicParam->NicIndex);
382 status = NDIS_STATUS_INVALID_PARAMETER;
385 OvsInitVportWithNicParam(switchContext, vport, nicParam);
386 if (nicParam->NicType == NdisSwitchNicTypeInternal ||
387 (nicParam->NicType == NdisSwitchNicTypeExternal &&
388 nicParam->NicIndex != 0)) {
389 AssignNicNameSpecial(vport);
393 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
394 if (status == STATUS_SUCCESS &&
395 (vport->portType == NdisSwitchPortTypeInternal ||
396 (vport->portType == NdisSwitchPortTypeExternal &&
397 nicParam->NicIndex != 0))) {
398 AssignNicNameSpecial(vport);
402 VPORT_NIC_EXIT(nicParam);
403 OVS_LOG_TRACE("Exit: status %8x.\n", status);
409 * --------------------------------------------------------------------------
410 * Function to process connection event of a NIC on the Hyper-V switch.
411 * --------------------------------------------------------------------------
414 HvConnectNic(POVS_SWITCH_CONTEXT switchContext,
415 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
417 LOCK_STATE_EX lockState;
418 POVS_VPORT_ENTRY vport;
421 VPORT_NIC_ENTER(nicParam);
423 /* Wait for lists to be initialized. */
424 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
426 if (!switchContext->isActivated) {
427 OVS_LOG_WARN("Switch is not activated yet.");
431 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
432 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
437 OVS_LOG_WARN("Vport not present.");
438 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
443 vport->ovsState = OVS_STATE_CONNECTED;
444 vport->nicState = NdisSwitchNicStateConnected;
445 portNo = vport->portNo;
447 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
449 if (nicParam->NicType == NdisSwitchNicTypeInternal) {
450 OvsInternalAdapterUp(&nicParam->NetCfgInstanceId);
454 VPORT_NIC_EXIT(nicParam);
459 * --------------------------------------------------------------------------
460 * Function to process updates to a NIC on the Hyper-V switch.
461 * --------------------------------------------------------------------------
464 HvUpdateNic(POVS_SWITCH_CONTEXT switchContext,
465 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
467 POVS_VPORT_ENTRY vport;
468 LOCK_STATE_EX lockState;
471 VPORT_NIC_ENTER(nicParam);
473 /* Wait for lists to be initialized. */
474 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
476 if (!switchContext->isActivated) {
477 OVS_LOG_WARN("Switch is not activated yet.");
478 goto update_nic_done;
481 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
482 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
486 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
487 OVS_LOG_WARN("Vport search failed.");
488 goto update_nic_done;
490 switch (nicParam->NicType) {
491 case NdisSwitchNicTypeExternal:
492 case NdisSwitchNicTypeInternal:
493 RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
495 AssignNicNameSpecial(vport);
497 case NdisSwitchNicTypeSynthetic:
498 case NdisSwitchNicTypeEmulated:
499 if (!RtlEqualMemory(vport->vmMacAddress, nicParam->VMMacAddress,
500 sizeof (vport->vmMacAddress))) {
501 event |= OVS_EVENT_MAC_CHANGE;
502 RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
503 sizeof (vport->vmMacAddress));
509 if (!RtlEqualMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
510 sizeof (vport->permMacAddress))) {
511 RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
512 sizeof (vport->permMacAddress));
513 event |= OVS_EVENT_MAC_CHANGE;
515 if (!RtlEqualMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
516 sizeof (vport->currMacAddress))) {
517 RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
518 sizeof (vport->currMacAddress));
519 event |= OVS_EVENT_MAC_CHANGE;
522 if (vport->mtu != nicParam->MTU) {
523 vport->mtu = nicParam->MTU;
524 event |= OVS_EVENT_MTU_CHANGE;
526 vport->numaNodeId = nicParam->NumaNodeId;
528 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
531 * XXX: Not sure what kind of event to post here. DPIF is not interested in
532 * changes to MAC address. Netdev-windows might be intrested, though.
533 * That said, if the name chagnes, not clear what kind of event to be
534 * posted. We might have to delete the vport, and have userspace recreate
539 VPORT_NIC_EXIT(nicParam);
543 * --------------------------------------------------------------------------
544 * Function to process disconnect event of a NIC on the Hyper-V switch.
545 * --------------------------------------------------------------------------
548 HvDisconnectNic(POVS_SWITCH_CONTEXT switchContext,
549 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
551 POVS_VPORT_ENTRY vport;
552 LOCK_STATE_EX lockState;
553 BOOLEAN isInternalPort = FALSE;
554 OVS_EVENT_ENTRY event;
556 VPORT_NIC_ENTER(nicParam);
558 /* Wait for lists to be initialized. */
559 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
561 if (!switchContext->isActivated) {
562 OVS_LOG_WARN("Switch is not activated yet.");
566 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
567 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
572 OVS_LOG_WARN("Vport not present.");
573 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
577 vport->nicState = NdisSwitchNicStateDisconnected;
578 vport->ovsState = OVS_STATE_NIC_CREATED;
580 if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
581 isInternalPort = TRUE;
584 event.portNo = vport->portNo;
585 event.ovsType = vport->ovsType;
586 event.upcallPid = vport->upcallPid;
587 RtlCopyMemory(&event.ovsName, &vport->ovsName, sizeof event.ovsName);
588 event.type = OVS_EVENT_LINK_DOWN;
590 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
593 * Delete the port from the hash tables accessible to userspace. After this
594 * point, userspace should not be able to access this port.
596 OvsRemoveAndDeleteVport(NULL, switchContext, vport, FALSE, TRUE);
597 OvsPostEvent(&event);
599 if (isInternalPort) {
600 OvsInternalAdapterDown();
604 VPORT_NIC_EXIT(nicParam);
608 * --------------------------------------------------------------------------
609 * Function to process delete event of a NIC on the Hyper-V switch.
610 * --------------------------------------------------------------------------
613 HvDeleteNic(POVS_SWITCH_CONTEXT switchContext,
614 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
616 LOCK_STATE_EX lockState;
617 POVS_VPORT_ENTRY vport;
619 VPORT_NIC_ENTER(nicParam);
620 /* Wait for lists to be initialized. */
621 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
623 if (!switchContext->isActivated) {
624 OVS_LOG_WARN("Switch is not activated yet.");
628 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
629 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
634 OVS_LOG_WARN("Vport not present.");
635 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
639 vport->nicState = NdisSwitchNicStateUnknown;
640 vport->ovsState = OVS_STATE_PORT_CREATED;
642 if (vport->portType == NdisSwitchPortTypeExternal &&
643 vport->nicIndex != 0) {
644 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
647 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
650 VPORT_NIC_EXIT(nicParam);
654 * OVS Vport related functionality.
657 OvsFindVportByPortNo(POVS_SWITCH_CONTEXT switchContext,
660 POVS_VPORT_ENTRY vport;
661 PLIST_ENTRY head, link;
662 UINT32 hash = OvsJhashBytes((const VOID *)&portNo, sizeof(portNo),
664 head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
665 LIST_FORALL(head, link) {
666 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
667 if (vport->portNo == portNo) {
676 OvsFindTunnelVportByDstPort(POVS_SWITCH_CONTEXT switchContext,
678 OVS_VPORT_TYPE ovsPortType)
680 POVS_VPORT_ENTRY vport;
681 PLIST_ENTRY head, link;
682 UINT32 hash = OvsJhashBytes((const VOID *)&dstPort, sizeof(dstPort),
684 head = &(switchContext->tunnelVportsArray[hash & OVS_VPORT_MASK]);
685 LIST_FORALL(head, link) {
686 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, tunnelVportLink);
687 if (GetPortFromPriv(vport) == dstPort &&
688 vport->ovsType == ovsPortType) {
697 OvsFindVportByOvsName(POVS_SWITCH_CONTEXT switchContext,
700 POVS_VPORT_ENTRY vport;
701 PLIST_ENTRY head, link;
703 SIZE_T length = strlen(name) + 1;
705 hash = OvsJhashBytes((const VOID *)name, length, OVS_HASH_BASIS);
706 head = &(switchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK]);
708 LIST_FORALL(head, link) {
709 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, ovsNameLink);
710 if (!strcmp(name, vport->ovsName)) {
718 /* OvsFindVportByHvName: "name" is assumed to be null-terminated */
720 OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
721 PWSTR wsName, SIZE_T wstrSize)
723 POVS_VPORT_ENTRY vport = NULL;
724 PLIST_ENTRY head, link;
727 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
728 head = &(switchContext->portIdHashArray[i]);
729 LIST_FORALL(head, link) {
730 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
733 * NOTE about portFriendlyName:
734 * If the string is NULL-terminated, the Length member does not
735 * include the terminating NULL character.
737 if (vport->portFriendlyName.Length == wstrSize &&
738 RtlEqualMemory(wsName, vport->portFriendlyName.String,
739 vport->portFriendlyName.Length)) {
748 * Look in the list of ports that were added from the Hyper-V switch and
752 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
753 head = &(switchContext->portNoHashArray[i]);
754 LIST_FORALL(head, link) {
755 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
756 if (vport->portFriendlyName.Length == wstrSize &&
757 RtlEqualMemory(wsName, vport->portFriendlyName.String,
758 vport->portFriendlyName.Length)) {
772 OvsFindVportByHvNameA(POVS_SWITCH_CONTEXT switchContext,
775 POVS_VPORT_ENTRY vport = NULL;
776 /* 'portFriendlyName' is not NUL-terminated. */
777 SIZE_T length = strlen(name);
778 SIZE_T wstrSize = length * sizeof(WCHAR);
781 PWSTR wsName = OvsAllocateMemoryWithTag(wstrSize, OVS_VPORT_POOL_TAG);
785 for (i = 0; i < length; i++) {
788 vport = OvsFindVportByHvNameW(switchContext, wsName, wstrSize);
789 OvsFreeMemoryWithTag(wsName, OVS_VPORT_POOL_TAG);
794 OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT switchContext,
795 NDIS_SWITCH_PORT_ID portId,
796 NDIS_SWITCH_NIC_INDEX index)
798 if (switchContext->virtualExternalVport &&
799 portId == switchContext->virtualExternalPortId &&
800 index == switchContext->virtualExternalVport->nicIndex) {
801 return (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
802 } else if (switchContext->internalVport &&
803 portId == switchContext->internalPortId &&
804 index == switchContext->internalVport->nicIndex) {
805 return (POVS_VPORT_ENTRY)switchContext->internalVport;
807 PLIST_ENTRY head, link;
808 POVS_VPORT_ENTRY vport;
810 hash = OvsJhashWords((UINT32 *)&portId, 1, OVS_HASH_BASIS);
811 head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
812 LIST_FORALL(head, link) {
813 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
814 if (portId == vport->portId && index == vport->nicIndex) {
823 OvsAllocateVport(VOID)
825 POVS_VPORT_ENTRY vport;
826 vport = (POVS_VPORT_ENTRY)OvsAllocateMemoryWithTag(
827 sizeof(OVS_VPORT_ENTRY), OVS_VPORT_POOL_TAG);
831 RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY));
832 vport->ovsState = OVS_STATE_UNKNOWN;
833 vport->isAbsentOnHv = FALSE;
834 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
836 InitializeListHead(&vport->ovsNameLink);
837 InitializeListHead(&vport->portIdLink);
838 InitializeListHead(&vport->portNoLink);
844 OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
845 PNDIS_SWITCH_PORT_PARAMETERS portParam)
847 vport->portType = portParam->PortType;
848 vport->portState = portParam->PortState;
849 vport->portId = portParam->PortId;
850 vport->nicState = NdisSwitchNicStateUnknown;
851 vport->isExternal = FALSE;
852 vport->isBridgeInternal = FALSE;
854 switch (vport->portType) {
855 case NdisSwitchPortTypeExternal:
856 vport->isExternal = TRUE;
857 vport->ovsType = OVS_VPORT_TYPE_NETDEV;
859 case NdisSwitchPortTypeInternal:
860 vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
862 case NdisSwitchPortTypeSynthetic:
863 case NdisSwitchPortTypeEmulated:
864 vport->ovsType = OVS_VPORT_TYPE_NETDEV;
867 RtlCopyMemory(&vport->hvPortName, &portParam->PortName,
868 sizeof (NDIS_SWITCH_PORT_NAME));
869 /* For external and internal ports, 'portFriendlyName' is overwritten
871 RtlCopyMemory(&vport->portFriendlyName, &portParam->PortFriendlyName,
872 sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
874 switch (vport->portState) {
875 case NdisSwitchPortStateCreated:
876 vport->ovsState = OVS_STATE_PORT_CREATED;
878 case NdisSwitchPortStateTeardown:
879 vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
881 case NdisSwitchPortStateDeleted:
882 vport->ovsState = OVS_STATE_PORT_DELETED;
889 OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
890 POVS_VPORT_ENTRY vport,
891 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
893 ASSERT(vport->portId == nicParam->PortId);
894 ASSERT(vport->ovsState == OVS_STATE_PORT_CREATED);
896 UNREFERENCED_PARAMETER(switchContext);
898 RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
899 sizeof (nicParam->PermanentMacAddress));
900 RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
901 sizeof (nicParam->CurrentMacAddress));
903 if (nicParam->NicType == NdisSwitchNicTypeSynthetic ||
904 nicParam->NicType == NdisSwitchNicTypeEmulated) {
905 RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
906 sizeof (nicParam->VMMacAddress));
907 RtlCopyMemory(&vport->vmName, &nicParam->VmName,
908 sizeof (nicParam->VmName));
910 RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
911 sizeof (nicParam->NetCfgInstanceId));
913 RtlCopyMemory(&vport->nicName, &nicParam->NicName,
914 sizeof (nicParam->NicName));
915 vport->mtu = nicParam->MTU;
916 vport->nicState = nicParam->NicState;
917 vport->nicIndex = nicParam->NicIndex;
918 vport->numaNodeId = nicParam->NumaNodeId;
920 switch (vport->nicState) {
921 case NdisSwitchNicStateCreated:
922 vport->ovsState = OVS_STATE_NIC_CREATED;
924 case NdisSwitchNicStateConnected:
925 vport->ovsState = OVS_STATE_CONNECTED;
927 case NdisSwitchNicStateDisconnected:
928 vport->ovsState = OVS_STATE_NIC_CREATED;
930 case NdisSwitchNicStateDeleted:
931 vport->ovsState = OVS_STATE_PORT_CREATED;
937 * --------------------------------------------------------------------------
938 * Populates 'portParam' based on 'vport'.
939 * --------------------------------------------------------------------------
942 OvsCopyPortParamsFromVport(POVS_VPORT_ENTRY vport,
943 PNDIS_SWITCH_PORT_PARAMETERS portParam)
945 portParam->Flags = 0;
946 portParam->PortId = vport->portId;
947 RtlCopyMemory(&portParam->PortName, &vport->hvPortName,
948 sizeof (NDIS_SWITCH_PORT_NAME));
949 RtlCopyMemory(&portParam->PortFriendlyName,
950 &vport->portFriendlyName,
951 sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
952 portParam->PortType = vport->portType;
953 portParam->IsValidationPort = FALSE;
954 portParam->PortState = vport->portState;
958 * --------------------------------------------------------------------------
959 * Initializes a tunnel vport.
960 * --------------------------------------------------------------------------
963 OvsInitTunnelVport(PVOID userContext,
964 POVS_VPORT_ENTRY vport,
965 OVS_VPORT_TYPE ovsType,
968 NTSTATUS status = STATUS_SUCCESS;
969 POVS_USER_PARAMS_CONTEXT usrParamsCtx =
970 (POVS_USER_PARAMS_CONTEXT)userContext;
972 vport->isBridgeInternal = FALSE;
973 vport->ovsType = ovsType;
974 vport->ovsState = OVS_STATE_PORT_CREATED;
976 case OVS_VPORT_TYPE_GRE:
978 case OVS_VPORT_TYPE_VXLAN:
980 POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
982 tunnelContext = OvsAllocateMemoryWithTag(sizeof(*tunnelContext),
984 if (tunnelContext == NULL) {
985 status = STATUS_INSUFFICIENT_RESOURCES;
988 tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
989 tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
990 tunnelContext->outputLength = usrParamsCtx->outputLength;
991 tunnelContext->vport = vport;
993 status = OvsInitVxlanTunnel(usrParamsCtx->irp,
996 OvsTunnelVportPendingInit,
997 (PVOID)tunnelContext);
998 if (status != STATUS_PENDING) {
999 OvsFreeMemoryWithTag(tunnelContext, OVS_VPORT_POOL_TAG);
1000 tunnelContext = NULL;
1004 case OVS_VPORT_TYPE_STT:
1005 status = OvsInitSttTunnel(vport, dstPort);
1014 * --------------------------------------------------------------------------
1015 * Initializes a bridge internal vport ie. a port of type
1016 * OVS_VPORT_TYPE_INTERNAL but not present on the Hyper-V switch.
1017 * --------------------------------------------------------------------------
1020 OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport)
1022 vport->isBridgeInternal = TRUE;
1023 vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
1024 /* Mark the status to be connected, since there is no other initialization
1026 vport->ovsState = OVS_STATE_CONNECTED;
1027 return STATUS_SUCCESS;
1031 * --------------------------------------------------------------------------
1032 * For external and internal vports 'portFriendlyName' parameter, provided by
1033 * Hyper-V, is overwritten with the interface alias name.
1034 * --------------------------------------------------------------------------
1037 AssignNicNameSpecial(POVS_VPORT_ENTRY vport)
1039 NTSTATUS status = STATUS_SUCCESS;
1040 WCHAR interfaceName[IF_MAX_STRING_SIZE] = { 0 };
1041 NET_LUID interfaceLuid = { 0 };
1044 ASSERT(vport->portType == NdisSwitchPortTypeExternal ||
1045 vport->portType == NdisSwitchPortTypeInternal);
1047 status = ConvertInterfaceGuidToLuid(&vport->netCfgInstanceId,
1049 if (status == STATUS_SUCCESS) {
1051 * Must be called from PASSIVE_LEVEL. Resulted in a
1052 * STATUS_INVALID_DEVICE_REQUEST if not.
1054 status = ConvertInterfaceLuidToAlias(&interfaceLuid, interfaceName,
1055 IF_MAX_STRING_SIZE + 1);
1056 if (status == STATUS_SUCCESS) {
1057 if (vport->portType == NdisSwitchPortTypeExternal &&
1058 vport->nicIndex == 0) {
1059 RtlStringCbPrintfW(vport->portFriendlyName.String, IF_MAX_STRING_SIZE,
1060 L"%s.virtualAdapter", interfaceName);
1062 RtlStringCbPrintfW(vport->portFriendlyName.String,
1063 IF_MAX_STRING_SIZE, L"%s", interfaceName);
1066 RtlStringCbLengthW(vport->portFriendlyName.String, IF_MAX_STRING_SIZE,
1068 vport->portFriendlyName.Length = (USHORT)len;
1070 OVS_LOG_INFO("Fail to convert interface LUID to alias, status: %x",
1074 OVS_LOG_INFO("Fail to convert interface GUID to LUID, status: %x",
1081 * --------------------------------------------------------------------------
1082 * Functionality common to any port on the Hyper-V switch. This function is not
1083 * to be called for a port that is not on the Hyper-V switch.
1085 * Inserts the port into 'portIdHashArray' and caches the pointer in the
1086 * 'switchContext' if needed.
1087 * --------------------------------------------------------------------------
1090 UpdateSwitchCtxWithVport(POVS_SWITCH_CONTEXT switchContext,
1091 POVS_VPORT_ENTRY vport,
1096 switch (vport->portType) {
1097 case NdisSwitchPortTypeExternal:
1098 if (vport->nicIndex == 0) {
1099 switchContext->virtualExternalPortId = vport->portId;
1100 switchContext->virtualExternalVport = vport;
1102 switchContext->numPhysicalNics++;
1105 case NdisSwitchPortTypeInternal:
1106 ASSERT(vport->isBridgeInternal == FALSE);
1107 switchContext->internalPortId = vport->portId;
1108 switchContext->internalVport = vport;
1110 case NdisSwitchPortTypeSynthetic:
1111 case NdisSwitchPortTypeEmulated:
1116 * It is important to not insert vport corresponding to virtual external
1117 * port into the 'portIdHashArray' since the port should not be exposed to
1120 if (vport->portType == NdisSwitchPortTypeExternal &&
1121 vport->nicIndex == 0) {
1126 * NOTE: OvsJhashWords has portId as "1" word. This should be ok, even
1127 * though sizeof(NDIS_SWITCH_PORT_ID) = 4, not 2, because the
1128 * hyper-v switch seems to use only 2 bytes out of 4.
1130 hash = OvsJhashWords(&vport->portId, 1, OVS_HASH_BASIS);
1131 InsertHeadList(&switchContext->portIdHashArray[hash & OVS_VPORT_MASK],
1132 &vport->portIdLink);
1134 switchContext->numHvVports++;
1140 * --------------------------------------------------------------------------
1141 * Functionality common to any port added from OVS userspace.
1143 * Inserts the port into 'portNoHashArray', 'ovsPortNameHashArray' and in
1144 * 'tunnelVportsArray' if appropriate.
1145 * --------------------------------------------------------------------------
1148 InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
1149 POVS_VPORT_ENTRY vport)
1153 switch(vport->ovsType) {
1154 case OVS_VPORT_TYPE_VXLAN:
1155 case OVS_VPORT_TYPE_STT:
1157 UINT16 dstPort = GetPortFromPriv(vport);
1158 hash = OvsJhashBytes(&dstPort,
1162 &gOvsSwitchContext->tunnelVportsArray[hash & OVS_VPORT_MASK],
1163 &vport->tunnelVportLink);
1164 switchContext->numNonHvVports++;
1167 case OVS_VPORT_TYPE_INTERNAL:
1168 if (vport->isBridgeInternal) {
1169 switchContext->numNonHvVports++;
1176 * Insert the port into the hash array of ports: by port number and ovs
1177 * and ovs (datapath) port name.
1178 * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1179 * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1181 hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
1182 InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
1183 &vport->portNoLink);
1185 hash = OvsJhashBytes(vport->ovsName, strlen(vport->ovsName) + 1,
1188 &gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
1189 &vport->ovsNameLink);
1191 return STATUS_SUCCESS;
1196 * --------------------------------------------------------------------------
1197 * Provides functionality that is partly complementatry to
1198 * InitOvsVportCommon()/UpdateSwitchCtxWithVport().
1200 * 'hvDelete' indicates if caller is removing the vport as a result of the
1201 * port being removed on the Hyper-V switch.
1202 * 'ovsDelete' indicates if caller is removing the vport as a result of the
1203 * port being removed from OVS userspace.
1204 * --------------------------------------------------------------------------
1207 OvsRemoveAndDeleteVport(PVOID usrParamsContext,
1208 POVS_SWITCH_CONTEXT switchContext,
1209 POVS_VPORT_ENTRY vport,
1213 POVS_USER_PARAMS_CONTEXT usrParamsCtx =
1214 (POVS_USER_PARAMS_CONTEXT)usrParamsContext;
1215 BOOLEAN hvSwitchPort = FALSE;
1216 BOOLEAN deletedOnOvs = FALSE;
1217 BOOLEAN deletedOnHv = FALSE;
1219 switch (vport->ovsType) {
1220 case OVS_VPORT_TYPE_INTERNAL:
1221 if (!vport->isBridgeInternal) {
1222 if (hvDelete && vport->isAbsentOnHv == FALSE) {
1223 switchContext->internalPortId = 0;
1224 switchContext->internalVport = NULL;
1225 OvsInternalAdapterDown();
1227 hvSwitchPort = TRUE;
1230 case OVS_VPORT_TYPE_VXLAN:
1233 status = OvsRemoveTunnelVport(usrParamsCtx, switchContext, vport,
1234 hvDelete, ovsDelete);
1235 if (status != STATUS_SUCCESS) {
1239 case OVS_VPORT_TYPE_STT:
1240 OvsCleanupSttTunnel(vport);
1242 case OVS_VPORT_TYPE_GRE:
1244 case OVS_VPORT_TYPE_NETDEV:
1245 if (vport->isExternal) {
1246 if (vport->nicIndex == 0) {
1247 /* Such a vport is not part of any of the hash tables, since it
1248 * is not exposed to userspace. See Vport.h for explanation. */
1249 ASSERT(hvDelete == TRUE);
1250 ASSERT(switchContext->numPhysicalNics == 0);
1251 switchContext->virtualExternalPortId = 0;
1252 switchContext->virtualExternalVport = NULL;
1253 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1254 return STATUS_SUCCESS;
1257 hvSwitchPort = TRUE;
1263 * 'hvDelete' == TRUE indicates that the port should be removed from the
1264 * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
1265 * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
1267 * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
1269 if (vport->isAbsentOnHv == TRUE) {
1272 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1273 deletedOnOvs = TRUE;
1276 if (hvDelete && !deletedOnHv) {
1277 vport->isAbsentOnHv = TRUE;
1279 if (vport->isExternal) {
1280 ASSERT(vport->nicIndex != 0);
1281 ASSERT(switchContext->numPhysicalNics);
1282 switchContext->numPhysicalNics--;
1285 /* Remove the port from the relevant lists. */
1286 RemoveEntryList(&vport->portIdLink);
1287 InitializeListHead(&vport->portIdLink);
1290 if (ovsDelete && !deletedOnOvs) {
1291 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
1292 vport->ovsName[0] = '\0';
1294 /* Remove the port from the relevant lists. */
1295 RemoveEntryList(&vport->ovsNameLink);
1296 InitializeListHead(&vport->ovsNameLink);
1297 RemoveEntryList(&vport->portNoLink);
1298 InitializeListHead(&vport->portNoLink);
1299 if (OVS_VPORT_TYPE_VXLAN == vport->ovsType ||
1300 OVS_VPORT_TYPE_STT == vport->ovsType) {
1301 RemoveEntryList(&vport->tunnelVportLink);
1302 InitializeListHead(&vport->tunnelVportLink);
1305 deletedOnOvs = TRUE;
1309 * Deallocate the port if it has been deleted on the Hyper-V switch as well
1312 if (deletedOnHv && deletedOnOvs) {
1314 switchContext->numHvVports--;
1316 switchContext->numNonHvVports--;
1318 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1321 return STATUS_SUCCESS;
1325 OvsRemoveTunnelVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1326 POVS_SWITCH_CONTEXT switchContext,
1327 POVS_VPORT_ENTRY vport,
1331 POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
1334 tunnelContext = OvsAllocateMemoryWithTag(sizeof(*tunnelContext),
1335 OVS_VPORT_POOL_TAG);
1336 if (tunnelContext == NULL) {
1337 return STATUS_INSUFFICIENT_RESOURCES;
1339 RtlZeroMemory(tunnelContext, sizeof(*tunnelContext));
1341 tunnelContext->switchContext = switchContext;
1342 tunnelContext->hvSwitchPort = FALSE;
1343 tunnelContext->hvDelete = hvDelete;
1344 tunnelContext->ovsDelete = ovsDelete;
1345 tunnelContext->vport = vport;
1348 tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
1349 tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
1350 tunnelContext->outputLength = usrParamsCtx->outputLength;
1351 irp = usrParamsCtx->irp;
1354 return OvsCleanupVxlanTunnel(irp, vport, OvsTunnelVportPendingRemove,
1359 * --------------------------------------------------------------------------
1360 * Enumerates the ports on the Hyper-V switch.
1361 * --------------------------------------------------------------------------
1364 OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
1366 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1368 PNDIS_SWITCH_PORT_PARAMETERS portParam;
1369 PNDIS_SWITCH_PORT_ARRAY portArray = NULL;
1371 OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
1373 status = OvsGetPortsOnSwitch(switchContext, &portArray);
1374 if (status != NDIS_STATUS_SUCCESS) {
1378 for (arrIndex = 0; arrIndex < portArray->NumElements; arrIndex++) {
1379 portParam = NDIS_SWITCH_PORT_AT_ARRAY_INDEX(portArray, arrIndex);
1381 if (portParam->IsValidationPort) {
1385 status = HvCreatePort(switchContext, portParam, 0);
1386 if (status != STATUS_SUCCESS && status != STATUS_DATA_NOT_ACCEPTED) {
1392 if (status != NDIS_STATUS_SUCCESS) {
1393 OvsClearAllSwitchVports(switchContext);
1396 OvsFreeSwitchPortsArray(portArray);
1398 OVS_LOG_TRACE("Exit: status: %x", status);
1404 * --------------------------------------------------------------------------
1405 * Enumerates the NICs on the Hyper-V switch.
1406 * --------------------------------------------------------------------------
1409 OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext)
1411 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1412 PNDIS_SWITCH_NIC_ARRAY nicArray = NULL;
1414 PNDIS_SWITCH_NIC_PARAMETERS nicParam;
1416 OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
1418 * Now, get NIC list.
1420 status = OvsGetNicsOnSwitch(switchContext, &nicArray);
1421 if (status != NDIS_STATUS_SUCCESS) {
1424 for (arrIndex = 0; arrIndex < nicArray->NumElements; ++arrIndex) {
1425 nicParam = NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray, arrIndex);
1428 * XXX: Check if the port is configured with a VLAN. Disallow such a
1429 * configuration, since we don't support tag-in-tag.
1430 * XXX: Check if the port is connected to a VF. Disconnect the VF in
1434 status = HvCreateNic(switchContext, nicParam);
1435 if (status == NDIS_STATUS_SUCCESS) {
1436 HvConnectNic(switchContext, nicParam);
1441 OvsFreeSwitchNicsArray(nicArray);
1443 OVS_LOG_TRACE("Exit: status: %x", status);
1448 * --------------------------------------------------------------------------
1449 * Deletes ports added from the Hyper-V switch as well as OVS usersapce. The
1450 * function deletes ports in 'portIdHashArray'. This will delete most of the
1451 * ports that are in the 'portNoHashArray' as well. Any remaining ports
1452 * are deleted by walking the 'portNoHashArray'.
1453 * --------------------------------------------------------------------------
1456 OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
1458 for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1459 PLIST_ENTRY head, link, next;
1461 head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
1462 LIST_FORALL_SAFE(head, link, next) {
1463 POVS_VPORT_ENTRY vport;
1464 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1465 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
1470 * Remove 'virtualExternalVport' as well. This port is not part of the
1471 * 'portIdHashArray'.
1473 if (switchContext->virtualExternalVport) {
1474 OvsRemoveAndDeleteVport(NULL, switchContext,
1475 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE);
1479 for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1480 PLIST_ENTRY head, link, next;
1481 head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
1482 LIST_FORALL_SAFE(head, link, next) {
1483 POVS_VPORT_ENTRY vport;
1484 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1485 ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
1486 (vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
1487 vport->isBridgeInternal) || vport->isAbsentOnHv == TRUE);
1488 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
1492 ASSERT(switchContext->virtualExternalVport == NULL);
1493 ASSERT(switchContext->internalVport == NULL);
1498 OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
1503 UNICODE_STRING ustr;
1507 ustr.Buffer = wStr->String;
1508 ustr.Length = wStr->Length;
1509 ustr.MaximumLength = IF_MAX_STRING_SIZE;
1512 astr.MaximumLength = maxStrLen;
1515 size = RtlUnicodeStringToAnsiSize(&ustr);
1516 if (size > maxStrLen) {
1517 return STATUS_BUFFER_OVERFLOW;
1520 status = RtlUnicodeStringToAnsiString(&astr, &ustr, FALSE);
1522 ASSERT(status == STATUS_SUCCESS);
1523 if (status != STATUS_SUCCESS) {
1526 ASSERT(astr.Length <= maxStrLen);
1527 str[astr.Length] = 0;
1528 return STATUS_SUCCESS;
1532 * --------------------------------------------------------------------------
1533 * Utility function that populates a 'OVS_VPORT_EXT_INFO' structure for the
1535 * --------------------------------------------------------------------------
1538 OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
1539 POVS_VPORT_EXT_INFO extInfo)
1541 POVS_VPORT_ENTRY vport;
1543 LOCK_STATE_EX lockState;
1544 NTSTATUS status = STATUS_SUCCESS;
1545 BOOLEAN doConvert = FALSE;
1547 RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO));
1548 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1549 if (vportGet->portNo == 0) {
1550 StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
1551 vport = OvsFindVportByHvNameA(gOvsSwitchContext, vportGet->name);
1552 if (vport == NULL) {
1553 /* If the port is not a Hyper-V port and it has been added earlier,
1554 * we'll find it in 'ovsPortNameHashArray'. */
1555 vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name);
1558 vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo);
1560 if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED &&
1561 vport->ovsState != OVS_STATE_NIC_CREATED)) {
1562 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1563 if (vportGet->portNo) {
1564 OVS_LOG_WARN("vport %u does not exist any more", vportGet->portNo);
1566 OVS_LOG_WARN("vport %s does not exist any more", vportGet->name);
1568 status = STATUS_DEVICE_DOES_NOT_EXIST;
1571 extInfo->dpNo = vportGet->dpNo;
1572 extInfo->portNo = vport->portNo;
1573 RtlCopyMemory(extInfo->macAddress, vport->currMacAddress,
1574 sizeof (vport->currMacAddress));
1575 RtlCopyMemory(extInfo->permMACAddress, vport->permMacAddress,
1576 sizeof (vport->permMacAddress));
1577 if (vport->ovsType == OVS_VPORT_TYPE_NETDEV) {
1578 RtlCopyMemory(extInfo->vmMACAddress, vport->vmMacAddress,
1579 sizeof (vport->vmMacAddress));
1581 extInfo->nicIndex = vport->nicIndex;
1582 extInfo->portId = vport->portId;
1583 extInfo->type = vport->ovsType;
1584 extInfo->mtu = vport->mtu;
1588 if (vport->ovsState == OVS_STATE_NIC_CREATED) {
1589 extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_DOWN;
1590 } else if (vport->ovsState == OVS_STATE_CONNECTED) {
1591 extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
1593 extInfo->status = OVS_EVENT_DISCONNECT;
1595 if (extInfo->type == OVS_VPORT_TYPE_NETDEV &&
1596 (vport->ovsState == OVS_STATE_NIC_CREATED ||
1597 vport->ovsState == OVS_STATE_CONNECTED)) {
1600 extInfo->vmUUID[0] = 0;
1601 extInfo->vifUUID[0] = 0;
1603 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1605 status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName,
1607 OVS_MAX_PORT_NAME_LENGTH);
1608 if (status != STATUS_SUCCESS) {
1609 OVS_LOG_INFO("Fail to convert NIC name.");
1610 extInfo->vmUUID[0] = 0;
1613 status = OvsConvertIfCountedStrToAnsiStr(&vport->vmName,
1615 OVS_MAX_VM_UUID_LEN);
1616 if (status != STATUS_SUCCESS) {
1617 OVS_LOG_INFO("Fail to convert VM name.");
1618 extInfo->vmUUID[0] = 0;
1621 status = OvsConvertIfCountedStrToAnsiStr(&vport->nicName,
1623 OVS_MAX_VIF_UUID_LEN);
1624 if (status != STATUS_SUCCESS) {
1625 OVS_LOG_INFO("Fail to convert nic UUID");
1626 extInfo->vifUUID[0] = 0;
1629 * for now ignore status
1631 status = STATUS_SUCCESS;
1639 * --------------------------------------------------------------------------
1640 * Command Handler for 'OVS_WIN_NETDEV_CMD_GET'.
1641 * --------------------------------------------------------------------------
1644 OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1647 NTSTATUS status = STATUS_SUCCESS;
1648 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1649 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1650 NL_ERROR nlError = NL_ERROR_SUCCESS;
1651 OVS_VPORT_GET vportGet;
1652 OVS_VPORT_EXT_INFO info;
1654 static const NL_POLICY ovsNetdevPolicy[] = {
1655 [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING,
1657 .maxLen = IFNAMSIZ },
1659 PNL_ATTR netdevAttrs[ARRAY_SIZE(ovsNetdevPolicy)];
1661 /* input buffer has been validated while validating transaction dev op. */
1662 ASSERT(usrParamsCtx->inputBuffer != NULL &&
1663 usrParamsCtx->inputLength > sizeof *msgIn);
1665 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1666 return STATUS_INVALID_BUFFER_SIZE;
1669 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1670 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1671 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1672 ovsNetdevPolicy, ARRAY_SIZE(ovsNetdevPolicy),
1673 netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
1674 return STATUS_INVALID_PARAMETER;
1677 vportGet.portNo = 0;
1678 RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
1679 NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
1681 status = OvsGetExtInfoIoctl(&vportGet, &info);
1682 if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
1683 nlError = NL_ERROR_NODEV;
1687 status = CreateNetlinkMesgForNetdev(&info, msgIn,
1688 usrParamsCtx->outputBuffer, usrParamsCtx->outputLength,
1689 gOvsSwitchContext->dpNo);
1690 if (status == STATUS_SUCCESS) {
1691 *replyLen = msgOut->nlMsg.nlmsgLen;
1695 if (nlError != NL_ERROR_SUCCESS) {
1696 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1697 usrParamsCtx->outputBuffer;
1699 NlBuildErrorMsg(msgIn, msgError, nlError);
1700 *replyLen = msgError->nlMsg.nlmsgLen;
1703 return STATUS_SUCCESS;
1708 * --------------------------------------------------------------------------
1709 * Utility function to construct an OVS_MESSAGE for the specified vport. The
1710 * OVS_MESSAGE contains the output of a netdev command.
1711 * --------------------------------------------------------------------------
1714 CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
1723 UINT32 netdevFlags = 0;
1725 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1727 ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1728 msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1729 msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1732 return STATUS_INVALID_BUFFER_SIZE;
1735 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
1738 return STATUS_INVALID_BUFFER_SIZE;
1741 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
1743 return STATUS_INVALID_BUFFER_SIZE;
1746 ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
1749 return STATUS_INVALID_BUFFER_SIZE;
1752 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
1753 (PCHAR)info->macAddress, sizeof (info->macAddress));
1755 return STATUS_INVALID_BUFFER_SIZE;
1758 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
1760 return STATUS_INVALID_BUFFER_SIZE;
1763 if (info->status != OVS_EVENT_CONNECT) {
1764 netdevFlags = OVS_WIN_NETDEV_IFF_UP;
1766 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
1769 return STATUS_INVALID_BUFFER_SIZE;
1773 * XXX: add netdev_stats when we have the definition available in the
1777 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1778 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1780 return STATUS_SUCCESS;
1783 static __inline VOID
1784 OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec)
1786 while ((!switchContext->isActivated) &&
1787 (!switchContext->isActivateFailed)) {
1788 /* Wait for the switch to be active and
1789 * the list of ports in OVS to be initialized. */
1790 NdisMSleep(sleepMicroSec);
1795 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1802 OVS_VPORT_FULL_STATS vportStats;
1806 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1808 ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1809 msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1810 msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1813 return STATUS_INVALID_BUFFER_SIZE;
1816 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1818 return STATUS_INVALID_BUFFER_SIZE;
1821 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1823 return STATUS_INVALID_BUFFER_SIZE;
1826 ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1828 return STATUS_INVALID_BUFFER_SIZE;
1832 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1833 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1834 * it means we have an array of pids, instead of a single pid.
1835 * ATM we assume we have one pid only.
1838 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1841 return STATUS_INVALID_BUFFER_SIZE;
1845 vportStats.rxPackets = vport->stats.rxPackets;
1846 vportStats.rxBytes = vport->stats.rxBytes;
1847 vportStats.txPackets = vport->stats.txPackets;
1848 vportStats.txBytes = vport->stats.txBytes;
1849 vportStats.rxErrors = vport->errStats.rxErrors;
1850 vportStats.txErrors = vport->errStats.txErrors;
1851 vportStats.rxDropped = vport->errStats.rxDropped;
1852 vportStats.txDropped = vport->errStats.txDropped;
1854 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1856 sizeof(OVS_VPORT_FULL_STATS));
1858 return STATUS_INVALID_BUFFER_SIZE;
1862 * XXX: when vxlan udp dest port becomes configurable, we will also need
1863 * to add vport options
1866 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1867 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1869 return STATUS_SUCCESS;
1873 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1877 POVS_OPEN_INSTANCE instance =
1878 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1879 LOCK_STATE_EX lockState;
1880 UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1883 * XXX: this function shares some code with other dump command(s).
1884 * In the future, we will need to refactor the dump functions
1887 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1889 if (instance->dumpState.ovsMsg == NULL) {
1891 return STATUS_INVALID_DEVICE_STATE;
1894 /* Output buffer has been validated while validating read dev op. */
1895 ASSERT(usrParamsCtx->outputBuffer != NULL);
1897 msgIn = instance->dumpState.ovsMsg;
1900 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1901 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1902 * it means we have an array of pids, instead of a single pid.
1903 * ATM we assume we have one pid only.
1905 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1907 if (gOvsSwitchContext->numHvVports > 0 ||
1908 gOvsSwitchContext->numNonHvVports > 0) {
1909 /* inBucket: the bucket, used for lookup */
1910 UINT32 inBucket = instance->dumpState.index[0];
1911 /* inIndex: index within the given bucket, used for lookup */
1912 UINT32 inIndex = instance->dumpState.index[1];
1913 /* the bucket to be used for the next dump operation */
1914 UINT32 outBucket = 0;
1915 /* the index within the outBucket to be used for the next dump */
1916 UINT32 outIndex = 0;
1918 for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1919 PLIST_ENTRY head, link;
1920 head = &(gOvsSwitchContext->portNoHashArray[i]);
1921 POVS_VPORT_ENTRY vport = NULL;
1924 LIST_FORALL(head, link) {
1927 * if one or more dumps were previously done on this same bucket,
1928 * inIndex will be > 0, so we'll need to reply with the
1929 * inIndex + 1 vport from the bucket.
1931 if (outIndex >= inIndex) {
1932 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1934 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
1935 OvsCreateMsgFromVport(vport, msgIn,
1936 usrParamsCtx->outputBuffer,
1937 usrParamsCtx->outputLength,
1938 gOvsSwitchContext->dpNo);
1951 * if no vport was found above, check the next bucket, beginning
1952 * with the first (i.e. index 0) elem from within that bucket
1959 /* XXX: what about NLMSG_DONE (as msg type)? */
1960 instance->dumpState.index[0] = outBucket;
1961 instance->dumpState.index[1] = outIndex;
1964 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1966 /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1967 if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1968 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1969 *replyLen = msgOut->nlMsg.nlmsgLen;
1972 * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1976 /* Free up the dump state, since there's no more data to continue. */
1977 FreeUserDumpState(instance);
1980 return STATUS_SUCCESS;
1984 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1987 NTSTATUS status = STATUS_SUCCESS;
1988 LOCK_STATE_EX lockState;
1990 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1991 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1992 POVS_VPORT_ENTRY vport = NULL;
1993 NL_ERROR nlError = NL_ERROR_SUCCESS;
1994 PCHAR portName = NULL;
1995 UINT32 portNameLen = 0;
1996 UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
1998 static const NL_POLICY ovsVportPolicy[] = {
1999 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2000 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
2005 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2007 /* input buffer has been validated while validating write dev op. */
2008 ASSERT(usrParamsCtx->inputBuffer != NULL);
2010 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2011 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2012 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2013 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2014 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2015 return STATUS_INVALID_PARAMETER;
2018 /* Output buffer has been validated while validating transact dev op. */
2019 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2021 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
2022 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2023 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2024 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2026 /* the port name is expected to be null-terminated */
2027 ASSERT(portName[portNameLen - 1] == '\0');
2029 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2030 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2031 portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2033 vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
2035 nlError = NL_ERROR_INVAL;
2036 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2041 nlError = NL_ERROR_NODEV;
2042 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2046 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2047 usrParamsCtx->outputLength,
2048 gOvsSwitchContext->dpNo);
2049 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2051 *replyLen = msgOut->nlMsg.nlmsgLen;
2054 if (nlError != NL_ERROR_SUCCESS) {
2055 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2056 usrParamsCtx->outputBuffer;
2058 NlBuildErrorMsg(msgIn, msgError, nlError);
2059 *replyLen = msgError->nlMsg.nlmsgLen;
2062 return STATUS_SUCCESS;
2066 * --------------------------------------------------------------------------
2067 * Command Handler for 'OVS_VPORT_CMD_GET'.
2069 * The function handles the initial call to setup the dump state, as well as
2070 * subsequent calls to continue dumping data.
2071 * --------------------------------------------------------------------------
2074 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2079 switch (usrParamsCtx->devOp) {
2080 case OVS_WRITE_DEV_OP:
2081 return OvsSetupDumpStart(usrParamsCtx);
2083 case OVS_READ_DEV_OP:
2084 return OvsGetVportDumpNext(usrParamsCtx, replyLen);
2086 case OVS_TRANSACTION_DEV_OP:
2087 return OvsGetVport(usrParamsCtx, replyLen);
2090 return STATUS_INVALID_DEVICE_REQUEST;
2096 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
2098 /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
2099 for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
2100 POVS_VPORT_ENTRY vport;
2102 vport = OvsFindVportByPortNo(switchContext, i);
2108 return OVS_DPPORT_NUMBER_INVALID;
2112 * --------------------------------------------------------------------------
2113 * Command Handler for 'OVS_VPORT_CMD_NEW'.
2114 * --------------------------------------------------------------------------
2117 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2120 NDIS_STATUS status = STATUS_SUCCESS;
2121 LOCK_STATE_EX lockState;
2123 NL_ERROR nlError = NL_ERROR_SUCCESS;
2124 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2125 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2126 POVS_VPORT_ENTRY vport = NULL;
2130 BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE;
2132 static const NL_POLICY ovsVportPolicy[] = {
2133 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2134 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2135 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2137 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2138 .optional = FALSE },
2139 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2142 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2144 /* input buffer has been validated while validating write dev op. */
2145 ASSERT(usrParamsCtx->inputBuffer != NULL);
2147 /* Output buffer has been validated while validating transact dev op. */
2148 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2150 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2151 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2152 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2153 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2154 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2155 return STATUS_INVALID_PARAMETER;
2158 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2159 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2160 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2162 /* we are expecting null terminated strings to be passed */
2163 ASSERT(portName[portNameLen - 1] == '\0');
2165 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2167 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2169 nlError = NL_ERROR_EXIST;
2173 if (portType == OVS_VPORT_TYPE_NETDEV) {
2174 /* External ports can also be looked up like VIF ports. */
2175 vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
2177 ASSERT(OvsIsTunnelVportType(portType) ||
2178 portType == OVS_VPORT_TYPE_INTERNAL);
2180 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
2181 if (vport == NULL) {
2182 nlError = NL_ERROR_NOMEM;
2185 vportAllocated = TRUE;
2187 if (OvsIsTunnelVportType(portType)) {
2188 UINT16 transportPortDest = 0;
2191 case OVS_VPORT_TYPE_VXLAN:
2192 transportPortDest = VXLAN_UDP_PORT;
2194 case OVS_VPORT_TYPE_STT:
2195 transportPortDest = STT_TCP_PORT;
2198 nlError = NL_ERROR_INVAL;
2202 if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2203 PNL_ATTR attr = NlAttrFindNested(vportAttrs[OVS_VPORT_ATTR_OPTIONS],
2204 OVS_TUNNEL_ATTR_DST_PORT);
2206 transportPortDest = NlAttrGetU16(attr);
2210 status = OvsInitTunnelVport(usrParamsCtx,
2215 nlError = NlMapStatusToNlErr(status);
2217 OvsInitBridgeInternalVport(vport);
2220 vportInitialized = TRUE;
2222 if (nlError == NL_ERROR_SUCCESS) {
2223 vport->ovsState = OVS_STATE_CONNECTED;
2224 vport->nicState = NdisSwitchNicStateConnected;
2227 * Allow the vport to be deleted, because there is no
2228 * corresponding hyper-v switch part.
2230 vport->isAbsentOnHv = TRUE;
2237 nlError = NL_ERROR_INVAL;
2240 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2241 nlError = NL_ERROR_EXIST;
2245 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2247 * XXX: when we implement the limit for ovs port number to be
2248 * MAXUINT16, we'll need to check the port number received from the
2251 vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2253 vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
2254 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2255 nlError = NL_ERROR_NOMEM;
2260 /* The ovs port name must be uninitialized. */
2261 ASSERT(vport->ovsName[0] == '\0');
2262 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2264 RtlCopyMemory(vport->ovsName, portName, portNameLen);
2265 /* if we don't have options, then vport->portOptions will be NULL */
2266 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2269 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2270 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2271 * it means we have an array of pids, instead of a single pid.
2272 * ATM we assume we have one pid only.
2274 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2276 status = InitOvsVportCommon(gOvsSwitchContext, vport);
2277 ASSERT(status == STATUS_SUCCESS);
2279 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2280 usrParamsCtx->outputLength,
2281 gOvsSwitchContext->dpNo);
2283 *replyLen = msgOut->nlMsg.nlmsgLen;
2286 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2288 if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2289 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2290 usrParamsCtx->outputBuffer;
2292 if (vport && vportAllocated == TRUE) {
2293 if (vportInitialized == TRUE) {
2294 if (OvsIsTunnelVportType(portType)) {
2295 switch (vport->ovsType) {
2296 case OVS_VPORT_TYPE_VXLAN:
2297 OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2299 case OVS_VPORT_TYPE_STT:
2300 OvsCleanupSttTunnel(vport);
2303 ASSERT(!"Invalid tunnel port type");
2307 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2310 NlBuildErrorMsg(msgIn, msgError, nlError);
2311 *replyLen = msgError->nlMsg.nlmsgLen;
2314 return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2319 * --------------------------------------------------------------------------
2320 * Command Handler for 'OVS_VPORT_CMD_SET'.
2321 * --------------------------------------------------------------------------
2324 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2327 NDIS_STATUS status = STATUS_SUCCESS;
2328 LOCK_STATE_EX lockState;
2330 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2331 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2332 POVS_VPORT_ENTRY vport = NULL;
2333 NL_ERROR nlError = NL_ERROR_SUCCESS;
2335 static const NL_POLICY ovsVportPolicy[] = {
2336 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2337 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
2338 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2340 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2342 [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
2343 .minLen = sizeof(OVS_VPORT_FULL_STATS),
2344 .maxLen = sizeof(OVS_VPORT_FULL_STATS),
2346 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2348 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2350 ASSERT(usrParamsCtx->inputBuffer != NULL);
2352 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2353 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2354 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2355 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2356 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2357 return STATUS_INVALID_PARAMETER;
2360 /* Output buffer has been validated while validating transact dev op. */
2361 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2363 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2364 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2365 PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2367 UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2369 /* the port name is expected to be null-terminated */
2370 ASSERT(portName[portNameLen - 1] == '\0');
2372 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2373 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2374 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2375 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2379 nlError = NL_ERROR_NODEV;
2384 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2385 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2386 * it means we have an array of pids, instead of a single pid.
2387 * Currently, we support only one pid.
2389 if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
2390 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2393 if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
2394 OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2395 if (type != vport->ovsType) {
2396 nlError = NL_ERROR_INVAL;
2401 if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2402 OVS_LOG_ERROR("Vport options not supported");
2403 nlError = NL_ERROR_NOTSUPP;
2407 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2408 usrParamsCtx->outputLength,
2409 gOvsSwitchContext->dpNo);
2411 *replyLen = msgOut->nlMsg.nlmsgLen;
2414 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2416 if (nlError != NL_ERROR_SUCCESS) {
2417 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2418 usrParamsCtx->outputBuffer;
2420 NlBuildErrorMsg(msgIn, msgError, nlError);
2421 *replyLen = msgError->nlMsg.nlmsgLen;
2424 return STATUS_SUCCESS;
2428 * --------------------------------------------------------------------------
2429 * Command Handler for 'OVS_VPORT_CMD_DEL'.
2430 * --------------------------------------------------------------------------
2433 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2436 NDIS_STATUS status = STATUS_SUCCESS;
2437 LOCK_STATE_EX lockState;
2439 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2440 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2441 POVS_VPORT_ENTRY vport = NULL;
2442 NL_ERROR nlError = NL_ERROR_SUCCESS;
2443 PSTR portName = NULL;
2444 UINT32 portNameLen = 0;
2446 static const NL_POLICY ovsVportPolicy[] = {
2447 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2448 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2451 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2453 ASSERT(usrParamsCtx->inputBuffer != NULL);
2455 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2456 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2457 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2458 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2459 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2460 return STATUS_INVALID_PARAMETER;
2463 /* Output buffer has been validated while validating transact dev op. */
2464 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2466 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2467 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2468 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2469 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2471 /* the port name is expected to be null-terminated */
2472 ASSERT(portName[portNameLen - 1] == '\0');
2474 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2476 else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2477 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2478 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2482 nlError = NL_ERROR_NODEV;
2486 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2487 usrParamsCtx->outputLength,
2488 gOvsSwitchContext->dpNo);
2490 *replyLen = msgOut->nlMsg.nlmsgLen;
2493 * Mark the port as deleted from OVS userspace. If the port does not exist
2494 * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
2496 status = OvsRemoveAndDeleteVport(usrParamsCtx,
2502 nlError = NlMapStatusToNlErr(status);
2506 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2508 if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2509 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2510 usrParamsCtx->outputBuffer;
2512 NlBuildErrorMsg(msgIn, msgError, nlError);
2513 *replyLen = msgError->nlMsg.nlmsgLen;
2516 return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2520 OvsTunnelVportPendingRemove(PVOID context,
2524 POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2525 (POVS_TUNFLT_INIT_CONTEXT) context;
2526 POVS_SWITCH_CONTEXT switchContext = tunnelContext->switchContext;
2527 POVS_VPORT_ENTRY vport = tunnelContext->vport;
2528 POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2529 POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2530 NL_ERROR nlError = NlMapStatusToNlErr(status);
2531 LOCK_STATE_EX lockState;
2533 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
2535 if (msgIn && msgOut) {
2536 /* Check the received status to reply to the caller. */
2537 if (STATUS_SUCCESS == status) {
2538 OvsCreateMsgFromVport(vport,
2541 tunnelContext->outputLength,
2542 switchContext->dpNo);
2544 *replyLen = msgOut->nlMsg.nlmsgLen;
2546 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)msgOut;
2548 NlBuildErrorMsg(msgIn, msgError, nlError);
2549 *replyLen = msgError->nlMsg.nlmsgLen;
2553 ASSERT(vport->isAbsentOnHv == TRUE);
2554 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
2556 /* Remove the port from the relevant lists. */
2557 switchContext->numNonHvVports--;
2558 RemoveEntryList(&vport->ovsNameLink);
2559 RemoveEntryList(&vport->portNoLink);
2560 RemoveEntryList(&vport->tunnelVportLink);
2563 OvsFreeMemoryWithTag(vport->priv, OVS_VXLAN_POOL_TAG);
2567 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2569 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
2573 OvsTunnelVportPendingInit(PVOID context,
2577 POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2578 (POVS_TUNFLT_INIT_CONTEXT) context;
2579 POVS_VPORT_ENTRY vport = tunnelContext->vport;
2580 POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2581 POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2583 ULONG portNameLen = 0;
2584 UINT32 portType = 0;
2585 NL_ERROR nlError = NL_ERROR_SUCCESS;
2586 BOOLEAN error = TRUE;
2589 if (!NT_SUCCESS(status)) {
2590 nlError = NlMapStatusToNlErr(status);
2594 static const NL_POLICY ovsVportPolicy[] = {
2595 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2596 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2597 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2598 .optional = FALSE },
2599 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2600 .optional = FALSE },
2601 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2604 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2606 /* input buffer has been validated while validating write dev op. */
2607 ASSERT(msgIn != NULL);
2609 /* Output buffer has been validated while validating transact dev op. */
2610 ASSERT(msgOut != NULL && tunnelContext->outputLength >= sizeof *msgOut);
2612 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2613 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2614 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2615 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2616 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2617 nlError = NL_ERROR_INVAL;
2621 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2622 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2623 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2625 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2626 nlError = NL_ERROR_EXIST;
2630 vport->ovsState = OVS_STATE_CONNECTED;
2631 vport->nicState = NdisSwitchNicStateConnected;
2634 * Allow the vport to be deleted, because there is no
2635 * corresponding hyper-v switch part.
2637 vport->isAbsentOnHv = TRUE;
2639 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2641 * XXX: when we implement the limit for OVS port number to be
2642 * MAXUINT16, we'll need to check the port number received from the
2646 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2649 OvsComputeVportNo(gOvsSwitchContext);
2650 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2651 nlError = NL_ERROR_NOMEM;
2656 /* The ovs port name must be uninitialized. */
2657 ASSERT(vport->ovsName[0] == '\0');
2658 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2660 RtlCopyMemory(vport->ovsName, portName, portNameLen);
2661 /* if we don't have options, then vport->portOptions will be NULL */
2662 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2665 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2666 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2667 * it means we have an array of pids, instead of a single pid.
2668 * ATM we assume we have one pid only.
2671 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2673 status = InitOvsVportCommon(gOvsSwitchContext, vport);
2674 ASSERT(status == STATUS_SUCCESS);
2676 OvsCreateMsgFromVport(vport,
2679 tunnelContext->outputLength,
2680 gOvsSwitchContext->dpNo);
2682 *replyLen = msgOut->nlMsg.nlmsgLen;
2688 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) msgOut;
2690 OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2691 OvsFreeMemory(vport);
2693 NlBuildErrorMsg(msgIn, msgError, nlError);
2694 *replyLen = msgError->nlMsg.nlmsgLen;