datapath-windows: refactor BuildReplyMsgFromMsgIn & BuildErrorMsg
[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             OvsFreeMemory(vport);
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         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 = OvsAllocateMemory(wstrSize);
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     OvsFreeMemory(wsName);
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)OvsAllocateMemory(sizeof (OVS_VPORT_ENTRY));
707     if (vport == NULL) {
708         return NULL;
709     }
710     RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY));
711     vport->ovsState = OVS_STATE_UNKNOWN;
712     vport->isPresentOnHv = FALSE;
713     vport->portNo = OVS_DPPORT_NUMBER_INVALID;
714
715     InitializeListHead(&vport->ovsNameLink);
716     InitializeListHead(&vport->portIdLink);
717     InitializeListHead(&vport->portNoLink);
718
719     return vport;
720 }
721
722 static VOID
723 OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
724                           PNDIS_SWITCH_PORT_PARAMETERS portParam)
725 {
726     vport->portType = portParam->PortType;
727     vport->portState = portParam->PortState;
728     vport->portId = portParam->PortId;
729     vport->nicState = NdisSwitchNicStateUnknown;
730     vport->isExternal = FALSE;
731     vport->isBridgeInternal = FALSE;
732
733     switch (vport->portType) {
734     case NdisSwitchPortTypeExternal:
735         vport->isExternal = TRUE;
736         vport->ovsType = OVS_VPORT_TYPE_NETDEV;
737         break;
738     case NdisSwitchPortTypeInternal:
739         vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
740         break;
741     case NdisSwitchPortTypeSynthetic:
742     case NdisSwitchPortTypeEmulated:
743         vport->ovsType = OVS_VPORT_TYPE_NETDEV;
744         break;
745     }
746     RtlCopyMemory(&vport->hvPortName, &portParam->PortName,
747                   sizeof (NDIS_SWITCH_PORT_NAME));
748     /* For external and internal ports, 'portFriendlyName' is overwritten
749      * later. */
750     RtlCopyMemory(&vport->portFriendlyName, &portParam->PortFriendlyName,
751                   sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
752
753     switch (vport->portState) {
754     case NdisSwitchPortStateCreated:
755         vport->ovsState = OVS_STATE_PORT_CREATED;
756         break;
757     case NdisSwitchPortStateTeardown:
758         vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
759         break;
760     case NdisSwitchPortStateDeleted:
761         vport->ovsState = OVS_STATE_PORT_DELETED;
762         break;
763     }
764 }
765
766
767 static VOID
768 OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
769                          POVS_VPORT_ENTRY vport,
770                          PNDIS_SWITCH_NIC_PARAMETERS nicParam)
771 {
772     ASSERT(vport->portId == nicParam->PortId);
773     ASSERT(vport->ovsState == OVS_STATE_PORT_CREATED);
774
775     UNREFERENCED_PARAMETER(switchContext);
776
777     RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
778                   sizeof (nicParam->PermanentMacAddress));
779     RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
780                   sizeof (nicParam->CurrentMacAddress));
781
782     if (nicParam->NicType == NdisSwitchNicTypeSynthetic ||
783         nicParam->NicType == NdisSwitchNicTypeEmulated) {
784         RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
785                       sizeof (nicParam->VMMacAddress));
786         RtlCopyMemory(&vport->vmName, &nicParam->VmName,
787                       sizeof (nicParam->VmName));
788     } else {
789         RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
790                       sizeof (nicParam->NetCfgInstanceId));
791     }
792     RtlCopyMemory(&vport->nicName, &nicParam->NicName,
793                   sizeof (nicParam->NicName));
794     vport->mtu = nicParam->MTU;
795     vport->nicState = nicParam->NicState;
796     vport->nicIndex = nicParam->NicIndex;
797     vport->numaNodeId = nicParam->NumaNodeId;
798
799     switch (vport->nicState) {
800     case NdisSwitchNicStateCreated:
801         vport->ovsState = OVS_STATE_NIC_CREATED;
802         break;
803     case NdisSwitchNicStateConnected:
804         vport->ovsState = OVS_STATE_CONNECTED;
805         break;
806     case NdisSwitchNicStateDisconnected:
807         vport->ovsState = OVS_STATE_NIC_CREATED;
808         break;
809     case NdisSwitchNicStateDeleted:
810         vport->ovsState = OVS_STATE_PORT_CREATED;
811         break;
812     }
813 }
814
815 /*
816  * --------------------------------------------------------------------------
817  * Copies the relevant NDIS port properties from a virtual (pseudo) external
818  * NIC to a physical (real) external NIC.
819  * --------------------------------------------------------------------------
820  */
821 static VOID
822 OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVport,
823                     POVS_VPORT_ENTRY virtExtVport,
824                     UINT32 physNicIndex)
825 {
826     physExtVport->portType = virtExtVport->portType;
827     physExtVport->portState = virtExtVport->portState;
828     physExtVport->portId = virtExtVport->portId;
829     physExtVport->nicState = NdisSwitchNicStateUnknown;
830     physExtVport->ovsType = OVS_VPORT_TYPE_NETDEV;
831     physExtVport->isExternal = TRUE;
832     physExtVport->isBridgeInternal = FALSE;
833     physExtVport->nicIndex = (NDIS_SWITCH_NIC_INDEX)physNicIndex;
834
835     RtlCopyMemory(&physExtVport->hvPortName, &virtExtVport->hvPortName,
836                   sizeof (NDIS_SWITCH_PORT_NAME));
837
838     /* 'portFriendlyName' is overwritten later. */
839     RtlCopyMemory(&physExtVport->portFriendlyName,
840                   &virtExtVport->portFriendlyName,
841                   sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
842
843     physExtVport->ovsState = OVS_STATE_PORT_CREATED;
844 }
845
846 /*
847  * --------------------------------------------------------------------------
848  * Initializes a tunnel vport.
849  * --------------------------------------------------------------------------
850  */
851 NTSTATUS
852 OvsInitTunnelVport(POVS_VPORT_ENTRY vport,
853                    OVS_VPORT_TYPE ovsType,
854                    UINT16 dstPort)
855 {
856     NTSTATUS status = STATUS_SUCCESS;
857
858     vport->isBridgeInternal = FALSE;
859     vport->ovsType = ovsType;
860     vport->ovsState = OVS_STATE_PORT_CREATED;
861     switch (ovsType) {
862     case OVS_VPORT_TYPE_GRE:
863         break;
864     case OVS_VPORT_TYPE_GRE64:
865         break;
866     case OVS_VPORT_TYPE_VXLAN:
867         status = OvsInitVxlanTunnel(vport, dstPort);
868         break;
869     default:
870         ASSERT(0);
871     }
872     return status;
873 }
874
875 /*
876  * --------------------------------------------------------------------------
877  * Initializes a bridge internal vport ie. a port of type
878  * OVS_VPORT_TYPE_INTERNAL but not present on the Hyper-V switch.
879  * --------------------------------------------------------------------------
880  */
881 NTSTATUS
882 OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport)
883 {
884     vport->isBridgeInternal = TRUE;
885     vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
886     /* Mark the status to be connected, since there is no other initialization
887      * for this port. */
888     vport->ovsState = OVS_STATE_CONNECTED;
889     return STATUS_SUCCESS;
890 }
891
892 /*
893  * --------------------------------------------------------------------------
894  * For external vports 'portFriendlyName' provided by Hyper-V is over-written
895  * by synthetic names.
896  * --------------------------------------------------------------------------
897  */
898 static VOID
899 AssignNicNameSpecial(POVS_VPORT_ENTRY vport)
900 {
901     size_t len;
902
903     if (vport->portType == NdisSwitchPortTypeExternal) {
904         if (vport->nicIndex == 0) {
905             ASSERT(vport->nicIndex == 0);
906             RtlStringCbPrintfW(vport->portFriendlyName.String,
907                                IF_MAX_STRING_SIZE,
908                                L"%s.virtualAdapter", OVS_DPPORT_EXTERNAL_NAME_W);
909         } else {
910             RtlStringCbPrintfW(vport->portFriendlyName.String,
911                                IF_MAX_STRING_SIZE,
912                                L"%s.%lu", OVS_DPPORT_EXTERNAL_NAME_W,
913                                (UINT32)vport->nicIndex);
914         }
915     } else {
916         RtlStringCbPrintfW(vport->portFriendlyName.String,
917                            IF_MAX_STRING_SIZE,
918                            L"%s", OVS_DPPORT_INTERNAL_NAME_W);
919     }
920
921     RtlStringCbLengthW(vport->portFriendlyName.String, IF_MAX_STRING_SIZE,
922                        &len);
923     vport->portFriendlyName.Length = (USHORT)len;
924 }
925
926
927 /*
928  * --------------------------------------------------------------------------
929  * Functionality common to any port on the Hyper-V switch. This function is not
930  * to be called for a port that is not on the Hyper-V switch.
931  *
932  * Inserts the port into 'portIdHashArray' and caches the pointer in the
933  * 'switchContext' if needed.
934  *
935  * For external NIC, assigns the name for the NIC.
936  * --------------------------------------------------------------------------
937  */
938 static NDIS_STATUS
939 InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
940                   POVS_VPORT_ENTRY vport,
941                   BOOLEAN newPort)
942 {
943     UINT32 hash;
944
945     switch (vport->portType) {
946     case NdisSwitchPortTypeExternal:
947         /*
948          * Overwrite the 'portFriendlyName' of this external vport. The reason
949          * for having this in common code is to be able to call it from the NDIS
950          * Port callback as well as the NDIS NIC callback.
951          */
952         AssignNicNameSpecial(vport);
953
954         if (vport->nicIndex == 0) {
955             switchContext->virtualExternalPortId = vport->portId;
956             switchContext->virtualExternalVport = vport;
957         } else {
958             switchContext->numPhysicalNics++;
959         }
960         break;
961     case NdisSwitchPortTypeInternal:
962         ASSERT(vport->isBridgeInternal == FALSE);
963
964         /* Overwrite the 'portFriendlyName' of the internal vport. */
965         AssignNicNameSpecial(vport);
966         switchContext->internalPortId = vport->portId;
967         switchContext->internalVport = vport;
968         break;
969     case NdisSwitchPortTypeSynthetic:
970     case NdisSwitchPortTypeEmulated:
971         break;
972     }
973
974     /*
975      * It is important to not insert vport corresponding to virtual external
976      * port into the 'portIdHashArray' since the port should not be exposed to
977      * OVS userspace.
978      */
979     if (vport->portType == NdisSwitchPortTypeExternal &&
980         vport->nicIndex == 0) {
981         return NDIS_STATUS_SUCCESS;
982     }
983
984     /*
985      * NOTE: OvsJhashWords has portId as "1" word. This should be ok, even
986      * though sizeof(NDIS_SWITCH_PORT_ID) = 4, not 2, because the
987      * hyper-v switch seems to use only 2 bytes out of 4.
988      */
989     hash = OvsJhashWords(&vport->portId, 1, OVS_HASH_BASIS);
990     InsertHeadList(&switchContext->portIdHashArray[hash & OVS_VPORT_MASK],
991                    &vport->portIdLink);
992     if (newPort) {
993         switchContext->numHvVports++;
994     }
995     return NDIS_STATUS_SUCCESS;
996 }
997
998 /*
999  * --------------------------------------------------------------------------
1000  * Functionality common to any port added from OVS userspace.
1001  *
1002  * Inserts the port into 'portIdHashArray', 'ovsPortNameHashArray' and caches
1003  * the pointer in the 'switchContext' if needed.
1004  * --------------------------------------------------------------------------
1005  */
1006 NDIS_STATUS
1007 InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
1008                    POVS_VPORT_ENTRY vport)
1009 {
1010     UINT32 hash;
1011
1012     switch(vport->ovsType) {
1013     case OVS_VPORT_TYPE_VXLAN:
1014         ASSERT(switchContext->vxlanVport == NULL);
1015         switchContext->vxlanVport = vport;
1016         switchContext->numNonHvVports++;
1017         break;
1018     case OVS_VPORT_TYPE_INTERNAL:
1019         if (vport->isBridgeInternal) {
1020             switchContext->numNonHvVports++;
1021         }
1022     default:
1023         break;
1024     }
1025
1026     /*
1027      * Insert the port into the hash array of ports: by port number and ovs
1028      * and ovs (datapath) port name.
1029      * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1030      * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1031      */
1032     hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
1033     InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
1034                    &vport->portNoLink);
1035
1036     hash = OvsJhashBytes(vport->ovsName, strlen(vport->ovsName) + 1,
1037                          OVS_HASH_BASIS);
1038     InsertHeadList(
1039         &gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
1040         &vport->ovsNameLink);
1041
1042     return STATUS_SUCCESS;
1043 }
1044
1045
1046 /*
1047  * --------------------------------------------------------------------------
1048  * Provides functionality that is partly complementatry to
1049  * InitOvsVportCommon()/InitHvVportCommon().
1050  *
1051  * 'hvDelete' indicates if caller is removing the vport as a result of the
1052  * port being removed on the Hyper-V switch.
1053  * 'ovsDelete' indicates if caller is removing the vport as a result of the
1054  * port being removed from OVS userspace.
1055  * --------------------------------------------------------------------------
1056  */
1057 VOID
1058 OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
1059                         POVS_VPORT_ENTRY vport,
1060                         BOOLEAN hvDelete,
1061                         BOOLEAN ovsDelete,
1062                         BOOLEAN *vportDeallocated)
1063 {
1064     BOOLEAN hvSwitchPort = FALSE;
1065     BOOLEAN deletedOnOvs = FALSE, deletedOnHv = FALSE;
1066
1067     if (vportDeallocated) {
1068         *vportDeallocated = FALSE;
1069     }
1070
1071     if (vport->isExternal) {
1072         if (vport->nicIndex == 0) {
1073             ASSERT(switchContext->numPhysicalNics == 0);
1074             switchContext->virtualExternalPortId = 0;
1075             switchContext->virtualExternalVport = NULL;
1076             OvsFreeMemory(vport);
1077             if (vportDeallocated) {
1078                 *vportDeallocated = TRUE;
1079             }
1080             return;
1081         } else {
1082             ASSERT(switchContext->numPhysicalNics);
1083             switchContext->numPhysicalNics--;
1084             hvSwitchPort = TRUE;
1085         }
1086     }
1087
1088     switch (vport->ovsType) {
1089     case OVS_VPORT_TYPE_INTERNAL:
1090         if (!vport->isBridgeInternal) {
1091             switchContext->internalPortId = 0;
1092             switchContext->internalVport = NULL;
1093             OvsInternalAdapterDown();
1094             hvSwitchPort = TRUE;
1095         }
1096         break;
1097     case OVS_VPORT_TYPE_VXLAN:
1098         OvsCleanupVxlanTunnel(vport);
1099         switchContext->vxlanVport = NULL;
1100         break;
1101     case OVS_VPORT_TYPE_GRE:
1102     case OVS_VPORT_TYPE_GRE64:
1103         break;
1104     case OVS_VPORT_TYPE_NETDEV:
1105         hvSwitchPort = TRUE;
1106     default:
1107         break;
1108     }
1109
1110     /*
1111      * 'hvDelete' == TRUE indicates that the port should be removed from the
1112      * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
1113      * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
1114      *
1115      * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
1116      */
1117     if (vport->isPresentOnHv == TRUE) {
1118         deletedOnHv = TRUE;
1119     }
1120     if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1121         deletedOnOvs = TRUE;
1122     }
1123
1124     if (hvDelete && !deletedOnHv) {
1125         vport->isPresentOnHv = TRUE;
1126
1127         /* Remove the port from the relevant lists. */
1128         RemoveEntryList(&vport->portIdLink);
1129         InitializeListHead(&vport->portIdLink);
1130         deletedOnHv = TRUE;
1131     }
1132     if (ovsDelete && !deletedOnOvs) {
1133         vport->portNo = OVS_DPPORT_NUMBER_INVALID;
1134         vport->ovsName[0] = '\0';
1135
1136         /* Remove the port from the relevant lists. */
1137         RemoveEntryList(&vport->ovsNameLink);
1138         InitializeListHead(&vport->ovsNameLink);
1139         RemoveEntryList(&vport->portNoLink);
1140         InitializeListHead(&vport->portNoLink);
1141         deletedOnOvs = TRUE;
1142     }
1143
1144     /*
1145      * Deallocate the port if it has been deleted on the Hyper-V switch as well
1146      * as OVS userspace.
1147      */
1148     if (deletedOnHv && deletedOnOvs) {
1149         if (hvSwitchPort) {
1150             switchContext->numHvVports--;
1151         } else {
1152             switchContext->numNonHvVports--;
1153         }
1154         OvsFreeMemory(vport);
1155         if (vportDeallocated) {
1156             *vportDeallocated = TRUE;
1157         }
1158     }
1159 }
1160
1161 NDIS_STATUS
1162 OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
1163 {
1164     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1165     ULONG arrIndex;
1166     PNDIS_SWITCH_PORT_PARAMETERS portParam;
1167     PNDIS_SWITCH_PORT_ARRAY portArray = NULL;
1168     POVS_VPORT_ENTRY vport;
1169
1170     OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
1171
1172     status = OvsGetPortsOnSwitch(switchContext, &portArray);
1173     if (status != NDIS_STATUS_SUCCESS) {
1174         goto cleanup;
1175     }
1176
1177     for (arrIndex = 0; arrIndex < portArray->NumElements; arrIndex++) {
1178          portParam = NDIS_SWITCH_PORT_AT_ARRAY_INDEX(portArray, arrIndex);
1179
1180          if (portParam->IsValidationPort) {
1181              continue;
1182          }
1183
1184          vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
1185          if (vport == NULL) {
1186              status = NDIS_STATUS_RESOURCES;
1187              goto cleanup;
1188          }
1189          OvsInitVportWithPortParam(vport, portParam);
1190          status = InitHvVportCommon(switchContext, vport, TRUE);
1191          if (status != NDIS_STATUS_SUCCESS) {
1192              OvsFreeMemory(vport);
1193              goto cleanup;
1194          }
1195     }
1196 cleanup:
1197     if (status != NDIS_STATUS_SUCCESS) {
1198         OvsClearAllSwitchVports(switchContext);
1199     }
1200
1201     if (portArray != NULL) {
1202         OvsFreeMemory(portArray);
1203     }
1204     OVS_LOG_TRACE("Exit: status: %x", status);
1205     return status;
1206 }
1207
1208
1209 NDIS_STATUS
1210 OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext)
1211 {
1212     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1213     PNDIS_SWITCH_NIC_ARRAY nicArray = NULL;
1214     ULONG arrIndex;
1215     PNDIS_SWITCH_NIC_PARAMETERS nicParam;
1216     POVS_VPORT_ENTRY vport;
1217
1218     OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
1219     /*
1220      * Now, get NIC list.
1221      */
1222     status = OvsGetNicsOnSwitch(switchContext, &nicArray);
1223     if (status != NDIS_STATUS_SUCCESS) {
1224         goto cleanup;
1225     }
1226     for (arrIndex = 0; arrIndex < nicArray->NumElements; ++arrIndex) {
1227
1228         nicParam = NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray, arrIndex);
1229
1230         /*
1231          * XXX: Check if the port is configured with a VLAN. Disallow such a
1232          * configuration, since we don't support tag-in-tag.
1233          */
1234
1235         /*
1236          * XXX: Check if the port is connected to a VF. Disconnect the VF in
1237          * such a case.
1238          */
1239
1240         if (nicParam->NicType == NdisSwitchNicTypeExternal &&
1241             nicParam->NicIndex != 0) {
1242             POVS_VPORT_ENTRY virtExtVport =
1243                    (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
1244
1245             vport = OvsAllocateVport();
1246             if (vport) {
1247                 OvsInitPhysNicVport(vport, virtExtVport,
1248                                     nicParam->NicIndex);
1249                 status = InitHvVportCommon(switchContext, vport, TRUE);
1250                 if (status != NDIS_STATUS_SUCCESS) {
1251                     OvsFreeMemory(vport);
1252                     vport = NULL;
1253                 }
1254             }
1255         } else {
1256             vport = OvsFindVportByPortIdAndNicIndex(switchContext,
1257                                                     nicParam->PortId,
1258                                                     nicParam->NicIndex);
1259         }
1260         if (vport == NULL) {
1261             OVS_LOG_ERROR("Fail to allocate vport");
1262             continue;
1263         }
1264         OvsInitVportWithNicParam(switchContext, vport, nicParam);
1265         if (nicParam->NicType == NdisSwitchNicTypeInternal) {
1266             OvsInternalAdapterUp(vport->portNo, &nicParam->NetCfgInstanceId);
1267         }
1268     }
1269 cleanup:
1270
1271     if (nicArray != NULL) {
1272         OvsFreeMemory(nicArray);
1273     }
1274     OVS_LOG_TRACE("Exit: status: %x", status);
1275     return status;
1276 }
1277
1278 /*
1279  * --------------------------------------------------------------------------
1280  * Deletes ports added from the Hyper-V switch as well as OVS usersapce. The
1281  * function deletes ports in 'portIdHashArray'. This will delete most of the
1282  * ports that are in the 'portNoHashArray' as well. Any remaining ports
1283  * are deleted by walking the the 'portNoHashArray'.
1284  * --------------------------------------------------------------------------
1285  */
1286 VOID
1287 OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
1288 {
1289     for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1290         PLIST_ENTRY head, link, next;
1291
1292         head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
1293         LIST_FORALL_SAFE(head, link, next) {
1294             POVS_VPORT_ENTRY vport;
1295             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1296             OvsRemoveAndDeleteVport(switchContext, vport, TRUE, TRUE, NULL);
1297         }
1298     }
1299     /*
1300      * Remove 'virtualExternalVport' as well. This port is not part of the
1301      * 'portIdHashArray'.
1302      */
1303     if (switchContext->virtualExternalVport) {
1304         OvsRemoveAndDeleteVport(switchContext,
1305             (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE,
1306             NULL);
1307     }
1308
1309     for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1310         PLIST_ENTRY head, link, next;
1311
1312         head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
1313         LIST_FORALL_SAFE(head, link, next) {
1314             POVS_VPORT_ENTRY vport;
1315             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1316             ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
1317                    (vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
1318                     vport->isBridgeInternal) || vport->isPresentOnHv == TRUE);
1319             OvsRemoveAndDeleteVport(switchContext, vport, TRUE, TRUE, NULL);
1320         }
1321     }
1322
1323     ASSERT(switchContext->virtualExternalVport == NULL);
1324     ASSERT(switchContext->internalVport == NULL);
1325     ASSERT(switchContext->vxlanVport == NULL);
1326 }
1327
1328
1329 NTSTATUS
1330 OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
1331                                 CHAR *str,
1332                                 UINT16 maxStrLen)
1333 {
1334     ANSI_STRING astr;
1335     UNICODE_STRING ustr;
1336     NTSTATUS status;
1337     UINT32 size;
1338
1339     ustr.Buffer = wStr->String;
1340     ustr.Length = wStr->Length;
1341     ustr.MaximumLength = IF_MAX_STRING_SIZE;
1342
1343     astr.Buffer = str;
1344     astr.MaximumLength = maxStrLen;
1345     astr.Length = 0;
1346
1347     size = RtlUnicodeStringToAnsiSize(&ustr);
1348     if (size > maxStrLen) {
1349         return STATUS_BUFFER_OVERFLOW;
1350     }
1351
1352     status = RtlUnicodeStringToAnsiString(&astr, &ustr, FALSE);
1353
1354     ASSERT(status == STATUS_SUCCESS);
1355     if (status != STATUS_SUCCESS) {
1356         return status;
1357     }
1358     ASSERT(astr.Length <= maxStrLen);
1359     str[astr.Length] = 0;
1360     return STATUS_SUCCESS;
1361 }
1362
1363 /*
1364  * --------------------------------------------------------------------------
1365  * Utility function that populates a 'OVS_VPORT_EXT_INFO' structure for the
1366  * specified vport.
1367  *
1368  * Assumes that 'gOvsCtrlLock' is held.
1369  * --------------------------------------------------------------------------
1370  */
1371 NTSTATUS
1372 OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
1373                    POVS_VPORT_EXT_INFO extInfo)
1374 {
1375     POVS_VPORT_ENTRY vport;
1376     size_t len;
1377     LOCK_STATE_EX lockState;
1378     NTSTATUS status = STATUS_SUCCESS;
1379     BOOLEAN doConvert = FALSE;
1380
1381     RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO));
1382     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1383     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1384                           NDIS_RWL_AT_DISPATCH_LEVEL);
1385     if (vportGet->portNo == 0) {
1386         StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
1387         vport = OvsFindVportByHvNameA(gOvsSwitchContext, vportGet->name);
1388         if (vport != NULL) {
1389             /* If the port is not a Hyper-V port and it has been added earlier,
1390              * we'll find it in 'ovsPortNameHashArray'. */
1391             vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name);
1392         }
1393     } else {
1394         vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo);
1395     }
1396     if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED &&
1397                           vport->ovsState != OVS_STATE_NIC_CREATED)) {
1398         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1399         if (vportGet->portNo) {
1400             OVS_LOG_WARN("vport %u does not exist any more", vportGet->portNo);
1401         } else {
1402             OVS_LOG_WARN("vport %s does not exist any more", vportGet->name);
1403         }
1404         status = STATUS_DEVICE_DOES_NOT_EXIST;
1405         goto ext_info_done;
1406     }
1407     extInfo->dpNo = vportGet->dpNo;
1408     extInfo->portNo = vport->portNo;
1409     RtlCopyMemory(extInfo->macAddress, vport->currMacAddress,
1410                   sizeof (vport->currMacAddress));
1411     RtlCopyMemory(extInfo->permMACAddress, vport->permMacAddress,
1412                   sizeof (vport->permMacAddress));
1413     if (vport->ovsType == OVS_VPORT_TYPE_NETDEV) {
1414         RtlCopyMemory(extInfo->vmMACAddress, vport->vmMacAddress,
1415                       sizeof (vport->vmMacAddress));
1416     }
1417     extInfo->nicIndex = vport->nicIndex;
1418     extInfo->portId = vport->portId;
1419     extInfo->type = vport->ovsType;
1420     extInfo->mtu = vport->mtu;
1421     /*
1422      * TO be revisit XXX
1423      */
1424     if (vport->ovsState == OVS_STATE_NIC_CREATED) {
1425        extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_DOWN;
1426     } else if (vport->ovsState == OVS_STATE_CONNECTED) {
1427        extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
1428     } else {
1429        extInfo->status = OVS_EVENT_DISCONNECT;
1430     }
1431     if (extInfo->type == OVS_VPORT_TYPE_NETDEV &&
1432         (vport->ovsState == OVS_STATE_NIC_CREATED  ||
1433          vport->ovsState == OVS_STATE_CONNECTED)) {
1434         doConvert = TRUE;
1435     } else {
1436         extInfo->vmUUID[0] = 0;
1437         extInfo->vifUUID[0] = 0;
1438     }
1439     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1440     if (doConvert) {
1441         status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName,
1442                                                  extInfo->name,
1443                                                  OVS_MAX_PORT_NAME_LENGTH);
1444         if (status != STATUS_SUCCESS) {
1445             OVS_LOG_INFO("Fail to convert NIC name.");
1446             extInfo->vmUUID[0] = 0;
1447         }
1448
1449         status = OvsConvertIfCountedStrToAnsiStr(&vport->vmName,
1450                                                  extInfo->vmUUID,
1451                                                  OVS_MAX_VM_UUID_LEN);
1452         if (status != STATUS_SUCCESS) {
1453             OVS_LOG_INFO("Fail to convert VM name.");
1454             extInfo->vmUUID[0] = 0;
1455         }
1456
1457         status = OvsConvertIfCountedStrToAnsiStr(&vport->nicName,
1458                                                  extInfo->vifUUID,
1459                                                  OVS_MAX_VIF_UUID_LEN);
1460         if (status != STATUS_SUCCESS) {
1461             OVS_LOG_INFO("Fail to convert nic UUID");
1462             extInfo->vifUUID[0] = 0;
1463         }
1464         /*
1465          * for now ignore status
1466          */
1467         status = STATUS_SUCCESS;
1468     }
1469
1470 ext_info_done:
1471     return status;
1472 }
1473
1474 /*
1475  * --------------------------------------------------------------------------
1476  *  Command Handler for 'OVS_WIN_NETDEV_CMD_GET'.
1477  * --------------------------------------------------------------------------
1478  */
1479 NTSTATUS
1480 OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1481                        UINT32 *replyLen)
1482 {
1483     NTSTATUS status = STATUS_SUCCESS;
1484     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1485     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1486     NL_ERROR nlError = NL_ERROR_SUCCESS;
1487     OVS_VPORT_GET vportGet;
1488     OVS_VPORT_EXT_INFO info;
1489
1490     static const NL_POLICY ovsNetdevPolicy[] = {
1491         [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING,
1492                                        .minLen = 2,
1493                                        .maxLen = IFNAMSIZ },
1494     };
1495     PNL_ATTR netdevAttrs[ARRAY_SIZE(ovsNetdevPolicy)];
1496
1497     /* input buffer has been validated while validating transaction dev op. */
1498     ASSERT(usrParamsCtx->inputBuffer != NULL &&
1499            usrParamsCtx->inputLength > sizeof *msgIn);
1500
1501     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1502         return STATUS_INVALID_BUFFER_SIZE;
1503     }
1504
1505     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1506         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1507         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1508         ovsNetdevPolicy, netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
1509         return STATUS_INVALID_PARAMETER;
1510     }
1511
1512     OvsAcquireCtrlLock();
1513
1514     vportGet.portNo = 0;
1515     RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
1516                   NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
1517
1518     status = OvsGetExtInfoIoctl(&vportGet, &info);
1519     if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
1520         nlError = NL_ERROR_NODEV;
1521         OvsReleaseCtrlLock();
1522         goto cleanup;
1523     }
1524
1525     status = CreateNetlinkMesgForNetdev(&info, msgIn,
1526                  usrParamsCtx->outputBuffer, usrParamsCtx->outputLength,
1527                  gOvsSwitchContext->dpNo);
1528     if (status == STATUS_SUCCESS) {
1529         *replyLen = msgOut->nlMsg.nlmsgLen;
1530     }
1531     OvsReleaseCtrlLock();
1532
1533 cleanup:
1534     if (nlError != NL_ERROR_SUCCESS) {
1535         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1536             usrParamsCtx->outputBuffer;
1537
1538         NlBuildErrorMsg(msgIn, msgError, nlError);
1539         *replyLen = msgError->nlMsg.nlmsgLen;
1540     }
1541
1542     return STATUS_SUCCESS;
1543 }
1544
1545
1546 /*
1547  * --------------------------------------------------------------------------
1548  *  Utility function to construct an OVS_MESSAGE for the specified vport. The
1549  *  OVS_MESSAGE contains the output of a netdev command.
1550  * --------------------------------------------------------------------------
1551  */
1552 static NTSTATUS
1553 CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
1554                            POVS_MESSAGE msgIn,
1555                            PVOID outBuffer,
1556                            UINT32 outBufLen,
1557                            int dpIfIndex)
1558 {
1559     NL_BUFFER nlBuffer;
1560     BOOLEAN ok;
1561     PNL_MSG_HDR nlMsg;
1562     UINT32 netdevFlags = 0;
1563
1564     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1565
1566     ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1567                       msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1568                       msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1569                       dpIfIndex) == STATUS_SUCCESS ? TRUE : FALSE;
1570     if (!ok) {
1571         return STATUS_INSUFFICIENT_RESOURCES;
1572     }
1573
1574     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
1575                          info->portNo);
1576     if (!ok) {
1577         return STATUS_INSUFFICIENT_RESOURCES;
1578     }
1579
1580     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
1581     if (!ok) {
1582         return STATUS_INSUFFICIENT_RESOURCES;
1583     }
1584
1585     ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
1586                             info->name);
1587     if (!ok) {
1588         return STATUS_INSUFFICIENT_RESOURCES;
1589     }
1590
1591     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
1592              (PCHAR)info->macAddress, sizeof (info->macAddress));
1593     if (!ok) {
1594         return STATUS_INSUFFICIENT_RESOURCES;
1595     }
1596
1597     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
1598     if (!ok) {
1599         return STATUS_INSUFFICIENT_RESOURCES;
1600     }
1601
1602     if (info->status != OVS_EVENT_CONNECT) {
1603         netdevFlags = OVS_WIN_NETDEV_IFF_UP;
1604     }
1605     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
1606                          netdevFlags);
1607     if (!ok) {
1608         return STATUS_INSUFFICIENT_RESOURCES;
1609     }
1610
1611     /*
1612      * XXX: add netdev_stats when we have the definition available in the
1613      * kernel.
1614      */
1615
1616     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1617     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1618
1619     return STATUS_SUCCESS;
1620 }
1621
1622 static __inline VOID
1623 OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec)
1624 {
1625     while ((!switchContext->isActivated) &&
1626           (!switchContext->isActivateFailed)) {
1627         /* Wait for the switch to be active and
1628          * the list of ports in OVS to be initialized. */
1629         NdisMSleep(sleepMicroSec);
1630     }
1631 }
1632
1633 static NTSTATUS
1634 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1635                       POVS_MESSAGE msgIn,
1636                       PVOID outBuffer,
1637                       UINT32 outBufLen,
1638                       int dpIfIndex)
1639 {
1640     NL_BUFFER nlBuffer;
1641     OVS_VPORT_FULL_STATS vportStats;
1642     BOOLEAN ok;
1643     PNL_MSG_HDR nlMsg;
1644
1645     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1646
1647     ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1648                       msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1649                       msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1650                       dpIfIndex) == STATUS_SUCCESS ? TRUE : FALSE;
1651     if (!ok) {
1652         return STATUS_INSUFFICIENT_RESOURCES;
1653     }
1654
1655     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1656     if (!ok) {
1657         return STATUS_INSUFFICIENT_RESOURCES;
1658     }
1659
1660     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1661     if (!ok) {
1662         return STATUS_INSUFFICIENT_RESOURCES;
1663     }
1664
1665     ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1666     if (!ok) {
1667         return STATUS_INSUFFICIENT_RESOURCES;
1668     }
1669
1670     /*
1671      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1672      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1673      * it means we have an array of pids, instead of a single pid.
1674      * ATM we assume we have one pid only.
1675     */
1676
1677     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1678                          vport->upcallPid);
1679     if (!ok) {
1680         return STATUS_INSUFFICIENT_RESOURCES;
1681     }
1682
1683     /*stats*/
1684     vportStats.rxPackets = vport->stats.rxPackets;
1685     vportStats.rxBytes = vport->stats.rxBytes;
1686     vportStats.txPackets = vport->stats.txPackets;
1687     vportStats.txBytes = vport->stats.txBytes;
1688     vportStats.rxErrors = vport->errStats.rxErrors;
1689     vportStats.txErrors = vport->errStats.txErrors;
1690     vportStats.rxDropped = vport->errStats.rxDropped;
1691     vportStats.txDropped = vport->errStats.txDropped;
1692
1693     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1694                             (PCHAR)&vportStats,
1695                             sizeof(OVS_VPORT_FULL_STATS));
1696     if (!ok) {
1697         return STATUS_INSUFFICIENT_RESOURCES;
1698     }
1699
1700     /*
1701      * XXX: when vxlan udp dest port becomes configurable, we will also need
1702      * to add vport options
1703     */
1704
1705     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1706     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1707
1708     return STATUS_SUCCESS;
1709 }
1710
1711 static NTSTATUS
1712 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1713                     UINT32 *replyLen)
1714 {
1715     POVS_MESSAGE msgIn;
1716     POVS_OPEN_INSTANCE instance =
1717         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1718     LOCK_STATE_EX lockState;
1719     UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1720
1721     /*
1722      * XXX: this function shares some code with other dump command(s).
1723      * In the future, we will need to refactor the dump functions
1724     */
1725
1726     ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1727
1728     if (instance->dumpState.ovsMsg == NULL) {
1729         ASSERT(FALSE);
1730         return STATUS_INVALID_DEVICE_STATE;
1731     }
1732
1733     /* Output buffer has been validated while validating read dev op. */
1734     ASSERT(usrParamsCtx->outputBuffer != NULL);
1735
1736     msgIn = instance->dumpState.ovsMsg;
1737
1738     OvsAcquireCtrlLock();
1739
1740     /*
1741      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1742      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1743      * it means we have an array of pids, instead of a single pid.
1744      * ATM we assume we have one pid only.
1745     */
1746     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1747     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1748                           NDIS_RWL_AT_DISPATCH_LEVEL);
1749
1750     if (gOvsSwitchContext->numHvVports > 0 ||
1751             gOvsSwitchContext->numNonHvVports > 0) {
1752         /* inBucket: the bucket, used for lookup */
1753         UINT32 inBucket = instance->dumpState.index[0];
1754         /* inIndex: index within the given bucket, used for lookup */
1755         UINT32 inIndex = instance->dumpState.index[1];
1756         /* the bucket to be used for the next dump operation */
1757         UINT32 outBucket = 0;
1758         /* the index within the outBucket to be used for the next dump */
1759         UINT32 outIndex = 0;
1760
1761         for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1762             PLIST_ENTRY head, link;
1763             head = &(gOvsSwitchContext->portNoHashArray[i]);
1764             POVS_VPORT_ENTRY vport = NULL;
1765
1766             outIndex = 0;
1767             LIST_FORALL(head, link) {
1768
1769                 /*
1770                  * if one or more dumps were previously done on this same bucket,
1771                  * inIndex will be > 0, so we'll need to reply with the
1772                  * inIndex + 1 vport from the bucket.
1773                 */
1774                 if (outIndex >= inIndex) {
1775                     vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1776
1777                     ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
1778                     OvsCreateMsgFromVport(vport, msgIn,
1779                                           usrParamsCtx->outputBuffer,
1780                                           usrParamsCtx->outputLength,
1781                                           gOvsSwitchContext->dpNo);
1782                     ++outIndex;
1783                     break;
1784                 }
1785
1786                 ++outIndex;
1787             }
1788
1789             if (vport) {
1790                 break;
1791             }
1792
1793             /*
1794              * if no vport was found above, check the next bucket, beginning
1795              * with the first (i.e. index 0) elem from within that bucket
1796             */
1797             inIndex = 0;
1798         }
1799
1800         outBucket = i;
1801
1802         /* XXX: what about NLMSG_DONE (as msg type)? */
1803         instance->dumpState.index[0] = outBucket;
1804         instance->dumpState.index[1] = outIndex;
1805     }
1806
1807     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1808
1809     OvsReleaseCtrlLock();
1810
1811     /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1812     if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1813         POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1814         *replyLen = msgOut->nlMsg.nlmsgLen;
1815     } else {
1816         /*
1817          * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1818          * it's dump done
1819          */
1820         *replyLen = 0;
1821         /* Free up the dump state, since there's no more data to continue. */
1822         FreeUserDumpState(instance);
1823     }
1824
1825     return STATUS_SUCCESS;
1826 }
1827
1828 static NTSTATUS
1829 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1830             UINT32 *replyLen)
1831 {
1832     NTSTATUS status = STATUS_SUCCESS;
1833     LOCK_STATE_EX lockState;
1834
1835     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1836     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1837     POVS_VPORT_ENTRY vport = NULL;
1838     NL_ERROR nlError = NL_ERROR_SUCCESS;
1839     PCHAR portName = NULL;
1840     UINT32 portNameLen = 0;
1841     UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
1842
1843     static const NL_POLICY ovsVportPolicy[] = {
1844         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1845         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1846                                   .minLen = 2,
1847                                   .maxLen = IFNAMSIZ,
1848                                   .optional = TRUE},
1849     };
1850     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1851
1852     /* input buffer has been validated while validating write dev op. */
1853     ASSERT(usrParamsCtx->inputBuffer != NULL);
1854
1855     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1856         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1857         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1858         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1859         return STATUS_INVALID_PARAMETER;
1860     }
1861
1862     /* Output buffer has been validated while validating transact dev op. */
1863     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1864
1865     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1866     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1867         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1868         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1869
1870         /* the port name is expected to be null-terminated */
1871         ASSERT(portName[portNameLen - 1] == '\0');
1872
1873         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1874     } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1875         portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1876
1877         vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
1878     } else {
1879         nlError = NL_ERROR_INVAL;
1880         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1881         goto Cleanup;
1882     }
1883
1884     if (!vport) {
1885         nlError = NL_ERROR_NODEV;
1886         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1887         goto Cleanup;
1888     }
1889
1890     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1891                                    usrParamsCtx->outputLength,
1892                                    gOvsSwitchContext->dpNo);
1893     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1894
1895     *replyLen = msgOut->nlMsg.nlmsgLen;
1896
1897 Cleanup:
1898     if (nlError != NL_ERROR_SUCCESS) {
1899         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1900             usrParamsCtx->outputBuffer;
1901
1902         NlBuildErrorMsg(msgIn, msgError, nlError);
1903         *replyLen = msgError->nlMsg.nlmsgLen;
1904     }
1905
1906     return STATUS_SUCCESS;
1907 }
1908
1909 /*
1910  * --------------------------------------------------------------------------
1911  *  Command Handler for 'OVS_VPORT_CMD_NEW'.
1912  *
1913  *  The function handles the initial call to setup the dump state, as well as
1914  *  subsequent calls to continue dumping data.
1915  * --------------------------------------------------------------------------
1916 */
1917 NTSTATUS
1918 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1919                       UINT32 *replyLen)
1920 {
1921     *replyLen = 0;
1922
1923     switch (usrParamsCtx->devOp) {
1924     case OVS_WRITE_DEV_OP:
1925         return OvsSetupDumpStart(usrParamsCtx);
1926
1927     case OVS_READ_DEV_OP:
1928         return OvsGetVportDumpNext(usrParamsCtx, replyLen);
1929
1930     case OVS_TRANSACTION_DEV_OP:
1931         return OvsGetVport(usrParamsCtx, replyLen);
1932
1933     default:
1934         return STATUS_INVALID_DEVICE_REQUEST;
1935     }
1936
1937 }
1938
1939 static UINT32
1940 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
1941 {
1942     /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
1943     for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
1944         POVS_VPORT_ENTRY vport;
1945
1946         vport = OvsFindVportByPortNo(switchContext, i);
1947         if (!vport) {
1948             return i;
1949         }
1950     }
1951
1952     return OVS_DPPORT_NUMBER_INVALID;
1953 }
1954
1955 /*
1956  * --------------------------------------------------------------------------
1957  *  Command Handler for 'OVS_VPORT_CMD_NEW'.
1958  * --------------------------------------------------------------------------
1959  */
1960 NTSTATUS
1961 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1962                       UINT32 *replyLen)
1963 {
1964     NDIS_STATUS status = STATUS_SUCCESS;
1965     LOCK_STATE_EX lockState;
1966
1967     NL_ERROR nlError = NL_ERROR_SUCCESS;
1968     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1969     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1970     POVS_VPORT_ENTRY vport = NULL;
1971     PCHAR portName;
1972     ULONG portNameLen;
1973     UINT32 portType;
1974     BOOLEAN isBridgeInternal = FALSE;
1975     BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE;
1976     BOOLEAN addInternalPortAsNetdev = FALSE;
1977
1978     static const NL_POLICY ovsVportPolicy[] = {
1979         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1980         [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
1981         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
1982                                   .optional = FALSE},
1983         [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
1984                                         .optional = FALSE },
1985         [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
1986     };
1987
1988     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1989
1990     /* input buffer has been validated while validating write dev op. */
1991     ASSERT(usrParamsCtx->inputBuffer != NULL);
1992
1993     /* Output buffer has been validated while validating transact dev op. */
1994     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1995
1996     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1997         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1998         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1999         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2000         return STATUS_INVALID_PARAMETER;
2001     }
2002
2003     portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2004     portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2005     portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2006
2007     /* we are expecting null terminated strings to be passed */
2008     ASSERT(portName[portNameLen - 1] == '\0');
2009
2010     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2011
2012     vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2013     if (vport) {
2014         nlError = NL_ERROR_EXIST;
2015         goto Cleanup;
2016     }
2017
2018     if (portName && portType == OVS_VPORT_TYPE_NETDEV &&
2019         !strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
2020         addInternalPortAsNetdev = TRUE;
2021     }
2022
2023     if (portName && portType == OVS_VPORT_TYPE_INTERNAL &&
2024         strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
2025         isBridgeInternal = TRUE;
2026     }
2027
2028     if (portType == OVS_VPORT_TYPE_INTERNAL && !isBridgeInternal) {
2029         vport = gOvsSwitchContext->internalVport;
2030     } else if (portType == OVS_VPORT_TYPE_NETDEV) {
2031         /* External ports can also be looked up like VIF ports. */
2032         vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
2033     } else {
2034         ASSERT(OvsIsTunnelVportType(portType) ||
2035                (portType == OVS_VPORT_TYPE_INTERNAL && isBridgeInternal));
2036         ASSERT(OvsGetTunnelVport(gOvsSwitchContext, portType) == NULL ||
2037                !OvsIsTunnelVportType(portType));
2038
2039         vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
2040         if (vport == NULL) {
2041             nlError = NL_ERROR_NOMEM;
2042             goto Cleanup;
2043         }
2044         vportAllocated = TRUE;
2045
2046         if (OvsIsTunnelVportType(portType)) {
2047             status = OvsInitTunnelVport(vport, portType, VXLAN_UDP_PORT);
2048             nlError = NlMapStatusToNlErr(status);
2049         } else {
2050             OvsInitBridgeInternalVport(vport);
2051         }
2052         vportInitialized = TRUE;
2053
2054         if (nlError == NL_ERROR_SUCCESS) {
2055             vport->ovsState = OVS_STATE_CONNECTED;
2056             vport->nicState = NdisSwitchNicStateConnected;
2057
2058             /*
2059              * Allow the vport to be deleted, because there is no
2060              * corresponding hyper-v switch part.
2061              */
2062             vport->isPresentOnHv = TRUE;
2063         }
2064     }
2065
2066     if (!vport) {
2067         nlError = NL_ERROR_INVAL;
2068         goto Cleanup;
2069     }
2070     if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2071         nlError = NL_ERROR_EXIST;
2072         goto Cleanup;
2073     }
2074
2075     /* Initialize the vport with OVS specific properties. */
2076     if (addInternalPortAsNetdev != TRUE) {
2077         vport->ovsType = portType;
2078     }
2079     if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2080         /*
2081          * XXX: when we implement the limit for ovs port number to be
2082          * MAXUINT16, we'll need to check the port number received from the
2083          * userspace.
2084          */
2085         vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2086     } else {
2087         vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
2088         if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2089             nlError = NL_ERROR_NOMEM;
2090             goto Cleanup;
2091         }
2092     }
2093
2094     /* The ovs port name must be uninitialized. */
2095     ASSERT(vport->ovsName[0] == '\0');
2096     ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2097
2098     RtlCopyMemory(vport->ovsName, portName, portNameLen);
2099     /* if we don't have options, then vport->portOptions will be NULL */
2100     vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2101
2102     /*
2103      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2104      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2105      * it means we have an array of pids, instead of a single pid.
2106      * ATM we assume we have one pid only.
2107      */
2108     vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2109
2110     status = InitOvsVportCommon(gOvsSwitchContext, vport);
2111     ASSERT(status == STATUS_SUCCESS);
2112
2113     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2114                                    usrParamsCtx->outputLength,
2115                                    gOvsSwitchContext->dpNo);
2116
2117     *replyLen = msgOut->nlMsg.nlmsgLen;
2118
2119 Cleanup:
2120     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2121
2122     if (nlError != NL_ERROR_SUCCESS) {
2123         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2124             usrParamsCtx->outputBuffer;
2125
2126         if (vport && vportAllocated == TRUE) {
2127             if (vportInitialized == TRUE) {
2128                 if (OvsIsTunnelVportType(portType)) {
2129                     OvsCleanupVxlanTunnel(vport);
2130                 }
2131             }
2132             OvsFreeMemory(vport);
2133         }
2134
2135         NlBuildErrorMsg(msgIn, msgError, nlError);
2136         *replyLen = msgError->nlMsg.nlmsgLen;
2137     }
2138
2139     return STATUS_SUCCESS;
2140 }
2141
2142
2143 /*
2144  * --------------------------------------------------------------------------
2145  *  Command Handler for 'OVS_VPORT_CMD_SET'.
2146  * --------------------------------------------------------------------------
2147  */
2148 NTSTATUS
2149 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2150                       UINT32 *replyLen)
2151 {
2152     NDIS_STATUS status = STATUS_SUCCESS;
2153     LOCK_STATE_EX lockState;
2154
2155     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2156     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2157     POVS_VPORT_ENTRY vport = NULL;
2158     NL_ERROR nlError = NL_ERROR_SUCCESS;
2159
2160     static const NL_POLICY ovsVportPolicy[] = {
2161         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2162         [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
2163         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2164                                   .optional = TRUE },
2165         [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2166                                         .optional = TRUE },
2167         [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
2168                                    .minLen = sizeof(OVS_VPORT_FULL_STATS),
2169                                    .maxLen = sizeof(OVS_VPORT_FULL_STATS),
2170                                    .optional = TRUE },
2171         [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2172     };
2173     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2174
2175     ASSERT(usrParamsCtx->inputBuffer != NULL);
2176
2177     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2178         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2179         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2180         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2181         return STATUS_INVALID_PARAMETER;
2182     }
2183
2184     /* Output buffer has been validated while validating transact dev op. */
2185     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2186
2187     OvsAcquireCtrlLock();
2188
2189     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2190     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2191         PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2192 #ifdef DBG
2193         UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2194 #endif
2195         /* the port name is expected to be null-terminated */
2196         ASSERT(portName[portNameLen - 1] == '\0');
2197
2198         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2199     } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2200         vport = OvsFindVportByPortNo(gOvsSwitchContext,
2201                     NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2202     }
2203
2204     if (!vport) {
2205         nlError = NL_ERROR_NODEV;
2206         goto Cleanup;
2207     }
2208
2209     /*
2210      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2211      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2212      * it means we have an array of pids, instead of a single pid.
2213      * Currently, we support only one pid.
2214      */
2215     if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
2216         vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2217     }
2218
2219     if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
2220         OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2221         if (type != vport->ovsType) {
2222             nlError = NL_ERROR_INVAL;
2223             goto Cleanup;
2224         }
2225     }
2226
2227     if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2228         OVS_LOG_ERROR("Vport options not supported");
2229         nlError = NL_ERROR_NOTSUPP;
2230         goto Cleanup;
2231     }
2232
2233     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2234                                    usrParamsCtx->outputLength,
2235                                    gOvsSwitchContext->dpNo);
2236
2237     *replyLen = msgOut->nlMsg.nlmsgLen;
2238
2239 Cleanup:
2240     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2241     OvsReleaseCtrlLock();
2242
2243     if (nlError != NL_ERROR_SUCCESS) {
2244         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2245             usrParamsCtx->outputBuffer;
2246
2247         NlBuildErrorMsg(msgIn, msgError, nlError);
2248         *replyLen = msgError->nlMsg.nlmsgLen;
2249     }
2250
2251     return STATUS_SUCCESS;
2252 }
2253
2254 /*
2255  * --------------------------------------------------------------------------
2256  *  Command Handler for 'OVS_VPORT_CMD_DEL'.
2257  * --------------------------------------------------------------------------
2258  */
2259 NTSTATUS
2260 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2261                          UINT32 *replyLen)
2262 {
2263     NDIS_STATUS status = STATUS_SUCCESS;
2264     LOCK_STATE_EX lockState;
2265
2266     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2267     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2268     POVS_VPORT_ENTRY vport = NULL;
2269     NL_ERROR nlError = NL_ERROR_SUCCESS;
2270     PSTR portName = NULL;
2271     UINT32 portNameLen = 0;
2272
2273     static const NL_POLICY ovsVportPolicy[] = {
2274         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2275         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2276                                   .optional = TRUE },
2277     };
2278     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2279
2280     ASSERT(usrParamsCtx->inputBuffer != NULL);
2281
2282     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2283         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2284         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2285         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2286         return STATUS_INVALID_PARAMETER;
2287     }
2288
2289     /* Output buffer has been validated while validating transact dev op. */
2290     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2291
2292     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2293     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2294         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2295         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2296
2297         /* the port name is expected to be null-terminated */
2298         ASSERT(portName[portNameLen - 1] == '\0');
2299
2300         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2301     }
2302     else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2303         vport = OvsFindVportByPortNo(gOvsSwitchContext,
2304             NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2305     }
2306
2307     if (!vport) {
2308         nlError = NL_ERROR_NODEV;
2309         goto Cleanup;
2310     }
2311
2312     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2313                                    usrParamsCtx->outputLength,
2314                                    gOvsSwitchContext->dpNo);
2315
2316     /*
2317      * Mark the port as deleted from OVS userspace. If the port does not exist
2318      * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
2319      */
2320     OvsRemoveAndDeleteVport(gOvsSwitchContext, vport, FALSE, TRUE, NULL);
2321
2322     *replyLen = msgOut->nlMsg.nlmsgLen;
2323
2324 Cleanup:
2325     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2326
2327     if (nlError != NL_ERROR_SUCCESS) {
2328         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2329             usrParamsCtx->outputBuffer;
2330
2331         NlBuildErrorMsg(msgIn, msgError, nlError);
2332         *replyLen = msgError->nlMsg.nlmsgLen;
2333     }
2334
2335     return STATUS_SUCCESS;
2336 }