2 * Copyright (c) 2014 VMware, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
31 #define OVS_DBG_MOD OVS_DBG_VPORT
34 #define VPORT_NIC_ENTER(_nic) \
35 OVS_LOG_TRACE("Enter: PortId: %x, NicIndex: %d", _nic->PortId, \
38 #define VPORT_NIC_EXIT(_nic) \
39 OVS_LOG_TRACE("Exit: PortId: %x, NicIndex: %d", _nic->PortId, \
42 #define VPORT_PORT_ENTER(_port) \
43 OVS_LOG_TRACE("Enter: PortId: %x", _port->PortId)
45 #define VPORT_PORT_EXIT(_port) \
46 OVS_LOG_TRACE("Exit: PortId: %x", _port->PortId)
48 #define OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC 100
50 extern POVS_SWITCH_CONTEXT gOvsSwitchContext
;
51 extern PNDIS_SPIN_LOCK gOvsCtrlLock
;
53 static VOID
OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport
,
54 PNDIS_SWITCH_PORT_PARAMETERS portParam
);
55 static VOID
OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext
,
56 POVS_VPORT_ENTRY vport
, PNDIS_SWITCH_NIC_PARAMETERS nicParam
);
57 static VOID
OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVPort
,
58 POVS_VPORT_ENTRY virtExtVport
, UINT32 nicIndex
);
59 static __inline VOID
OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext
,
61 static NTSTATUS
OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet
,
62 POVS_VPORT_EXT_INFO extInfo
);
63 static NTSTATUS
CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info
,
68 static POVS_VPORT_ENTRY
OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext
,
69 PWSTR wsName
, SIZE_T wstrSize
);
70 static NDIS_STATUS
InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext
,
71 POVS_VPORT_ENTRY vport
,
75 * Functions implemented in relaton to NDIS port manipulation.
78 HvCreatePort(POVS_SWITCH_CONTEXT switchContext
,
79 PNDIS_SWITCH_PORT_PARAMETERS portParam
)
81 POVS_VPORT_ENTRY vport
;
82 LOCK_STATE_EX lockState
;
83 NDIS_STATUS status
= NDIS_STATUS_SUCCESS
;
84 BOOLEAN newPort
= FALSE
;
86 VPORT_PORT_ENTER(portParam
);
88 NdisAcquireRWLockWrite(switchContext
->dispatchLock
, &lockState
, 0);
89 /* Lookup by port ID. */
90 vport
= OvsFindVportByPortIdAndNicIndex(switchContext
,
91 portParam
->PortId
, 0);
93 OVS_LOG_ERROR("Port add failed due to duplicate port name, "
94 "port Id: %u", portParam
->PortId
);
95 status
= STATUS_DATA_NOT_ACCEPTED
;
96 goto create_port_done
;
100 * Lookup by port name to see if this port with this name had been added
101 * (and deleted) previously.
103 vport
= OvsFindVportByHvNameW(gOvsSwitchContext
,
104 portParam
->PortFriendlyName
.String
,
105 portParam
->PortFriendlyName
.Length
);
106 if (vport
&& vport
->isPresentOnHv
== FALSE
) {
107 OVS_LOG_ERROR("Port add failed since a port already exists on "
108 "the specified port Id: %u, ovsName: %s",
109 portParam
->PortId
, vport
->ovsName
);
110 status
= STATUS_DATA_NOT_ACCEPTED
;
111 goto create_port_done
;
115 ASSERT(vport
->isPresentOnHv
);
116 ASSERT(vport
->portNo
!= OVS_DPPORT_NUMBER_INVALID
);
119 * It should be possible to simply just mark this port as "not deleted"
120 * given that the port Id and the name are the same and also provided
121 * that the other properties that we cache have not changed.
123 if (vport
->portType
!= portParam
->PortType
) {
124 OVS_LOG_INFO("Port add failed due to PortType change, port Id: %u"
125 " old: %u, new: %u", portParam
->PortId
,
126 vport
->portType
, portParam
->PortType
);
127 status
= STATUS_DATA_NOT_ACCEPTED
;
128 goto create_port_done
;
130 vport
->isPresentOnHv
= FALSE
;
132 vport
= (POVS_VPORT_ENTRY
)OvsAllocateVport();
134 status
= NDIS_STATUS_RESOURCES
;
135 goto create_port_done
;
139 OvsInitVportWithPortParam(vport
, portParam
);
140 InitHvVportCommon(switchContext
, vport
, newPort
);
143 NdisReleaseRWLock(switchContext
->dispatchLock
, &lockState
);
144 VPORT_PORT_EXIT(portParam
);
150 * Function updating the port properties
153 HvUpdatePort(POVS_SWITCH_CONTEXT switchContext
,
154 PNDIS_SWITCH_PORT_PARAMETERS portParam
)
156 POVS_VPORT_ENTRY vport
;
157 LOCK_STATE_EX lockState
;
158 OVS_VPORT_STATE ovsState
;
159 NDIS_SWITCH_NIC_STATE nicState
;
161 VPORT_PORT_ENTER(portParam
);
163 NdisAcquireRWLockWrite(switchContext
->dispatchLock
, &lockState
, 0);
164 vport
= OvsFindVportByPortIdAndNicIndex(switchContext
,
165 portParam
->PortId
, 0);
167 * Update properties only for NETDEV ports for supprting PS script
168 * We don't allow changing the names of the internal or external ports
170 if (vport
== NULL
|| ( vport
->portType
!= NdisSwitchPortTypeSynthetic
) ||
171 ( vport
->portType
!= NdisSwitchPortTypeEmulated
)) {
172 goto update_port_done
;
175 /* Store the nic and the OVS states as Nic Create won't be called */
176 ovsState
= vport
->ovsState
;
177 nicState
= vport
->nicState
;
180 * Currently only the port friendly name is being updated
181 * Make sure that no other properties are changed
183 ASSERT(portParam
->PortId
== vport
->portId
);
184 ASSERT(portParam
->PortState
== vport
->portState
);
185 ASSERT(portParam
->PortType
== vport
->portType
);
188 * Call the set parameters function the handle all properties
189 * change in a single place in case future version supports change of
192 OvsInitVportWithPortParam(vport
, portParam
);
193 /* Retore the nic and OVS states */
194 vport
->nicState
= nicState
;
195 vport
->ovsState
= ovsState
;
198 NdisReleaseRWLock(switchContext
->dispatchLock
, &lockState
);
199 VPORT_PORT_EXIT(portParam
);
201 /* Must always return success */
202 return NDIS_STATUS_SUCCESS
;
206 HvTeardownPort(POVS_SWITCH_CONTEXT switchContext
,
207 PNDIS_SWITCH_PORT_PARAMETERS portParam
)
209 POVS_VPORT_ENTRY vport
;
210 LOCK_STATE_EX lockState
;
212 VPORT_PORT_ENTER(portParam
);
214 NdisAcquireRWLockWrite(switchContext
->dispatchLock
, &lockState
, 0);
215 vport
= OvsFindVportByPortIdAndNicIndex(switchContext
,
216 portParam
->PortId
, 0);
218 /* add assertion here */
219 vport
->portState
= NdisSwitchPortStateTeardown
;
220 vport
->ovsState
= OVS_STATE_PORT_TEAR_DOWN
;
222 OVS_LOG_WARN("Vport not present.");
224 NdisReleaseRWLock(switchContext
->dispatchLock
, &lockState
);
226 VPORT_PORT_EXIT(portParam
);
231 HvDeletePort(POVS_SWITCH_CONTEXT switchContext
,
232 PNDIS_SWITCH_PORT_PARAMETERS portParams
)
234 POVS_VPORT_ENTRY vport
;
235 LOCK_STATE_EX lockState
;
237 VPORT_PORT_ENTER(portParams
);
239 NdisAcquireRWLockWrite(switchContext
->dispatchLock
, &lockState
, 0);
240 vport
= OvsFindVportByPortIdAndNicIndex(switchContext
,
241 portParams
->PortId
, 0);
244 * XXX: we can only destroy and remove the port if its datapath port
245 * counterpart was deleted. If the datapath port counterpart is present,
246 * we only mark the vport for deletion, so that a netlink command vport
247 * delete will delete the vport.
250 OvsRemoveAndDeleteVport(switchContext
, vport
, TRUE
, FALSE
, NULL
);
252 OVS_LOG_WARN("Vport not present.");
254 NdisReleaseRWLock(switchContext
->dispatchLock
, &lockState
);
256 VPORT_PORT_EXIT(portParams
);
261 * Functions implemented in relaton to NDIS NIC manipulation.
264 HvCreateNic(POVS_SWITCH_CONTEXT switchContext
,
265 PNDIS_SWITCH_NIC_PARAMETERS nicParam
)
267 POVS_VPORT_ENTRY vport
;
270 NDIS_STATUS status
= NDIS_STATUS_SUCCESS
;
272 LOCK_STATE_EX lockState
;
274 VPORT_NIC_ENTER(nicParam
);
276 /* Wait for lists to be initialized. */
277 OvsWaitActivate(switchContext
, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC
);
279 if (!switchContext
->isActivated
) {
280 OVS_LOG_WARN("Switch is not activated yet.");
281 /* Veto the creation of nic */
282 status
= NDIS_STATUS_NOT_SUPPORTED
;
286 NdisAcquireRWLockWrite(switchContext
->dispatchLock
, &lockState
, 0);
287 vport
= OvsFindVportByPortIdAndNicIndex(switchContext
, nicParam
->PortId
, 0);
289 OVS_LOG_ERROR("Create NIC without Switch Port,"
290 " PortId: %x, NicIndex: %d",
291 nicParam
->PortId
, nicParam
->NicIndex
);
292 status
= NDIS_STATUS_INVALID_PARAMETER
;
296 if (nicParam
->NicType
== NdisSwitchNicTypeExternal
&&
297 nicParam
->NicIndex
!= 0) {
298 POVS_VPORT_ENTRY virtExtVport
=
299 (POVS_VPORT_ENTRY
)switchContext
->virtualExternalVport
;
301 vport
= (POVS_VPORT_ENTRY
)OvsAllocateVport();
303 status
= NDIS_STATUS_RESOURCES
;
306 OvsInitPhysNicVport(vport
, virtExtVport
, nicParam
->NicIndex
);
307 status
= InitHvVportCommon(switchContext
, vport
, TRUE
);
308 if (status
!= NDIS_STATUS_SUCCESS
) {
309 OvsFreeMemory(vport
);
313 OvsInitVportWithNicParam(switchContext
, vport
, nicParam
);
314 portNo
= vport
->portNo
;
315 if (vport
->ovsState
== OVS_STATE_CONNECTED
) {
316 event
= OVS_EVENT_CONNECT
| OVS_EVENT_LINK_UP
;
317 } else if (vport
->ovsState
== OVS_STATE_NIC_CREATED
) {
318 event
= OVS_EVENT_CONNECT
;
322 NdisReleaseRWLock(switchContext
->dispatchLock
, &lockState
);
323 if (portNo
!= OVS_DPPORT_NUMBER_INVALID
&& event
) {
324 OvsPostEvent(portNo
, event
);
328 VPORT_NIC_EXIT(nicParam
);
329 OVS_LOG_TRACE("Exit: status %8x.\n", status
);
335 /* Mark already created NIC as connected. */
337 HvConnectNic(POVS_SWITCH_CONTEXT switchContext
,
338 PNDIS_SWITCH_NIC_PARAMETERS nicParam
)
340 LOCK_STATE_EX lockState
;
341 POVS_VPORT_ENTRY vport
;
344 VPORT_NIC_ENTER(nicParam
);
346 /* Wait for lists to be initialized. */
347 OvsWaitActivate(switchContext
, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC
);
349 if (!switchContext
->isActivated
) {
350 OVS_LOG_WARN("Switch is not activated yet.");
354 NdisAcquireRWLockWrite(switchContext
->dispatchLock
, &lockState
, 0);
355 vport
= OvsFindVportByPortIdAndNicIndex(switchContext
,
360 OVS_LOG_WARN("Vport not present.");
361 NdisReleaseRWLock(switchContext
->dispatchLock
, &lockState
);
366 vport
->ovsState
= OVS_STATE_CONNECTED
;
367 vport
->nicState
= NdisSwitchNicStateConnected
;
368 portNo
= vport
->portNo
;
370 NdisReleaseRWLock(switchContext
->dispatchLock
, &lockState
);
372 /* XXX only if portNo != INVALID or always? */
373 OvsPostEvent(portNo
, OVS_EVENT_LINK_UP
);
375 if (nicParam
->NicType
== NdisSwitchNicTypeInternal
) {
376 OvsInternalAdapterUp(portNo
, &nicParam
->NetCfgInstanceId
);
380 VPORT_NIC_EXIT(nicParam
);
384 HvUpdateNic(POVS_SWITCH_CONTEXT switchContext
,
385 PNDIS_SWITCH_NIC_PARAMETERS nicParam
)
387 POVS_VPORT_ENTRY vport
;
388 LOCK_STATE_EX lockState
;
390 UINT32 status
= 0, portNo
= 0;
392 VPORT_NIC_ENTER(nicParam
);
394 /* Wait for lists to be initialized. */
395 OvsWaitActivate(switchContext
, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC
);
397 if (!switchContext
->isActivated
) {
398 OVS_LOG_WARN("Switch is not activated yet.");
399 goto update_nic_done
;
402 NdisAcquireRWLockWrite(switchContext
->dispatchLock
, &lockState
, 0);
403 vport
= OvsFindVportByPortIdAndNicIndex(switchContext
,
407 OVS_LOG_WARN("Vport search failed.");
408 goto update_nic_done
;
410 switch (nicParam
->NicType
) {
411 case NdisSwitchNicTypeExternal
:
412 case NdisSwitchNicTypeInternal
:
413 RtlCopyMemory(&vport
->netCfgInstanceId
, &nicParam
->NetCfgInstanceId
,
416 case NdisSwitchNicTypeSynthetic
:
417 case NdisSwitchNicTypeEmulated
:
418 if (!RtlEqualMemory(vport
->vmMacAddress
, nicParam
->VMMacAddress
,
419 sizeof (vport
->vmMacAddress
))) {
420 status
|= OVS_EVENT_MAC_CHANGE
;
421 RtlCopyMemory(vport
->vmMacAddress
, nicParam
->VMMacAddress
,
422 sizeof (vport
->vmMacAddress
));
428 if (!RtlEqualMemory(vport
->permMacAddress
, nicParam
->PermanentMacAddress
,
429 sizeof (vport
->permMacAddress
))) {
430 RtlCopyMemory(vport
->permMacAddress
, nicParam
->PermanentMacAddress
,
431 sizeof (vport
->permMacAddress
));
432 status
|= OVS_EVENT_MAC_CHANGE
;
434 if (!RtlEqualMemory(vport
->currMacAddress
, nicParam
->CurrentMacAddress
,
435 sizeof (vport
->currMacAddress
))) {
436 RtlCopyMemory(vport
->currMacAddress
, nicParam
->CurrentMacAddress
,
437 sizeof (vport
->currMacAddress
));
438 status
|= OVS_EVENT_MAC_CHANGE
;
441 if (vport
->mtu
!= nicParam
->MTU
) {
442 vport
->mtu
= nicParam
->MTU
;
443 status
|= OVS_EVENT_MTU_CHANGE
;
445 vport
->numaNodeId
= nicParam
->NumaNodeId
;
446 portNo
= vport
->portNo
;
448 NdisReleaseRWLock(switchContext
->dispatchLock
, &lockState
);
449 if (status
&& portNo
) {
450 OvsPostEvent(portNo
, status
);
453 VPORT_NIC_EXIT(nicParam
);
458 HvDisconnectNic(POVS_SWITCH_CONTEXT switchContext
,
459 PNDIS_SWITCH_NIC_PARAMETERS nicParam
)
461 POVS_VPORT_ENTRY vport
;
463 LOCK_STATE_EX lockState
;
464 BOOLEAN isInternalPort
= FALSE
;
466 VPORT_NIC_ENTER(nicParam
);
468 /* Wait for lists to be initialized. */
469 OvsWaitActivate(switchContext
, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC
);
471 if (!switchContext
->isActivated
) {
472 OVS_LOG_WARN("Switch is not activated yet.");
476 NdisAcquireRWLockWrite(switchContext
->dispatchLock
, &lockState
, 0);
477 vport
= OvsFindVportByPortIdAndNicIndex(switchContext
,
482 OVS_LOG_WARN("Vport not present.");
483 NdisReleaseRWLock(switchContext
->dispatchLock
, &lockState
);
487 vport
->nicState
= NdisSwitchNicStateDisconnected
;
488 vport
->ovsState
= OVS_STATE_NIC_CREATED
;
489 portNo
= vport
->portNo
;
491 if (vport
->ovsType
== OVS_VPORT_TYPE_INTERNAL
) {
492 isInternalPort
= TRUE
;
495 NdisReleaseRWLock(switchContext
->dispatchLock
, &lockState
);
497 /* XXX if portNo != INVALID or always? */
498 OvsPostEvent(portNo
, OVS_EVENT_LINK_DOWN
);
500 if (isInternalPort
) {
501 OvsInternalAdapterDown();
505 VPORT_NIC_EXIT(nicParam
);
510 HvDeleteNic(POVS_SWITCH_CONTEXT switchContext
,
511 PNDIS_SWITCH_NIC_PARAMETERS nicParam
)
513 LOCK_STATE_EX lockState
;
514 POVS_VPORT_ENTRY vport
;
517 VPORT_NIC_ENTER(nicParam
);
518 /* Wait for lists to be initialized. */
519 OvsWaitActivate(switchContext
, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC
);
521 if (!switchContext
->isActivated
) {
522 OVS_LOG_WARN("Switch is not activated yet.");
526 NdisAcquireRWLockWrite(switchContext
->dispatchLock
, &lockState
, 0);
527 vport
= OvsFindVportByPortIdAndNicIndex(switchContext
,
532 OVS_LOG_WARN("Vport not present.");
533 NdisReleaseRWLock(switchContext
->dispatchLock
, &lockState
);
537 portNo
= vport
->portNo
;
538 if (vport
->portType
== NdisSwitchPortTypeExternal
&&
539 vport
->nicIndex
!= 0) {
540 OvsRemoveAndDeleteVport(switchContext
, vport
, TRUE
, FALSE
, NULL
);
542 vport
->nicState
= NdisSwitchNicStateUnknown
;
543 vport
->ovsState
= OVS_STATE_PORT_CREATED
;
545 NdisReleaseRWLock(switchContext
->dispatchLock
, &lockState
);
546 /* XXX if portNo != INVALID or always? */
547 OvsPostEvent(portNo
, OVS_EVENT_DISCONNECT
);
550 VPORT_NIC_EXIT(nicParam
);
555 * OVS Vport related functionality.
558 OvsFindVportByPortNo(POVS_SWITCH_CONTEXT switchContext
,
561 POVS_VPORT_ENTRY vport
;
562 PLIST_ENTRY head
, link
;
563 UINT32 hash
= OvsJhashBytes((const VOID
*)&portNo
, sizeof(portNo
),
565 head
= &(switchContext
->portNoHashArray
[hash
& OVS_VPORT_MASK
]);
566 LIST_FORALL(head
, link
) {
567 vport
= CONTAINING_RECORD(link
, OVS_VPORT_ENTRY
, portNoLink
);
568 if (vport
->portNo
== portNo
) {
577 OvsFindVportByOvsName(POVS_SWITCH_CONTEXT switchContext
,
580 POVS_VPORT_ENTRY vport
;
581 PLIST_ENTRY head
, link
;
583 SIZE_T length
= strlen(name
) + 1;
585 hash
= OvsJhashBytes((const VOID
*)name
, length
, OVS_HASH_BASIS
);
586 head
= &(switchContext
->ovsPortNameHashArray
[hash
& OVS_VPORT_MASK
]);
588 LIST_FORALL(head
, link
) {
589 vport
= CONTAINING_RECORD(link
, OVS_VPORT_ENTRY
, ovsNameLink
);
590 if (!strcmp(name
, vport
->ovsName
)) {
598 /* OvsFindVportByHvName: "name" is assumed to be null-terminated */
600 OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext
,
601 PWSTR wsName
, SIZE_T wstrSize
)
603 POVS_VPORT_ENTRY vport
= NULL
;
604 PLIST_ENTRY head
, link
;
607 for (i
= 0; i
< OVS_MAX_VPORT_ARRAY_SIZE
; i
++) {
608 head
= &(switchContext
->portIdHashArray
[i
]);
609 LIST_FORALL(head
, link
) {
610 vport
= CONTAINING_RECORD(link
, OVS_VPORT_ENTRY
, portIdLink
);
613 * NOTE about portFriendlyName:
614 * If the string is NULL-terminated, the Length member does not
615 * include the terminating NULL character.
617 if (vport
->portFriendlyName
.Length
== wstrSize
&&
618 RtlEqualMemory(wsName
, vport
->portFriendlyName
.String
,
619 vport
->portFriendlyName
.Length
)) {
628 * Look in the list of ports that were added from the Hyper-V switch and
632 for (i
= 0; i
< OVS_MAX_VPORT_ARRAY_SIZE
; i
++) {
633 head
= &(switchContext
->portNoHashArray
[i
]);
634 LIST_FORALL(head
, link
) {
635 vport
= CONTAINING_RECORD(link
, OVS_VPORT_ENTRY
, portNoLink
);
636 if (vport
->portFriendlyName
.Length
== wstrSize
&&
637 RtlEqualMemory(wsName
, vport
->portFriendlyName
.String
,
638 vport
->portFriendlyName
.Length
)) {
652 OvsFindVportByHvNameA(POVS_SWITCH_CONTEXT switchContext
,
655 POVS_VPORT_ENTRY vport
= NULL
;
656 /* 'portFriendlyName' is not NUL-terminated. */
657 SIZE_T length
= strlen(name
);
658 SIZE_T wstrSize
= length
* sizeof(WCHAR
);
661 PWSTR wsName
= OvsAllocateMemory(wstrSize
);
665 for (i
= 0; i
< length
; i
++) {
668 vport
= OvsFindVportByHvNameW(switchContext
, wsName
, wstrSize
);
669 OvsFreeMemory(wsName
);
674 OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT switchContext
,
675 NDIS_SWITCH_PORT_ID portId
,
676 NDIS_SWITCH_NIC_INDEX index
)
678 if (switchContext
->virtualExternalVport
&&
679 portId
== switchContext
->virtualExternalPortId
&&
680 index
== switchContext
->virtualExternalVport
->nicIndex
) {
681 return (POVS_VPORT_ENTRY
)switchContext
->virtualExternalVport
;
682 } else if (switchContext
->internalVport
&&
683 portId
== switchContext
->internalPortId
&&
684 index
== switchContext
->internalVport
->nicIndex
) {
685 return (POVS_VPORT_ENTRY
)switchContext
->internalVport
;
687 PLIST_ENTRY head
, link
;
688 POVS_VPORT_ENTRY vport
;
690 hash
= OvsJhashWords((UINT32
*)&portId
, 1, OVS_HASH_BASIS
);
691 head
= &(switchContext
->portIdHashArray
[hash
& OVS_VPORT_MASK
]);
692 LIST_FORALL(head
, link
) {
693 vport
= CONTAINING_RECORD(link
, OVS_VPORT_ENTRY
, portIdLink
);
694 if (portId
== vport
->portId
&& index
== vport
->nicIndex
) {
703 OvsAllocateVport(VOID
)
705 POVS_VPORT_ENTRY vport
;
706 vport
= (POVS_VPORT_ENTRY
)OvsAllocateMemory(sizeof (OVS_VPORT_ENTRY
));
710 RtlZeroMemory(vport
, sizeof (OVS_VPORT_ENTRY
));
711 vport
->ovsState
= OVS_STATE_UNKNOWN
;
712 vport
->isPresentOnHv
= FALSE
;
713 vport
->portNo
= OVS_DPPORT_NUMBER_INVALID
;
715 InitializeListHead(&vport
->ovsNameLink
);
716 InitializeListHead(&vport
->portIdLink
);
717 InitializeListHead(&vport
->portNoLink
);
723 OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport
,
724 PNDIS_SWITCH_PORT_PARAMETERS portParam
)
726 vport
->portType
= portParam
->PortType
;
727 vport
->portState
= portParam
->PortState
;
728 vport
->portId
= portParam
->PortId
;
729 vport
->nicState
= NdisSwitchNicStateUnknown
;
730 vport
->isExternal
= FALSE
;
731 vport
->isBridgeInternal
= FALSE
;
733 switch (vport
->portType
) {
734 case NdisSwitchPortTypeExternal
:
735 vport
->isExternal
= TRUE
;
736 vport
->ovsType
= OVS_VPORT_TYPE_NETDEV
;
738 case NdisSwitchPortTypeInternal
:
739 vport
->ovsType
= OVS_VPORT_TYPE_INTERNAL
;
741 case NdisSwitchPortTypeSynthetic
:
742 case NdisSwitchPortTypeEmulated
:
743 vport
->ovsType
= OVS_VPORT_TYPE_NETDEV
;
746 RtlCopyMemory(&vport
->hvPortName
, &portParam
->PortName
,
747 sizeof (NDIS_SWITCH_PORT_NAME
));
748 /* For external and internal ports, 'portFriendlyName' is overwritten
750 RtlCopyMemory(&vport
->portFriendlyName
, &portParam
->PortFriendlyName
,
751 sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME
));
753 switch (vport
->portState
) {
754 case NdisSwitchPortStateCreated
:
755 vport
->ovsState
= OVS_STATE_PORT_CREATED
;
757 case NdisSwitchPortStateTeardown
:
758 vport
->ovsState
= OVS_STATE_PORT_TEAR_DOWN
;
760 case NdisSwitchPortStateDeleted
:
761 vport
->ovsState
= OVS_STATE_PORT_DELETED
;
768 OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext
,
769 POVS_VPORT_ENTRY vport
,
770 PNDIS_SWITCH_NIC_PARAMETERS nicParam
)
772 ASSERT(vport
->portId
== nicParam
->PortId
);
773 ASSERT(vport
->ovsState
== OVS_STATE_PORT_CREATED
);
775 UNREFERENCED_PARAMETER(switchContext
);
777 RtlCopyMemory(vport
->permMacAddress
, nicParam
->PermanentMacAddress
,
778 sizeof (nicParam
->PermanentMacAddress
));
779 RtlCopyMemory(vport
->currMacAddress
, nicParam
->CurrentMacAddress
,
780 sizeof (nicParam
->CurrentMacAddress
));
782 if (nicParam
->NicType
== NdisSwitchNicTypeSynthetic
||
783 nicParam
->NicType
== NdisSwitchNicTypeEmulated
) {
784 RtlCopyMemory(vport
->vmMacAddress
, nicParam
->VMMacAddress
,
785 sizeof (nicParam
->VMMacAddress
));
786 RtlCopyMemory(&vport
->vmName
, &nicParam
->VmName
,
787 sizeof (nicParam
->VmName
));
789 RtlCopyMemory(&vport
->netCfgInstanceId
, &nicParam
->NetCfgInstanceId
,
790 sizeof (nicParam
->NetCfgInstanceId
));
792 RtlCopyMemory(&vport
->nicName
, &nicParam
->NicName
,
793 sizeof (nicParam
->NicName
));
794 vport
->mtu
= nicParam
->MTU
;
795 vport
->nicState
= nicParam
->NicState
;
796 vport
->nicIndex
= nicParam
->NicIndex
;
797 vport
->numaNodeId
= nicParam
->NumaNodeId
;
799 switch (vport
->nicState
) {
800 case NdisSwitchNicStateCreated
:
801 vport
->ovsState
= OVS_STATE_NIC_CREATED
;
803 case NdisSwitchNicStateConnected
:
804 vport
->ovsState
= OVS_STATE_CONNECTED
;
806 case NdisSwitchNicStateDisconnected
:
807 vport
->ovsState
= OVS_STATE_NIC_CREATED
;
809 case NdisSwitchNicStateDeleted
:
810 vport
->ovsState
= OVS_STATE_PORT_CREATED
;
816 * --------------------------------------------------------------------------
817 * Copies the relevant NDIS port properties from a virtual (pseudo) external
818 * NIC to a physical (real) external NIC.
819 * --------------------------------------------------------------------------
822 OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVport
,
823 POVS_VPORT_ENTRY virtExtVport
,
826 physExtVport
->portType
= virtExtVport
->portType
;
827 physExtVport
->portState
= virtExtVport
->portState
;
828 physExtVport
->portId
= virtExtVport
->portId
;
829 physExtVport
->nicState
= NdisSwitchNicStateUnknown
;
830 physExtVport
->ovsType
= OVS_VPORT_TYPE_NETDEV
;
831 physExtVport
->isExternal
= TRUE
;
832 physExtVport
->isBridgeInternal
= FALSE
;
833 physExtVport
->nicIndex
= (NDIS_SWITCH_NIC_INDEX
)physNicIndex
;
835 RtlCopyMemory(&physExtVport
->hvPortName
, &virtExtVport
->hvPortName
,
836 sizeof (NDIS_SWITCH_PORT_NAME
));
838 /* 'portFriendlyName' is overwritten later. */
839 RtlCopyMemory(&physExtVport
->portFriendlyName
,
840 &virtExtVport
->portFriendlyName
,
841 sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME
));
843 physExtVport
->ovsState
= OVS_STATE_PORT_CREATED
;
847 * --------------------------------------------------------------------------
848 * Initializes a tunnel vport.
849 * --------------------------------------------------------------------------
852 OvsInitTunnelVport(POVS_VPORT_ENTRY vport
,
853 OVS_VPORT_TYPE ovsType
,
856 NTSTATUS status
= STATUS_SUCCESS
;
858 vport
->isBridgeInternal
= FALSE
;
859 vport
->ovsType
= ovsType
;
860 vport
->ovsState
= OVS_STATE_PORT_CREATED
;
862 case OVS_VPORT_TYPE_GRE
:
864 case OVS_VPORT_TYPE_GRE64
:
866 case OVS_VPORT_TYPE_VXLAN
:
867 status
= OvsInitVxlanTunnel(vport
, dstPort
);
876 * --------------------------------------------------------------------------
877 * Initializes a bridge internal vport ie. a port of type
878 * OVS_VPORT_TYPE_INTERNAL but not present on the Hyper-V switch.
879 * --------------------------------------------------------------------------
882 OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport
)
884 vport
->isBridgeInternal
= TRUE
;
885 vport
->ovsType
= OVS_VPORT_TYPE_INTERNAL
;
886 /* Mark the status to be connected, since there is no other initialization
888 vport
->ovsState
= OVS_STATE_CONNECTED
;
889 return STATUS_SUCCESS
;
893 * --------------------------------------------------------------------------
894 * For external vports 'portFriendlyName' provided by Hyper-V is over-written
895 * by synthetic names.
896 * --------------------------------------------------------------------------
899 AssignNicNameSpecial(POVS_VPORT_ENTRY vport
)
903 if (vport
->portType
== NdisSwitchPortTypeExternal
) {
904 if (vport
->nicIndex
== 0) {
905 ASSERT(vport
->nicIndex
== 0);
906 RtlStringCbPrintfW(vport
->portFriendlyName
.String
,
908 L
"%s.virtualAdapter", OVS_DPPORT_EXTERNAL_NAME_W
);
910 RtlStringCbPrintfW(vport
->portFriendlyName
.String
,
912 L
"%s.%lu", OVS_DPPORT_EXTERNAL_NAME_W
,
913 (UINT32
)vport
->nicIndex
);
916 RtlStringCbPrintfW(vport
->portFriendlyName
.String
,
918 L
"%s", OVS_DPPORT_INTERNAL_NAME_W
);
921 RtlStringCbLengthW(vport
->portFriendlyName
.String
, IF_MAX_STRING_SIZE
,
923 vport
->portFriendlyName
.Length
= (USHORT
)len
;
928 * --------------------------------------------------------------------------
929 * Functionality common to any port on the Hyper-V switch. This function is not
930 * to be called for a port that is not on the Hyper-V switch.
932 * Inserts the port into 'portIdHashArray' and caches the pointer in the
933 * 'switchContext' if needed.
935 * For external NIC, assigns the name for the NIC.
936 * --------------------------------------------------------------------------
939 InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext
,
940 POVS_VPORT_ENTRY vport
,
945 switch (vport
->portType
) {
946 case NdisSwitchPortTypeExternal
:
948 * Overwrite the 'portFriendlyName' of this external vport. The reason
949 * for having this in common code is to be able to call it from the NDIS
950 * Port callback as well as the NDIS NIC callback.
952 AssignNicNameSpecial(vport
);
954 if (vport
->nicIndex
== 0) {
955 switchContext
->virtualExternalPortId
= vport
->portId
;
956 switchContext
->virtualExternalVport
= vport
;
958 switchContext
->numPhysicalNics
++;
961 case NdisSwitchPortTypeInternal
:
962 ASSERT(vport
->isBridgeInternal
== FALSE
);
964 /* Overwrite the 'portFriendlyName' of the internal vport. */
965 AssignNicNameSpecial(vport
);
966 switchContext
->internalPortId
= vport
->portId
;
967 switchContext
->internalVport
= vport
;
969 case NdisSwitchPortTypeSynthetic
:
970 case NdisSwitchPortTypeEmulated
:
975 * It is important to not insert vport corresponding to virtual external
976 * port into the 'portIdHashArray' since the port should not be exposed to
979 if (vport
->portType
== NdisSwitchPortTypeExternal
&&
980 vport
->nicIndex
== 0) {
981 return NDIS_STATUS_SUCCESS
;
985 * NOTE: OvsJhashWords has portId as "1" word. This should be ok, even
986 * though sizeof(NDIS_SWITCH_PORT_ID) = 4, not 2, because the
987 * hyper-v switch seems to use only 2 bytes out of 4.
989 hash
= OvsJhashWords(&vport
->portId
, 1, OVS_HASH_BASIS
);
990 InsertHeadList(&switchContext
->portIdHashArray
[hash
& OVS_VPORT_MASK
],
993 switchContext
->numHvVports
++;
995 return NDIS_STATUS_SUCCESS
;
999 * --------------------------------------------------------------------------
1000 * Functionality common to any port added from OVS userspace.
1002 * Inserts the port into 'portIdHashArray', 'ovsPortNameHashArray' and caches
1003 * the pointer in the 'switchContext' if needed.
1004 * --------------------------------------------------------------------------
1007 InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext
,
1008 POVS_VPORT_ENTRY vport
)
1012 switch(vport
->ovsType
) {
1013 case OVS_VPORT_TYPE_VXLAN
:
1014 ASSERT(switchContext
->vxlanVport
== NULL
);
1015 switchContext
->vxlanVport
= vport
;
1016 switchContext
->numNonHvVports
++;
1018 case OVS_VPORT_TYPE_INTERNAL
:
1019 if (vport
->isBridgeInternal
) {
1020 switchContext
->numNonHvVports
++;
1027 * Insert the port into the hash array of ports: by port number and ovs
1028 * and ovs (datapath) port name.
1029 * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1030 * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1032 hash
= OvsJhashWords(&vport
->portNo
, 1, OVS_HASH_BASIS
);
1033 InsertHeadList(&gOvsSwitchContext
->portNoHashArray
[hash
& OVS_VPORT_MASK
],
1034 &vport
->portNoLink
);
1036 hash
= OvsJhashBytes(vport
->ovsName
, strlen(vport
->ovsName
) + 1,
1039 &gOvsSwitchContext
->ovsPortNameHashArray
[hash
& OVS_VPORT_MASK
],
1040 &vport
->ovsNameLink
);
1042 return STATUS_SUCCESS
;
1047 * --------------------------------------------------------------------------
1048 * Provides functionality that is partly complementatry to
1049 * InitOvsVportCommon()/InitHvVportCommon().
1051 * 'hvDelete' indicates if caller is removing the vport as a result of the
1052 * port being removed on the Hyper-V switch.
1053 * 'ovsDelete' indicates if caller is removing the vport as a result of the
1054 * port being removed from OVS userspace.
1055 * --------------------------------------------------------------------------
1058 OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext
,
1059 POVS_VPORT_ENTRY vport
,
1062 BOOLEAN
*vportDeallocated
)
1064 BOOLEAN hvSwitchPort
= FALSE
;
1065 BOOLEAN deletedOnOvs
= FALSE
, deletedOnHv
= FALSE
;
1067 if (vportDeallocated
) {
1068 *vportDeallocated
= FALSE
;
1071 if (vport
->isExternal
) {
1072 if (vport
->nicIndex
== 0) {
1073 ASSERT(switchContext
->numPhysicalNics
== 0);
1074 switchContext
->virtualExternalPortId
= 0;
1075 switchContext
->virtualExternalVport
= NULL
;
1076 OvsFreeMemory(vport
);
1077 if (vportDeallocated
) {
1078 *vportDeallocated
= TRUE
;
1082 ASSERT(switchContext
->numPhysicalNics
);
1083 switchContext
->numPhysicalNics
--;
1084 hvSwitchPort
= TRUE
;
1088 switch (vport
->ovsType
) {
1089 case OVS_VPORT_TYPE_INTERNAL
:
1090 if (!vport
->isBridgeInternal
) {
1091 switchContext
->internalPortId
= 0;
1092 switchContext
->internalVport
= NULL
;
1093 OvsInternalAdapterDown();
1094 hvSwitchPort
= TRUE
;
1097 case OVS_VPORT_TYPE_VXLAN
:
1098 OvsCleanupVxlanTunnel(vport
);
1099 switchContext
->vxlanVport
= NULL
;
1101 case OVS_VPORT_TYPE_GRE
:
1102 case OVS_VPORT_TYPE_GRE64
:
1104 case OVS_VPORT_TYPE_NETDEV
:
1105 hvSwitchPort
= TRUE
;
1111 * 'hvDelete' == TRUE indicates that the port should be removed from the
1112 * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
1113 * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
1115 * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
1117 if (vport
->isPresentOnHv
== TRUE
) {
1120 if (vport
->portNo
== OVS_DPPORT_NUMBER_INVALID
) {
1121 deletedOnOvs
= TRUE
;
1124 if (hvDelete
&& !deletedOnHv
) {
1125 vport
->isPresentOnHv
= TRUE
;
1127 /* Remove the port from the relevant lists. */
1128 RemoveEntryList(&vport
->portIdLink
);
1129 InitializeListHead(&vport
->portIdLink
);
1132 if (ovsDelete
&& !deletedOnOvs
) {
1133 vport
->portNo
= OVS_DPPORT_NUMBER_INVALID
;
1134 vport
->ovsName
[0] = '\0';
1136 /* Remove the port from the relevant lists. */
1137 RemoveEntryList(&vport
->ovsNameLink
);
1138 InitializeListHead(&vport
->ovsNameLink
);
1139 RemoveEntryList(&vport
->portNoLink
);
1140 InitializeListHead(&vport
->portNoLink
);
1141 deletedOnOvs
= TRUE
;
1145 * Deallocate the port if it has been deleted on the Hyper-V switch as well
1148 if (deletedOnHv
&& deletedOnOvs
) {
1150 switchContext
->numHvVports
--;
1152 switchContext
->numNonHvVports
--;
1154 OvsFreeMemory(vport
);
1155 if (vportDeallocated
) {
1156 *vportDeallocated
= TRUE
;
1162 OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext
)
1164 NDIS_STATUS status
= NDIS_STATUS_SUCCESS
;
1166 PNDIS_SWITCH_PORT_PARAMETERS portParam
;
1167 PNDIS_SWITCH_PORT_ARRAY portArray
= NULL
;
1168 POVS_VPORT_ENTRY vport
;
1170 OVS_LOG_TRACE("Enter: switchContext:%p", switchContext
);
1172 status
= OvsGetPortsOnSwitch(switchContext
, &portArray
);
1173 if (status
!= NDIS_STATUS_SUCCESS
) {
1177 for (arrIndex
= 0; arrIndex
< portArray
->NumElements
; arrIndex
++) {
1178 portParam
= NDIS_SWITCH_PORT_AT_ARRAY_INDEX(portArray
, arrIndex
);
1180 if (portParam
->IsValidationPort
) {
1184 vport
= (POVS_VPORT_ENTRY
)OvsAllocateVport();
1185 if (vport
== NULL
) {
1186 status
= NDIS_STATUS_RESOURCES
;
1189 OvsInitVportWithPortParam(vport
, portParam
);
1190 status
= InitHvVportCommon(switchContext
, vport
, TRUE
);
1191 if (status
!= NDIS_STATUS_SUCCESS
) {
1192 OvsFreeMemory(vport
);
1197 if (status
!= NDIS_STATUS_SUCCESS
) {
1198 OvsClearAllSwitchVports(switchContext
);
1201 if (portArray
!= NULL
) {
1202 OvsFreeMemory(portArray
);
1204 OVS_LOG_TRACE("Exit: status: %x", status
);
1210 OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext
)
1212 NDIS_STATUS status
= NDIS_STATUS_SUCCESS
;
1213 PNDIS_SWITCH_NIC_ARRAY nicArray
= NULL
;
1215 PNDIS_SWITCH_NIC_PARAMETERS nicParam
;
1216 POVS_VPORT_ENTRY vport
;
1218 OVS_LOG_TRACE("Enter: switchContext: %p", switchContext
);
1220 * Now, get NIC list.
1222 status
= OvsGetNicsOnSwitch(switchContext
, &nicArray
);
1223 if (status
!= NDIS_STATUS_SUCCESS
) {
1226 for (arrIndex
= 0; arrIndex
< nicArray
->NumElements
; ++arrIndex
) {
1228 nicParam
= NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray
, arrIndex
);
1231 * XXX: Check if the port is configured with a VLAN. Disallow such a
1232 * configuration, since we don't support tag-in-tag.
1236 * XXX: Check if the port is connected to a VF. Disconnect the VF in
1240 if (nicParam
->NicType
== NdisSwitchNicTypeExternal
&&
1241 nicParam
->NicIndex
!= 0) {
1242 POVS_VPORT_ENTRY virtExtVport
=
1243 (POVS_VPORT_ENTRY
)switchContext
->virtualExternalVport
;
1245 vport
= OvsAllocateVport();
1247 OvsInitPhysNicVport(vport
, virtExtVport
,
1248 nicParam
->NicIndex
);
1249 status
= InitHvVportCommon(switchContext
, vport
, TRUE
);
1250 if (status
!= NDIS_STATUS_SUCCESS
) {
1251 OvsFreeMemory(vport
);
1256 vport
= OvsFindVportByPortIdAndNicIndex(switchContext
,
1258 nicParam
->NicIndex
);
1260 if (vport
== NULL
) {
1261 OVS_LOG_ERROR("Fail to allocate vport");
1264 OvsInitVportWithNicParam(switchContext
, vport
, nicParam
);
1265 if (nicParam
->NicType
== NdisSwitchNicTypeInternal
) {
1266 OvsInternalAdapterUp(vport
->portNo
, &nicParam
->NetCfgInstanceId
);
1271 if (nicArray
!= NULL
) {
1272 OvsFreeMemory(nicArray
);
1274 OVS_LOG_TRACE("Exit: status: %x", status
);
1279 * --------------------------------------------------------------------------
1280 * Deletes ports added from the Hyper-V switch as well as OVS usersapce. The
1281 * function deletes ports in 'portIdHashArray'. This will delete most of the
1282 * ports that are in the 'portNoHashArray' as well. Any remaining ports
1283 * are deleted by walking the the 'portNoHashArray'.
1284 * --------------------------------------------------------------------------
1287 OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext
)
1289 for (UINT hash
= 0; hash
< OVS_MAX_VPORT_ARRAY_SIZE
; hash
++) {
1290 PLIST_ENTRY head
, link
, next
;
1292 head
= &(switchContext
->portIdHashArray
[hash
& OVS_VPORT_MASK
]);
1293 LIST_FORALL_SAFE(head
, link
, next
) {
1294 POVS_VPORT_ENTRY vport
;
1295 vport
= CONTAINING_RECORD(link
, OVS_VPORT_ENTRY
, portIdLink
);
1296 OvsRemoveAndDeleteVport(switchContext
, vport
, TRUE
, TRUE
, NULL
);
1300 * Remove 'virtualExternalVport' as well. This port is not part of the
1301 * 'portIdHashArray'.
1303 if (switchContext
->virtualExternalVport
) {
1304 OvsRemoveAndDeleteVport(switchContext
,
1305 (POVS_VPORT_ENTRY
)switchContext
->virtualExternalVport
, TRUE
, TRUE
,
1309 for (UINT hash
= 0; hash
< OVS_MAX_VPORT_ARRAY_SIZE
; hash
++) {
1310 PLIST_ENTRY head
, link
, next
;
1312 head
= &(switchContext
->portNoHashArray
[hash
& OVS_VPORT_MASK
]);
1313 LIST_FORALL_SAFE(head
, link
, next
) {
1314 POVS_VPORT_ENTRY vport
;
1315 vport
= CONTAINING_RECORD(link
, OVS_VPORT_ENTRY
, portNoLink
);
1316 ASSERT(OvsIsTunnelVportType(vport
->ovsType
) ||
1317 (vport
->ovsType
== OVS_VPORT_TYPE_INTERNAL
&&
1318 vport
->isBridgeInternal
) || vport
->isPresentOnHv
== TRUE
);
1319 OvsRemoveAndDeleteVport(switchContext
, vport
, TRUE
, TRUE
, NULL
);
1323 ASSERT(switchContext
->virtualExternalVport
== NULL
);
1324 ASSERT(switchContext
->internalVport
== NULL
);
1325 ASSERT(switchContext
->vxlanVport
== NULL
);
1330 OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr
,
1335 UNICODE_STRING ustr
;
1339 ustr
.Buffer
= wStr
->String
;
1340 ustr
.Length
= wStr
->Length
;
1341 ustr
.MaximumLength
= IF_MAX_STRING_SIZE
;
1344 astr
.MaximumLength
= maxStrLen
;
1347 size
= RtlUnicodeStringToAnsiSize(&ustr
);
1348 if (size
> maxStrLen
) {
1349 return STATUS_BUFFER_OVERFLOW
;
1352 status
= RtlUnicodeStringToAnsiString(&astr
, &ustr
, FALSE
);
1354 ASSERT(status
== STATUS_SUCCESS
);
1355 if (status
!= STATUS_SUCCESS
) {
1358 ASSERT(astr
.Length
<= maxStrLen
);
1359 str
[astr
.Length
] = 0;
1360 return STATUS_SUCCESS
;
1364 * --------------------------------------------------------------------------
1365 * Utility function that populates a 'OVS_VPORT_EXT_INFO' structure for the
1368 * Assumes that 'gOvsCtrlLock' is held.
1369 * --------------------------------------------------------------------------
1372 OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet
,
1373 POVS_VPORT_EXT_INFO extInfo
)
1375 POVS_VPORT_ENTRY vport
;
1377 LOCK_STATE_EX lockState
;
1378 NTSTATUS status
= STATUS_SUCCESS
;
1379 BOOLEAN doConvert
= FALSE
;
1381 RtlZeroMemory(extInfo
, sizeof (POVS_VPORT_EXT_INFO
));
1382 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
1383 NdisAcquireRWLockRead(gOvsSwitchContext
->dispatchLock
, &lockState
,
1384 NDIS_RWL_AT_DISPATCH_LEVEL
);
1385 if (vportGet
->portNo
== 0) {
1386 StringCbLengthA(vportGet
->name
, OVS_MAX_PORT_NAME_LENGTH
- 1, &len
);
1387 vport
= OvsFindVportByHvNameA(gOvsSwitchContext
, vportGet
->name
);
1388 if (vport
!= NULL
) {
1389 /* If the port is not a Hyper-V port and it has been added earlier,
1390 * we'll find it in 'ovsPortNameHashArray'. */
1391 vport
= OvsFindVportByOvsName(gOvsSwitchContext
, vportGet
->name
);
1394 vport
= OvsFindVportByPortNo(gOvsSwitchContext
, vportGet
->portNo
);
1396 if (vport
== NULL
|| (vport
->ovsState
!= OVS_STATE_CONNECTED
&&
1397 vport
->ovsState
!= OVS_STATE_NIC_CREATED
)) {
1398 NdisReleaseRWLock(gOvsSwitchContext
->dispatchLock
, &lockState
);
1399 if (vportGet
->portNo
) {
1400 OVS_LOG_WARN("vport %u does not exist any more", vportGet
->portNo
);
1402 OVS_LOG_WARN("vport %s does not exist any more", vportGet
->name
);
1404 status
= STATUS_DEVICE_DOES_NOT_EXIST
;
1407 extInfo
->dpNo
= vportGet
->dpNo
;
1408 extInfo
->portNo
= vport
->portNo
;
1409 RtlCopyMemory(extInfo
->macAddress
, vport
->currMacAddress
,
1410 sizeof (vport
->currMacAddress
));
1411 RtlCopyMemory(extInfo
->permMACAddress
, vport
->permMacAddress
,
1412 sizeof (vport
->permMacAddress
));
1413 if (vport
->ovsType
== OVS_VPORT_TYPE_NETDEV
) {
1414 RtlCopyMemory(extInfo
->vmMACAddress
, vport
->vmMacAddress
,
1415 sizeof (vport
->vmMacAddress
));
1417 extInfo
->nicIndex
= vport
->nicIndex
;
1418 extInfo
->portId
= vport
->portId
;
1419 extInfo
->type
= vport
->ovsType
;
1420 extInfo
->mtu
= vport
->mtu
;
1424 if (vport
->ovsState
== OVS_STATE_NIC_CREATED
) {
1425 extInfo
->status
= OVS_EVENT_CONNECT
| OVS_EVENT_LINK_DOWN
;
1426 } else if (vport
->ovsState
== OVS_STATE_CONNECTED
) {
1427 extInfo
->status
= OVS_EVENT_CONNECT
| OVS_EVENT_LINK_UP
;
1429 extInfo
->status
= OVS_EVENT_DISCONNECT
;
1431 if (extInfo
->type
== OVS_VPORT_TYPE_NETDEV
&&
1432 (vport
->ovsState
== OVS_STATE_NIC_CREATED
||
1433 vport
->ovsState
== OVS_STATE_CONNECTED
)) {
1436 extInfo
->vmUUID
[0] = 0;
1437 extInfo
->vifUUID
[0] = 0;
1439 NdisReleaseRWLock(gOvsSwitchContext
->dispatchLock
, &lockState
);
1441 status
= OvsConvertIfCountedStrToAnsiStr(&vport
->portFriendlyName
,
1443 OVS_MAX_PORT_NAME_LENGTH
);
1444 if (status
!= STATUS_SUCCESS
) {
1445 OVS_LOG_INFO("Fail to convert NIC name.");
1446 extInfo
->vmUUID
[0] = 0;
1449 status
= OvsConvertIfCountedStrToAnsiStr(&vport
->vmName
,
1451 OVS_MAX_VM_UUID_LEN
);
1452 if (status
!= STATUS_SUCCESS
) {
1453 OVS_LOG_INFO("Fail to convert VM name.");
1454 extInfo
->vmUUID
[0] = 0;
1457 status
= OvsConvertIfCountedStrToAnsiStr(&vport
->nicName
,
1459 OVS_MAX_VIF_UUID_LEN
);
1460 if (status
!= STATUS_SUCCESS
) {
1461 OVS_LOG_INFO("Fail to convert nic UUID");
1462 extInfo
->vifUUID
[0] = 0;
1465 * for now ignore status
1467 status
= STATUS_SUCCESS
;
1475 * --------------------------------------------------------------------------
1476 * Command Handler for 'OVS_WIN_NETDEV_CMD_GET'.
1477 * --------------------------------------------------------------------------
1480 OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1483 NTSTATUS status
= STATUS_SUCCESS
;
1484 POVS_MESSAGE msgIn
= (POVS_MESSAGE
)usrParamsCtx
->inputBuffer
;
1485 POVS_MESSAGE msgOut
= (POVS_MESSAGE
)usrParamsCtx
->outputBuffer
;
1486 NL_ERROR nlError
= NL_ERROR_SUCCESS
;
1487 OVS_VPORT_GET vportGet
;
1488 OVS_VPORT_EXT_INFO info
;
1490 static const NL_POLICY ovsNetdevPolicy
[] = {
1491 [OVS_WIN_NETDEV_ATTR_NAME
] = { .type
= NL_A_STRING
,
1493 .maxLen
= IFNAMSIZ
},
1495 PNL_ATTR netdevAttrs
[ARRAY_SIZE(ovsNetdevPolicy
)];
1497 /* input buffer has been validated while validating transaction dev op. */
1498 ASSERT(usrParamsCtx
->inputBuffer
!= NULL
&&
1499 usrParamsCtx
->inputLength
> sizeof *msgIn
);
1501 if (msgOut
== NULL
|| usrParamsCtx
->outputLength
< sizeof *msgOut
) {
1502 return STATUS_INVALID_BUFFER_SIZE
;
1505 if (!NlAttrParse((PNL_MSG_HDR
)msgIn
,
1506 NLMSG_HDRLEN
+ GENL_HDRLEN
+ OVS_HDRLEN
,
1507 NlMsgAttrsLen((PNL_MSG_HDR
)msgIn
),
1508 ovsNetdevPolicy
, netdevAttrs
, ARRAY_SIZE(netdevAttrs
))) {
1509 return STATUS_INVALID_PARAMETER
;
1512 OvsAcquireCtrlLock();
1514 vportGet
.portNo
= 0;
1515 RtlCopyMemory(&vportGet
.name
, NlAttrGet(netdevAttrs
[OVS_VPORT_ATTR_NAME
]),
1516 NlAttrGetSize(netdevAttrs
[OVS_VPORT_ATTR_NAME
]));
1518 status
= OvsGetExtInfoIoctl(&vportGet
, &info
);
1519 if (status
== STATUS_DEVICE_DOES_NOT_EXIST
) {
1520 nlError
= NL_ERROR_NODEV
;
1521 OvsReleaseCtrlLock();
1525 status
= CreateNetlinkMesgForNetdev(&info
, msgIn
,
1526 usrParamsCtx
->outputBuffer
, usrParamsCtx
->outputLength
,
1527 gOvsSwitchContext
->dpNo
);
1528 if (status
== STATUS_SUCCESS
) {
1529 *replyLen
= msgOut
->nlMsg
.nlmsgLen
;
1531 OvsReleaseCtrlLock();
1534 if (nlError
!= NL_ERROR_SUCCESS
) {
1535 POVS_MESSAGE_ERROR msgError
= (POVS_MESSAGE_ERROR
)
1536 usrParamsCtx
->outputBuffer
;
1538 NlBuildErrorMsg(msgIn
, msgError
, nlError
);
1539 *replyLen
= msgError
->nlMsg
.nlmsgLen
;
1542 return STATUS_SUCCESS
;
1547 * --------------------------------------------------------------------------
1548 * Utility function to construct an OVS_MESSAGE for the specified vport. The
1549 * OVS_MESSAGE contains the output of a netdev command.
1550 * --------------------------------------------------------------------------
1553 CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info
,
1562 UINT32 netdevFlags
= 0;
1564 NlBufInit(&nlBuffer
, outBuffer
, outBufLen
);
1566 ok
= NlFillOvsMsg(&nlBuffer
, msgIn
->nlMsg
.nlmsgType
, NLM_F_MULTI
,
1567 msgIn
->nlMsg
.nlmsgSeq
, msgIn
->nlMsg
.nlmsgPid
,
1568 msgIn
->genlMsg
.cmd
, msgIn
->genlMsg
.version
,
1571 return STATUS_INVALID_BUFFER_SIZE
;
1574 ok
= NlMsgPutTailU32(&nlBuffer
, OVS_WIN_NETDEV_ATTR_PORT_NO
,
1577 return STATUS_INVALID_BUFFER_SIZE
;
1580 ok
= NlMsgPutTailU32(&nlBuffer
, OVS_WIN_NETDEV_ATTR_TYPE
, info
->type
);
1582 return STATUS_INVALID_BUFFER_SIZE
;
1585 ok
= NlMsgPutTailString(&nlBuffer
, OVS_WIN_NETDEV_ATTR_NAME
,
1588 return STATUS_INVALID_BUFFER_SIZE
;
1591 ok
= NlMsgPutTailUnspec(&nlBuffer
, OVS_WIN_NETDEV_ATTR_MAC_ADDR
,
1592 (PCHAR
)info
->macAddress
, sizeof (info
->macAddress
));
1594 return STATUS_INVALID_BUFFER_SIZE
;
1597 ok
= NlMsgPutTailU32(&nlBuffer
, OVS_WIN_NETDEV_ATTR_MTU
, info
->mtu
);
1599 return STATUS_INVALID_BUFFER_SIZE
;
1602 if (info
->status
!= OVS_EVENT_CONNECT
) {
1603 netdevFlags
= OVS_WIN_NETDEV_IFF_UP
;
1605 ok
= NlMsgPutTailU32(&nlBuffer
, OVS_WIN_NETDEV_ATTR_IF_FLAGS
,
1608 return STATUS_INVALID_BUFFER_SIZE
;
1612 * XXX: add netdev_stats when we have the definition available in the
1616 nlMsg
= (PNL_MSG_HDR
)NlBufAt(&nlBuffer
, 0, 0);
1617 nlMsg
->nlmsgLen
= NlBufSize(&nlBuffer
);
1619 return STATUS_SUCCESS
;
1622 static __inline VOID
1623 OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext
, ULONG sleepMicroSec
)
1625 while ((!switchContext
->isActivated
) &&
1626 (!switchContext
->isActivateFailed
)) {
1627 /* Wait for the switch to be active and
1628 * the list of ports in OVS to be initialized. */
1629 NdisMSleep(sleepMicroSec
);
1634 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport
,
1641 OVS_VPORT_FULL_STATS vportStats
;
1645 NlBufInit(&nlBuffer
, outBuffer
, outBufLen
);
1647 ok
= NlFillOvsMsg(&nlBuffer
, msgIn
->nlMsg
.nlmsgType
, NLM_F_MULTI
,
1648 msgIn
->nlMsg
.nlmsgSeq
, msgIn
->nlMsg
.nlmsgPid
,
1649 msgIn
->genlMsg
.cmd
, msgIn
->genlMsg
.version
,
1652 return STATUS_INVALID_BUFFER_SIZE
;
1655 ok
= NlMsgPutTailU32(&nlBuffer
, OVS_VPORT_ATTR_PORT_NO
, vport
->portNo
);
1657 return STATUS_INVALID_BUFFER_SIZE
;
1660 ok
= NlMsgPutTailU32(&nlBuffer
, OVS_VPORT_ATTR_TYPE
, vport
->ovsType
);
1662 return STATUS_INVALID_BUFFER_SIZE
;
1665 ok
= NlMsgPutTailString(&nlBuffer
, OVS_VPORT_ATTR_NAME
, vport
->ovsName
);
1667 return STATUS_INVALID_BUFFER_SIZE
;
1671 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1672 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1673 * it means we have an array of pids, instead of a single pid.
1674 * ATM we assume we have one pid only.
1677 ok
= NlMsgPutTailU32(&nlBuffer
, OVS_VPORT_ATTR_UPCALL_PID
,
1680 return STATUS_INVALID_BUFFER_SIZE
;
1684 vportStats
.rxPackets
= vport
->stats
.rxPackets
;
1685 vportStats
.rxBytes
= vport
->stats
.rxBytes
;
1686 vportStats
.txPackets
= vport
->stats
.txPackets
;
1687 vportStats
.txBytes
= vport
->stats
.txBytes
;
1688 vportStats
.rxErrors
= vport
->errStats
.rxErrors
;
1689 vportStats
.txErrors
= vport
->errStats
.txErrors
;
1690 vportStats
.rxDropped
= vport
->errStats
.rxDropped
;
1691 vportStats
.txDropped
= vport
->errStats
.txDropped
;
1693 ok
= NlMsgPutTailUnspec(&nlBuffer
, OVS_VPORT_ATTR_STATS
,
1695 sizeof(OVS_VPORT_FULL_STATS
));
1697 return STATUS_INVALID_BUFFER_SIZE
;
1701 * XXX: when vxlan udp dest port becomes configurable, we will also need
1702 * to add vport options
1705 nlMsg
= (PNL_MSG_HDR
)NlBufAt(&nlBuffer
, 0, 0);
1706 nlMsg
->nlmsgLen
= NlBufSize(&nlBuffer
);
1708 return STATUS_SUCCESS
;
1712 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1716 POVS_OPEN_INSTANCE instance
=
1717 (POVS_OPEN_INSTANCE
)usrParamsCtx
->ovsInstance
;
1718 LOCK_STATE_EX lockState
;
1719 UINT32 i
= OVS_MAX_VPORT_ARRAY_SIZE
;
1722 * XXX: this function shares some code with other dump command(s).
1723 * In the future, we will need to refactor the dump functions
1726 ASSERT(usrParamsCtx
->devOp
== OVS_READ_DEV_OP
);
1728 if (instance
->dumpState
.ovsMsg
== NULL
) {
1730 return STATUS_INVALID_DEVICE_STATE
;
1733 /* Output buffer has been validated while validating read dev op. */
1734 ASSERT(usrParamsCtx
->outputBuffer
!= NULL
);
1736 msgIn
= instance
->dumpState
.ovsMsg
;
1738 OvsAcquireCtrlLock();
1741 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1742 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1743 * it means we have an array of pids, instead of a single pid.
1744 * ATM we assume we have one pid only.
1746 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
1747 NdisAcquireRWLockRead(gOvsSwitchContext
->dispatchLock
, &lockState
,
1748 NDIS_RWL_AT_DISPATCH_LEVEL
);
1750 if (gOvsSwitchContext
->numHvVports
> 0 ||
1751 gOvsSwitchContext
->numNonHvVports
> 0) {
1752 /* inBucket: the bucket, used for lookup */
1753 UINT32 inBucket
= instance
->dumpState
.index
[0];
1754 /* inIndex: index within the given bucket, used for lookup */
1755 UINT32 inIndex
= instance
->dumpState
.index
[1];
1756 /* the bucket to be used for the next dump operation */
1757 UINT32 outBucket
= 0;
1758 /* the index within the outBucket to be used for the next dump */
1759 UINT32 outIndex
= 0;
1761 for (i
= inBucket
; i
< OVS_MAX_VPORT_ARRAY_SIZE
; i
++) {
1762 PLIST_ENTRY head
, link
;
1763 head
= &(gOvsSwitchContext
->portNoHashArray
[i
]);
1764 POVS_VPORT_ENTRY vport
= NULL
;
1767 LIST_FORALL(head
, link
) {
1770 * if one or more dumps were previously done on this same bucket,
1771 * inIndex will be > 0, so we'll need to reply with the
1772 * inIndex + 1 vport from the bucket.
1774 if (outIndex
>= inIndex
) {
1775 vport
= CONTAINING_RECORD(link
, OVS_VPORT_ENTRY
, portNoLink
);
1777 ASSERT(vport
->portNo
!= OVS_DPPORT_NUMBER_INVALID
);
1778 OvsCreateMsgFromVport(vport
, msgIn
,
1779 usrParamsCtx
->outputBuffer
,
1780 usrParamsCtx
->outputLength
,
1781 gOvsSwitchContext
->dpNo
);
1794 * if no vport was found above, check the next bucket, beginning
1795 * with the first (i.e. index 0) elem from within that bucket
1802 /* XXX: what about NLMSG_DONE (as msg type)? */
1803 instance
->dumpState
.index
[0] = outBucket
;
1804 instance
->dumpState
.index
[1] = outIndex
;
1807 NdisReleaseRWLock(gOvsSwitchContext
->dispatchLock
, &lockState
);
1809 OvsReleaseCtrlLock();
1811 /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1812 if (i
< OVS_MAX_VPORT_ARRAY_SIZE
) {
1813 POVS_MESSAGE msgOut
= (POVS_MESSAGE
)usrParamsCtx
->outputBuffer
;
1814 *replyLen
= msgOut
->nlMsg
.nlmsgLen
;
1817 * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1821 /* Free up the dump state, since there's no more data to continue. */
1822 FreeUserDumpState(instance
);
1825 return STATUS_SUCCESS
;
1829 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1832 NTSTATUS status
= STATUS_SUCCESS
;
1833 LOCK_STATE_EX lockState
;
1835 POVS_MESSAGE msgIn
= (POVS_MESSAGE
)usrParamsCtx
->inputBuffer
;
1836 POVS_MESSAGE msgOut
= (POVS_MESSAGE
)usrParamsCtx
->outputBuffer
;
1837 POVS_VPORT_ENTRY vport
= NULL
;
1838 NL_ERROR nlError
= NL_ERROR_SUCCESS
;
1839 PCHAR portName
= NULL
;
1840 UINT32 portNameLen
= 0;
1841 UINT32 portNumber
= OVS_DPPORT_NUMBER_INVALID
;
1843 static const NL_POLICY ovsVportPolicy
[] = {
1844 [OVS_VPORT_ATTR_PORT_NO
] = { .type
= NL_A_U32
, .optional
= TRUE
},
1845 [OVS_VPORT_ATTR_NAME
] = { .type
= NL_A_STRING
,
1850 PNL_ATTR vportAttrs
[ARRAY_SIZE(ovsVportPolicy
)];
1852 /* input buffer has been validated while validating write dev op. */
1853 ASSERT(usrParamsCtx
->inputBuffer
!= NULL
);
1855 if (!NlAttrParse((PNL_MSG_HDR
)msgIn
,
1856 NLMSG_HDRLEN
+ GENL_HDRLEN
+ OVS_HDRLEN
,
1857 NlMsgAttrsLen((PNL_MSG_HDR
)msgIn
),
1858 ovsVportPolicy
, vportAttrs
, ARRAY_SIZE(vportAttrs
))) {
1859 return STATUS_INVALID_PARAMETER
;
1862 /* Output buffer has been validated while validating transact dev op. */
1863 ASSERT(msgOut
!= NULL
&& usrParamsCtx
->outputLength
>= sizeof *msgOut
);
1865 NdisAcquireRWLockRead(gOvsSwitchContext
->dispatchLock
, &lockState
, 0);
1866 if (vportAttrs
[OVS_VPORT_ATTR_NAME
] != NULL
) {
1867 portName
= NlAttrGet(vportAttrs
[OVS_VPORT_ATTR_NAME
]);
1868 portNameLen
= NlAttrGetSize(vportAttrs
[OVS_VPORT_ATTR_NAME
]);
1870 /* the port name is expected to be null-terminated */
1871 ASSERT(portName
[portNameLen
- 1] == '\0');
1873 vport
= OvsFindVportByOvsName(gOvsSwitchContext
, portName
);
1874 } else if (vportAttrs
[OVS_VPORT_ATTR_PORT_NO
] != NULL
) {
1875 portNumber
= NlAttrGetU32(vportAttrs
[OVS_VPORT_ATTR_PORT_NO
]);
1877 vport
= OvsFindVportByPortNo(gOvsSwitchContext
, portNumber
);
1879 nlError
= NL_ERROR_INVAL
;
1880 NdisReleaseRWLock(gOvsSwitchContext
->dispatchLock
, &lockState
);
1885 nlError
= NL_ERROR_NODEV
;
1886 NdisReleaseRWLock(gOvsSwitchContext
->dispatchLock
, &lockState
);
1890 status
= OvsCreateMsgFromVport(vport
, msgIn
, usrParamsCtx
->outputBuffer
,
1891 usrParamsCtx
->outputLength
,
1892 gOvsSwitchContext
->dpNo
);
1893 NdisReleaseRWLock(gOvsSwitchContext
->dispatchLock
, &lockState
);
1895 *replyLen
= msgOut
->nlMsg
.nlmsgLen
;
1898 if (nlError
!= NL_ERROR_SUCCESS
) {
1899 POVS_MESSAGE_ERROR msgError
= (POVS_MESSAGE_ERROR
)
1900 usrParamsCtx
->outputBuffer
;
1902 NlBuildErrorMsg(msgIn
, msgError
, nlError
);
1903 *replyLen
= msgError
->nlMsg
.nlmsgLen
;
1906 return STATUS_SUCCESS
;
1910 * --------------------------------------------------------------------------
1911 * Command Handler for 'OVS_VPORT_CMD_NEW'.
1913 * The function handles the initial call to setup the dump state, as well as
1914 * subsequent calls to continue dumping data.
1915 * --------------------------------------------------------------------------
1918 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1923 switch (usrParamsCtx
->devOp
) {
1924 case OVS_WRITE_DEV_OP
:
1925 return OvsSetupDumpStart(usrParamsCtx
);
1927 case OVS_READ_DEV_OP
:
1928 return OvsGetVportDumpNext(usrParamsCtx
, replyLen
);
1930 case OVS_TRANSACTION_DEV_OP
:
1931 return OvsGetVport(usrParamsCtx
, replyLen
);
1934 return STATUS_INVALID_DEVICE_REQUEST
;
1940 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext
)
1942 /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
1943 for (ULONG i
= OVS_DPPORT_NUMBER_LOCAL
+ 1; i
< MAXUINT16
; ++i
) {
1944 POVS_VPORT_ENTRY vport
;
1946 vport
= OvsFindVportByPortNo(switchContext
, i
);
1952 return OVS_DPPORT_NUMBER_INVALID
;
1956 * --------------------------------------------------------------------------
1957 * Command Handler for 'OVS_VPORT_CMD_NEW'.
1958 * --------------------------------------------------------------------------
1961 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
1964 NDIS_STATUS status
= STATUS_SUCCESS
;
1965 LOCK_STATE_EX lockState
;
1967 NL_ERROR nlError
= NL_ERROR_SUCCESS
;
1968 POVS_MESSAGE msgIn
= (POVS_MESSAGE
)usrParamsCtx
->inputBuffer
;
1969 POVS_MESSAGE msgOut
= (POVS_MESSAGE
)usrParamsCtx
->outputBuffer
;
1970 POVS_VPORT_ENTRY vport
= NULL
;
1974 BOOLEAN isBridgeInternal
= FALSE
;
1975 BOOLEAN vportAllocated
= FALSE
, vportInitialized
= FALSE
;
1976 BOOLEAN addInternalPortAsNetdev
= FALSE
;
1978 static const NL_POLICY ovsVportPolicy
[] = {
1979 [OVS_VPORT_ATTR_PORT_NO
] = { .type
= NL_A_U32
, .optional
= TRUE
},
1980 [OVS_VPORT_ATTR_TYPE
] = { .type
= NL_A_U32
, .optional
= FALSE
},
1981 [OVS_VPORT_ATTR_NAME
] = { .type
= NL_A_STRING
, .maxLen
= IFNAMSIZ
,
1983 [OVS_VPORT_ATTR_UPCALL_PID
] = { .type
= NL_A_UNSPEC
,
1984 .optional
= FALSE
},
1985 [OVS_VPORT_ATTR_OPTIONS
] = { .type
= NL_A_NESTED
, .optional
= TRUE
},
1988 PNL_ATTR vportAttrs
[ARRAY_SIZE(ovsVportPolicy
)];
1990 /* input buffer has been validated while validating write dev op. */
1991 ASSERT(usrParamsCtx
->inputBuffer
!= NULL
);
1993 /* Output buffer has been validated while validating transact dev op. */
1994 ASSERT(msgOut
!= NULL
&& usrParamsCtx
->outputLength
>= sizeof *msgOut
);
1996 if (!NlAttrParse((PNL_MSG_HDR
)msgIn
,
1997 NLMSG_HDRLEN
+ GENL_HDRLEN
+ OVS_HDRLEN
,
1998 NlMsgAttrsLen((PNL_MSG_HDR
)msgIn
),
1999 ovsVportPolicy
, vportAttrs
, ARRAY_SIZE(vportAttrs
))) {
2000 return STATUS_INVALID_PARAMETER
;
2003 portName
= NlAttrGet(vportAttrs
[OVS_VPORT_ATTR_NAME
]);
2004 portNameLen
= NlAttrGetSize(vportAttrs
[OVS_VPORT_ATTR_NAME
]);
2005 portType
= NlAttrGetU32(vportAttrs
[OVS_VPORT_ATTR_TYPE
]);
2007 /* we are expecting null terminated strings to be passed */
2008 ASSERT(portName
[portNameLen
- 1] == '\0');
2010 NdisAcquireRWLockWrite(gOvsSwitchContext
->dispatchLock
, &lockState
, 0);
2012 vport
= OvsFindVportByOvsName(gOvsSwitchContext
, portName
);
2014 nlError
= NL_ERROR_EXIST
;
2018 if (portName
&& portType
== OVS_VPORT_TYPE_NETDEV
&&
2019 !strcmp(OVS_DPPORT_INTERNAL_NAME_A
, portName
)) {
2020 addInternalPortAsNetdev
= TRUE
;
2023 if (portName
&& portType
== OVS_VPORT_TYPE_INTERNAL
&&
2024 strcmp(OVS_DPPORT_INTERNAL_NAME_A
, portName
)) {
2025 isBridgeInternal
= TRUE
;
2028 if (portType
== OVS_VPORT_TYPE_INTERNAL
&& !isBridgeInternal
) {
2029 vport
= gOvsSwitchContext
->internalVport
;
2030 } else if (portType
== OVS_VPORT_TYPE_NETDEV
) {
2031 /* External ports can also be looked up like VIF ports. */
2032 vport
= OvsFindVportByHvNameA(gOvsSwitchContext
, portName
);
2034 ASSERT(OvsIsTunnelVportType(portType
) ||
2035 (portType
== OVS_VPORT_TYPE_INTERNAL
&& isBridgeInternal
));
2036 ASSERT(OvsGetTunnelVport(gOvsSwitchContext
, portType
) == NULL
||
2037 !OvsIsTunnelVportType(portType
));
2039 vport
= (POVS_VPORT_ENTRY
)OvsAllocateVport();
2040 if (vport
== NULL
) {
2041 nlError
= NL_ERROR_NOMEM
;
2044 vportAllocated
= TRUE
;
2046 if (OvsIsTunnelVportType(portType
)) {
2047 status
= OvsInitTunnelVport(vport
, portType
, VXLAN_UDP_PORT
);
2048 nlError
= NlMapStatusToNlErr(status
);
2050 OvsInitBridgeInternalVport(vport
);
2052 vportInitialized
= TRUE
;
2054 if (nlError
== NL_ERROR_SUCCESS
) {
2055 vport
->ovsState
= OVS_STATE_CONNECTED
;
2056 vport
->nicState
= NdisSwitchNicStateConnected
;
2059 * Allow the vport to be deleted, because there is no
2060 * corresponding hyper-v switch part.
2062 vport
->isPresentOnHv
= TRUE
;
2067 nlError
= NL_ERROR_INVAL
;
2070 if (vport
->portNo
!= OVS_DPPORT_NUMBER_INVALID
) {
2071 nlError
= NL_ERROR_EXIST
;
2075 /* Initialize the vport with OVS specific properties. */
2076 if (addInternalPortAsNetdev
!= TRUE
) {
2077 vport
->ovsType
= portType
;
2079 if (vportAttrs
[OVS_VPORT_ATTR_PORT_NO
] != NULL
) {
2081 * XXX: when we implement the limit for ovs port number to be
2082 * MAXUINT16, we'll need to check the port number received from the
2085 vport
->portNo
= NlAttrGetU32(vportAttrs
[OVS_VPORT_ATTR_PORT_NO
]);
2087 vport
->portNo
= OvsComputeVportNo(gOvsSwitchContext
);
2088 if (vport
->portNo
== OVS_DPPORT_NUMBER_INVALID
) {
2089 nlError
= NL_ERROR_NOMEM
;
2094 /* The ovs port name must be uninitialized. */
2095 ASSERT(vport
->ovsName
[0] == '\0');
2096 ASSERT(portNameLen
<= OVS_MAX_PORT_NAME_LENGTH
);
2098 RtlCopyMemory(vport
->ovsName
, portName
, portNameLen
);
2099 /* if we don't have options, then vport->portOptions will be NULL */
2100 vport
->portOptions
= vportAttrs
[OVS_VPORT_ATTR_OPTIONS
];
2103 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2104 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2105 * it means we have an array of pids, instead of a single pid.
2106 * ATM we assume we have one pid only.
2108 vport
->upcallPid
= NlAttrGetU32(vportAttrs
[OVS_VPORT_ATTR_UPCALL_PID
]);
2110 status
= InitOvsVportCommon(gOvsSwitchContext
, vport
);
2111 ASSERT(status
== STATUS_SUCCESS
);
2113 status
= OvsCreateMsgFromVport(vport
, msgIn
, usrParamsCtx
->outputBuffer
,
2114 usrParamsCtx
->outputLength
,
2115 gOvsSwitchContext
->dpNo
);
2117 *replyLen
= msgOut
->nlMsg
.nlmsgLen
;
2120 NdisReleaseRWLock(gOvsSwitchContext
->dispatchLock
, &lockState
);
2122 if (nlError
!= NL_ERROR_SUCCESS
) {
2123 POVS_MESSAGE_ERROR msgError
= (POVS_MESSAGE_ERROR
)
2124 usrParamsCtx
->outputBuffer
;
2126 if (vport
&& vportAllocated
== TRUE
) {
2127 if (vportInitialized
== TRUE
) {
2128 if (OvsIsTunnelVportType(portType
)) {
2129 OvsCleanupVxlanTunnel(vport
);
2132 OvsFreeMemory(vport
);
2135 NlBuildErrorMsg(msgIn
, msgError
, nlError
);
2136 *replyLen
= msgError
->nlMsg
.nlmsgLen
;
2139 return STATUS_SUCCESS
;
2144 * --------------------------------------------------------------------------
2145 * Command Handler for 'OVS_VPORT_CMD_SET'.
2146 * --------------------------------------------------------------------------
2149 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
2152 NDIS_STATUS status
= STATUS_SUCCESS
;
2153 LOCK_STATE_EX lockState
;
2155 POVS_MESSAGE msgIn
= (POVS_MESSAGE
)usrParamsCtx
->inputBuffer
;
2156 POVS_MESSAGE msgOut
= (POVS_MESSAGE
)usrParamsCtx
->outputBuffer
;
2157 POVS_VPORT_ENTRY vport
= NULL
;
2158 NL_ERROR nlError
= NL_ERROR_SUCCESS
;
2160 static const NL_POLICY ovsVportPolicy
[] = {
2161 [OVS_VPORT_ATTR_PORT_NO
] = { .type
= NL_A_U32
, .optional
= TRUE
},
2162 [OVS_VPORT_ATTR_TYPE
] = { .type
= NL_A_U32
, .optional
= TRUE
},
2163 [OVS_VPORT_ATTR_NAME
] = { .type
= NL_A_STRING
, .maxLen
= IFNAMSIZ
,
2165 [OVS_VPORT_ATTR_UPCALL_PID
] = { .type
= NL_A_UNSPEC
,
2167 [OVS_VPORT_ATTR_STATS
] = { .type
= NL_A_UNSPEC
,
2168 .minLen
= sizeof(OVS_VPORT_FULL_STATS
),
2169 .maxLen
= sizeof(OVS_VPORT_FULL_STATS
),
2171 [OVS_VPORT_ATTR_OPTIONS
] = { .type
= NL_A_NESTED
, .optional
= TRUE
},
2173 PNL_ATTR vportAttrs
[ARRAY_SIZE(ovsVportPolicy
)];
2175 ASSERT(usrParamsCtx
->inputBuffer
!= NULL
);
2177 if (!NlAttrParse((PNL_MSG_HDR
)msgIn
,
2178 NLMSG_HDRLEN
+ GENL_HDRLEN
+ OVS_HDRLEN
,
2179 NlMsgAttrsLen((PNL_MSG_HDR
)msgIn
),
2180 ovsVportPolicy
, vportAttrs
, ARRAY_SIZE(vportAttrs
))) {
2181 return STATUS_INVALID_PARAMETER
;
2184 /* Output buffer has been validated while validating transact dev op. */
2185 ASSERT(msgOut
!= NULL
&& usrParamsCtx
->outputLength
>= sizeof *msgOut
);
2187 OvsAcquireCtrlLock();
2189 NdisAcquireRWLockWrite(gOvsSwitchContext
->dispatchLock
, &lockState
, 0);
2190 if (vportAttrs
[OVS_VPORT_ATTR_NAME
] != NULL
) {
2191 PSTR portName
= NlAttrGet(vportAttrs
[OVS_VPORT_ATTR_NAME
]);
2193 UINT32 portNameLen
= NlAttrGetSize(vportAttrs
[OVS_VPORT_ATTR_NAME
]);
2195 /* the port name is expected to be null-terminated */
2196 ASSERT(portName
[portNameLen
- 1] == '\0');
2198 vport
= OvsFindVportByOvsName(gOvsSwitchContext
, portName
);
2199 } else if (vportAttrs
[OVS_VPORT_ATTR_PORT_NO
] != NULL
) {
2200 vport
= OvsFindVportByPortNo(gOvsSwitchContext
,
2201 NlAttrGetU32(vportAttrs
[OVS_VPORT_ATTR_PORT_NO
]));
2205 nlError
= NL_ERROR_NODEV
;
2210 * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2211 * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2212 * it means we have an array of pids, instead of a single pid.
2213 * Currently, we support only one pid.
2215 if (vportAttrs
[OVS_VPORT_ATTR_UPCALL_PID
]) {
2216 vport
->upcallPid
= NlAttrGetU32(vportAttrs
[OVS_VPORT_ATTR_UPCALL_PID
]);
2219 if (vportAttrs
[OVS_VPORT_ATTR_TYPE
]) {
2220 OVS_VPORT_TYPE type
= NlAttrGetU32(vportAttrs
[OVS_VPORT_ATTR_TYPE
]);
2221 if (type
!= vport
->ovsType
) {
2222 nlError
= NL_ERROR_INVAL
;
2227 if (vportAttrs
[OVS_VPORT_ATTR_OPTIONS
]) {
2228 OVS_LOG_ERROR("Vport options not supported");
2229 nlError
= NL_ERROR_NOTSUPP
;
2233 status
= OvsCreateMsgFromVport(vport
, msgIn
, usrParamsCtx
->outputBuffer
,
2234 usrParamsCtx
->outputLength
,
2235 gOvsSwitchContext
->dpNo
);
2237 *replyLen
= msgOut
->nlMsg
.nlmsgLen
;
2240 NdisReleaseRWLock(gOvsSwitchContext
->dispatchLock
, &lockState
);
2241 OvsReleaseCtrlLock();
2243 if (nlError
!= NL_ERROR_SUCCESS
) {
2244 POVS_MESSAGE_ERROR msgError
= (POVS_MESSAGE_ERROR
)
2245 usrParamsCtx
->outputBuffer
;
2247 NlBuildErrorMsg(msgIn
, msgError
, nlError
);
2248 *replyLen
= msgError
->nlMsg
.nlmsgLen
;
2251 return STATUS_SUCCESS
;
2255 * --------------------------------------------------------------------------
2256 * Command Handler for 'OVS_VPORT_CMD_DEL'.
2257 * --------------------------------------------------------------------------
2260 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx
,
2263 NDIS_STATUS status
= STATUS_SUCCESS
;
2264 LOCK_STATE_EX lockState
;
2266 POVS_MESSAGE msgIn
= (POVS_MESSAGE
)usrParamsCtx
->inputBuffer
;
2267 POVS_MESSAGE msgOut
= (POVS_MESSAGE
)usrParamsCtx
->outputBuffer
;
2268 POVS_VPORT_ENTRY vport
= NULL
;
2269 NL_ERROR nlError
= NL_ERROR_SUCCESS
;
2270 PSTR portName
= NULL
;
2271 UINT32 portNameLen
= 0;
2273 static const NL_POLICY ovsVportPolicy
[] = {
2274 [OVS_VPORT_ATTR_PORT_NO
] = { .type
= NL_A_U32
, .optional
= TRUE
},
2275 [OVS_VPORT_ATTR_NAME
] = { .type
= NL_A_STRING
, .maxLen
= IFNAMSIZ
,
2278 PNL_ATTR vportAttrs
[ARRAY_SIZE(ovsVportPolicy
)];
2280 ASSERT(usrParamsCtx
->inputBuffer
!= NULL
);
2282 if (!NlAttrParse((PNL_MSG_HDR
)msgIn
,
2283 NLMSG_HDRLEN
+ GENL_HDRLEN
+ OVS_HDRLEN
,
2284 NlMsgAttrsLen((PNL_MSG_HDR
)msgIn
),
2285 ovsVportPolicy
, vportAttrs
, ARRAY_SIZE(vportAttrs
))) {
2286 return STATUS_INVALID_PARAMETER
;
2289 /* Output buffer has been validated while validating transact dev op. */
2290 ASSERT(msgOut
!= NULL
&& usrParamsCtx
->outputLength
>= sizeof *msgOut
);
2292 NdisAcquireRWLockWrite(gOvsSwitchContext
->dispatchLock
, &lockState
, 0);
2293 if (vportAttrs
[OVS_VPORT_ATTR_NAME
] != NULL
) {
2294 portName
= NlAttrGet(vportAttrs
[OVS_VPORT_ATTR_NAME
]);
2295 portNameLen
= NlAttrGetSize(vportAttrs
[OVS_VPORT_ATTR_NAME
]);
2297 /* the port name is expected to be null-terminated */
2298 ASSERT(portName
[portNameLen
- 1] == '\0');
2300 vport
= OvsFindVportByOvsName(gOvsSwitchContext
, portName
);
2302 else if (vportAttrs
[OVS_VPORT_ATTR_PORT_NO
] != NULL
) {
2303 vport
= OvsFindVportByPortNo(gOvsSwitchContext
,
2304 NlAttrGetU32(vportAttrs
[OVS_VPORT_ATTR_PORT_NO
]));
2308 nlError
= NL_ERROR_NODEV
;
2312 status
= OvsCreateMsgFromVport(vport
, msgIn
, usrParamsCtx
->outputBuffer
,
2313 usrParamsCtx
->outputLength
,
2314 gOvsSwitchContext
->dpNo
);
2317 * Mark the port as deleted from OVS userspace. If the port does not exist
2318 * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
2320 OvsRemoveAndDeleteVport(gOvsSwitchContext
, vport
, FALSE
, TRUE
, NULL
);
2322 *replyLen
= msgOut
->nlMsg
.nlmsgLen
;
2325 NdisReleaseRWLock(gOvsSwitchContext
->dispatchLock
, &lockState
);
2327 if (nlError
!= NL_ERROR_SUCCESS
) {
2328 POVS_MESSAGE_ERROR msgError
= (POVS_MESSAGE_ERROR
)
2329 usrParamsCtx
->outputBuffer
;
2331 NlBuildErrorMsg(msgIn
, msgError
, nlError
);
2332 *replyLen
= msgError
->nlMsg
.nlmsgLen
;
2335 return STATUS_SUCCESS
;