Merge "master" into "ovn".
[cascardo/ovs.git] / datapath-windows / ovsext / Vport.c
1 /*
2  * Copyright (c) 2014 VMware, Inc.
3  *
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:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "precomp.h"
18 #include "Jhash.h"
19 #include "Switch.h"
20 #include "Vport.h"
21 #include "Event.h"
22 #include "User.h"
23 #include "Vxlan.h"
24 #include "IpHelper.h"
25 #include "Oid.h"
26 #include "Datapath.h"
27
28 #ifdef OVS_DBG_MOD
29 #undef OVS_DBG_MOD
30 #endif
31 #define OVS_DBG_MOD OVS_DBG_VPORT
32 #include "Debug.h"
33
34 #define VPORT_NIC_ENTER(_nic) \
35     OVS_LOG_TRACE("Enter: PortId: %x, NicIndex: %d", _nic->PortId, \
36                                                      _nic->NicIndex)
37
38 #define VPORT_NIC_EXIT(_nic) \
39     OVS_LOG_TRACE("Exit: PortId: %x, NicIndex: %d", _nic->PortId, \
40                                                     _nic->NicIndex)
41
42 #define VPORT_PORT_ENTER(_port) \
43     OVS_LOG_TRACE("Enter: PortId: %x", _port->PortId)
44
45 #define VPORT_PORT_EXIT(_port) \
46     OVS_LOG_TRACE("Exit: PortId: %x", _port->PortId)
47
48 #define OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC    100
49
50 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
51
52 static VOID OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
53                 PNDIS_SWITCH_PORT_PARAMETERS portParam);
54 static VOID OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
55                 POVS_VPORT_ENTRY vport, PNDIS_SWITCH_NIC_PARAMETERS nicParam);
56 static VOID OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVPort,
57                 POVS_VPORT_ENTRY virtExtVport, UINT32 nicIndex);
58 static __inline VOID OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext,
59                                      ULONG sleepMicroSec);
60 static NTSTATUS OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
61                                    POVS_VPORT_EXT_INFO extInfo);
62 static NTSTATUS CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
63                                            POVS_MESSAGE msgIn,
64                                            PVOID outBuffer,
65                                            UINT32 outBufLen,
66                                            int dpIfIndex);
67 static POVS_VPORT_ENTRY OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
68                                               PWSTR wsName, SIZE_T wstrSize);
69 static NDIS_STATUS InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
70                                      POVS_VPORT_ENTRY vport,
71                                      BOOLEAN newPort);
72
73 /*
74  * Functions implemented in relaton to NDIS port manipulation.
75  */
76 NDIS_STATUS
77 HvCreatePort(POVS_SWITCH_CONTEXT switchContext,
78              PNDIS_SWITCH_PORT_PARAMETERS portParam)
79 {
80     POVS_VPORT_ENTRY vport;
81     LOCK_STATE_EX lockState;
82     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
83     BOOLEAN newPort = FALSE;
84
85     VPORT_PORT_ENTER(portParam);
86
87     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
88     /* Lookup by port ID. */
89     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
90                                             portParam->PortId, 0);
91     if (vport != NULL) {
92         OVS_LOG_ERROR("Port add failed due to duplicate port name, "
93                       "port Id: %u", portParam->PortId);
94         status = STATUS_DATA_NOT_ACCEPTED;
95         goto create_port_done;
96     }
97
98     /*
99      * Lookup by port name to see if this port with this name had been added
100      * (and deleted) previously.
101      */
102     vport = OvsFindVportByHvNameW(gOvsSwitchContext,
103                                   portParam->PortFriendlyName.String,
104                                   portParam->PortFriendlyName.Length);
105     if (vport && vport->isPresentOnHv == FALSE) {
106         OVS_LOG_ERROR("Port add failed since a port already exists on "
107                       "the specified port Id: %u, ovsName: %s",
108                       portParam->PortId, vport->ovsName);
109         status = STATUS_DATA_NOT_ACCEPTED;
110         goto create_port_done;
111     }
112
113     if (vport != NULL) {
114         ASSERT(vport->isPresentOnHv);
115         ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
116
117         /*
118          * It should be possible to simply just mark this port as "not deleted"
119          * given that the port Id and the name are the same and also provided
120          * that the other properties that we cache have not changed.
121          */
122         if (vport->portType != portParam->PortType) {
123             OVS_LOG_INFO("Port add failed due to PortType change, port Id: %u"
124                          " old: %u, new: %u", portParam->PortId,
125                          vport->portType, portParam->PortType);
126             status = STATUS_DATA_NOT_ACCEPTED;
127             goto create_port_done;
128         }
129         vport->isPresentOnHv = FALSE;
130     } else {
131         vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
132         if (vport == NULL) {
133             status = NDIS_STATUS_RESOURCES;
134             goto create_port_done;
135         }
136         newPort = TRUE;
137     }
138     OvsInitVportWithPortParam(vport, portParam);
139     InitHvVportCommon(switchContext, vport, newPort);
140
141 create_port_done:
142     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
143     VPORT_PORT_EXIT(portParam);
144     return status;
145 }
146
147
148 /*
149  * Function updating the port properties
150  */
151 NDIS_STATUS
152 HvUpdatePort(POVS_SWITCH_CONTEXT switchContext,
153              PNDIS_SWITCH_PORT_PARAMETERS portParam)
154 {
155     POVS_VPORT_ENTRY vport;
156     LOCK_STATE_EX lockState;
157     OVS_VPORT_STATE ovsState;
158     NDIS_SWITCH_NIC_STATE nicState;
159
160     VPORT_PORT_ENTER(portParam);
161
162     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
163     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
164                                             portParam->PortId, 0);
165     /*
166      * Update properties only for NETDEV ports for supprting PS script
167      * We don't allow changing the names of the internal or external ports
168      */
169     if (vport == NULL || (( vport->portType != NdisSwitchPortTypeSynthetic) &&
170         ( vport->portType != NdisSwitchPortTypeEmulated))) {
171         goto update_port_done;
172     }
173
174     /* Store the nic and the OVS states as Nic Create won't be called */
175     ovsState = vport->ovsState;
176     nicState = vport->nicState;
177     
178     /*
179      * Currently only the port friendly name is being updated
180      * Make sure that no other properties are changed
181      */
182     ASSERT(portParam->PortId == vport->portId);
183     ASSERT(portParam->PortState == vport->portState);
184     ASSERT(portParam->PortType == vport->portType);
185
186     /*
187      * Call the set parameters function the handle all properties
188      * change in a single place in case future version supports change of
189      * other properties
190      */
191     OvsInitVportWithPortParam(vport, portParam);
192     /* Retore the nic and OVS states */
193     vport->nicState = nicState;
194     vport->ovsState = ovsState;
195
196 update_port_done:
197     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
198     VPORT_PORT_EXIT(portParam);
199
200     /* Must always return success */
201     return NDIS_STATUS_SUCCESS;
202 }
203
204 VOID
205 HvTeardownPort(POVS_SWITCH_CONTEXT switchContext,
206                PNDIS_SWITCH_PORT_PARAMETERS portParam)
207 {
208     POVS_VPORT_ENTRY vport;
209     LOCK_STATE_EX lockState;
210
211     VPORT_PORT_ENTER(portParam);
212
213     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
214     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
215                                             portParam->PortId, 0);
216     if (vport) {
217         /* add assertion here */
218         vport->portState = NdisSwitchPortStateTeardown;
219         vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
220     } else {
221         OVS_LOG_WARN("Vport not present.");
222     }
223     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
224
225     VPORT_PORT_EXIT(portParam);
226 }
227
228
229 VOID
230 HvDeletePort(POVS_SWITCH_CONTEXT switchContext,
231              PNDIS_SWITCH_PORT_PARAMETERS portParams)
232 {
233     POVS_VPORT_ENTRY vport;
234     LOCK_STATE_EX lockState;
235
236     VPORT_PORT_ENTER(portParams);
237
238     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
239     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
240                                             portParams->PortId, 0);
241
242     /*
243      * XXX: we can only destroy and remove the port if its datapath port
244      * counterpart was deleted. If the datapath port counterpart is present,
245      * we only mark the vport for deletion, so that a netlink command vport
246      * delete will delete the vport.
247     */
248     if (vport) {
249         OvsRemoveAndDeleteVport(switchContext, vport, TRUE, FALSE, NULL);
250     } else {
251         OVS_LOG_WARN("Vport not present.");
252     }
253     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
254
255     VPORT_PORT_EXIT(portParams);
256 }
257
258
259 /*
260  * Functions implemented in relaton to NDIS NIC manipulation.
261  */
262 NDIS_STATUS
263 HvCreateNic(POVS_SWITCH_CONTEXT switchContext,
264             PNDIS_SWITCH_NIC_PARAMETERS nicParam)
265 {
266     POVS_VPORT_ENTRY vport;
267     UINT32 portNo = 0;
268     UINT32 event = 0;
269     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
270
271     LOCK_STATE_EX lockState;
272
273     VPORT_NIC_ENTER(nicParam);
274
275     /* Wait for lists to be initialized. */
276     OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
277
278     if (!switchContext->isActivated) {
279         OVS_LOG_WARN("Switch is not activated yet.");
280         /* Veto the creation of nic */
281         status = NDIS_STATUS_NOT_SUPPORTED;
282         goto done;
283     }
284
285     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
286     vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId, 0);
287     if (vport == NULL) {
288         OVS_LOG_ERROR("Create NIC without Switch Port,"
289                       " PortId: %x, NicIndex: %d",
290                       nicParam->PortId, nicParam->NicIndex);
291         status = NDIS_STATUS_INVALID_PARAMETER;
292         goto add_nic_done;
293     }
294
295     if (nicParam->NicType == NdisSwitchNicTypeExternal &&
296         nicParam->NicIndex != 0) {
297         POVS_VPORT_ENTRY virtExtVport =
298             (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
299
300         vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
301         if (vport == NULL) {
302             status = NDIS_STATUS_RESOURCES;
303             goto add_nic_done;
304         }
305         OvsInitPhysNicVport(vport, virtExtVport, nicParam->NicIndex);
306         status = InitHvVportCommon(switchContext, vport, TRUE);
307         if (status != NDIS_STATUS_SUCCESS) {
308             OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
309             goto add_nic_done;
310         }
311     }
312     OvsInitVportWithNicParam(switchContext, vport, nicParam);
313     portNo = vport->portNo;
314     if (vport->ovsState == OVS_STATE_CONNECTED) {
315         event = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
316     } else if (vport->ovsState == OVS_STATE_NIC_CREATED) {
317         event = OVS_EVENT_CONNECT;
318     }
319
320 add_nic_done:
321     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
322     if (portNo != OVS_DPPORT_NUMBER_INVALID && event) {
323         OvsPostEvent(portNo, event);
324     }
325
326 done:
327     VPORT_NIC_EXIT(nicParam);
328     OVS_LOG_TRACE("Exit: status %8x.\n", status);
329
330     return status;
331 }
332
333
334 /* Mark already created NIC as connected. */
335 VOID
336 HvConnectNic(POVS_SWITCH_CONTEXT switchContext,
337              PNDIS_SWITCH_NIC_PARAMETERS nicParam)
338 {
339     LOCK_STATE_EX lockState;
340     POVS_VPORT_ENTRY vport;
341     UINT32 portNo = 0;
342
343     VPORT_NIC_ENTER(nicParam);
344
345     /* Wait for lists to be initialized. */
346     OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
347
348     if (!switchContext->isActivated) {
349         OVS_LOG_WARN("Switch is not activated yet.");
350         goto done;
351     }
352
353     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
354     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
355                                             nicParam->PortId,
356                                             nicParam->NicIndex);
357
358     if (!vport) {
359         OVS_LOG_WARN("Vport not present.");
360         NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
361         ASSERT(0);
362         goto done;
363     }
364
365     vport->ovsState = OVS_STATE_CONNECTED;
366     vport->nicState = NdisSwitchNicStateConnected;
367     portNo = vport->portNo;
368
369     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
370
371     /* XXX only if portNo != INVALID or always? */
372     OvsPostEvent(portNo, OVS_EVENT_LINK_UP);
373
374     if (nicParam->NicType == NdisSwitchNicTypeInternal) {
375         OvsInternalAdapterUp(portNo, &nicParam->NetCfgInstanceId);
376     }
377
378 done:
379     VPORT_NIC_EXIT(nicParam);
380 }
381
382 VOID
383 HvUpdateNic(POVS_SWITCH_CONTEXT switchContext,
384             PNDIS_SWITCH_NIC_PARAMETERS nicParam)
385 {
386     POVS_VPORT_ENTRY vport;
387     LOCK_STATE_EX lockState;
388
389     UINT32 status = 0, portNo = 0;
390
391     VPORT_NIC_ENTER(nicParam);
392
393     /* Wait for lists to be initialized. */
394     OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
395
396     if (!switchContext->isActivated) {
397         OVS_LOG_WARN("Switch is not activated yet.");
398         goto update_nic_done;
399     }
400
401     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
402     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
403                                             nicParam->PortId,
404                                             nicParam->NicIndex);
405     if (vport == NULL) {
406         NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
407         OVS_LOG_WARN("Vport search failed.");
408         goto update_nic_done;
409     }
410     switch (nicParam->NicType) {
411     case NdisSwitchNicTypeExternal:
412     case NdisSwitchNicTypeInternal:
413         RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
414                       sizeof (GUID));
415         break;
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));
423         }
424         break;
425     default:
426         ASSERT(0);
427     }
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;
433     }
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;
439     }
440
441     if (vport->mtu != nicParam->MTU) {
442         vport->mtu = nicParam->MTU;
443         status |= OVS_EVENT_MTU_CHANGE;
444     }
445     vport->numaNodeId = nicParam->NumaNodeId;
446     portNo = vport->portNo;
447
448     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
449     if (status && portNo) {
450         OvsPostEvent(portNo, status);
451     }
452 update_nic_done:
453     VPORT_NIC_EXIT(nicParam);
454 }
455
456
457 VOID
458 HvDisconnectNic(POVS_SWITCH_CONTEXT switchContext,
459                 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
460 {
461     POVS_VPORT_ENTRY vport;
462     UINT32 portNo = 0;
463     LOCK_STATE_EX lockState;
464     BOOLEAN isInternalPort = FALSE;
465
466     VPORT_NIC_ENTER(nicParam);
467
468     /* Wait for lists to be initialized. */
469     OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
470
471     if (!switchContext->isActivated) {
472         OVS_LOG_WARN("Switch is not activated yet.");
473         goto done;
474     }
475
476     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
477     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
478                                             nicParam->PortId,
479                                             nicParam->NicIndex);
480
481     if (!vport) {
482         OVS_LOG_WARN("Vport not present.");
483         NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
484         goto done;
485     }
486
487     vport->nicState = NdisSwitchNicStateDisconnected;
488     vport->ovsState = OVS_STATE_NIC_CREATED;
489     portNo = vport->portNo;
490
491     if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
492         isInternalPort = TRUE;
493     }
494
495     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
496
497     /* XXX if portNo != INVALID or always? */
498     OvsPostEvent(portNo, OVS_EVENT_LINK_DOWN);
499
500     if (isInternalPort) {
501         OvsInternalAdapterDown();
502     }
503
504 done:
505     VPORT_NIC_EXIT(nicParam);
506 }
507
508
509 VOID
510 HvDeleteNic(POVS_SWITCH_CONTEXT switchContext,
511             PNDIS_SWITCH_NIC_PARAMETERS nicParam)
512 {
513     LOCK_STATE_EX lockState;
514     POVS_VPORT_ENTRY vport;
515     UINT32 portNo = 0;
516
517     VPORT_NIC_ENTER(nicParam);
518     /* Wait for lists to be initialized. */
519     OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
520
521     if (!switchContext->isActivated) {
522         OVS_LOG_WARN("Switch is not activated yet.");
523         goto done;
524     }
525
526     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
527     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
528                                             nicParam->PortId,
529                                             nicParam->NicIndex);
530
531     if (!vport) {
532         OVS_LOG_WARN("Vport not present.");
533         NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
534         goto done;
535     }
536
537     portNo = vport->portNo;
538     if (vport->portType == NdisSwitchPortTypeExternal &&
539         vport->nicIndex != 0) {
540         OvsRemoveAndDeleteVport(switchContext, vport, TRUE, FALSE, NULL);
541     }
542     vport->nicState = NdisSwitchNicStateUnknown;
543     vport->ovsState = OVS_STATE_PORT_CREATED;
544
545     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
546     /* XXX if portNo != INVALID or always? */
547     OvsPostEvent(portNo, OVS_EVENT_DISCONNECT);
548
549 done:
550     VPORT_NIC_EXIT(nicParam);
551 }
552
553
554 /*
555  * OVS Vport related functionality.
556  */
557 POVS_VPORT_ENTRY
558 OvsFindVportByPortNo(POVS_SWITCH_CONTEXT switchContext,
559                      UINT32 portNo)
560 {
561     POVS_VPORT_ENTRY vport;
562     PLIST_ENTRY head, link;
563     UINT32 hash = OvsJhashBytes((const VOID *)&portNo, sizeof(portNo),
564                                 OVS_HASH_BASIS);
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) {
569             return vport;
570         }
571     }
572     return NULL;
573 }
574
575
576 POVS_VPORT_ENTRY
577 OvsFindVportByOvsName(POVS_SWITCH_CONTEXT switchContext,
578                       PSTR name)
579 {
580     POVS_VPORT_ENTRY vport;
581     PLIST_ENTRY head, link;
582     UINT32 hash;
583     SIZE_T length = strlen(name) + 1;
584
585     hash = OvsJhashBytes((const VOID *)name, length, OVS_HASH_BASIS);
586     head = &(switchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK]);
587
588     LIST_FORALL(head, link) {
589         vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, ovsNameLink);
590         if (!strcmp(name, vport->ovsName)) {
591             return vport;
592         }
593     }
594
595     return NULL;
596 }
597
598 /* OvsFindVportByHvName: "name" is assumed to be null-terminated */
599 POVS_VPORT_ENTRY
600 OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
601                       PWSTR wsName, SIZE_T wstrSize)
602 {
603     POVS_VPORT_ENTRY vport = NULL;
604     PLIST_ENTRY head, link;
605     UINT i;
606
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);
611
612             /*
613              * NOTE about portFriendlyName:
614              * If the string is NULL-terminated, the Length member does not
615              * include the terminating NULL character.
616              */
617             if (vport->portFriendlyName.Length == wstrSize &&
618                 RtlEqualMemory(wsName, vport->portFriendlyName.String,
619                                vport->portFriendlyName.Length)) {
620                 goto Cleanup;
621             }
622
623             vport = NULL;
624         }
625     }
626
627     /*
628      * Look in the list of ports that were added from the Hyper-V switch and
629      * deleted.
630      */
631     if (vport == NULL) {
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)) {
639                     goto Cleanup;
640                 }
641
642                 vport = NULL;
643             }
644         }
645     }
646
647 Cleanup:
648     return vport;
649 }
650
651 POVS_VPORT_ENTRY
652 OvsFindVportByHvNameA(POVS_SWITCH_CONTEXT switchContext,
653                       PSTR name)
654 {
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);
659     UINT i;
660
661     PWSTR wsName = OvsAllocateMemoryWithTag(wstrSize, OVS_VPORT_POOL_TAG);
662     if (!wsName) {
663         return NULL;
664     }
665     for (i = 0; i < length; i++) {
666         wsName[i] = name[i];
667     }
668     vport = OvsFindVportByHvNameW(switchContext, wsName, wstrSize);
669     OvsFreeMemoryWithTag(wsName, OVS_VPORT_POOL_TAG);
670     return vport;
671 }
672
673 POVS_VPORT_ENTRY
674 OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT switchContext,
675                                 NDIS_SWITCH_PORT_ID portId,
676                                 NDIS_SWITCH_NIC_INDEX index)
677 {
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;
686     } else {
687         PLIST_ENTRY head, link;
688         POVS_VPORT_ENTRY vport;
689         UINT32 hash;
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) {
695                 return vport;
696             }
697         }
698         return NULL;
699     }
700 }
701
702 POVS_VPORT_ENTRY
703 OvsAllocateVport(VOID)
704 {
705     POVS_VPORT_ENTRY vport;
706     vport = (POVS_VPORT_ENTRY)OvsAllocateMemoryWithTag(
707         sizeof(OVS_VPORT_ENTRY), OVS_VPORT_POOL_TAG);
708     if (vport == NULL) {
709         return NULL;
710     }
711     RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY));
712     vport->ovsState = OVS_STATE_UNKNOWN;
713     vport->isPresentOnHv = FALSE;
714     vport->portNo = OVS_DPPORT_NUMBER_INVALID;
715
716     InitializeListHead(&vport->ovsNameLink);
717     InitializeListHead(&vport->portIdLink);
718     InitializeListHead(&vport->portNoLink);
719
720     return vport;
721 }
722
723 static VOID
724 OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
725                           PNDIS_SWITCH_PORT_PARAMETERS portParam)
726 {
727     vport->portType = portParam->PortType;
728     vport->portState = portParam->PortState;
729     vport->portId = portParam->PortId;
730     vport->nicState = NdisSwitchNicStateUnknown;
731     vport->isExternal = FALSE;
732     vport->isBridgeInternal = FALSE;
733
734     switch (vport->portType) {
735     case NdisSwitchPortTypeExternal:
736         vport->isExternal = TRUE;
737         vport->ovsType = OVS_VPORT_TYPE_NETDEV;
738         break;
739     case NdisSwitchPortTypeInternal:
740         vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
741         break;
742     case NdisSwitchPortTypeSynthetic:
743     case NdisSwitchPortTypeEmulated:
744         vport->ovsType = OVS_VPORT_TYPE_NETDEV;
745         break;
746     }
747     RtlCopyMemory(&vport->hvPortName, &portParam->PortName,
748                   sizeof (NDIS_SWITCH_PORT_NAME));
749     /* For external and internal ports, 'portFriendlyName' is overwritten
750      * later. */
751     RtlCopyMemory(&vport->portFriendlyName, &portParam->PortFriendlyName,
752                   sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
753
754     switch (vport->portState) {
755     case NdisSwitchPortStateCreated:
756         vport->ovsState = OVS_STATE_PORT_CREATED;
757         break;
758     case NdisSwitchPortStateTeardown:
759         vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
760         break;
761     case NdisSwitchPortStateDeleted:
762         vport->ovsState = OVS_STATE_PORT_DELETED;
763         break;
764     }
765 }
766
767
768 static VOID
769 OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
770                          POVS_VPORT_ENTRY vport,
771                          PNDIS_SWITCH_NIC_PARAMETERS nicParam)
772 {
773     ASSERT(vport->portId == nicParam->PortId);
774     ASSERT(vport->ovsState == OVS_STATE_PORT_CREATED);
775
776     UNREFERENCED_PARAMETER(switchContext);
777
778     RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
779                   sizeof (nicParam->PermanentMacAddress));
780     RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
781                   sizeof (nicParam->CurrentMacAddress));
782
783     if (nicParam->NicType == NdisSwitchNicTypeSynthetic ||
784         nicParam->NicType == NdisSwitchNicTypeEmulated) {
785         RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
786                       sizeof (nicParam->VMMacAddress));
787         RtlCopyMemory(&vport->vmName, &nicParam->VmName,
788                       sizeof (nicParam->VmName));
789     } else {
790         RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
791                       sizeof (nicParam->NetCfgInstanceId));
792     }
793     RtlCopyMemory(&vport->nicName, &nicParam->NicName,
794                   sizeof (nicParam->NicName));
795     vport->mtu = nicParam->MTU;
796     vport->nicState = nicParam->NicState;
797     vport->nicIndex = nicParam->NicIndex;
798     vport->numaNodeId = nicParam->NumaNodeId;
799
800     switch (vport->nicState) {
801     case NdisSwitchNicStateCreated:
802         vport->ovsState = OVS_STATE_NIC_CREATED;
803         break;
804     case NdisSwitchNicStateConnected:
805         vport->ovsState = OVS_STATE_CONNECTED;
806         break;
807     case NdisSwitchNicStateDisconnected:
808         vport->ovsState = OVS_STATE_NIC_CREATED;
809         break;
810     case NdisSwitchNicStateDeleted:
811         vport->ovsState = OVS_STATE_PORT_CREATED;
812         break;
813     }
814 }
815
816 /*
817  * --------------------------------------------------------------------------
818  * Copies the relevant NDIS port properties from a virtual (pseudo) external
819  * NIC to a physical (real) external NIC.
820  * --------------------------------------------------------------------------
821  */
822 static VOID
823 OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVport,
824                     POVS_VPORT_ENTRY virtExtVport,
825                     UINT32 physNicIndex)
826 {
827     physExtVport->portType = virtExtVport->portType;
828     physExtVport->portState = virtExtVport->portState;
829     physExtVport->portId = virtExtVport->portId;
830     physExtVport->nicState = NdisSwitchNicStateUnknown;
831     physExtVport->ovsType = OVS_VPORT_TYPE_NETDEV;
832     physExtVport->isExternal = TRUE;
833     physExtVport->isBridgeInternal = FALSE;
834     physExtVport->nicIndex = (NDIS_SWITCH_NIC_INDEX)physNicIndex;
835
836     RtlCopyMemory(&physExtVport->hvPortName, &virtExtVport->hvPortName,
837                   sizeof (NDIS_SWITCH_PORT_NAME));
838
839     /* 'portFriendlyName' is overwritten later. */
840     RtlCopyMemory(&physExtVport->portFriendlyName,
841                   &virtExtVport->portFriendlyName,
842                   sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
843
844     physExtVport->ovsState = OVS_STATE_PORT_CREATED;
845 }
846
847 /*
848  * --------------------------------------------------------------------------
849  * Initializes a tunnel vport.
850  * --------------------------------------------------------------------------
851  */
852 NTSTATUS
853 OvsInitTunnelVport(POVS_VPORT_ENTRY vport,
854                    OVS_VPORT_TYPE ovsType,
855                    UINT16 dstPort)
856 {
857     NTSTATUS status = STATUS_SUCCESS;
858
859     vport->isBridgeInternal = FALSE;
860     vport->ovsType = ovsType;
861     vport->ovsState = OVS_STATE_PORT_CREATED;
862     switch (ovsType) {
863     case OVS_VPORT_TYPE_GRE:
864         break;
865     case OVS_VPORT_TYPE_GRE64:
866         break;
867     case OVS_VPORT_TYPE_VXLAN:
868         status = OvsInitVxlanTunnel(vport, dstPort);
869         break;
870     default:
871         ASSERT(0);
872     }
873     return status;
874 }
875
876 /*
877  * --------------------------------------------------------------------------
878  * Initializes a bridge internal vport ie. a port of type
879  * OVS_VPORT_TYPE_INTERNAL but not present on the Hyper-V switch.
880  * --------------------------------------------------------------------------
881  */
882 NTSTATUS
883 OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport)
884 {
885     vport->isBridgeInternal = TRUE;
886     vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
887     /* Mark the status to be connected, since there is no other initialization
888      * for this port. */
889     vport->ovsState = OVS_STATE_CONNECTED;
890     return STATUS_SUCCESS;
891 }
892
893 /*
894  * --------------------------------------------------------------------------
895  * For external vports 'portFriendlyName' provided by Hyper-V is over-written
896  * by synthetic names.
897  * --------------------------------------------------------------------------
898  */
899 static VOID
900 AssignNicNameSpecial(POVS_VPORT_ENTRY vport)
901 {
902     size_t len;
903
904     if (vport->portType == NdisSwitchPortTypeExternal) {
905         if (vport->nicIndex == 0) {
906             ASSERT(vport->nicIndex == 0);
907             RtlStringCbPrintfW(vport->portFriendlyName.String,
908                                IF_MAX_STRING_SIZE,
909                                L"%s.virtualAdapter", OVS_DPPORT_EXTERNAL_NAME_W);
910         } else {
911             RtlStringCbPrintfW(vport->portFriendlyName.String,
912                                IF_MAX_STRING_SIZE,
913                                L"%s.%lu", OVS_DPPORT_EXTERNAL_NAME_W,
914                                (UINT32)vport->nicIndex);
915         }
916     } else {
917         RtlStringCbPrintfW(vport->portFriendlyName.String,
918                            IF_MAX_STRING_SIZE,
919                            L"%s", OVS_DPPORT_INTERNAL_NAME_W);
920     }
921
922     RtlStringCbLengthW(vport->portFriendlyName.String, IF_MAX_STRING_SIZE,
923                        &len);
924     vport->portFriendlyName.Length = (USHORT)len;
925 }
926
927
928 /*
929  * --------------------------------------------------------------------------
930  * Functionality common to any port on the Hyper-V switch. This function is not
931  * to be called for a port that is not on the Hyper-V switch.
932  *
933  * Inserts the port into 'portIdHashArray' and caches the pointer in the
934  * 'switchContext' if needed.
935  *
936  * For external NIC, assigns the name for the NIC.
937  * --------------------------------------------------------------------------
938  */
939 static NDIS_STATUS
940 InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
941                   POVS_VPORT_ENTRY vport,
942                   BOOLEAN newPort)
943 {
944     UINT32 hash;
945
946     switch (vport->portType) {
947     case NdisSwitchPortTypeExternal:
948         /*
949          * Overwrite the 'portFriendlyName' of this external vport. The reason
950          * for having this in common code is to be able to call it from the NDIS
951          * Port callback as well as the NDIS NIC callback.
952          */
953         AssignNicNameSpecial(vport);
954
955         if (vport->nicIndex == 0) {
956             switchContext->virtualExternalPortId = vport->portId;
957             switchContext->virtualExternalVport = vport;
958         } else {
959             switchContext->numPhysicalNics++;
960         }
961         break;
962     case NdisSwitchPortTypeInternal:
963         ASSERT(vport->isBridgeInternal == FALSE);
964
965         /* Overwrite the 'portFriendlyName' of the internal vport. */
966         AssignNicNameSpecial(vport);
967         switchContext->internalPortId = vport->portId;
968         switchContext->internalVport = vport;
969         break;
970     case NdisSwitchPortTypeSynthetic:
971     case NdisSwitchPortTypeEmulated:
972         break;
973     }
974
975     /*
976      * It is important to not insert vport corresponding to virtual external
977      * port into the 'portIdHashArray' since the port should not be exposed to
978      * OVS userspace.
979      */
980     if (vport->portType == NdisSwitchPortTypeExternal &&
981         vport->nicIndex == 0) {
982         return NDIS_STATUS_SUCCESS;
983     }
984
985     /*
986      * NOTE: OvsJhashWords has portId as "1" word. This should be ok, even
987      * though sizeof(NDIS_SWITCH_PORT_ID) = 4, not 2, because the
988      * hyper-v switch seems to use only 2 bytes out of 4.
989      */
990     hash = OvsJhashWords(&vport->portId, 1, OVS_HASH_BASIS);
991     InsertHeadList(&switchContext->portIdHashArray[hash & OVS_VPORT_MASK],
992                    &vport->portIdLink);
993     if (newPort) {
994         switchContext->numHvVports++;
995     }
996     return NDIS_STATUS_SUCCESS;
997 }
998
999 /*
1000  * --------------------------------------------------------------------------
1001  * Functionality common to any port added from OVS userspace.
1002  *
1003  * Inserts the port into 'portIdHashArray', 'ovsPortNameHashArray' and caches
1004  * the pointer in the 'switchContext' if needed.
1005  * --------------------------------------------------------------------------
1006  */
1007 NDIS_STATUS
1008 InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
1009                    POVS_VPORT_ENTRY vport)
1010 {
1011     UINT32 hash;
1012
1013     switch(vport->ovsType) {
1014     case OVS_VPORT_TYPE_VXLAN:
1015         ASSERT(switchContext->vxlanVport == NULL);
1016         switchContext->vxlanVport = vport;
1017         switchContext->numNonHvVports++;
1018         break;
1019     case OVS_VPORT_TYPE_INTERNAL:
1020         if (vport->isBridgeInternal) {
1021             switchContext->numNonHvVports++;
1022         }
1023     default:
1024         break;
1025     }
1026
1027     /*
1028      * Insert the port into the hash array of ports: by port number and ovs
1029      * and ovs (datapath) port name.
1030      * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1031      * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1032      */
1033     hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
1034     InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
1035                    &vport->portNoLink);
1036
1037     hash = OvsJhashBytes(vport->ovsName, strlen(vport->ovsName) + 1,
1038                          OVS_HASH_BASIS);
1039     InsertHeadList(
1040         &gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
1041         &vport->ovsNameLink);
1042
1043     return STATUS_SUCCESS;
1044 }
1045
1046
1047 /*
1048  * --------------------------------------------------------------------------
1049  * Provides functionality that is partly complementatry to
1050  * InitOvsVportCommon()/InitHvVportCommon().
1051  *
1052  * 'hvDelete' indicates if caller is removing the vport as a result of the
1053  * port being removed on the Hyper-V switch.
1054  * 'ovsDelete' indicates if caller is removing the vport as a result of the
1055  * port being removed from OVS userspace.
1056  * --------------------------------------------------------------------------
1057  */
1058 VOID
1059 OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
1060                         POVS_VPORT_ENTRY vport,
1061                         BOOLEAN hvDelete,
1062                         BOOLEAN ovsDelete,
1063                         BOOLEAN *vportDeallocated)
1064 {
1065     BOOLEAN hvSwitchPort = FALSE;
1066     BOOLEAN deletedOnOvs = FALSE, deletedOnHv = FALSE;
1067
1068     if (vportDeallocated) {
1069         *vportDeallocated = FALSE;
1070     }
1071
1072     if (vport->isExternal) {
1073         if (vport->nicIndex == 0) {
1074             ASSERT(switchContext->numPhysicalNics == 0);
1075             switchContext->virtualExternalPortId = 0;
1076             switchContext->virtualExternalVport = NULL;
1077             OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1078             if (vportDeallocated) {
1079                 *vportDeallocated = TRUE;
1080             }
1081             return;
1082         } else {
1083             ASSERT(switchContext->numPhysicalNics);
1084             switchContext->numPhysicalNics--;
1085             hvSwitchPort = TRUE;
1086         }
1087     }
1088
1089     switch (vport->ovsType) {
1090     case OVS_VPORT_TYPE_INTERNAL:
1091         if (!vport->isBridgeInternal) {
1092             switchContext->internalPortId = 0;
1093             switchContext->internalVport = NULL;
1094             OvsInternalAdapterDown();
1095             hvSwitchPort = TRUE;
1096         }
1097         break;
1098     case OVS_VPORT_TYPE_VXLAN:
1099         OvsCleanupVxlanTunnel(vport);
1100         switchContext->vxlanVport = NULL;
1101         break;
1102     case OVS_VPORT_TYPE_GRE:
1103     case OVS_VPORT_TYPE_GRE64:
1104         break;
1105     case OVS_VPORT_TYPE_NETDEV:
1106         hvSwitchPort = TRUE;
1107     default:
1108         break;
1109     }
1110
1111     /*
1112      * 'hvDelete' == TRUE indicates that the port should be removed from the
1113      * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
1114      * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
1115      *
1116      * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
1117      */
1118     if (vport->isPresentOnHv == TRUE) {
1119         deletedOnHv = TRUE;
1120     }
1121     if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1122         deletedOnOvs = TRUE;
1123     }
1124
1125     if (hvDelete && !deletedOnHv) {
1126         vport->isPresentOnHv = TRUE;
1127
1128         /* Remove the port from the relevant lists. */
1129         RemoveEntryList(&vport->portIdLink);
1130         InitializeListHead(&vport->portIdLink);
1131         deletedOnHv = TRUE;
1132     }
1133     if (ovsDelete && !deletedOnOvs) {
1134         vport->portNo = OVS_DPPORT_NUMBER_INVALID;
1135         vport->ovsName[0] = '\0';
1136
1137         /* Remove the port from the relevant lists. */
1138         RemoveEntryList(&vport->ovsNameLink);
1139         InitializeListHead(&vport->ovsNameLink);
1140         RemoveEntryList(&vport->portNoLink);
1141         InitializeListHead(&vport->portNoLink);
1142         deletedOnOvs = TRUE;
1143     }
1144
1145     /*
1146      * Deallocate the port if it has been deleted on the Hyper-V switch as well
1147      * as OVS userspace.
1148      */
1149     if (deletedOnHv && deletedOnOvs) {
1150         if (hvSwitchPort) {
1151             switchContext->numHvVports--;
1152         } else {
1153             switchContext->numNonHvVports--;
1154         }
1155         OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1156         if (vportDeallocated) {
1157             *vportDeallocated = TRUE;
1158         }
1159     }
1160 }
1161
1162 NDIS_STATUS
1163 OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
1164 {
1165     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1166     ULONG arrIndex;
1167     PNDIS_SWITCH_PORT_PARAMETERS portParam;
1168     PNDIS_SWITCH_PORT_ARRAY portArray = NULL;
1169     POVS_VPORT_ENTRY vport;
1170
1171     OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
1172
1173     status = OvsGetPortsOnSwitch(switchContext, &portArray);
1174     if (status != NDIS_STATUS_SUCCESS) {
1175         goto cleanup;
1176     }
1177
1178     for (arrIndex = 0; arrIndex < portArray->NumElements; arrIndex++) {
1179          portParam = NDIS_SWITCH_PORT_AT_ARRAY_INDEX(portArray, arrIndex);
1180
1181          if (portParam->IsValidationPort) {
1182              continue;
1183          }
1184
1185          vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
1186          if (vport == NULL) {
1187              status = NDIS_STATUS_RESOURCES;
1188              goto cleanup;
1189          }
1190          OvsInitVportWithPortParam(vport, portParam);
1191          status = InitHvVportCommon(switchContext, vport, TRUE);
1192          if (status != NDIS_STATUS_SUCCESS) {
1193              OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1194              goto cleanup;
1195          }
1196     }
1197
1198 cleanup:
1199     if (status != NDIS_STATUS_SUCCESS) {
1200         OvsClearAllSwitchVports(switchContext);
1201     }
1202
1203     OvsFreeSwitchPortsArray(portArray);
1204
1205     OVS_LOG_TRACE("Exit: status: %x", status);
1206
1207     return status;
1208 }
1209
1210
1211 NDIS_STATUS
1212 OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext)
1213 {
1214     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1215     PNDIS_SWITCH_NIC_ARRAY nicArray = NULL;
1216     ULONG arrIndex;
1217     PNDIS_SWITCH_NIC_PARAMETERS nicParam;
1218     POVS_VPORT_ENTRY vport;
1219
1220     OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
1221     /*
1222      * Now, get NIC list.
1223      */
1224     status = OvsGetNicsOnSwitch(switchContext, &nicArray);
1225     if (status != NDIS_STATUS_SUCCESS) {
1226         goto cleanup;
1227     }
1228     for (arrIndex = 0; arrIndex < nicArray->NumElements; ++arrIndex) {
1229
1230         nicParam = NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray, arrIndex);
1231
1232         /*
1233          * XXX: Check if the port is configured with a VLAN. Disallow such a
1234          * configuration, since we don't support tag-in-tag.
1235          */
1236
1237         /*
1238          * XXX: Check if the port is connected to a VF. Disconnect the VF in
1239          * such a case.
1240          */
1241
1242         if (nicParam->NicType == NdisSwitchNicTypeExternal &&
1243             nicParam->NicIndex != 0) {
1244             POVS_VPORT_ENTRY virtExtVport =
1245                    (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
1246
1247             vport = OvsAllocateVport();
1248             if (vport) {
1249                 OvsInitPhysNicVport(vport, virtExtVport,
1250                                     nicParam->NicIndex);
1251                 status = InitHvVportCommon(switchContext, vport, TRUE);
1252                 if (status != NDIS_STATUS_SUCCESS) {
1253                     OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1254                     vport = NULL;
1255                 }
1256             }
1257         } else {
1258             vport = OvsFindVportByPortIdAndNicIndex(switchContext,
1259                                                     nicParam->PortId,
1260                                                     nicParam->NicIndex);
1261         }
1262         if (vport == NULL) {
1263             OVS_LOG_ERROR("Fail to allocate vport");
1264             continue;
1265         }
1266         OvsInitVportWithNicParam(switchContext, vport, nicParam);
1267         if (nicParam->NicType == NdisSwitchNicTypeInternal) {
1268             OvsInternalAdapterUp(vport->portNo, &nicParam->NetCfgInstanceId);
1269         }
1270     }
1271 cleanup:
1272
1273     OvsFreeSwitchNicsArray(nicArray);
1274
1275     OVS_LOG_TRACE("Exit: status: %x", status);
1276     return status;
1277 }
1278
1279 /*
1280  * --------------------------------------------------------------------------
1281  * Deletes ports added from the Hyper-V switch as well as OVS usersapce. The
1282  * function deletes ports in 'portIdHashArray'. This will delete most of the
1283  * ports that are in the 'portNoHashArray' as well. Any remaining ports
1284  * are deleted by walking the the 'portNoHashArray'.
1285  * --------------------------------------------------------------------------
1286  */
1287 VOID
1288 OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
1289 {
1290     for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1291         PLIST_ENTRY head, link, next;
1292
1293         head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
1294         LIST_FORALL_SAFE(head, link, next) {
1295             POVS_VPORT_ENTRY vport;
1296             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1297             OvsRemoveAndDeleteVport(switchContext, vport, TRUE, TRUE, NULL);
1298         }
1299     }
1300     /*
1301      * Remove 'virtualExternalVport' as well. This port is not part of the
1302      * 'portIdHashArray'.
1303      */
1304     if (switchContext->virtualExternalVport) {
1305         OvsRemoveAndDeleteVport(switchContext,
1306             (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE,
1307             NULL);
1308     }
1309
1310     for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1311         PLIST_ENTRY head, link, next;
1312
1313         head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
1314         LIST_FORALL_SAFE(head, link, next) {
1315             POVS_VPORT_ENTRY vport;
1316             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1317             ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
1318                    (vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
1319                     vport->isBridgeInternal) || vport->isPresentOnHv == TRUE);
1320             OvsRemoveAndDeleteVport(switchContext, vport, TRUE, TRUE, NULL);
1321         }
1322     }
1323
1324     ASSERT(switchContext->virtualExternalVport == NULL);
1325     ASSERT(switchContext->internalVport == NULL);
1326     ASSERT(switchContext->vxlanVport == NULL);
1327 }
1328
1329
1330 NTSTATUS
1331 OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
1332                                 CHAR *str,
1333                                 UINT16 maxStrLen)
1334 {
1335     ANSI_STRING astr;
1336     UNICODE_STRING ustr;
1337     NTSTATUS status;
1338     UINT32 size;
1339
1340     ustr.Buffer = wStr->String;
1341     ustr.Length = wStr->Length;
1342     ustr.MaximumLength = IF_MAX_STRING_SIZE;
1343
1344     astr.Buffer = str;
1345     astr.MaximumLength = maxStrLen;
1346     astr.Length = 0;
1347
1348     size = RtlUnicodeStringToAnsiSize(&ustr);
1349     if (size > maxStrLen) {
1350         return STATUS_BUFFER_OVERFLOW;
1351     }
1352
1353     status = RtlUnicodeStringToAnsiString(&astr, &ustr, FALSE);
1354
1355     ASSERT(status == STATUS_SUCCESS);
1356     if (status != STATUS_SUCCESS) {
1357         return status;
1358     }
1359     ASSERT(astr.Length <= maxStrLen);
1360     str[astr.Length] = 0;
1361     return STATUS_SUCCESS;
1362 }
1363
1364 /*
1365  * --------------------------------------------------------------------------
1366  * Utility function that populates a 'OVS_VPORT_EXT_INFO' structure for the
1367  * specified vport.
1368  * --------------------------------------------------------------------------
1369  */
1370 NTSTATUS
1371 OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
1372                    POVS_VPORT_EXT_INFO extInfo)
1373 {
1374     POVS_VPORT_ENTRY vport;
1375     size_t len;
1376     LOCK_STATE_EX lockState;
1377     NTSTATUS status = STATUS_SUCCESS;
1378     BOOLEAN doConvert = FALSE;
1379
1380     RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO));
1381     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1382     if (vportGet->portNo == 0) {
1383         StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
1384         vport = OvsFindVportByHvNameA(gOvsSwitchContext, vportGet->name);
1385         if (vport != NULL) {
1386             /* If the port is not a Hyper-V port and it has been added earlier,
1387              * we'll find it in 'ovsPortNameHashArray'. */
1388             vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name);
1389         }
1390     } else {
1391         vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo);
1392     }
1393     if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED &&
1394                           vport->ovsState != OVS_STATE_NIC_CREATED)) {
1395         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1396         if (vportGet->portNo) {
1397             OVS_LOG_WARN("vport %u does not exist any more", vportGet->portNo);
1398         } else {
1399             OVS_LOG_WARN("vport %s does not exist any more", vportGet->name);
1400         }
1401         status = STATUS_DEVICE_DOES_NOT_EXIST;
1402         goto ext_info_done;
1403     }
1404     extInfo->dpNo = vportGet->dpNo;
1405     extInfo->portNo = vport->portNo;
1406     RtlCopyMemory(extInfo->macAddress, vport->currMacAddress,
1407                   sizeof (vport->currMacAddress));
1408     RtlCopyMemory(extInfo->permMACAddress, vport->permMacAddress,
1409                   sizeof (vport->permMacAddress));
1410     if (vport->ovsType == OVS_VPORT_TYPE_NETDEV) {
1411         RtlCopyMemory(extInfo->vmMACAddress, vport->vmMacAddress,
1412                       sizeof (vport->vmMacAddress));
1413     }
1414     extInfo->nicIndex = vport->nicIndex;
1415     extInfo->portId = vport->portId;
1416     extInfo->type = vport->ovsType;
1417     extInfo->mtu = vport->mtu;
1418     /*
1419      * TO be revisit XXX
1420      */
1421     if (vport->ovsState == OVS_STATE_NIC_CREATED) {
1422        extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_DOWN;
1423     } else if (vport->ovsState == OVS_STATE_CONNECTED) {
1424        extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
1425     } else {
1426        extInfo->status = OVS_EVENT_DISCONNECT;
1427     }
1428     if (extInfo->type == OVS_VPORT_TYPE_NETDEV &&
1429         (vport->ovsState == OVS_STATE_NIC_CREATED  ||
1430          vport->ovsState == OVS_STATE_CONNECTED)) {
1431         doConvert = TRUE;
1432     } else {
1433         extInfo->vmUUID[0] = 0;
1434         extInfo->vifUUID[0] = 0;
1435     }
1436     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1437     if (doConvert) {
1438         status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName,
1439                                                  extInfo->name,
1440                                                  OVS_MAX_PORT_NAME_LENGTH);
1441         if (status != STATUS_SUCCESS) {
1442             OVS_LOG_INFO("Fail to convert NIC name.");
1443             extInfo->vmUUID[0] = 0;
1444         }
1445
1446         status = OvsConvertIfCountedStrToAnsiStr(&vport->vmName,
1447                                                  extInfo->vmUUID,
1448                                                  OVS_MAX_VM_UUID_LEN);
1449         if (status != STATUS_SUCCESS) {
1450             OVS_LOG_INFO("Fail to convert VM name.");
1451             extInfo->vmUUID[0] = 0;
1452         }
1453
1454         status = OvsConvertIfCountedStrToAnsiStr(&vport->nicName,
1455                                                  extInfo->vifUUID,
1456                                                  OVS_MAX_VIF_UUID_LEN);
1457         if (status != STATUS_SUCCESS) {
1458             OVS_LOG_INFO("Fail to convert nic UUID");
1459             extInfo->vifUUID[0] = 0;
1460         }
1461         /*
1462          * for now ignore status
1463          */
1464         status = STATUS_SUCCESS;
1465     }
1466
1467 ext_info_done:
1468     return status;
1469 }
1470
1471 /*
1472  * --------------------------------------------------------------------------
1473  *  Command Handler for 'OVS_WIN_NETDEV_CMD_GET'.
1474  * --------------------------------------------------------------------------
1475  */
1476 NTSTATUS
1477 OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1478                        UINT32 *replyLen)
1479 {
1480     NTSTATUS status = STATUS_SUCCESS;
1481     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1482     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1483     NL_ERROR nlError = NL_ERROR_SUCCESS;
1484     OVS_VPORT_GET vportGet;
1485     OVS_VPORT_EXT_INFO info;
1486
1487     static const NL_POLICY ovsNetdevPolicy[] = {
1488         [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING,
1489                                        .minLen = 2,
1490                                        .maxLen = IFNAMSIZ },
1491     };
1492     PNL_ATTR netdevAttrs[ARRAY_SIZE(ovsNetdevPolicy)];
1493
1494     /* input buffer has been validated while validating transaction dev op. */
1495     ASSERT(usrParamsCtx->inputBuffer != NULL &&
1496            usrParamsCtx->inputLength > sizeof *msgIn);
1497
1498     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1499         return STATUS_INVALID_BUFFER_SIZE;
1500     }
1501
1502     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1503         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1504         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1505         ovsNetdevPolicy, netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
1506         return STATUS_INVALID_PARAMETER;
1507     }
1508
1509     vportGet.portNo = 0;
1510     RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
1511                   NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
1512
1513     status = OvsGetExtInfoIoctl(&vportGet, &info);
1514     if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
1515         nlError = NL_ERROR_NODEV;
1516         goto cleanup;
1517     }
1518
1519     status = CreateNetlinkMesgForNetdev(&info, msgIn,
1520                  usrParamsCtx->outputBuffer, usrParamsCtx->outputLength,
1521                  gOvsSwitchContext->dpNo);
1522     if (status == STATUS_SUCCESS) {
1523         *replyLen = msgOut->nlMsg.nlmsgLen;
1524     }
1525
1526 cleanup:
1527     if (nlError != NL_ERROR_SUCCESS) {
1528         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1529             usrParamsCtx->outputBuffer;
1530
1531         NlBuildErrorMsg(msgIn, msgError, nlError);
1532         *replyLen = msgError->nlMsg.nlmsgLen;
1533     }
1534
1535     return STATUS_SUCCESS;
1536 }
1537
1538
1539 /*
1540  * --------------------------------------------------------------------------
1541  *  Utility function to construct an OVS_MESSAGE for the specified vport. The
1542  *  OVS_MESSAGE contains the output of a netdev command.
1543  * --------------------------------------------------------------------------
1544  */
1545 static NTSTATUS
1546 CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
1547                            POVS_MESSAGE msgIn,
1548                            PVOID outBuffer,
1549                            UINT32 outBufLen,
1550                            int dpIfIndex)
1551 {
1552     NL_BUFFER nlBuffer;
1553     BOOLEAN ok;
1554     PNL_MSG_HDR nlMsg;
1555     UINT32 netdevFlags = 0;
1556
1557     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1558
1559     ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1560                       msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1561                       msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1562                       dpIfIndex);
1563     if (!ok) {
1564         return STATUS_INVALID_BUFFER_SIZE;
1565     }
1566
1567     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
1568                          info->portNo);
1569     if (!ok) {
1570         return STATUS_INVALID_BUFFER_SIZE;
1571     }
1572
1573     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
1574     if (!ok) {
1575         return STATUS_INVALID_BUFFER_SIZE;
1576     }
1577
1578     ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
1579                             info->name);
1580     if (!ok) {
1581         return STATUS_INVALID_BUFFER_SIZE;
1582     }
1583
1584     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
1585              (PCHAR)info->macAddress, sizeof (info->macAddress));
1586     if (!ok) {
1587         return STATUS_INVALID_BUFFER_SIZE;
1588     }
1589
1590     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
1591     if (!ok) {
1592         return STATUS_INVALID_BUFFER_SIZE;
1593     }
1594
1595     if (info->status != OVS_EVENT_CONNECT) {
1596         netdevFlags = OVS_WIN_NETDEV_IFF_UP;
1597     }
1598     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
1599                          netdevFlags);
1600     if (!ok) {
1601         return STATUS_INVALID_BUFFER_SIZE;
1602     }
1603
1604     /*
1605      * XXX: add netdev_stats when we have the definition available in the
1606      * kernel.
1607      */
1608
1609     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1610     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1611
1612     return STATUS_SUCCESS;
1613 }
1614
1615 static __inline VOID
1616 OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec)
1617 {
1618     while ((!switchContext->isActivated) &&
1619           (!switchContext->isActivateFailed)) {
1620         /* Wait for the switch to be active and
1621          * the list of ports in OVS to be initialized. */
1622         NdisMSleep(sleepMicroSec);
1623     }
1624 }
1625
1626 static NTSTATUS
1627 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1628                       POVS_MESSAGE msgIn,
1629                       PVOID outBuffer,
1630                       UINT32 outBufLen,
1631                       int dpIfIndex)
1632 {
1633     NL_BUFFER nlBuffer;
1634     OVS_VPORT_FULL_STATS vportStats;
1635     BOOLEAN ok;
1636     PNL_MSG_HDR nlMsg;
1637
1638     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1639
1640     ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1641                       msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1642                       msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1643                       dpIfIndex);
1644     if (!ok) {
1645         return STATUS_INVALID_BUFFER_SIZE;
1646     }
1647
1648     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1649     if (!ok) {
1650         return STATUS_INVALID_BUFFER_SIZE;
1651     }
1652
1653     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1654     if (!ok) {
1655         return STATUS_INVALID_BUFFER_SIZE;
1656     }
1657
1658     ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1659     if (!ok) {
1660         return STATUS_INVALID_BUFFER_SIZE;
1661     }
1662
1663     /*
1664      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1665      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1666      * it means we have an array of pids, instead of a single pid.
1667      * ATM we assume we have one pid only.
1668     */
1669
1670     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1671                          vport->upcallPid);
1672     if (!ok) {
1673         return STATUS_INVALID_BUFFER_SIZE;
1674     }
1675
1676     /*stats*/
1677     vportStats.rxPackets = vport->stats.rxPackets;
1678     vportStats.rxBytes = vport->stats.rxBytes;
1679     vportStats.txPackets = vport->stats.txPackets;
1680     vportStats.txBytes = vport->stats.txBytes;
1681     vportStats.rxErrors = vport->errStats.rxErrors;
1682     vportStats.txErrors = vport->errStats.txErrors;
1683     vportStats.rxDropped = vport->errStats.rxDropped;
1684     vportStats.txDropped = vport->errStats.txDropped;
1685
1686     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1687                             (PCHAR)&vportStats,
1688                             sizeof(OVS_VPORT_FULL_STATS));
1689     if (!ok) {
1690         return STATUS_INVALID_BUFFER_SIZE;
1691     }
1692
1693     /*
1694      * XXX: when vxlan udp dest port becomes configurable, we will also need
1695      * to add vport options
1696     */
1697
1698     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1699     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1700
1701     return STATUS_SUCCESS;
1702 }
1703
1704 static NTSTATUS
1705 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1706                     UINT32 *replyLen)
1707 {
1708     POVS_MESSAGE msgIn;
1709     POVS_OPEN_INSTANCE instance =
1710         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1711     LOCK_STATE_EX lockState;
1712     UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1713
1714     /*
1715      * XXX: this function shares some code with other dump command(s).
1716      * In the future, we will need to refactor the dump functions
1717     */
1718
1719     ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1720
1721     if (instance->dumpState.ovsMsg == NULL) {
1722         ASSERT(FALSE);
1723         return STATUS_INVALID_DEVICE_STATE;
1724     }
1725
1726     /* Output buffer has been validated while validating read dev op. */
1727     ASSERT(usrParamsCtx->outputBuffer != NULL);
1728
1729     msgIn = instance->dumpState.ovsMsg;
1730
1731     /*
1732      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1733      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1734      * it means we have an array of pids, instead of a single pid.
1735      * ATM we assume we have one pid only.
1736     */
1737     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1738
1739     if (gOvsSwitchContext->numHvVports > 0 ||
1740             gOvsSwitchContext->numNonHvVports > 0) {
1741         /* inBucket: the bucket, used for lookup */
1742         UINT32 inBucket = instance->dumpState.index[0];
1743         /* inIndex: index within the given bucket, used for lookup */
1744         UINT32 inIndex = instance->dumpState.index[1];
1745         /* the bucket to be used for the next dump operation */
1746         UINT32 outBucket = 0;
1747         /* the index within the outBucket to be used for the next dump */
1748         UINT32 outIndex = 0;
1749
1750         for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1751             PLIST_ENTRY head, link;
1752             head = &(gOvsSwitchContext->portNoHashArray[i]);
1753             POVS_VPORT_ENTRY vport = NULL;
1754
1755             outIndex = 0;
1756             LIST_FORALL(head, link) {
1757
1758                 /*
1759                  * if one or more dumps were previously done on this same bucket,
1760                  * inIndex will be > 0, so we'll need to reply with the
1761                  * inIndex + 1 vport from the bucket.
1762                 */
1763                 if (outIndex >= inIndex) {
1764                     vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1765
1766                     ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
1767                     OvsCreateMsgFromVport(vport, msgIn,
1768                                           usrParamsCtx->outputBuffer,
1769                                           usrParamsCtx->outputLength,
1770                                           gOvsSwitchContext->dpNo);
1771                     ++outIndex;
1772                     break;
1773                 }
1774
1775                 ++outIndex;
1776             }
1777
1778             if (vport) {
1779                 break;
1780             }
1781
1782             /*
1783              * if no vport was found above, check the next bucket, beginning
1784              * with the first (i.e. index 0) elem from within that bucket
1785             */
1786             inIndex = 0;
1787         }
1788
1789         outBucket = i;
1790
1791         /* XXX: what about NLMSG_DONE (as msg type)? */
1792         instance->dumpState.index[0] = outBucket;
1793         instance->dumpState.index[1] = outIndex;
1794     }
1795
1796     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1797
1798     /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1799     if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1800         POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1801         *replyLen = msgOut->nlMsg.nlmsgLen;
1802     } else {
1803         /*
1804          * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1805          * it's dump done
1806          */
1807         *replyLen = 0;
1808         /* Free up the dump state, since there's no more data to continue. */
1809         FreeUserDumpState(instance);
1810     }
1811
1812     return STATUS_SUCCESS;
1813 }
1814
1815 static NTSTATUS
1816 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1817             UINT32 *replyLen)
1818 {
1819     NTSTATUS status = STATUS_SUCCESS;
1820     LOCK_STATE_EX lockState;
1821
1822     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1823     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1824     POVS_VPORT_ENTRY vport = NULL;
1825     NL_ERROR nlError = NL_ERROR_SUCCESS;
1826     PCHAR portName = NULL;
1827     UINT32 portNameLen = 0;
1828     UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
1829
1830     static const NL_POLICY ovsVportPolicy[] = {
1831         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1832         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1833                                   .minLen = 2,
1834                                   .maxLen = IFNAMSIZ,
1835                                   .optional = TRUE},
1836     };
1837     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1838
1839     /* input buffer has been validated while validating write dev op. */
1840     ASSERT(usrParamsCtx->inputBuffer != NULL);
1841
1842     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1843         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1844         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1845         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1846         return STATUS_INVALID_PARAMETER;
1847     }
1848
1849     /* Output buffer has been validated while validating transact dev op. */
1850     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1851
1852     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1853     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1854         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1855         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1856
1857         /* the port name is expected to be null-terminated */
1858         ASSERT(portName[portNameLen - 1] == '\0');
1859
1860         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1861     } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1862         portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1863
1864         vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
1865     } else {
1866         nlError = NL_ERROR_INVAL;
1867         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1868         goto Cleanup;
1869     }
1870
1871     if (!vport) {
1872         nlError = NL_ERROR_NODEV;
1873         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1874         goto Cleanup;
1875     }
1876
1877     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1878                                    usrParamsCtx->outputLength,
1879                                    gOvsSwitchContext->dpNo);
1880     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1881
1882     *replyLen = msgOut->nlMsg.nlmsgLen;
1883
1884 Cleanup:
1885     if (nlError != NL_ERROR_SUCCESS) {
1886         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1887             usrParamsCtx->outputBuffer;
1888
1889         NlBuildErrorMsg(msgIn, msgError, nlError);
1890         *replyLen = msgError->nlMsg.nlmsgLen;
1891     }
1892
1893     return STATUS_SUCCESS;
1894 }
1895
1896 /*
1897  * --------------------------------------------------------------------------
1898  *  Command Handler for 'OVS_VPORT_CMD_NEW'.
1899  *
1900  *  The function handles the initial call to setup the dump state, as well as
1901  *  subsequent calls to continue dumping data.
1902  * --------------------------------------------------------------------------
1903 */
1904 NTSTATUS
1905 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1906                       UINT32 *replyLen)
1907 {
1908     *replyLen = 0;
1909
1910     switch (usrParamsCtx->devOp) {
1911     case OVS_WRITE_DEV_OP:
1912         return OvsSetupDumpStart(usrParamsCtx);
1913
1914     case OVS_READ_DEV_OP:
1915         return OvsGetVportDumpNext(usrParamsCtx, replyLen);
1916
1917     case OVS_TRANSACTION_DEV_OP:
1918         return OvsGetVport(usrParamsCtx, replyLen);
1919
1920     default:
1921         return STATUS_INVALID_DEVICE_REQUEST;
1922     }
1923
1924 }
1925
1926 static UINT32
1927 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
1928 {
1929     /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
1930     for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
1931         POVS_VPORT_ENTRY vport;
1932
1933         vport = OvsFindVportByPortNo(switchContext, i);
1934         if (!vport) {
1935             return i;
1936         }
1937     }
1938
1939     return OVS_DPPORT_NUMBER_INVALID;
1940 }
1941
1942 /*
1943  * --------------------------------------------------------------------------
1944  *  Command Handler for 'OVS_VPORT_CMD_NEW'.
1945  * --------------------------------------------------------------------------
1946  */
1947 NTSTATUS
1948 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1949                       UINT32 *replyLen)
1950 {
1951     NDIS_STATUS status = STATUS_SUCCESS;
1952     LOCK_STATE_EX lockState;
1953
1954     NL_ERROR nlError = NL_ERROR_SUCCESS;
1955     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1956     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1957     POVS_VPORT_ENTRY vport = NULL;
1958     PCHAR portName;
1959     ULONG portNameLen;
1960     UINT32 portType;
1961     BOOLEAN isBridgeInternal = FALSE;
1962     BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE;
1963     BOOLEAN addInternalPortAsNetdev = FALSE;
1964
1965     static const NL_POLICY ovsVportPolicy[] = {
1966         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1967         [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
1968         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
1969                                   .optional = FALSE},
1970         [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
1971                                         .optional = FALSE },
1972         [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
1973     };
1974
1975     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1976
1977     /* input buffer has been validated while validating write dev op. */
1978     ASSERT(usrParamsCtx->inputBuffer != NULL);
1979
1980     /* Output buffer has been validated while validating transact dev op. */
1981     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1982
1983     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1984         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1985         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1986         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1987         return STATUS_INVALID_PARAMETER;
1988     }
1989
1990     portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1991     portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1992     portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
1993
1994     /* we are expecting null terminated strings to be passed */
1995     ASSERT(portName[portNameLen - 1] == '\0');
1996
1997     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
1998
1999     vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2000     if (vport) {
2001         nlError = NL_ERROR_EXIST;
2002         goto Cleanup;
2003     }
2004
2005     if (portName && portType == OVS_VPORT_TYPE_NETDEV &&
2006         !strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
2007         addInternalPortAsNetdev = TRUE;
2008     }
2009
2010     if (portName && portType == OVS_VPORT_TYPE_INTERNAL &&
2011         strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
2012         isBridgeInternal = TRUE;
2013     }
2014
2015     if (portType == OVS_VPORT_TYPE_INTERNAL && !isBridgeInternal) {
2016         vport = gOvsSwitchContext->internalVport;
2017     } else if (portType == OVS_VPORT_TYPE_NETDEV) {
2018         /* External ports can also be looked up like VIF ports. */
2019         vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
2020     } else {
2021         ASSERT(OvsIsTunnelVportType(portType) ||
2022                (portType == OVS_VPORT_TYPE_INTERNAL && isBridgeInternal));
2023         ASSERT(OvsGetTunnelVport(gOvsSwitchContext, portType) == NULL ||
2024                !OvsIsTunnelVportType(portType));
2025
2026         vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
2027         if (vport == NULL) {
2028             nlError = NL_ERROR_NOMEM;
2029             goto Cleanup;
2030         }
2031         vportAllocated = TRUE;
2032
2033         if (OvsIsTunnelVportType(portType)) {
2034             status = OvsInitTunnelVport(vport, portType, VXLAN_UDP_PORT);
2035             nlError = NlMapStatusToNlErr(status);
2036         } else {
2037             OvsInitBridgeInternalVport(vport);
2038         }
2039         vportInitialized = TRUE;
2040
2041         if (nlError == NL_ERROR_SUCCESS) {
2042             vport->ovsState = OVS_STATE_CONNECTED;
2043             vport->nicState = NdisSwitchNicStateConnected;
2044
2045             /*
2046              * Allow the vport to be deleted, because there is no
2047              * corresponding hyper-v switch part.
2048              */
2049             vport->isPresentOnHv = TRUE;
2050         }
2051     }
2052
2053     if (!vport) {
2054         nlError = NL_ERROR_INVAL;
2055         goto Cleanup;
2056     }
2057     if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2058         nlError = NL_ERROR_EXIST;
2059         goto Cleanup;
2060     }
2061
2062     /* Initialize the vport with OVS specific properties. */
2063     if (addInternalPortAsNetdev != TRUE) {
2064         vport->ovsType = portType;
2065     }
2066     if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2067         /*
2068          * XXX: when we implement the limit for ovs port number to be
2069          * MAXUINT16, we'll need to check the port number received from the
2070          * userspace.
2071          */
2072         vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2073     } else {
2074         vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
2075         if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2076             nlError = NL_ERROR_NOMEM;
2077             goto Cleanup;
2078         }
2079     }
2080
2081     /* The ovs port name must be uninitialized. */
2082     ASSERT(vport->ovsName[0] == '\0');
2083     ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2084
2085     RtlCopyMemory(vport->ovsName, portName, portNameLen);
2086     /* if we don't have options, then vport->portOptions will be NULL */
2087     vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2088
2089     /*
2090      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2091      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2092      * it means we have an array of pids, instead of a single pid.
2093      * ATM we assume we have one pid only.
2094      */
2095     vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2096
2097     status = InitOvsVportCommon(gOvsSwitchContext, vport);
2098     ASSERT(status == STATUS_SUCCESS);
2099
2100     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2101                                    usrParamsCtx->outputLength,
2102                                    gOvsSwitchContext->dpNo);
2103
2104     *replyLen = msgOut->nlMsg.nlmsgLen;
2105
2106 Cleanup:
2107     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2108
2109     if (nlError != NL_ERROR_SUCCESS) {
2110         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2111             usrParamsCtx->outputBuffer;
2112
2113         if (vport && vportAllocated == TRUE) {
2114             if (vportInitialized == TRUE) {
2115                 if (OvsIsTunnelVportType(portType)) {
2116                     OvsCleanupVxlanTunnel(vport);
2117                 }
2118             }
2119             OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2120         }
2121
2122         NlBuildErrorMsg(msgIn, msgError, nlError);
2123         *replyLen = msgError->nlMsg.nlmsgLen;
2124     }
2125
2126     return STATUS_SUCCESS;
2127 }
2128
2129
2130 /*
2131  * --------------------------------------------------------------------------
2132  *  Command Handler for 'OVS_VPORT_CMD_SET'.
2133  * --------------------------------------------------------------------------
2134  */
2135 NTSTATUS
2136 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2137                       UINT32 *replyLen)
2138 {
2139     NDIS_STATUS status = STATUS_SUCCESS;
2140     LOCK_STATE_EX lockState;
2141
2142     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2143     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2144     POVS_VPORT_ENTRY vport = NULL;
2145     NL_ERROR nlError = NL_ERROR_SUCCESS;
2146
2147     static const NL_POLICY ovsVportPolicy[] = {
2148         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2149         [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
2150         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2151                                   .optional = TRUE },
2152         [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2153                                         .optional = TRUE },
2154         [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
2155                                    .minLen = sizeof(OVS_VPORT_FULL_STATS),
2156                                    .maxLen = sizeof(OVS_VPORT_FULL_STATS),
2157                                    .optional = TRUE },
2158         [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2159     };
2160     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2161
2162     ASSERT(usrParamsCtx->inputBuffer != NULL);
2163
2164     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2165         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2166         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2167         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2168         return STATUS_INVALID_PARAMETER;
2169     }
2170
2171     /* Output buffer has been validated while validating transact dev op. */
2172     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2173
2174     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2175     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2176         PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2177 #ifdef DBG
2178         UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2179 #endif
2180         /* the port name is expected to be null-terminated */
2181         ASSERT(portName[portNameLen - 1] == '\0');
2182
2183         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2184     } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2185         vport = OvsFindVportByPortNo(gOvsSwitchContext,
2186                     NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2187     }
2188
2189     if (!vport) {
2190         nlError = NL_ERROR_NODEV;
2191         goto Cleanup;
2192     }
2193
2194     /*
2195      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2196      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2197      * it means we have an array of pids, instead of a single pid.
2198      * Currently, we support only one pid.
2199      */
2200     if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
2201         vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2202     }
2203
2204     if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
2205         OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2206         if (type != vport->ovsType) {
2207             nlError = NL_ERROR_INVAL;
2208             goto Cleanup;
2209         }
2210     }
2211
2212     if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2213         OVS_LOG_ERROR("Vport options not supported");
2214         nlError = NL_ERROR_NOTSUPP;
2215         goto Cleanup;
2216     }
2217
2218     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2219                                    usrParamsCtx->outputLength,
2220                                    gOvsSwitchContext->dpNo);
2221
2222     *replyLen = msgOut->nlMsg.nlmsgLen;
2223
2224 Cleanup:
2225     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2226
2227     if (nlError != NL_ERROR_SUCCESS) {
2228         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2229             usrParamsCtx->outputBuffer;
2230
2231         NlBuildErrorMsg(msgIn, msgError, nlError);
2232         *replyLen = msgError->nlMsg.nlmsgLen;
2233     }
2234
2235     return STATUS_SUCCESS;
2236 }
2237
2238 /*
2239  * --------------------------------------------------------------------------
2240  *  Command Handler for 'OVS_VPORT_CMD_DEL'.
2241  * --------------------------------------------------------------------------
2242  */
2243 NTSTATUS
2244 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2245                          UINT32 *replyLen)
2246 {
2247     NDIS_STATUS status = STATUS_SUCCESS;
2248     LOCK_STATE_EX lockState;
2249
2250     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2251     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2252     POVS_VPORT_ENTRY vport = NULL;
2253     NL_ERROR nlError = NL_ERROR_SUCCESS;
2254     PSTR portName = NULL;
2255     UINT32 portNameLen = 0;
2256
2257     static const NL_POLICY ovsVportPolicy[] = {
2258         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2259         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2260                                   .optional = TRUE },
2261     };
2262     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2263
2264     ASSERT(usrParamsCtx->inputBuffer != NULL);
2265
2266     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2267         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2268         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2269         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2270         return STATUS_INVALID_PARAMETER;
2271     }
2272
2273     /* Output buffer has been validated while validating transact dev op. */
2274     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2275
2276     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2277     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2278         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2279         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2280
2281         /* the port name is expected to be null-terminated */
2282         ASSERT(portName[portNameLen - 1] == '\0');
2283
2284         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2285     }
2286     else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2287         vport = OvsFindVportByPortNo(gOvsSwitchContext,
2288             NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2289     }
2290
2291     if (!vport) {
2292         nlError = NL_ERROR_NODEV;
2293         goto Cleanup;
2294     }
2295
2296     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2297                                    usrParamsCtx->outputLength,
2298                                    gOvsSwitchContext->dpNo);
2299
2300     /*
2301      * Mark the port as deleted from OVS userspace. If the port does not exist
2302      * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
2303      */
2304     OvsRemoveAndDeleteVport(gOvsSwitchContext, vport, FALSE, TRUE, NULL);
2305
2306     *replyLen = msgOut->nlMsg.nlmsgLen;
2307
2308 Cleanup:
2309     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2310
2311     if (nlError != NL_ERROR_SUCCESS) {
2312         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2313             usrParamsCtx->outputBuffer;
2314
2315         NlBuildErrorMsg(msgIn, msgError, nlError);
2316         *replyLen = msgError->nlMsg.nlmsgLen;
2317     }
2318
2319     return STATUS_SUCCESS;
2320 }