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