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