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.
34 #define OVS_DBG_MOD OVS_DBG_VPORT
37 #define VPORT_NIC_ENTER(_nic) \
38 OVS_LOG_TRACE("Enter: PortId: %x, NicIndex: %d", _nic->PortId, \
41 #define VPORT_NIC_EXIT(_nic) \
42 OVS_LOG_TRACE("Exit: PortId: %x, NicIndex: %d", _nic->PortId, \
45 #define VPORT_PORT_ENTER(_port) \
46 OVS_LOG_TRACE("Enter: PortId: %x", _port->PortId)
48 #define VPORT_PORT_EXIT(_port) \
49 OVS_LOG_TRACE("Exit: PortId: %x", _port->PortId)
51 #define OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC 100
53 /* Context structure used to pass back and forth information to the tunnel
55 typedef struct _OVS_TUNFLT_INIT_CONTEXT {
56 POVS_SWITCH_CONTEXT switchContext;
60 POVS_VPORT_ENTRY vport;
64 } OVS_TUNFLT_INIT_CONTEXT, *POVS_TUNFLT_INIT_CONTEXT;
67 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
69 static VOID OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
70 PNDIS_SWITCH_PORT_PARAMETERS portParam);
71 static VOID OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
72 POVS_VPORT_ENTRY vport, PNDIS_SWITCH_NIC_PARAMETERS nicParam);
73 static VOID OvsCopyPortParamsFromVport(POVS_VPORT_ENTRY vport,
74 PNDIS_SWITCH_PORT_PARAMETERS portParam);
75 static __inline VOID OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext,
77 static NTSTATUS OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
78 POVS_VPORT_EXT_INFO extInfo);
79 static NTSTATUS CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
84 static POVS_VPORT_ENTRY OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
85 PWSTR wsName, SIZE_T wstrSize);
86 static VOID UpdateSwitchCtxWithVport(POVS_SWITCH_CONTEXT switchContext,
87 POVS_VPORT_ENTRY vport, BOOLEAN newPort);
88 static NTSTATUS OvsRemoveTunnelVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
89 POVS_SWITCH_CONTEXT switchContext,
90 POVS_VPORT_ENTRY vport,
93 static VOID OvsTunnelVportPendingInit(PVOID context,
96 static VOID OvsTunnelVportPendingRemove(PVOID context,
99 static NTSTATUS GetNICAlias(GUID *netCfgInstanceId,
100 IF_COUNTED_STRING *portFriendlyName);
103 * --------------------------------------------------------------------------
104 * Creates a Vport entry for a Hyper-V switch port. 'nicIndex' is typically
105 * associated with a NIC than a port. We use it here for the special case
106 * where we need to create a Vport for an external NIC with NicIndex > 0.
107 * --------------------------------------------------------------------------
110 HvCreatePort(POVS_SWITCH_CONTEXT switchContext,
111 PNDIS_SWITCH_PORT_PARAMETERS portParam,
112 NDIS_SWITCH_NIC_INDEX nicIndex)
114 POVS_VPORT_ENTRY vport;
115 LOCK_STATE_EX lockState;
116 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
117 BOOLEAN newPort = FALSE;
119 VPORT_PORT_ENTER(portParam);
121 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
122 /* Lookup by port ID. */
123 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
124 portParam->PortId, nicIndex);
126 OVS_LOG_ERROR("Port add failed due to duplicate port name, "
127 "port Id: %u", portParam->PortId);
128 status = STATUS_DATA_NOT_ACCEPTED;
129 goto create_port_done;
133 * Lookup by port name to see if this port with this name had been added
134 * (and deleted) previously.
136 vport = OvsFindVportByHvNameW(gOvsSwitchContext,
137 portParam->PortFriendlyName.String,
138 portParam->PortFriendlyName.Length);
139 if (vport && vport->isAbsentOnHv == FALSE) {
140 OVS_LOG_ERROR("Port add failed since a port already exists on "
141 "the specified port Id: %u, ovsName: %s",
142 portParam->PortId, vport->ovsName);
143 status = STATUS_DATA_NOT_ACCEPTED;
144 goto create_port_done;
148 ASSERT(vport->isAbsentOnHv);
149 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
152 * It should be possible to simply just mark this port as "not deleted"
153 * given that the port Id and the name are the same and also provided
154 * that the other properties that we cache have not changed.
156 if (vport->portType != portParam->PortType) {
157 OVS_LOG_INFO("Port add failed due to PortType change, port Id: %u"
158 " old: %u, new: %u", portParam->PortId,
159 vport->portType, portParam->PortType);
160 status = STATUS_DATA_NOT_ACCEPTED;
161 goto create_port_done;
163 vport->isAbsentOnHv = FALSE;
165 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
167 status = NDIS_STATUS_RESOURCES;
168 goto create_port_done;
172 OvsInitVportWithPortParam(vport, portParam);
173 vport->nicIndex = nicIndex;
174 UpdateSwitchCtxWithVport(switchContext, vport, newPort);
177 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
178 VPORT_PORT_EXIT(portParam);
184 * --------------------------------------------------------------------------
185 * Function to process updates to a port on the Hyper-Vs witch.
186 * --------------------------------------------------------------------------
189 HvUpdatePort(POVS_SWITCH_CONTEXT switchContext,
190 PNDIS_SWITCH_PORT_PARAMETERS portParam)
192 POVS_VPORT_ENTRY vport;
193 LOCK_STATE_EX lockState;
194 OVS_VPORT_STATE ovsState;
195 NDIS_SWITCH_NIC_STATE nicState;
197 VPORT_PORT_ENTER(portParam);
199 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
200 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
201 portParam->PortId, 0);
203 * Update properties only for NETDEV ports for supprting PS script
206 goto update_port_done;
209 /* Store the nic and the OVS states as Nic Create won't be called */
210 ovsState = vport->ovsState;
211 nicState = vport->nicState;
214 * Currently only the port friendly name is being updated
215 * Make sure that no other properties are changed
217 ASSERT(portParam->PortId == vport->portId);
218 ASSERT(portParam->PortState == vport->portState);
219 ASSERT(portParam->PortType == vport->portType);
222 * Call the set parameters function the handle all properties
223 * change in a single place in case future version supports change of
226 OvsInitVportWithPortParam(vport, portParam);
227 /* Retore the nic and OVS states */
228 vport->nicState = nicState;
229 vport->ovsState = ovsState;
232 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
233 VPORT_PORT_EXIT(portParam);
235 /* Must always return success */
236 return NDIS_STATUS_SUCCESS;
241 * --------------------------------------------------------------------------
242 * Function to process teardown of a port on the Hyper-V switch.
243 * --------------------------------------------------------------------------
246 HvTeardownPort(POVS_SWITCH_CONTEXT switchContext,
247 PNDIS_SWITCH_PORT_PARAMETERS portParam)
249 POVS_VPORT_ENTRY vport;
250 LOCK_STATE_EX lockState;
252 VPORT_PORT_ENTER(portParam);
254 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
255 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
256 portParam->PortId, 0);
258 /* add assertion here */
259 vport->portState = NdisSwitchPortStateTeardown;
260 vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
262 OVS_LOG_WARN("Vport not present.");
264 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
266 VPORT_PORT_EXIT(portParam);
270 * --------------------------------------------------------------------------
271 * Function to process deletion of a port on the Hyper-V switch.
272 * --------------------------------------------------------------------------
275 HvDeletePort(POVS_SWITCH_CONTEXT switchContext,
276 PNDIS_SWITCH_PORT_PARAMETERS portParams)
278 POVS_VPORT_ENTRY vport;
279 LOCK_STATE_EX lockState;
281 VPORT_PORT_ENTER(portParams);
283 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
284 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
285 portParams->PortId, 0);
288 * XXX: we can only destroy and remove the port if its datapath port
289 * counterpart was deleted. If the datapath port counterpart is present,
290 * we only mark the vport for deletion, so that a netlink command vport
291 * delete will delete the vport.
294 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
296 OVS_LOG_WARN("Vport not present.");
298 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
300 VPORT_PORT_EXIT(portParams);
305 * --------------------------------------------------------------------------
306 * Function to process addition of a NIC connection on the Hyper-V switch.
307 * XXX: Posting an event to DPIF is incorrect here. However, it might be useful
308 * to post an event to netdev-windows.c.
309 * --------------------------------------------------------------------------
312 HvCreateNic(POVS_SWITCH_CONTEXT switchContext,
313 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
315 POVS_VPORT_ENTRY vport;
316 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
317 IF_COUNTED_STRING portFriendlyName = {0};
318 LOCK_STATE_EX lockState;
320 VPORT_NIC_ENTER(nicParam);
322 /* Wait for lists to be initialized. */
323 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
325 if (!switchContext->isActivated) {
326 OVS_LOG_WARN("Switch is not activated yet.");
327 /* Veto the creation of nic */
328 status = NDIS_STATUS_NOT_SUPPORTED;
332 if (OvsIsInternalNIC(nicParam->NicType) ||
333 OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) {
334 GetNICAlias(&nicParam->NetCfgInstanceId, &portFriendlyName);
337 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
339 * There can be one or more NICs for the external port. We create a vport
340 * structure for each such NIC, and each NIC inherits a lot of properties
341 * from the parent external port.
343 if (OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) {
344 NDIS_SWITCH_PORT_PARAMETERS portParam;
345 POVS_VPORT_ENTRY virtExtVport =
346 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
348 ASSERT(virtExtVport);
349 ASSERT(OvsFindVportByPortIdAndNicIndex(switchContext,
351 nicParam->NicIndex) == NULL);
352 OvsCopyPortParamsFromVport(virtExtVport, &portParam);
353 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
354 status = HvCreatePort(switchContext, &portParam,
356 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
357 if (status != NDIS_STATUS_SUCCESS) {
362 vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId,
365 OVS_LOG_ERROR("Create NIC without Switch Port,"
366 " PortId: %x, NicIndex: %d",
367 nicParam->PortId, nicParam->NicIndex);
368 status = NDIS_STATUS_INVALID_PARAMETER;
371 OvsInitVportWithNicParam(switchContext, vport, nicParam);
372 if (OvsIsInternalNIC(nicParam->NicType) ||
373 OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) {
374 RtlCopyMemory(&vport->portFriendlyName, &portFriendlyName,
375 sizeof portFriendlyName);
379 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
382 VPORT_NIC_EXIT(nicParam);
383 OVS_LOG_TRACE("Exit: status %8x.\n", status);
389 * --------------------------------------------------------------------------
390 * Function to process connection event of a NIC on the Hyper-V switch.
391 * --------------------------------------------------------------------------
394 HvConnectNic(POVS_SWITCH_CONTEXT switchContext,
395 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
397 LOCK_STATE_EX lockState;
398 POVS_VPORT_ENTRY vport;
401 VPORT_NIC_ENTER(nicParam);
403 /* Wait for lists to be initialized. */
404 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
406 if (!switchContext->isActivated) {
407 OVS_LOG_WARN("Switch is not activated yet.");
411 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
412 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
417 OVS_LOG_WARN("Vport not present.");
418 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
423 vport->ovsState = OVS_STATE_CONNECTED;
424 vport->nicState = NdisSwitchNicStateConnected;
425 portNo = vport->portNo;
427 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
429 if (nicParam->NicType == NdisSwitchNicTypeInternal) {
430 OvsInternalAdapterUp(&nicParam->NetCfgInstanceId);
434 VPORT_NIC_EXIT(nicParam);
439 * --------------------------------------------------------------------------
440 * Function to process updates to a NIC on the Hyper-V switch.
441 * --------------------------------------------------------------------------
444 HvUpdateNic(POVS_SWITCH_CONTEXT switchContext,
445 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
447 POVS_VPORT_ENTRY vport;
448 LOCK_STATE_EX lockState;
450 IF_COUNTED_STRING portFriendlyName = {0};
451 BOOLEAN nameChanged = FALSE;
452 BOOLEAN aliasLookup = FALSE;
454 VPORT_NIC_ENTER(nicParam);
456 /* Wait for lists to be initialized. */
457 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
459 if (!switchContext->isActivated) {
460 OVS_LOG_WARN("Switch is not activated yet.");
461 goto update_nic_done;
464 /* GetNICAlias() must be called outside of a lock. */
465 if (nicParam->NicType == NdisSwitchNicTypeInternal ||
466 OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) {
467 GetNICAlias(&nicParam->NetCfgInstanceId, &portFriendlyName);
471 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
472 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
476 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
477 OVS_LOG_WARN("Vport search failed.");
478 goto update_nic_done;
480 switch (nicParam->NicType) {
481 case NdisSwitchNicTypeExternal:
482 case NdisSwitchNicTypeInternal:
483 RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
486 if (RtlCompareMemory(&vport->portFriendlyName,
487 &portFriendlyName, vport->portFriendlyName.Length) !=
488 vport->portFriendlyName.Length) {
489 RtlCopyMemory(&vport->portFriendlyName, &portFriendlyName,
490 sizeof portFriendlyName);
495 case NdisSwitchNicTypeSynthetic:
496 case NdisSwitchNicTypeEmulated:
497 if (!RtlEqualMemory(vport->vmMacAddress, nicParam->VMMacAddress,
498 sizeof (vport->vmMacAddress))) {
499 event |= OVS_EVENT_MAC_CHANGE;
500 RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
501 sizeof (vport->vmMacAddress));
507 if (!RtlEqualMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
508 sizeof (vport->permMacAddress))) {
509 RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
510 sizeof (vport->permMacAddress));
511 event |= OVS_EVENT_MAC_CHANGE;
513 if (!RtlEqualMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
514 sizeof (vport->currMacAddress))) {
515 RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
516 sizeof (vport->currMacAddress));
517 event |= OVS_EVENT_MAC_CHANGE;
520 if (vport->mtu != nicParam->MTU) {
521 vport->mtu = nicParam->MTU;
522 event |= OVS_EVENT_MTU_CHANGE;
524 vport->numaNodeId = nicParam->NumaNodeId;
527 OVS_EVENT_ENTRY event;
528 event.portNo = vport->portNo;
529 event.ovsType = vport->ovsType;
530 event.upcallPid = vport->upcallPid;
531 RtlCopyMemory(&event.ovsName, &vport->ovsName, sizeof event.ovsName);
532 event.type = OVS_EVENT_LINK_DOWN;
533 OvsRemoveAndDeleteVport(NULL, switchContext, vport, FALSE, TRUE);
534 OvsPostEvent(&event);
537 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
540 * XXX: Not sure what kind of event to post here. DPIF is not interested in
541 * changes to MAC address. Netdev-windows might be intrested, though.
542 * That said, if the name chagnes, not clear what kind of event to be
543 * posted. We might have to delete the vport, and have userspace recreate
548 VPORT_NIC_EXIT(nicParam);
552 * --------------------------------------------------------------------------
553 * Function to process disconnect event of a NIC on the Hyper-V switch.
554 * --------------------------------------------------------------------------
557 HvDisconnectNic(POVS_SWITCH_CONTEXT switchContext,
558 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
560 POVS_VPORT_ENTRY vport;
561 LOCK_STATE_EX lockState;
562 BOOLEAN isInternalPort = FALSE;
563 OVS_EVENT_ENTRY event;
565 VPORT_NIC_ENTER(nicParam);
567 /* Wait for lists to be initialized. */
568 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
570 if (!switchContext->isActivated) {
571 OVS_LOG_WARN("Switch is not activated yet.");
575 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
576 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
581 OVS_LOG_WARN("Vport not present.");
582 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
586 vport->nicState = NdisSwitchNicStateDisconnected;
587 vport->ovsState = OVS_STATE_NIC_CREATED;
589 if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
590 isInternalPort = TRUE;
593 event.portNo = vport->portNo;
594 event.ovsType = vport->ovsType;
595 event.upcallPid = vport->upcallPid;
596 RtlCopyMemory(&event.ovsName, &vport->ovsName, sizeof event.ovsName);
597 event.type = OVS_EVENT_LINK_DOWN;
600 * Delete the port from the hash tables accessible to userspace. After this
601 * point, userspace should not be able to access this port.
603 if (OvsIsRealExternalVport(vport)) {
604 OvsRemoveAndDeleteVport(NULL, switchContext, vport, FALSE, TRUE);
605 OvsPostEvent(&event);
607 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
609 if (isInternalPort) {
610 OvsInternalAdapterDown();
614 VPORT_NIC_EXIT(nicParam);
618 * --------------------------------------------------------------------------
619 * Function to process delete event of a NIC on the Hyper-V switch.
620 * --------------------------------------------------------------------------
623 HvDeleteNic(POVS_SWITCH_CONTEXT switchContext,
624 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
626 LOCK_STATE_EX lockState;
627 POVS_VPORT_ENTRY vport;
629 VPORT_NIC_ENTER(nicParam);
630 /* Wait for lists to be initialized. */
631 OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
633 if (!switchContext->isActivated) {
634 OVS_LOG_WARN("Switch is not activated yet.");
638 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
639 vport = OvsFindVportByPortIdAndNicIndex(switchContext,
644 OVS_LOG_WARN("Vport not present.");
645 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
649 vport->nicState = NdisSwitchNicStateUnknown;
650 vport->ovsState = OVS_STATE_PORT_CREATED;
652 if (OvsIsRealExternalVport(vport)) {
653 /* This vport was created in HvCreateNic(). */
654 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
657 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
660 VPORT_NIC_EXIT(nicParam);
664 * OVS Vport related functionality.
667 OvsFindVportByPortNo(POVS_SWITCH_CONTEXT switchContext,
670 POVS_VPORT_ENTRY vport;
671 PLIST_ENTRY head, link;
672 UINT32 hash = OvsJhashBytes((const VOID *)&portNo, sizeof(portNo),
674 head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
675 LIST_FORALL(head, link) {
676 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
677 if (vport->portNo == portNo) {
686 OvsFindTunnelVportByDstPort(POVS_SWITCH_CONTEXT switchContext,
688 OVS_VPORT_TYPE ovsPortType)
690 POVS_VPORT_ENTRY vport;
691 PLIST_ENTRY head, link;
692 UINT32 hash = OvsJhashBytes((const VOID *)&dstPort, sizeof(dstPort),
694 head = &(switchContext->tunnelVportsArray[hash & OVS_VPORT_MASK]);
695 LIST_FORALL(head, link) {
696 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, tunnelVportLink);
697 if (GetPortFromPriv(vport) == dstPort &&
698 vport->ovsType == ovsPortType) {
706 OvsFindTunnelVportByPortType(POVS_SWITCH_CONTEXT switchContext,
707 OVS_VPORT_TYPE ovsPortType)
709 POVS_VPORT_ENTRY vport;
710 PLIST_ENTRY head, link;
712 UINT32 hash = OvsJhashBytes((const VOID *)&dstPort, sizeof(dstPort),
714 head = &(switchContext->tunnelVportsArray[hash & OVS_VPORT_MASK]);
715 LIST_FORALL(head, link) {
716 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, tunnelVportLink);
717 if (vport->ovsType == ovsPortType) {
725 OvsFindVportByOvsName(POVS_SWITCH_CONTEXT switchContext,
728 POVS_VPORT_ENTRY vport;
729 PLIST_ENTRY head, link;
731 SIZE_T length = strlen(name) + 1;
733 hash = OvsJhashBytes((const VOID *)name, length, OVS_HASH_BASIS);
734 head = &(switchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK]);
736 LIST_FORALL(head, link) {
737 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, ovsNameLink);
738 if (!strcmp(name, vport->ovsName)) {
746 /* OvsFindVportByHvName: "name" is assumed to be null-terminated */
748 OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
749 PWSTR wsName, SIZE_T wstrSize)
751 POVS_VPORT_ENTRY vport = NULL;
752 PLIST_ENTRY head, link;
755 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
756 head = &(switchContext->portIdHashArray[i]);
757 LIST_FORALL(head, link) {
758 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
761 * NOTE about portFriendlyName:
762 * If the string is NULL-terminated, the Length member does not
763 * include the terminating NULL character.
765 if (vport->portFriendlyName.Length == wstrSize &&
766 RtlEqualMemory(wsName, vport->portFriendlyName.String,
767 vport->portFriendlyName.Length)) {
776 * Look in the list of ports that were added from the Hyper-V switch and
780 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
781 head = &(switchContext->portNoHashArray[i]);
782 LIST_FORALL(head, link) {
783 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
784 if (vport->portFriendlyName.Length == wstrSize &&
785 RtlEqualMemory(wsName, vport->portFriendlyName.String,
786 vport->portFriendlyName.Length)) {
800 OvsFindVportByHvNameA(POVS_SWITCH_CONTEXT switchContext,
803 POVS_VPORT_ENTRY vport = NULL;
804 /* 'portFriendlyName' is not NUL-terminated. */
805 SIZE_T length = strlen(name);
806 SIZE_T wstrSize = length * sizeof(WCHAR);
809 PWSTR wsName = OvsAllocateMemoryWithTag(wstrSize, OVS_VPORT_POOL_TAG);
813 for (i = 0; i < length; i++) {
816 vport = OvsFindVportByHvNameW(switchContext, wsName, wstrSize);
817 OvsFreeMemoryWithTag(wsName, OVS_VPORT_POOL_TAG);
822 OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT switchContext,
823 NDIS_SWITCH_PORT_ID portId,
824 NDIS_SWITCH_NIC_INDEX index)
826 if (switchContext->virtualExternalVport &&
827 portId == switchContext->virtualExternalPortId &&
828 index == switchContext->virtualExternalVport->nicIndex) {
829 return (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
830 } else if (switchContext->internalVport &&
831 portId == switchContext->internalPortId &&
832 index == switchContext->internalVport->nicIndex) {
833 return (POVS_VPORT_ENTRY)switchContext->internalVport;
835 PLIST_ENTRY head, link;
836 POVS_VPORT_ENTRY vport;
838 hash = OvsJhashWords((UINT32 *)&portId, 1, OVS_HASH_BASIS);
839 head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
840 LIST_FORALL(head, link) {
841 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
842 if (portId == vport->portId && index == vport->nicIndex) {
851 OvsAllocateVport(VOID)
853 POVS_VPORT_ENTRY vport;
854 vport = (POVS_VPORT_ENTRY)OvsAllocateMemoryWithTag(
855 sizeof(OVS_VPORT_ENTRY), OVS_VPORT_POOL_TAG);
859 RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY));
860 vport->ovsState = OVS_STATE_UNKNOWN;
861 vport->isAbsentOnHv = FALSE;
862 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
864 InitializeListHead(&vport->ovsNameLink);
865 InitializeListHead(&vport->portIdLink);
866 InitializeListHead(&vport->portNoLink);
872 OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
873 PNDIS_SWITCH_PORT_PARAMETERS portParam)
875 vport->portType = portParam->PortType;
876 vport->portState = portParam->PortState;
877 vport->portId = portParam->PortId;
878 vport->nicState = NdisSwitchNicStateUnknown;
879 vport->isExternal = FALSE;
880 vport->isBridgeInternal = FALSE;
882 switch (vport->portType) {
883 case NdisSwitchPortTypeExternal:
884 vport->isExternal = TRUE;
885 vport->ovsType = OVS_VPORT_TYPE_NETDEV;
887 case NdisSwitchPortTypeInternal:
888 vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
890 case NdisSwitchPortTypeSynthetic:
891 case NdisSwitchPortTypeEmulated:
892 vport->ovsType = OVS_VPORT_TYPE_NETDEV;
895 RtlCopyMemory(&vport->hvPortName, &portParam->PortName,
896 sizeof (NDIS_SWITCH_PORT_NAME));
897 /* For external and internal ports, 'portFriendlyName' is overwritten
899 RtlCopyMemory(&vport->portFriendlyName, &portParam->PortFriendlyName,
900 sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
902 switch (vport->portState) {
903 case NdisSwitchPortStateCreated:
904 vport->ovsState = OVS_STATE_PORT_CREATED;
906 case NdisSwitchPortStateTeardown:
907 vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
909 case NdisSwitchPortStateDeleted:
910 vport->ovsState = OVS_STATE_PORT_DELETED;
917 OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
918 POVS_VPORT_ENTRY vport,
919 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
921 ASSERT(vport->portId == nicParam->PortId);
922 ASSERT(vport->ovsState == OVS_STATE_PORT_CREATED);
924 UNREFERENCED_PARAMETER(switchContext);
926 RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
927 sizeof (nicParam->PermanentMacAddress));
928 RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
929 sizeof (nicParam->CurrentMacAddress));
931 if (nicParam->NicType == NdisSwitchNicTypeSynthetic ||
932 nicParam->NicType == NdisSwitchNicTypeEmulated) {
933 RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
934 sizeof (nicParam->VMMacAddress));
935 RtlCopyMemory(&vport->vmName, &nicParam->VmName,
936 sizeof (nicParam->VmName));
938 RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
939 sizeof (nicParam->NetCfgInstanceId));
941 RtlCopyMemory(&vport->nicName, &nicParam->NicName,
942 sizeof (nicParam->NicName));
943 vport->mtu = nicParam->MTU;
944 vport->nicState = nicParam->NicState;
945 vport->nicIndex = nicParam->NicIndex;
946 vport->nicType = nicParam->NicType;
947 vport->numaNodeId = nicParam->NumaNodeId;
949 switch (vport->nicState) {
950 case NdisSwitchNicStateCreated:
951 vport->ovsState = OVS_STATE_NIC_CREATED;
953 case NdisSwitchNicStateConnected:
954 vport->ovsState = OVS_STATE_CONNECTED;
956 case NdisSwitchNicStateDisconnected:
957 vport->ovsState = OVS_STATE_NIC_CREATED;
959 case NdisSwitchNicStateDeleted:
960 vport->ovsState = OVS_STATE_PORT_CREATED;
966 * --------------------------------------------------------------------------
967 * Populates 'portParam' based on 'vport'.
968 * --------------------------------------------------------------------------
971 OvsCopyPortParamsFromVport(POVS_VPORT_ENTRY vport,
972 PNDIS_SWITCH_PORT_PARAMETERS portParam)
974 portParam->Flags = 0;
975 portParam->PortId = vport->portId;
976 RtlCopyMemory(&portParam->PortName, &vport->hvPortName,
977 sizeof (NDIS_SWITCH_PORT_NAME));
978 RtlCopyMemory(&portParam->PortFriendlyName,
979 &vport->portFriendlyName,
980 sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
981 portParam->PortType = vport->portType;
982 portParam->IsValidationPort = FALSE;
983 portParam->PortState = vport->portState;
987 * --------------------------------------------------------------------------
988 * Initializes a tunnel vport.
989 * --------------------------------------------------------------------------
992 OvsInitTunnelVport(PVOID userContext,
993 POVS_VPORT_ENTRY vport,
994 OVS_VPORT_TYPE ovsType,
997 NTSTATUS status = STATUS_SUCCESS;
998 POVS_USER_PARAMS_CONTEXT usrParamsCtx =
999 (POVS_USER_PARAMS_CONTEXT)userContext;
1001 vport->isBridgeInternal = FALSE;
1002 vport->ovsType = ovsType;
1003 vport->ovsState = OVS_STATE_PORT_CREATED;
1005 case OVS_VPORT_TYPE_GRE:
1006 status = OvsInitGreTunnel(vport);
1008 case OVS_VPORT_TYPE_VXLAN:
1010 POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
1012 tunnelContext = OvsAllocateMemoryWithTag(sizeof(*tunnelContext),
1013 OVS_VPORT_POOL_TAG);
1014 if (tunnelContext == NULL) {
1015 status = STATUS_INSUFFICIENT_RESOURCES;
1018 tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
1019 tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
1020 tunnelContext->outputLength = usrParamsCtx->outputLength;
1021 tunnelContext->vport = vport;
1023 status = OvsInitVxlanTunnel(usrParamsCtx->irp,
1026 OvsTunnelVportPendingInit,
1027 (PVOID)tunnelContext);
1028 if (status != STATUS_PENDING) {
1029 OvsFreeMemoryWithTag(tunnelContext, OVS_VPORT_POOL_TAG);
1030 tunnelContext = NULL;
1034 case OVS_VPORT_TYPE_STT:
1035 status = OvsInitSttTunnel(vport, dstPort);
1044 * --------------------------------------------------------------------------
1045 * Initializes a bridge internal vport ie. a port of type
1046 * OVS_VPORT_TYPE_INTERNAL but not present on the Hyper-V switch.
1047 * --------------------------------------------------------------------------
1050 OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport)
1052 vport->isBridgeInternal = TRUE;
1053 vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
1054 /* Mark the status to be connected, since there is no other initialization
1056 vport->ovsState = OVS_STATE_CONNECTED;
1057 return STATUS_SUCCESS;
1061 * --------------------------------------------------------------------------
1062 * For external and internal vports 'portFriendlyName' parameter, provided by
1063 * Hyper-V, is overwritten with the interface alias name.
1064 * --------------------------------------------------------------------------
1067 GetNICAlias(GUID *netCfgInstanceId,
1068 IF_COUNTED_STRING *portFriendlyName)
1070 NTSTATUS status = STATUS_SUCCESS;
1071 WCHAR interfaceName[IF_MAX_STRING_SIZE] = { 0 };
1072 NET_LUID interfaceLuid = { 0 };
1075 status = ConvertInterfaceGuidToLuid(netCfgInstanceId,
1077 if (status == STATUS_SUCCESS) {
1079 * Must be called from PASSIVE_LEVEL. Resulted in a
1080 * STATUS_INVALID_DEVICE_REQUEST if not.
1082 status = ConvertInterfaceLuidToAlias(&interfaceLuid, interfaceName,
1083 IF_MAX_STRING_SIZE + 1);
1084 if (status == STATUS_SUCCESS) {
1085 RtlStringCbPrintfW(portFriendlyName->String,
1086 IF_MAX_STRING_SIZE, L"%s", interfaceName);
1087 RtlStringCbLengthW(portFriendlyName->String, IF_MAX_STRING_SIZE,
1089 portFriendlyName->Length = (USHORT)len;
1091 OVS_LOG_ERROR("Fail to convert interface LUID to alias, status: %x",
1095 OVS_LOG_ERROR("Fail to convert interface GUID to LUID, status: %x",
1104 * --------------------------------------------------------------------------
1105 * Functionality common to any port on the Hyper-V switch. This function is not
1106 * to be called for a port that is not on the Hyper-V switch.
1108 * Inserts the port into 'portIdHashArray' and caches the pointer in the
1109 * 'switchContext' if needed.
1110 * --------------------------------------------------------------------------
1113 UpdateSwitchCtxWithVport(POVS_SWITCH_CONTEXT switchContext,
1114 POVS_VPORT_ENTRY vport,
1119 switch (vport->portType) {
1120 case NdisSwitchPortTypeExternal:
1121 if (vport->nicIndex == 0) {
1122 switchContext->virtualExternalPortId = vport->portId;
1123 switchContext->virtualExternalVport = vport;
1125 switchContext->numPhysicalNics++;
1128 case NdisSwitchPortTypeInternal:
1129 ASSERT(vport->isBridgeInternal == FALSE);
1130 switchContext->internalPortId = vport->portId;
1131 switchContext->internalVport = vport;
1133 case NdisSwitchPortTypeSynthetic:
1134 case NdisSwitchPortTypeEmulated:
1139 * It is important to not insert vport corresponding to virtual external
1140 * port into the 'portIdHashArray' since the port should not be exposed to
1143 if (vport->portType == NdisSwitchPortTypeExternal &&
1144 vport->nicIndex == 0) {
1149 * NOTE: OvsJhashWords has portId as "1" word. This should be ok, even
1150 * though sizeof(NDIS_SWITCH_PORT_ID) = 4, not 2, because the
1151 * hyper-v switch seems to use only 2 bytes out of 4.
1153 hash = OvsJhashWords(&vport->portId, 1, OVS_HASH_BASIS);
1154 InsertHeadList(&switchContext->portIdHashArray[hash & OVS_VPORT_MASK],
1155 &vport->portIdLink);
1157 switchContext->numHvVports++;
1163 * --------------------------------------------------------------------------
1164 * Functionality common to any port added from OVS userspace.
1166 * Inserts the port into 'portNoHashArray', 'ovsPortNameHashArray' and in
1167 * 'tunnelVportsArray' if appropriate.
1168 * --------------------------------------------------------------------------
1171 InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
1172 POVS_VPORT_ENTRY vport)
1176 switch(vport->ovsType) {
1177 case OVS_VPORT_TYPE_GRE:
1178 case OVS_VPORT_TYPE_VXLAN:
1179 case OVS_VPORT_TYPE_STT:
1181 UINT16 dstPort = GetPortFromPriv(vport);
1182 hash = OvsJhashBytes(&dstPort,
1186 &gOvsSwitchContext->tunnelVportsArray[hash & OVS_VPORT_MASK],
1187 &vport->tunnelVportLink);
1188 switchContext->numNonHvVports++;
1191 case OVS_VPORT_TYPE_INTERNAL:
1192 if (vport->isBridgeInternal) {
1193 switchContext->numNonHvVports++;
1200 * Insert the port into the hash array of ports: by port number and ovs
1201 * and ovs (datapath) port name.
1202 * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1203 * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1205 hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
1206 InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
1207 &vport->portNoLink);
1209 hash = OvsJhashBytes(vport->ovsName, strlen(vport->ovsName) + 1,
1212 &gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
1213 &vport->ovsNameLink);
1215 return STATUS_SUCCESS;
1220 * --------------------------------------------------------------------------
1221 * Provides functionality that is partly complementatry to
1222 * InitOvsVportCommon()/UpdateSwitchCtxWithVport().
1224 * 'hvDelete' indicates if caller is removing the vport as a result of the
1225 * port being removed on the Hyper-V switch.
1226 * 'ovsDelete' indicates if caller is removing the vport as a result of the
1227 * port being removed from OVS userspace.
1228 * --------------------------------------------------------------------------
1231 OvsRemoveAndDeleteVport(PVOID usrParamsContext,
1232 POVS_SWITCH_CONTEXT switchContext,
1233 POVS_VPORT_ENTRY vport,
1237 POVS_USER_PARAMS_CONTEXT usrParamsCtx =
1238 (POVS_USER_PARAMS_CONTEXT)usrParamsContext;
1239 BOOLEAN hvSwitchPort = FALSE;
1240 BOOLEAN deletedOnOvs = FALSE;
1241 BOOLEAN deletedOnHv = FALSE;
1243 switch (vport->ovsType) {
1244 case OVS_VPORT_TYPE_INTERNAL:
1245 if (!vport->isBridgeInternal) {
1246 if (hvDelete && vport->isAbsentOnHv == FALSE) {
1247 switchContext->internalPortId = 0;
1248 switchContext->internalVport = NULL;
1249 OvsInternalAdapterDown();
1251 hvSwitchPort = TRUE;
1254 case OVS_VPORT_TYPE_VXLAN:
1257 status = OvsRemoveTunnelVport(usrParamsCtx, switchContext, vport,
1258 hvDelete, ovsDelete);
1259 if (status != STATUS_SUCCESS) {
1263 case OVS_VPORT_TYPE_STT:
1264 OvsCleanupSttTunnel(vport);
1266 case OVS_VPORT_TYPE_GRE:
1267 OvsCleanupGreTunnel(vport);
1269 case OVS_VPORT_TYPE_NETDEV:
1270 if (vport->isExternal) {
1271 if (vport->nicIndex == 0) {
1272 /* Such a vport is not part of any of the hash tables, since it
1273 * is not exposed to userspace. See Vport.h for explanation. */
1274 ASSERT(hvDelete == TRUE);
1275 ASSERT(switchContext->numPhysicalNics == 0);
1276 switchContext->virtualExternalPortId = 0;
1277 switchContext->virtualExternalVport = NULL;
1278 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1279 return STATUS_SUCCESS;
1282 hvSwitchPort = TRUE;
1288 * 'hvDelete' == TRUE indicates that the port should be removed from the
1289 * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
1290 * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
1292 * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
1294 if (vport->isAbsentOnHv == TRUE) {
1297 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1298 deletedOnOvs = TRUE;
1301 if (hvDelete && !deletedOnHv) {
1302 vport->isAbsentOnHv = TRUE;
1304 if (vport->isExternal) {
1305 ASSERT(vport->nicIndex != 0);
1306 ASSERT(switchContext->numPhysicalNics);
1307 switchContext->numPhysicalNics--;
1310 /* Remove the port from the relevant lists. */
1311 RemoveEntryList(&vport->portIdLink);
1312 InitializeListHead(&vport->portIdLink);
1315 if (ovsDelete && !deletedOnOvs) {
1316 vport->portNo = OVS_DPPORT_NUMBER_INVALID;
1317 vport->ovsName[0] = '\0';
1319 /* Remove the port from the relevant lists. */
1320 RemoveEntryList(&vport->ovsNameLink);
1321 InitializeListHead(&vport->ovsNameLink);
1322 RemoveEntryList(&vport->portNoLink);
1323 InitializeListHead(&vport->portNoLink);
1324 if (OVS_VPORT_TYPE_VXLAN == vport->ovsType ||
1325 OVS_VPORT_TYPE_STT == vport->ovsType ||
1326 OVS_VPORT_TYPE_GRE == vport->ovsType) {
1327 RemoveEntryList(&vport->tunnelVportLink);
1328 InitializeListHead(&vport->tunnelVportLink);
1331 deletedOnOvs = TRUE;
1335 * Deallocate the port if it has been deleted on the Hyper-V switch as well
1338 if (deletedOnHv && deletedOnOvs) {
1340 switchContext->numHvVports--;
1342 switchContext->numNonHvVports--;
1344 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1347 return STATUS_SUCCESS;
1351 OvsRemoveTunnelVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1352 POVS_SWITCH_CONTEXT switchContext,
1353 POVS_VPORT_ENTRY vport,
1357 POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
1360 tunnelContext = OvsAllocateMemoryWithTag(sizeof(*tunnelContext),
1361 OVS_VPORT_POOL_TAG);
1362 if (tunnelContext == NULL) {
1363 return STATUS_INSUFFICIENT_RESOURCES;
1365 RtlZeroMemory(tunnelContext, sizeof(*tunnelContext));
1367 tunnelContext->switchContext = switchContext;
1368 tunnelContext->hvSwitchPort = FALSE;
1369 tunnelContext->hvDelete = hvDelete;
1370 tunnelContext->ovsDelete = ovsDelete;
1371 tunnelContext->vport = vport;
1374 tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
1375 tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
1376 tunnelContext->outputLength = usrParamsCtx->outputLength;
1377 irp = usrParamsCtx->irp;
1380 return OvsCleanupVxlanTunnel(irp, vport, OvsTunnelVportPendingRemove,
1385 * --------------------------------------------------------------------------
1386 * Enumerates the ports on the Hyper-V switch.
1387 * --------------------------------------------------------------------------
1390 OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
1392 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1394 PNDIS_SWITCH_PORT_PARAMETERS portParam;
1395 PNDIS_SWITCH_PORT_ARRAY portArray = NULL;
1397 OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
1399 status = OvsGetPortsOnSwitch(switchContext, &portArray);
1400 if (status != NDIS_STATUS_SUCCESS) {
1404 for (arrIndex = 0; arrIndex < portArray->NumElements; arrIndex++) {
1405 portParam = NDIS_SWITCH_PORT_AT_ARRAY_INDEX(portArray, arrIndex);
1407 if (portParam->IsValidationPort) {
1411 status = HvCreatePort(switchContext, portParam, 0);
1412 if (status != STATUS_SUCCESS && status != STATUS_DATA_NOT_ACCEPTED) {
1418 if (status != NDIS_STATUS_SUCCESS) {
1419 OvsClearAllSwitchVports(switchContext);
1422 OvsFreeSwitchPortsArray(portArray);
1424 OVS_LOG_TRACE("Exit: status: %x", status);
1430 * --------------------------------------------------------------------------
1431 * Enumerates the NICs on the Hyper-V switch.
1432 * --------------------------------------------------------------------------
1435 OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext)
1437 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1438 PNDIS_SWITCH_NIC_ARRAY nicArray = NULL;
1440 PNDIS_SWITCH_NIC_PARAMETERS nicParam;
1442 OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
1444 * Now, get NIC list.
1446 status = OvsGetNicsOnSwitch(switchContext, &nicArray);
1447 if (status != NDIS_STATUS_SUCCESS) {
1450 for (arrIndex = 0; arrIndex < nicArray->NumElements; ++arrIndex) {
1451 nicParam = NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray, arrIndex);
1454 * XXX: Check if the port is configured with a VLAN. Disallow such a
1455 * configuration, since we don't support tag-in-tag.
1456 * XXX: Check if the port is connected to a VF. Disconnect the VF in
1460 status = HvCreateNic(switchContext, nicParam);
1461 if (status == NDIS_STATUS_SUCCESS) {
1462 HvConnectNic(switchContext, nicParam);
1467 OvsFreeSwitchNicsArray(nicArray);
1469 OVS_LOG_TRACE("Exit: status: %x", status);
1474 * --------------------------------------------------------------------------
1475 * Deletes ports added from the Hyper-V switch as well as OVS usersapce. The
1476 * function deletes ports in 'portIdHashArray'. This will delete most of the
1477 * ports that are in the 'portNoHashArray' as well. Any remaining ports
1478 * are deleted by walking the 'portNoHashArray'.
1479 * --------------------------------------------------------------------------
1482 OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
1484 for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1485 PLIST_ENTRY head, link, next;
1487 head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
1488 LIST_FORALL_SAFE(head, link, next) {
1489 POVS_VPORT_ENTRY vport;
1490 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1491 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
1496 * Remove 'virtualExternalVport' as well. This port is not part of the
1497 * 'portIdHashArray'.
1499 if (switchContext->virtualExternalVport) {
1500 OvsRemoveAndDeleteVport(NULL, switchContext,
1501 (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE);
1505 for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1506 PLIST_ENTRY head, link, next;
1507 head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
1508 LIST_FORALL_SAFE(head, link, next) {
1509 POVS_VPORT_ENTRY vport;
1510 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1511 ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
1512 (vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
1513 vport->isBridgeInternal) || vport->isAbsentOnHv == TRUE);
1514 OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
1518 ASSERT(switchContext->virtualExternalVport == NULL);
1519 ASSERT(switchContext->internalVport == NULL);
1524 OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
1529 UNICODE_STRING ustr;
1533 ustr.Buffer = wStr->String;
1534 ustr.Length = wStr->Length;
1535 ustr.MaximumLength = IF_MAX_STRING_SIZE;
1538 astr.MaximumLength = maxStrLen;
1541 size = RtlUnicodeStringToAnsiSize(&ustr);
1542 if (size > maxStrLen) {
1543 return STATUS_BUFFER_OVERFLOW;
1546 status = RtlUnicodeStringToAnsiString(&astr, &ustr, FALSE);
1548 ASSERT(status == STATUS_SUCCESS);
1549 if (status != STATUS_SUCCESS) {
1552 ASSERT(astr.Length <= maxStrLen);
1553 str[astr.Length] = 0;
1554 return STATUS_SUCCESS;
1558 * --------------------------------------------------------------------------
1559 * Utility function that populates a 'OVS_VPORT_EXT_INFO' structure for the
1561 * --------------------------------------------------------------------------
1564 OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
1565 POVS_VPORT_EXT_INFO extInfo)
1567 POVS_VPORT_ENTRY vport;
1569 LOCK_STATE_EX lockState;
1570 NTSTATUS status = STATUS_SUCCESS;
1571 BOOLEAN doConvert = FALSE;
1573 RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO));
1574 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1575 if (vportGet->portNo == 0) {
1576 StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
1577 vport = OvsFindVportByHvNameA(gOvsSwitchContext, vportGet->name);
1578 if (vport == NULL) {
1579 /* If the port is not a Hyper-V port and it has been added earlier,
1580 * we'll find it in 'ovsPortNameHashArray'. */
1581 vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name);
1584 vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo);
1586 if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED &&
1587 vport->ovsState != OVS_STATE_NIC_CREATED)) {
1588 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1589 if (vportGet->portNo) {
1590 OVS_LOG_WARN("vport %u does not exist any more", vportGet->portNo);
1592 OVS_LOG_WARN("vport %s does not exist any more", vportGet->name);
1594 status = STATUS_DEVICE_DOES_NOT_EXIST;
1597 extInfo->dpNo = vportGet->dpNo;
1598 extInfo->portNo = vport->portNo;
1599 RtlCopyMemory(extInfo->macAddress, vport->currMacAddress,
1600 sizeof (vport->currMacAddress));
1601 RtlCopyMemory(extInfo->permMACAddress, vport->permMacAddress,
1602 sizeof (vport->permMacAddress));
1603 if (vport->ovsType == OVS_VPORT_TYPE_NETDEV) {
1604 RtlCopyMemory(extInfo->vmMACAddress, vport->vmMacAddress,
1605 sizeof (vport->vmMacAddress));
1607 extInfo->nicIndex = vport->nicIndex;
1608 extInfo->portId = vport->portId;
1609 extInfo->type = vport->ovsType;
1610 extInfo->mtu = vport->mtu;
1614 if (vport->ovsState == OVS_STATE_NIC_CREATED) {
1615 extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_DOWN;
1616 } else if (vport->ovsState == OVS_STATE_CONNECTED) {
1617 extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
1619 extInfo->status = OVS_EVENT_DISCONNECT;
1621 if (extInfo->type == OVS_VPORT_TYPE_NETDEV &&
1622 (vport->ovsState == OVS_STATE_NIC_CREATED ||
1623 vport->ovsState == OVS_STATE_CONNECTED)) {
1626 extInfo->vmUUID[0] = 0;
1627 extInfo->vifUUID[0] = 0;
1629 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1631 status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName,
1633 OVS_MAX_PORT_NAME_LENGTH);
1634 if (status != STATUS_SUCCESS) {
1635 OVS_LOG_INFO("Fail to convert NIC name.");
1636 extInfo->vmUUID[0] = 0;
1639 status = OvsConvertIfCountedStrToAnsiStr(&vport->vmName,
1641 OVS_MAX_VM_UUID_LEN);
1642 if (status != STATUS_SUCCESS) {
1643 OVS_LOG_INFO("Fail to convert VM name.");
1644 extInfo->vmUUID[0] = 0;
1647 status = OvsConvertIfCountedStrToAnsiStr(&vport->nicName,
1649 OVS_MAX_VIF_UUID_LEN);
1650 if (status != STATUS_SUCCESS) {
1651 OVS_LOG_INFO("Fail to convert nic UUID");
1652 extInfo->vifUUID[0] = 0;
1655 * for now ignore status
1657 status = STATUS_SUCCESS;
1665 * --------------------------------------------------------------------------
1666 * Command Handler for 'OVS_WIN_NETDEV_CMD_GET'.
1667 * --------------------------------------------------------------------------
1670 OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1673 NTSTATUS status = STATUS_SUCCESS;
1674 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1675 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1676 NL_ERROR nlError = NL_ERROR_SUCCESS;
1677 OVS_VPORT_GET vportGet;
1678 OVS_VPORT_EXT_INFO info;
1680 static const NL_POLICY ovsNetdevPolicy[] = {
1681 [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING,
1683 .maxLen = IFNAMSIZ },
1685 PNL_ATTR netdevAttrs[ARRAY_SIZE(ovsNetdevPolicy)];
1687 /* input buffer has been validated while validating transaction dev op. */
1688 ASSERT(usrParamsCtx->inputBuffer != NULL &&
1689 usrParamsCtx->inputLength > sizeof *msgIn);
1691 if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1692 return STATUS_INVALID_BUFFER_SIZE;
1695 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1696 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1697 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1698 ovsNetdevPolicy, ARRAY_SIZE(ovsNetdevPolicy),
1699 netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
1700 return STATUS_INVALID_PARAMETER;
1703 vportGet.portNo = 0;
1704 RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
1705 NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
1707 status = OvsGetExtInfoIoctl(&vportGet, &info);
1708 if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
1709 nlError = NL_ERROR_NODEV;
1713 status = CreateNetlinkMesgForNetdev(&info, msgIn,
1714 usrParamsCtx->outputBuffer, usrParamsCtx->outputLength,
1715 gOvsSwitchContext->dpNo);
1716 if (status == STATUS_SUCCESS) {
1717 *replyLen = msgOut->nlMsg.nlmsgLen;
1721 if (nlError != NL_ERROR_SUCCESS) {
1722 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1723 usrParamsCtx->outputBuffer;
1725 NlBuildErrorMsg(msgIn, msgError, nlError);
1726 *replyLen = msgError->nlMsg.nlmsgLen;
1729 return STATUS_SUCCESS;
1734 * --------------------------------------------------------------------------
1735 * Utility function to construct an OVS_MESSAGE for the specified vport. The
1736 * OVS_MESSAGE contains the output of a netdev command.
1737 * --------------------------------------------------------------------------
1740 CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
1749 UINT32 netdevFlags = 0;
1751 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1753 ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1754 msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1755 msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1758 return STATUS_INVALID_BUFFER_SIZE;
1761 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
1764 return STATUS_INVALID_BUFFER_SIZE;
1767 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
1769 return STATUS_INVALID_BUFFER_SIZE;
1772 ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
1775 return STATUS_INVALID_BUFFER_SIZE;
1778 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
1779 (PCHAR)info->macAddress, sizeof (info->macAddress));
1781 return STATUS_INVALID_BUFFER_SIZE;
1784 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
1786 return STATUS_INVALID_BUFFER_SIZE;
1789 if (info->status != OVS_EVENT_CONNECT) {
1790 netdevFlags = OVS_WIN_NETDEV_IFF_UP;
1792 ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
1795 return STATUS_INVALID_BUFFER_SIZE;
1799 * XXX: add netdev_stats when we have the definition available in the
1803 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1804 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1806 return STATUS_SUCCESS;
1809 static __inline VOID
1810 OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec)
1812 while ((!switchContext->isActivated) &&
1813 (!switchContext->isActivateFailed)) {
1814 /* Wait for the switch to be active and
1815 * the list of ports in OVS to be initialized. */
1816 NdisMSleep(sleepMicroSec);
1821 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1828 OVS_VPORT_FULL_STATS vportStats;
1832 NlBufInit(&nlBuffer, outBuffer, outBufLen);
1834 ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1835 msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1836 msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1839 return STATUS_INVALID_BUFFER_SIZE;
1842 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1844 return STATUS_INVALID_BUFFER_SIZE;
1847 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1849 return STATUS_INVALID_BUFFER_SIZE;
1852 ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1854 return STATUS_INVALID_BUFFER_SIZE;
1858 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1859 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1860 * it means we have an array of pids, instead of a single pid.
1861 * ATM we assume we have one pid only.
1864 ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1867 return STATUS_INVALID_BUFFER_SIZE;
1871 vportStats.rxPackets = vport->stats.rxPackets;
1872 vportStats.rxBytes = vport->stats.rxBytes;
1873 vportStats.txPackets = vport->stats.txPackets;
1874 vportStats.txBytes = vport->stats.txBytes;
1875 vportStats.rxErrors = vport->errStats.rxErrors;
1876 vportStats.txErrors = vport->errStats.txErrors;
1877 vportStats.rxDropped = vport->errStats.rxDropped;
1878 vportStats.txDropped = vport->errStats.txDropped;
1880 ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1882 sizeof(OVS_VPORT_FULL_STATS));
1884 return STATUS_INVALID_BUFFER_SIZE;
1888 * XXX: when vxlan udp dest port becomes configurable, we will also need
1889 * to add vport options
1892 nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1893 nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1895 return STATUS_SUCCESS;
1899 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1903 POVS_OPEN_INSTANCE instance =
1904 (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1905 LOCK_STATE_EX lockState;
1906 UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1909 * XXX: this function shares some code with other dump command(s).
1910 * In the future, we will need to refactor the dump functions
1913 ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1915 if (instance->dumpState.ovsMsg == NULL) {
1917 return STATUS_INVALID_DEVICE_STATE;
1920 /* Output buffer has been validated while validating read dev op. */
1921 ASSERT(usrParamsCtx->outputBuffer != NULL);
1923 msgIn = instance->dumpState.ovsMsg;
1926 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1927 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1928 * it means we have an array of pids, instead of a single pid.
1929 * ATM we assume we have one pid only.
1931 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1933 if (gOvsSwitchContext->numHvVports > 0 ||
1934 gOvsSwitchContext->numNonHvVports > 0) {
1935 /* inBucket: the bucket, used for lookup */
1936 UINT32 inBucket = instance->dumpState.index[0];
1937 /* inIndex: index within the given bucket, used for lookup */
1938 UINT32 inIndex = instance->dumpState.index[1];
1939 /* the bucket to be used for the next dump operation */
1940 UINT32 outBucket = 0;
1941 /* the index within the outBucket to be used for the next dump */
1942 UINT32 outIndex = 0;
1944 for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1945 PLIST_ENTRY head, link;
1946 head = &(gOvsSwitchContext->portNoHashArray[i]);
1947 POVS_VPORT_ENTRY vport = NULL;
1950 LIST_FORALL(head, link) {
1953 * if one or more dumps were previously done on this same bucket,
1954 * inIndex will be > 0, so we'll need to reply with the
1955 * inIndex + 1 vport from the bucket.
1957 if (outIndex >= inIndex) {
1958 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1960 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
1961 OvsCreateMsgFromVport(vport, msgIn,
1962 usrParamsCtx->outputBuffer,
1963 usrParamsCtx->outputLength,
1964 gOvsSwitchContext->dpNo);
1977 * if no vport was found above, check the next bucket, beginning
1978 * with the first (i.e. index 0) elem from within that bucket
1985 /* XXX: what about NLMSG_DONE (as msg type)? */
1986 instance->dumpState.index[0] = outBucket;
1987 instance->dumpState.index[1] = outIndex;
1990 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1992 /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1993 if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1994 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1995 *replyLen = msgOut->nlMsg.nlmsgLen;
1998 * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
2002 /* Free up the dump state, since there's no more data to continue. */
2003 FreeUserDumpState(instance);
2006 return STATUS_SUCCESS;
2010 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2013 NTSTATUS status = STATUS_SUCCESS;
2014 LOCK_STATE_EX lockState;
2016 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2017 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2018 POVS_VPORT_ENTRY vport = NULL;
2019 NL_ERROR nlError = NL_ERROR_SUCCESS;
2020 PCHAR portName = NULL;
2021 UINT32 portNameLen = 0;
2022 UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
2024 static const NL_POLICY ovsVportPolicy[] = {
2025 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2026 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
2031 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2033 /* input buffer has been validated while validating write dev op. */
2034 ASSERT(usrParamsCtx->inputBuffer != NULL);
2036 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2037 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2038 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2039 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2040 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2041 return STATUS_INVALID_PARAMETER;
2044 /* Output buffer has been validated while validating transact dev op. */
2045 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2047 NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
2048 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2049 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2050 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2052 /* the port name is expected to be null-terminated */
2053 ASSERT(portName[portNameLen - 1] == '\0');
2055 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2056 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2057 portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2059 vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
2061 nlError = NL_ERROR_INVAL;
2062 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2067 nlError = NL_ERROR_NODEV;
2068 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2072 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2073 usrParamsCtx->outputLength,
2074 gOvsSwitchContext->dpNo);
2075 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2077 *replyLen = msgOut->nlMsg.nlmsgLen;
2080 if (nlError != NL_ERROR_SUCCESS) {
2081 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2082 usrParamsCtx->outputBuffer;
2084 NlBuildErrorMsg(msgIn, msgError, nlError);
2085 *replyLen = msgError->nlMsg.nlmsgLen;
2088 return STATUS_SUCCESS;
2092 * --------------------------------------------------------------------------
2093 * Command Handler for 'OVS_VPORT_CMD_GET'.
2095 * The function handles the initial call to setup the dump state, as well as
2096 * subsequent calls to continue dumping data.
2097 * --------------------------------------------------------------------------
2100 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2105 switch (usrParamsCtx->devOp) {
2106 case OVS_WRITE_DEV_OP:
2107 return OvsSetupDumpStart(usrParamsCtx);
2109 case OVS_READ_DEV_OP:
2110 return OvsGetVportDumpNext(usrParamsCtx, replyLen);
2112 case OVS_TRANSACTION_DEV_OP:
2113 return OvsGetVport(usrParamsCtx, replyLen);
2116 return STATUS_INVALID_DEVICE_REQUEST;
2122 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
2124 /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
2125 for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
2126 POVS_VPORT_ENTRY vport;
2128 vport = OvsFindVportByPortNo(switchContext, i);
2134 return OVS_DPPORT_NUMBER_INVALID;
2138 * --------------------------------------------------------------------------
2139 * Command Handler for 'OVS_VPORT_CMD_NEW'.
2140 * --------------------------------------------------------------------------
2143 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2146 NDIS_STATUS status = STATUS_SUCCESS;
2147 LOCK_STATE_EX lockState;
2149 NL_ERROR nlError = NL_ERROR_SUCCESS;
2150 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2151 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2152 POVS_VPORT_ENTRY vport = NULL;
2156 BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE;
2158 static const NL_POLICY ovsVportPolicy[] = {
2159 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2160 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2161 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2163 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2164 .optional = FALSE },
2165 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2168 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2170 /* input buffer has been validated while validating write dev op. */
2171 ASSERT(usrParamsCtx->inputBuffer != NULL);
2173 /* Output buffer has been validated while validating transact dev op. */
2174 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2176 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2177 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2178 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2179 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2180 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2181 return STATUS_INVALID_PARAMETER;
2184 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2185 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2186 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2188 /* we are expecting null terminated strings to be passed */
2189 ASSERT(portName[portNameLen - 1] == '\0');
2191 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2193 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2195 nlError = NL_ERROR_EXIST;
2199 if (portType == OVS_VPORT_TYPE_NETDEV) {
2200 /* External ports can also be looked up like VIF ports. */
2201 vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
2203 ASSERT(OvsIsTunnelVportType(portType) ||
2204 portType == OVS_VPORT_TYPE_INTERNAL);
2206 vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
2207 if (vport == NULL) {
2208 nlError = NL_ERROR_NOMEM;
2211 vportAllocated = TRUE;
2213 if (OvsIsTunnelVportType(portType)) {
2214 UINT16 transportPortDest = 0;
2217 case OVS_VPORT_TYPE_GRE:
2219 case OVS_VPORT_TYPE_VXLAN:
2220 transportPortDest = VXLAN_UDP_PORT;
2222 case OVS_VPORT_TYPE_STT:
2223 transportPortDest = STT_TCP_PORT;
2226 nlError = NL_ERROR_INVAL;
2230 if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2231 PNL_ATTR attr = NlAttrFindNested(vportAttrs[OVS_VPORT_ATTR_OPTIONS],
2232 OVS_TUNNEL_ATTR_DST_PORT);
2234 transportPortDest = NlAttrGetU16(attr);
2238 status = OvsInitTunnelVport(usrParamsCtx,
2243 nlError = NlMapStatusToNlErr(status);
2245 OvsInitBridgeInternalVport(vport);
2248 vportInitialized = TRUE;
2250 if (nlError == NL_ERROR_SUCCESS) {
2251 vport->ovsState = OVS_STATE_CONNECTED;
2252 vport->nicState = NdisSwitchNicStateConnected;
2255 * Allow the vport to be deleted, because there is no
2256 * corresponding hyper-v switch part.
2258 vport->isAbsentOnHv = TRUE;
2265 nlError = NL_ERROR_INVAL;
2268 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2269 nlError = NL_ERROR_EXIST;
2273 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2275 * XXX: when we implement the limit for ovs port number to be
2276 * MAXUINT16, we'll need to check the port number received from the
2279 vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2281 vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
2282 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2283 nlError = NL_ERROR_NOMEM;
2288 /* The ovs port name must be uninitialized. */
2289 ASSERT(vport->ovsName[0] == '\0');
2290 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2292 RtlCopyMemory(vport->ovsName, portName, portNameLen);
2293 /* if we don't have options, then vport->portOptions will be NULL */
2294 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2297 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2298 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2299 * it means we have an array of pids, instead of a single pid.
2300 * ATM we assume we have one pid only.
2302 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2304 status = InitOvsVportCommon(gOvsSwitchContext, vport);
2305 ASSERT(status == STATUS_SUCCESS);
2307 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2308 usrParamsCtx->outputLength,
2309 gOvsSwitchContext->dpNo);
2311 *replyLen = msgOut->nlMsg.nlmsgLen;
2314 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2316 if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2317 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2318 usrParamsCtx->outputBuffer;
2320 if (vport && vportAllocated == TRUE) {
2321 if (vportInitialized == TRUE) {
2322 if (OvsIsTunnelVportType(portType)) {
2323 switch (vport->ovsType) {
2324 case OVS_VPORT_TYPE_VXLAN:
2325 OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2327 case OVS_VPORT_TYPE_STT:
2328 OvsCleanupSttTunnel(vport);
2331 ASSERT(!"Invalid tunnel port type");
2335 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2338 NlBuildErrorMsg(msgIn, msgError, nlError);
2339 *replyLen = msgError->nlMsg.nlmsgLen;
2342 return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2347 * --------------------------------------------------------------------------
2348 * Command Handler for 'OVS_VPORT_CMD_SET'.
2349 * --------------------------------------------------------------------------
2352 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2355 NDIS_STATUS status = STATUS_SUCCESS;
2356 LOCK_STATE_EX lockState;
2358 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2359 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2360 POVS_VPORT_ENTRY vport = NULL;
2361 NL_ERROR nlError = NL_ERROR_SUCCESS;
2363 static const NL_POLICY ovsVportPolicy[] = {
2364 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2365 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
2366 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2368 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2370 [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
2371 .minLen = sizeof(OVS_VPORT_FULL_STATS),
2372 .maxLen = sizeof(OVS_VPORT_FULL_STATS),
2374 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2376 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2378 ASSERT(usrParamsCtx->inputBuffer != NULL);
2380 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2381 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2382 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2383 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2384 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2385 return STATUS_INVALID_PARAMETER;
2388 /* Output buffer has been validated while validating transact dev op. */
2389 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2391 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2392 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2393 PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2395 UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2397 /* the port name is expected to be null-terminated */
2398 ASSERT(portName[portNameLen - 1] == '\0');
2400 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2401 } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2402 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2403 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2407 nlError = NL_ERROR_NODEV;
2412 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2413 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2414 * it means we have an array of pids, instead of a single pid.
2415 * Currently, we support only one pid.
2417 if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
2418 vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2421 if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
2422 OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2423 if (type != vport->ovsType) {
2424 nlError = NL_ERROR_INVAL;
2429 if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2430 OVS_LOG_ERROR("Vport options not supported");
2431 nlError = NL_ERROR_NOTSUPP;
2435 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2436 usrParamsCtx->outputLength,
2437 gOvsSwitchContext->dpNo);
2439 *replyLen = msgOut->nlMsg.nlmsgLen;
2442 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2444 if (nlError != NL_ERROR_SUCCESS) {
2445 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2446 usrParamsCtx->outputBuffer;
2448 NlBuildErrorMsg(msgIn, msgError, nlError);
2449 *replyLen = msgError->nlMsg.nlmsgLen;
2452 return STATUS_SUCCESS;
2456 * --------------------------------------------------------------------------
2457 * Command Handler for 'OVS_VPORT_CMD_DEL'.
2458 * --------------------------------------------------------------------------
2461 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2464 NDIS_STATUS status = STATUS_SUCCESS;
2465 LOCK_STATE_EX lockState;
2467 POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2468 POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2469 POVS_VPORT_ENTRY vport = NULL;
2470 NL_ERROR nlError = NL_ERROR_SUCCESS;
2471 PSTR portName = NULL;
2472 UINT32 portNameLen = 0;
2474 static const NL_POLICY ovsVportPolicy[] = {
2475 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2476 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2479 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2481 ASSERT(usrParamsCtx->inputBuffer != NULL);
2483 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2484 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2485 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2486 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2487 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2488 return STATUS_INVALID_PARAMETER;
2491 /* Output buffer has been validated while validating transact dev op. */
2492 ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2494 NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2495 if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2496 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2497 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2499 /* the port name is expected to be null-terminated */
2500 ASSERT(portName[portNameLen - 1] == '\0');
2502 vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2504 else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2505 vport = OvsFindVportByPortNo(gOvsSwitchContext,
2506 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2510 nlError = NL_ERROR_NODEV;
2514 status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2515 usrParamsCtx->outputLength,
2516 gOvsSwitchContext->dpNo);
2518 *replyLen = msgOut->nlMsg.nlmsgLen;
2521 * Mark the port as deleted from OVS userspace. If the port does not exist
2522 * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
2524 status = OvsRemoveAndDeleteVport(usrParamsCtx,
2530 nlError = NlMapStatusToNlErr(status);
2534 NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2536 if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2537 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2538 usrParamsCtx->outputBuffer;
2540 NlBuildErrorMsg(msgIn, msgError, nlError);
2541 *replyLen = msgError->nlMsg.nlmsgLen;
2544 return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2548 OvsTunnelVportPendingRemove(PVOID context,
2552 POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2553 (POVS_TUNFLT_INIT_CONTEXT) context;
2554 POVS_SWITCH_CONTEXT switchContext = tunnelContext->switchContext;
2555 POVS_VPORT_ENTRY vport = tunnelContext->vport;
2556 POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2557 POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2558 NL_ERROR nlError = NlMapStatusToNlErr(status);
2559 LOCK_STATE_EX lockState;
2561 NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
2563 if (msgIn && msgOut) {
2564 /* Check the received status to reply to the caller. */
2565 if (STATUS_SUCCESS == status) {
2566 OvsCreateMsgFromVport(vport,
2569 tunnelContext->outputLength,
2570 switchContext->dpNo);
2572 *replyLen = msgOut->nlMsg.nlmsgLen;
2574 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)msgOut;
2576 NlBuildErrorMsg(msgIn, msgError, nlError);
2577 *replyLen = msgError->nlMsg.nlmsgLen;
2581 ASSERT(vport->isAbsentOnHv == TRUE);
2582 ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
2584 /* Remove the port from the relevant lists. */
2585 switchContext->numNonHvVports--;
2586 RemoveEntryList(&vport->ovsNameLink);
2587 RemoveEntryList(&vport->portNoLink);
2588 RemoveEntryList(&vport->tunnelVportLink);
2591 OvsFreeMemoryWithTag(vport->priv, OVS_VXLAN_POOL_TAG);
2595 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2597 NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
2601 OvsTunnelVportPendingInit(PVOID context,
2605 POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2606 (POVS_TUNFLT_INIT_CONTEXT) context;
2607 POVS_VPORT_ENTRY vport = tunnelContext->vport;
2608 POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2609 POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2611 ULONG portNameLen = 0;
2612 UINT32 portType = 0;
2613 NL_ERROR nlError = NL_ERROR_SUCCESS;
2614 BOOLEAN error = TRUE;
2617 if (!NT_SUCCESS(status)) {
2618 nlError = NlMapStatusToNlErr(status);
2622 static const NL_POLICY ovsVportPolicy[] = {
2623 [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2624 [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2625 [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2626 .optional = FALSE },
2627 [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2628 .optional = FALSE },
2629 [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2632 PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2634 /* input buffer has been validated while validating write dev op. */
2635 ASSERT(msgIn != NULL);
2637 /* Output buffer has been validated while validating transact dev op. */
2638 ASSERT(msgOut != NULL && tunnelContext->outputLength >= sizeof *msgOut);
2640 if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2641 NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2642 NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2643 ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2644 vportAttrs, ARRAY_SIZE(vportAttrs))) {
2645 nlError = NL_ERROR_INVAL;
2649 portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2650 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2651 portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2653 if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2654 nlError = NL_ERROR_EXIST;
2658 vport->ovsState = OVS_STATE_CONNECTED;
2659 vport->nicState = NdisSwitchNicStateConnected;
2662 * Allow the vport to be deleted, because there is no
2663 * corresponding hyper-v switch part.
2665 vport->isAbsentOnHv = TRUE;
2667 if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2669 * XXX: when we implement the limit for OVS port number to be
2670 * MAXUINT16, we'll need to check the port number received from the
2674 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2677 OvsComputeVportNo(gOvsSwitchContext);
2678 if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2679 nlError = NL_ERROR_NOMEM;
2684 /* The ovs port name must be uninitialized. */
2685 ASSERT(vport->ovsName[0] == '\0');
2686 ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2688 RtlCopyMemory(vport->ovsName, portName, portNameLen);
2689 /* if we don't have options, then vport->portOptions will be NULL */
2690 vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2693 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2694 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2695 * it means we have an array of pids, instead of a single pid.
2696 * ATM we assume we have one pid only.
2699 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2701 status = InitOvsVportCommon(gOvsSwitchContext, vport);
2702 ASSERT(status == STATUS_SUCCESS);
2704 OvsCreateMsgFromVport(vport,
2707 tunnelContext->outputLength,
2708 gOvsSwitchContext->dpNo);
2710 *replyLen = msgOut->nlMsg.nlmsgLen;
2716 POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) msgOut;
2718 OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2719 OvsFreeMemory(vport);
2721 NlBuildErrorMsg(msgIn, msgError, nlError);
2722 *replyLen = msgError->nlMsg.nlmsgLen;