datapath-windows: Avoid BSOD when cleaning up a tunnel vport
[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 = OvsAllocateMemoryWithTag(sizeof(*tunnelContext),
924                                                  OVS_VPORT_POOL_TAG);
925         if (tunnelContext == NULL) {
926             status = STATUS_INSUFFICIENT_RESOURCES;
927             break;
928         }
929         tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
930         tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
931         tunnelContext->outputLength = usrParamsCtx->outputLength;
932         tunnelContext->vport = vport;
933
934         status = OvsInitVxlanTunnel(usrParamsCtx->irp,
935                                     vport,
936                                     dstPort,
937                                     OvsTunnelVportPendingInit,
938                                     (PVOID)tunnelContext);
939         if (status != STATUS_PENDING) {
940             OvsFreeMemoryWithTag(tunnelContext, OVS_VPORT_POOL_TAG);
941             tunnelContext = NULL;
942         }
943         break;
944     }
945     case OVS_VPORT_TYPE_STT:
946         status = OvsInitSttTunnel(vport, dstPort);
947         break;
948     default:
949         ASSERT(0);
950     }
951     return status;
952 }
953
954 /*
955  * --------------------------------------------------------------------------
956  * Initializes a bridge internal vport ie. a port of type
957  * OVS_VPORT_TYPE_INTERNAL but not present on the Hyper-V switch.
958  * --------------------------------------------------------------------------
959  */
960 NTSTATUS
961 OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport)
962 {
963     vport->isBridgeInternal = TRUE;
964     vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
965     /* Mark the status to be connected, since there is no other initialization
966      * for this port. */
967     vport->ovsState = OVS_STATE_CONNECTED;
968     return STATUS_SUCCESS;
969 }
970
971 /*
972  * --------------------------------------------------------------------------
973  * For external vports 'portFriendlyName' provided by Hyper-V is over-written
974  * by synthetic names.
975  * --------------------------------------------------------------------------
976  */
977 static VOID
978 AssignNicNameSpecial(POVS_VPORT_ENTRY vport)
979 {
980     size_t len;
981
982     if (vport->portType == NdisSwitchPortTypeExternal) {
983         if (vport->nicIndex == 0) {
984             ASSERT(vport->nicIndex == 0);
985             RtlStringCbPrintfW(vport->portFriendlyName.String,
986                                IF_MAX_STRING_SIZE,
987                                L"%s.virtualAdapter", OVS_DPPORT_EXTERNAL_NAME_W);
988         } else {
989             RtlStringCbPrintfW(vport->portFriendlyName.String,
990                                IF_MAX_STRING_SIZE,
991                                L"%s.%lu", OVS_DPPORT_EXTERNAL_NAME_W,
992                                (UINT32)vport->nicIndex);
993         }
994     } else {
995         RtlStringCbPrintfW(vport->portFriendlyName.String,
996                            IF_MAX_STRING_SIZE,
997                            L"%s", OVS_DPPORT_INTERNAL_NAME_W);
998     }
999
1000     RtlStringCbLengthW(vport->portFriendlyName.String, IF_MAX_STRING_SIZE,
1001                        &len);
1002     vport->portFriendlyName.Length = (USHORT)len;
1003 }
1004
1005
1006 /*
1007  * --------------------------------------------------------------------------
1008  * Functionality common to any port on the Hyper-V switch. This function is not
1009  * to be called for a port that is not on the Hyper-V switch.
1010  *
1011  * Inserts the port into 'portIdHashArray' and caches the pointer in the
1012  * 'switchContext' if needed.
1013  *
1014  * For external NIC, assigns the name for the NIC.
1015  * --------------------------------------------------------------------------
1016  */
1017 static NDIS_STATUS
1018 InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
1019                   POVS_VPORT_ENTRY vport,
1020                   BOOLEAN newPort)
1021 {
1022     UINT32 hash;
1023
1024     switch (vport->portType) {
1025     case NdisSwitchPortTypeExternal:
1026         /*
1027          * Overwrite the 'portFriendlyName' of this external vport. The reason
1028          * for having this in common code is to be able to call it from the NDIS
1029          * Port callback as well as the NDIS NIC callback.
1030          */
1031         AssignNicNameSpecial(vport);
1032
1033         if (vport->nicIndex == 0) {
1034             switchContext->virtualExternalPortId = vport->portId;
1035             switchContext->virtualExternalVport = vport;
1036         } else {
1037             switchContext->numPhysicalNics++;
1038         }
1039         break;
1040     case NdisSwitchPortTypeInternal:
1041         ASSERT(vport->isBridgeInternal == FALSE);
1042
1043         /* Overwrite the 'portFriendlyName' of the internal vport. */
1044         AssignNicNameSpecial(vport);
1045         switchContext->internalPortId = vport->portId;
1046         switchContext->internalVport = vport;
1047         break;
1048     case NdisSwitchPortTypeSynthetic:
1049     case NdisSwitchPortTypeEmulated:
1050         break;
1051     }
1052
1053     /*
1054      * It is important to not insert vport corresponding to virtual external
1055      * port into the 'portIdHashArray' since the port should not be exposed to
1056      * OVS userspace.
1057      */
1058     if (vport->portType == NdisSwitchPortTypeExternal &&
1059         vport->nicIndex == 0) {
1060         return NDIS_STATUS_SUCCESS;
1061     }
1062
1063     /*
1064      * NOTE: OvsJhashWords has portId as "1" word. This should be ok, even
1065      * though sizeof(NDIS_SWITCH_PORT_ID) = 4, not 2, because the
1066      * hyper-v switch seems to use only 2 bytes out of 4.
1067      */
1068     hash = OvsJhashWords(&vport->portId, 1, OVS_HASH_BASIS);
1069     InsertHeadList(&switchContext->portIdHashArray[hash & OVS_VPORT_MASK],
1070                    &vport->portIdLink);
1071     if (newPort) {
1072         switchContext->numHvVports++;
1073     }
1074     return NDIS_STATUS_SUCCESS;
1075 }
1076
1077 /*
1078  * --------------------------------------------------------------------------
1079  * Functionality common to any port added from OVS userspace.
1080  *
1081  * Inserts the port into 'portNoHashArray', 'ovsPortNameHashArray' and in
1082  * 'tunnelVportsArray' if appropriate.
1083  * --------------------------------------------------------------------------
1084  */
1085 NDIS_STATUS
1086 InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
1087                    POVS_VPORT_ENTRY vport)
1088 {
1089     UINT32 hash;
1090
1091     switch(vport->ovsType) {
1092     case OVS_VPORT_TYPE_VXLAN:
1093     case OVS_VPORT_TYPE_STT:
1094     {
1095         UINT16 dstPort = GetPortFromPriv(vport);
1096         hash = OvsJhashBytes(&dstPort,
1097                              sizeof(dstPort),
1098                              OVS_HASH_BASIS);
1099         InsertHeadList(
1100             &gOvsSwitchContext->tunnelVportsArray[hash & OVS_VPORT_MASK],
1101             &vport->tunnelVportLink);
1102         switchContext->numNonHvVports++;
1103         break;
1104     }
1105     case OVS_VPORT_TYPE_INTERNAL:
1106         if (vport->isBridgeInternal) {
1107             switchContext->numNonHvVports++;
1108         }
1109     default:
1110         break;
1111     }
1112
1113     /*
1114      * Insert the port into the hash array of ports: by port number and ovs
1115      * and ovs (datapath) port name.
1116      * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1117      * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1118      */
1119     hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
1120     InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
1121                    &vport->portNoLink);
1122
1123     hash = OvsJhashBytes(vport->ovsName, strlen(vport->ovsName) + 1,
1124                          OVS_HASH_BASIS);
1125     InsertHeadList(
1126         &gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
1127         &vport->ovsNameLink);
1128
1129     return STATUS_SUCCESS;
1130 }
1131
1132
1133 /*
1134  * --------------------------------------------------------------------------
1135  * Provides functionality that is partly complementatry to
1136  * InitOvsVportCommon()/InitHvVportCommon().
1137  *
1138  * 'hvDelete' indicates if caller is removing the vport as a result of the
1139  * port being removed on the Hyper-V switch.
1140  * 'ovsDelete' indicates if caller is removing the vport as a result of the
1141  * port being removed from OVS userspace.
1142  * --------------------------------------------------------------------------
1143  */
1144 NTSTATUS
1145 OvsRemoveAndDeleteVport(PVOID usrParamsContext,
1146                         POVS_SWITCH_CONTEXT switchContext,
1147                         POVS_VPORT_ENTRY vport,
1148                         BOOLEAN hvDelete,
1149                         BOOLEAN ovsDelete)
1150 {
1151     POVS_USER_PARAMS_CONTEXT usrParamsCtx =
1152         (POVS_USER_PARAMS_CONTEXT)usrParamsContext;
1153     BOOLEAN hvSwitchPort = FALSE;
1154     BOOLEAN deletedOnOvs = FALSE;
1155     BOOLEAN deletedOnHv = FALSE;
1156
1157     switch (vport->ovsType) {
1158     case OVS_VPORT_TYPE_INTERNAL:
1159         if (!vport->isBridgeInternal) {
1160             if (hvDelete && vport->isAbsentOnHv == FALSE) {
1161                 switchContext->internalPortId = 0;
1162                 switchContext->internalVport = NULL;
1163                 OvsInternalAdapterDown();
1164             }
1165             hvSwitchPort = TRUE;
1166         }
1167         break;
1168     case OVS_VPORT_TYPE_VXLAN:
1169     {
1170         NTSTATUS status;
1171         status = OvsRemoveTunnelVport(usrParamsCtx, switchContext, vport,
1172                                       hvDelete, ovsDelete);
1173         if (status != STATUS_SUCCESS) {
1174             return status;
1175         }
1176     }
1177     case OVS_VPORT_TYPE_STT:
1178         OvsCleanupSttTunnel(vport);
1179         break;
1180     case OVS_VPORT_TYPE_GRE:
1181     case OVS_VPORT_TYPE_GRE64:
1182         break;
1183     case OVS_VPORT_TYPE_NETDEV:
1184         if (vport->isExternal) {
1185             if (vport->nicIndex == 0) {
1186                 /* Such a vport is not part of any of the hash tables, since it
1187                  * is not exposed to userspace. See Vport.h for explanation. */
1188                 ASSERT(hvDelete == TRUE);
1189                 ASSERT(switchContext->numPhysicalNics == 0);
1190                 switchContext->virtualExternalPortId = 0;
1191                 switchContext->virtualExternalVport = NULL;
1192                 OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1193                 return STATUS_SUCCESS;
1194             }
1195         }
1196         hvSwitchPort = TRUE;
1197     default:
1198         break;
1199     }
1200
1201     /*
1202      * 'hvDelete' == TRUE indicates that the port should be removed from the
1203      * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
1204      * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
1205      *
1206      * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
1207      */
1208     if (vport->isAbsentOnHv == TRUE) {
1209         deletedOnHv = TRUE;
1210     }
1211     if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1212         deletedOnOvs = TRUE;
1213     }
1214
1215     if (hvDelete && !deletedOnHv) {
1216         vport->isAbsentOnHv = TRUE;
1217
1218         if (vport->isExternal) {
1219             ASSERT(vport->nicIndex != 0);
1220             ASSERT(switchContext->numPhysicalNics);
1221             switchContext->numPhysicalNics--;
1222         }
1223
1224         /* Remove the port from the relevant lists. */
1225         RemoveEntryList(&vport->portIdLink);
1226         InitializeListHead(&vport->portIdLink);
1227         deletedOnHv = TRUE;
1228     }
1229     if (ovsDelete && !deletedOnOvs) {
1230         vport->portNo = OVS_DPPORT_NUMBER_INVALID;
1231         vport->ovsName[0] = '\0';
1232
1233         /* Remove the port from the relevant lists. */
1234         RemoveEntryList(&vport->ovsNameLink);
1235         InitializeListHead(&vport->ovsNameLink);
1236         RemoveEntryList(&vport->portNoLink);
1237         InitializeListHead(&vport->portNoLink);
1238         if (OVS_VPORT_TYPE_VXLAN == vport->ovsType ||
1239             OVS_VPORT_TYPE_STT == vport->ovsType) {
1240             RemoveEntryList(&vport->tunnelVportLink);
1241             InitializeListHead(&vport->tunnelVportLink);
1242         }
1243
1244         deletedOnOvs = TRUE;
1245     }
1246
1247     /*
1248      * Deallocate the port if it has been deleted on the Hyper-V switch as well
1249      * as OVS userspace.
1250      */
1251     if (deletedOnHv && deletedOnOvs) {
1252         if (hvSwitchPort) {
1253             switchContext->numHvVports--;
1254         } else {
1255             switchContext->numNonHvVports--;
1256         }
1257         OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1258     }
1259
1260     return STATUS_SUCCESS;
1261 }
1262
1263 static NTSTATUS
1264 OvsRemoveTunnelVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1265                      POVS_SWITCH_CONTEXT switchContext,
1266                      POVS_VPORT_ENTRY vport,
1267                      BOOLEAN hvDelete,
1268                      BOOLEAN ovsDelete)
1269 {
1270     POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
1271     PIRP irp = NULL;
1272
1273     tunnelContext = OvsAllocateMemory(sizeof(*tunnelContext));
1274     if (tunnelContext == NULL) {
1275         return STATUS_INSUFFICIENT_RESOURCES;
1276     }
1277     RtlZeroMemory(tunnelContext, sizeof(*tunnelContext));
1278
1279     tunnelContext->switchContext = switchContext;
1280     tunnelContext->hvSwitchPort = FALSE;
1281     tunnelContext->hvDelete = hvDelete;
1282     tunnelContext->ovsDelete = ovsDelete;
1283     tunnelContext->vport = vport;
1284
1285     if (usrParamsCtx) {
1286         tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
1287         tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
1288         tunnelContext->outputLength = usrParamsCtx->outputLength;
1289         irp = usrParamsCtx->irp;
1290     }
1291
1292     return OvsCleanupVxlanTunnel(irp, vport, OvsTunnelVportPendingRemove,
1293                                  tunnelContext);
1294 }
1295
1296
1297
1298 NDIS_STATUS
1299 OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
1300 {
1301     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1302     ULONG arrIndex;
1303     PNDIS_SWITCH_PORT_PARAMETERS portParam;
1304     PNDIS_SWITCH_PORT_ARRAY portArray = NULL;
1305     POVS_VPORT_ENTRY vport;
1306
1307     OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
1308
1309     status = OvsGetPortsOnSwitch(switchContext, &portArray);
1310     if (status != NDIS_STATUS_SUCCESS) {
1311         goto cleanup;
1312     }
1313
1314     for (arrIndex = 0; arrIndex < portArray->NumElements; arrIndex++) {
1315          portParam = NDIS_SWITCH_PORT_AT_ARRAY_INDEX(portArray, arrIndex);
1316
1317          if (portParam->IsValidationPort) {
1318              continue;
1319          }
1320
1321          vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
1322          if (vport == NULL) {
1323              status = NDIS_STATUS_RESOURCES;
1324              goto cleanup;
1325          }
1326          OvsInitVportWithPortParam(vport, portParam);
1327          status = InitHvVportCommon(switchContext, vport, TRUE);
1328          if (status != NDIS_STATUS_SUCCESS) {
1329              OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1330              goto cleanup;
1331          }
1332     }
1333
1334 cleanup:
1335     if (status != NDIS_STATUS_SUCCESS) {
1336         OvsClearAllSwitchVports(switchContext);
1337     }
1338
1339     OvsFreeSwitchPortsArray(portArray);
1340
1341     OVS_LOG_TRACE("Exit: status: %x", status);
1342
1343     return status;
1344 }
1345
1346
1347 NDIS_STATUS
1348 OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext)
1349 {
1350     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1351     PNDIS_SWITCH_NIC_ARRAY nicArray = NULL;
1352     ULONG arrIndex;
1353     PNDIS_SWITCH_NIC_PARAMETERS nicParam;
1354     POVS_VPORT_ENTRY vport;
1355
1356     OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
1357     /*
1358      * Now, get NIC list.
1359      */
1360     status = OvsGetNicsOnSwitch(switchContext, &nicArray);
1361     if (status != NDIS_STATUS_SUCCESS) {
1362         goto cleanup;
1363     }
1364     for (arrIndex = 0; arrIndex < nicArray->NumElements; ++arrIndex) {
1365
1366         nicParam = NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray, arrIndex);
1367
1368         /*
1369          * XXX: Check if the port is configured with a VLAN. Disallow such a
1370          * configuration, since we don't support tag-in-tag.
1371          */
1372
1373         /*
1374          * XXX: Check if the port is connected to a VF. Disconnect the VF in
1375          * such a case.
1376          */
1377
1378         if (nicParam->NicType == NdisSwitchNicTypeExternal &&
1379             nicParam->NicIndex != 0) {
1380             POVS_VPORT_ENTRY virtExtVport =
1381                    (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
1382
1383             vport = OvsAllocateVport();
1384             if (vport) {
1385                 OvsInitPhysNicVport(vport, virtExtVport,
1386                                     nicParam->NicIndex);
1387                 status = InitHvVportCommon(switchContext, vport, TRUE);
1388                 if (status != NDIS_STATUS_SUCCESS) {
1389                     OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1390                     vport = NULL;
1391                 }
1392             }
1393         } else {
1394             vport = OvsFindVportByPortIdAndNicIndex(switchContext,
1395                                                     nicParam->PortId,
1396                                                     nicParam->NicIndex);
1397         }
1398         if (vport == NULL) {
1399             OVS_LOG_ERROR("Fail to allocate vport");
1400             continue;
1401         }
1402         OvsInitVportWithNicParam(switchContext, vport, nicParam);
1403         if (nicParam->NicType == NdisSwitchNicTypeInternal) {
1404             OvsInternalAdapterUp(vport->portNo, &nicParam->NetCfgInstanceId);
1405         }
1406     }
1407 cleanup:
1408
1409     OvsFreeSwitchNicsArray(nicArray);
1410
1411     OVS_LOG_TRACE("Exit: status: %x", status);
1412     return status;
1413 }
1414
1415 /*
1416  * --------------------------------------------------------------------------
1417  * Deletes ports added from the Hyper-V switch as well as OVS usersapce. The
1418  * function deletes ports in 'portIdHashArray'. This will delete most of the
1419  * ports that are in the 'portNoHashArray' as well. Any remaining ports
1420  * are deleted by walking the the 'portNoHashArray'.
1421  * --------------------------------------------------------------------------
1422  */
1423 VOID
1424 OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
1425 {
1426     for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1427         PLIST_ENTRY head, link, next;
1428
1429         head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
1430         LIST_FORALL_SAFE(head, link, next) {
1431             POVS_VPORT_ENTRY vport;
1432             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1433             OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
1434         }
1435     }
1436
1437     /*
1438      * Remove 'virtualExternalVport' as well. This port is not part of the
1439      * 'portIdHashArray'.
1440      */
1441     if (switchContext->virtualExternalVport) {
1442         OvsRemoveAndDeleteVport(NULL, switchContext,
1443             (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE);
1444     }
1445
1446
1447     for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1448         PLIST_ENTRY head, link, next;
1449         head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
1450         LIST_FORALL_SAFE(head, link, next) {
1451             POVS_VPORT_ENTRY vport;
1452             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1453             ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
1454                    (vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
1455                     vport->isBridgeInternal) || vport->isAbsentOnHv == TRUE);
1456             OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
1457         }
1458     }
1459
1460     ASSERT(switchContext->virtualExternalVport == NULL);
1461     ASSERT(switchContext->internalVport == NULL);
1462 }
1463
1464
1465 NTSTATUS
1466 OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
1467                                 CHAR *str,
1468                                 UINT16 maxStrLen)
1469 {
1470     ANSI_STRING astr;
1471     UNICODE_STRING ustr;
1472     NTSTATUS status;
1473     UINT32 size;
1474
1475     ustr.Buffer = wStr->String;
1476     ustr.Length = wStr->Length;
1477     ustr.MaximumLength = IF_MAX_STRING_SIZE;
1478
1479     astr.Buffer = str;
1480     astr.MaximumLength = maxStrLen;
1481     astr.Length = 0;
1482
1483     size = RtlUnicodeStringToAnsiSize(&ustr);
1484     if (size > maxStrLen) {
1485         return STATUS_BUFFER_OVERFLOW;
1486     }
1487
1488     status = RtlUnicodeStringToAnsiString(&astr, &ustr, FALSE);
1489
1490     ASSERT(status == STATUS_SUCCESS);
1491     if (status != STATUS_SUCCESS) {
1492         return status;
1493     }
1494     ASSERT(astr.Length <= maxStrLen);
1495     str[astr.Length] = 0;
1496     return STATUS_SUCCESS;
1497 }
1498
1499 /*
1500  * --------------------------------------------------------------------------
1501  * Utility function that populates a 'OVS_VPORT_EXT_INFO' structure for the
1502  * specified vport.
1503  * --------------------------------------------------------------------------
1504  */
1505 NTSTATUS
1506 OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
1507                    POVS_VPORT_EXT_INFO extInfo)
1508 {
1509     POVS_VPORT_ENTRY vport;
1510     size_t len;
1511     LOCK_STATE_EX lockState;
1512     NTSTATUS status = STATUS_SUCCESS;
1513     BOOLEAN doConvert = FALSE;
1514
1515     RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO));
1516     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1517     if (vportGet->portNo == 0) {
1518         StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
1519         vport = OvsFindVportByHvNameA(gOvsSwitchContext, vportGet->name);
1520         if (vport != NULL) {
1521             /* If the port is not a Hyper-V port and it has been added earlier,
1522              * we'll find it in 'ovsPortNameHashArray'. */
1523             vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name);
1524         }
1525     } else {
1526         vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo);
1527     }
1528     if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED &&
1529                           vport->ovsState != OVS_STATE_NIC_CREATED)) {
1530         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1531         if (vportGet->portNo) {
1532             OVS_LOG_WARN("vport %u does not exist any more", vportGet->portNo);
1533         } else {
1534             OVS_LOG_WARN("vport %s does not exist any more", vportGet->name);
1535         }
1536         status = STATUS_DEVICE_DOES_NOT_EXIST;
1537         goto ext_info_done;
1538     }
1539     extInfo->dpNo = vportGet->dpNo;
1540     extInfo->portNo = vport->portNo;
1541     RtlCopyMemory(extInfo->macAddress, vport->currMacAddress,
1542                   sizeof (vport->currMacAddress));
1543     RtlCopyMemory(extInfo->permMACAddress, vport->permMacAddress,
1544                   sizeof (vport->permMacAddress));
1545     if (vport->ovsType == OVS_VPORT_TYPE_NETDEV) {
1546         RtlCopyMemory(extInfo->vmMACAddress, vport->vmMacAddress,
1547                       sizeof (vport->vmMacAddress));
1548     }
1549     extInfo->nicIndex = vport->nicIndex;
1550     extInfo->portId = vport->portId;
1551     extInfo->type = vport->ovsType;
1552     extInfo->mtu = vport->mtu;
1553     /*
1554      * TO be revisit XXX
1555      */
1556     if (vport->ovsState == OVS_STATE_NIC_CREATED) {
1557        extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_DOWN;
1558     } else if (vport->ovsState == OVS_STATE_CONNECTED) {
1559        extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
1560     } else {
1561        extInfo->status = OVS_EVENT_DISCONNECT;
1562     }
1563     if (extInfo->type == OVS_VPORT_TYPE_NETDEV &&
1564         (vport->ovsState == OVS_STATE_NIC_CREATED  ||
1565          vport->ovsState == OVS_STATE_CONNECTED)) {
1566         doConvert = TRUE;
1567     } else {
1568         extInfo->vmUUID[0] = 0;
1569         extInfo->vifUUID[0] = 0;
1570     }
1571     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1572     if (doConvert) {
1573         status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName,
1574                                                  extInfo->name,
1575                                                  OVS_MAX_PORT_NAME_LENGTH);
1576         if (status != STATUS_SUCCESS) {
1577             OVS_LOG_INFO("Fail to convert NIC name.");
1578             extInfo->vmUUID[0] = 0;
1579         }
1580
1581         status = OvsConvertIfCountedStrToAnsiStr(&vport->vmName,
1582                                                  extInfo->vmUUID,
1583                                                  OVS_MAX_VM_UUID_LEN);
1584         if (status != STATUS_SUCCESS) {
1585             OVS_LOG_INFO("Fail to convert VM name.");
1586             extInfo->vmUUID[0] = 0;
1587         }
1588
1589         status = OvsConvertIfCountedStrToAnsiStr(&vport->nicName,
1590                                                  extInfo->vifUUID,
1591                                                  OVS_MAX_VIF_UUID_LEN);
1592         if (status != STATUS_SUCCESS) {
1593             OVS_LOG_INFO("Fail to convert nic UUID");
1594             extInfo->vifUUID[0] = 0;
1595         }
1596         /*
1597          * for now ignore status
1598          */
1599         status = STATUS_SUCCESS;
1600     }
1601
1602 ext_info_done:
1603     return status;
1604 }
1605
1606 /*
1607  * --------------------------------------------------------------------------
1608  *  Command Handler for 'OVS_WIN_NETDEV_CMD_GET'.
1609  * --------------------------------------------------------------------------
1610  */
1611 NTSTATUS
1612 OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1613                        UINT32 *replyLen)
1614 {
1615     NTSTATUS status = STATUS_SUCCESS;
1616     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1617     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1618     NL_ERROR nlError = NL_ERROR_SUCCESS;
1619     OVS_VPORT_GET vportGet;
1620     OVS_VPORT_EXT_INFO info;
1621
1622     static const NL_POLICY ovsNetdevPolicy[] = {
1623         [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING,
1624                                        .minLen = 2,
1625                                        .maxLen = IFNAMSIZ },
1626     };
1627     PNL_ATTR netdevAttrs[ARRAY_SIZE(ovsNetdevPolicy)];
1628
1629     /* input buffer has been validated while validating transaction dev op. */
1630     ASSERT(usrParamsCtx->inputBuffer != NULL &&
1631            usrParamsCtx->inputLength > sizeof *msgIn);
1632
1633     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1634         return STATUS_INVALID_BUFFER_SIZE;
1635     }
1636
1637     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1638         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1639         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1640         ovsNetdevPolicy, netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
1641         return STATUS_INVALID_PARAMETER;
1642     }
1643
1644     vportGet.portNo = 0;
1645     RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
1646                   NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
1647
1648     status = OvsGetExtInfoIoctl(&vportGet, &info);
1649     if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
1650         nlError = NL_ERROR_NODEV;
1651         goto cleanup;
1652     }
1653
1654     status = CreateNetlinkMesgForNetdev(&info, msgIn,
1655                  usrParamsCtx->outputBuffer, usrParamsCtx->outputLength,
1656                  gOvsSwitchContext->dpNo);
1657     if (status == STATUS_SUCCESS) {
1658         *replyLen = msgOut->nlMsg.nlmsgLen;
1659     }
1660
1661 cleanup:
1662     if (nlError != NL_ERROR_SUCCESS) {
1663         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1664             usrParamsCtx->outputBuffer;
1665
1666         NlBuildErrorMsg(msgIn, msgError, nlError);
1667         *replyLen = msgError->nlMsg.nlmsgLen;
1668     }
1669
1670     return STATUS_SUCCESS;
1671 }
1672
1673
1674 /*
1675  * --------------------------------------------------------------------------
1676  *  Utility function to construct an OVS_MESSAGE for the specified vport. The
1677  *  OVS_MESSAGE contains the output of a netdev command.
1678  * --------------------------------------------------------------------------
1679  */
1680 static NTSTATUS
1681 CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
1682                            POVS_MESSAGE msgIn,
1683                            PVOID outBuffer,
1684                            UINT32 outBufLen,
1685                            int dpIfIndex)
1686 {
1687     NL_BUFFER nlBuffer;
1688     BOOLEAN ok;
1689     PNL_MSG_HDR nlMsg;
1690     UINT32 netdevFlags = 0;
1691
1692     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1693
1694     ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1695                       msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1696                       msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1697                       dpIfIndex);
1698     if (!ok) {
1699         return STATUS_INVALID_BUFFER_SIZE;
1700     }
1701
1702     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
1703                          info->portNo);
1704     if (!ok) {
1705         return STATUS_INVALID_BUFFER_SIZE;
1706     }
1707
1708     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
1709     if (!ok) {
1710         return STATUS_INVALID_BUFFER_SIZE;
1711     }
1712
1713     ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
1714                             info->name);
1715     if (!ok) {
1716         return STATUS_INVALID_BUFFER_SIZE;
1717     }
1718
1719     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
1720              (PCHAR)info->macAddress, sizeof (info->macAddress));
1721     if (!ok) {
1722         return STATUS_INVALID_BUFFER_SIZE;
1723     }
1724
1725     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
1726     if (!ok) {
1727         return STATUS_INVALID_BUFFER_SIZE;
1728     }
1729
1730     if (info->status != OVS_EVENT_CONNECT) {
1731         netdevFlags = OVS_WIN_NETDEV_IFF_UP;
1732     }
1733     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
1734                          netdevFlags);
1735     if (!ok) {
1736         return STATUS_INVALID_BUFFER_SIZE;
1737     }
1738
1739     /*
1740      * XXX: add netdev_stats when we have the definition available in the
1741      * kernel.
1742      */
1743
1744     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1745     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1746
1747     return STATUS_SUCCESS;
1748 }
1749
1750 static __inline VOID
1751 OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec)
1752 {
1753     while ((!switchContext->isActivated) &&
1754           (!switchContext->isActivateFailed)) {
1755         /* Wait for the switch to be active and
1756          * the list of ports in OVS to be initialized. */
1757         NdisMSleep(sleepMicroSec);
1758     }
1759 }
1760
1761 static NTSTATUS
1762 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1763                       POVS_MESSAGE msgIn,
1764                       PVOID outBuffer,
1765                       UINT32 outBufLen,
1766                       int dpIfIndex)
1767 {
1768     NL_BUFFER nlBuffer;
1769     OVS_VPORT_FULL_STATS vportStats;
1770     BOOLEAN ok;
1771     PNL_MSG_HDR nlMsg;
1772
1773     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1774
1775     ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1776                       msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1777                       msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1778                       dpIfIndex);
1779     if (!ok) {
1780         return STATUS_INVALID_BUFFER_SIZE;
1781     }
1782
1783     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1784     if (!ok) {
1785         return STATUS_INVALID_BUFFER_SIZE;
1786     }
1787
1788     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1789     if (!ok) {
1790         return STATUS_INVALID_BUFFER_SIZE;
1791     }
1792
1793     ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1794     if (!ok) {
1795         return STATUS_INVALID_BUFFER_SIZE;
1796     }
1797
1798     /*
1799      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1800      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1801      * it means we have an array of pids, instead of a single pid.
1802      * ATM we assume we have one pid only.
1803     */
1804
1805     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1806                          vport->upcallPid);
1807     if (!ok) {
1808         return STATUS_INVALID_BUFFER_SIZE;
1809     }
1810
1811     /*stats*/
1812     vportStats.rxPackets = vport->stats.rxPackets;
1813     vportStats.rxBytes = vport->stats.rxBytes;
1814     vportStats.txPackets = vport->stats.txPackets;
1815     vportStats.txBytes = vport->stats.txBytes;
1816     vportStats.rxErrors = vport->errStats.rxErrors;
1817     vportStats.txErrors = vport->errStats.txErrors;
1818     vportStats.rxDropped = vport->errStats.rxDropped;
1819     vportStats.txDropped = vport->errStats.txDropped;
1820
1821     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1822                             (PCHAR)&vportStats,
1823                             sizeof(OVS_VPORT_FULL_STATS));
1824     if (!ok) {
1825         return STATUS_INVALID_BUFFER_SIZE;
1826     }
1827
1828     /*
1829      * XXX: when vxlan udp dest port becomes configurable, we will also need
1830      * to add vport options
1831     */
1832
1833     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1834     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1835
1836     return STATUS_SUCCESS;
1837 }
1838
1839 static NTSTATUS
1840 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1841                     UINT32 *replyLen)
1842 {
1843     POVS_MESSAGE msgIn;
1844     POVS_OPEN_INSTANCE instance =
1845         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1846     LOCK_STATE_EX lockState;
1847     UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1848
1849     /*
1850      * XXX: this function shares some code with other dump command(s).
1851      * In the future, we will need to refactor the dump functions
1852     */
1853
1854     ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1855
1856     if (instance->dumpState.ovsMsg == NULL) {
1857         ASSERT(FALSE);
1858         return STATUS_INVALID_DEVICE_STATE;
1859     }
1860
1861     /* Output buffer has been validated while validating read dev op. */
1862     ASSERT(usrParamsCtx->outputBuffer != NULL);
1863
1864     msgIn = instance->dumpState.ovsMsg;
1865
1866     /*
1867      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1868      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1869      * it means we have an array of pids, instead of a single pid.
1870      * ATM we assume we have one pid only.
1871     */
1872     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1873
1874     if (gOvsSwitchContext->numHvVports > 0 ||
1875             gOvsSwitchContext->numNonHvVports > 0) {
1876         /* inBucket: the bucket, used for lookup */
1877         UINT32 inBucket = instance->dumpState.index[0];
1878         /* inIndex: index within the given bucket, used for lookup */
1879         UINT32 inIndex = instance->dumpState.index[1];
1880         /* the bucket to be used for the next dump operation */
1881         UINT32 outBucket = 0;
1882         /* the index within the outBucket to be used for the next dump */
1883         UINT32 outIndex = 0;
1884
1885         for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1886             PLIST_ENTRY head, link;
1887             head = &(gOvsSwitchContext->portNoHashArray[i]);
1888             POVS_VPORT_ENTRY vport = NULL;
1889
1890             outIndex = 0;
1891             LIST_FORALL(head, link) {
1892
1893                 /*
1894                  * if one or more dumps were previously done on this same bucket,
1895                  * inIndex will be > 0, so we'll need to reply with the
1896                  * inIndex + 1 vport from the bucket.
1897                 */
1898                 if (outIndex >= inIndex) {
1899                     vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1900
1901                     ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
1902                     OvsCreateMsgFromVport(vport, msgIn,
1903                                           usrParamsCtx->outputBuffer,
1904                                           usrParamsCtx->outputLength,
1905                                           gOvsSwitchContext->dpNo);
1906                     ++outIndex;
1907                     break;
1908                 }
1909
1910                 ++outIndex;
1911             }
1912
1913             if (vport) {
1914                 break;
1915             }
1916
1917             /*
1918              * if no vport was found above, check the next bucket, beginning
1919              * with the first (i.e. index 0) elem from within that bucket
1920             */
1921             inIndex = 0;
1922         }
1923
1924         outBucket = i;
1925
1926         /* XXX: what about NLMSG_DONE (as msg type)? */
1927         instance->dumpState.index[0] = outBucket;
1928         instance->dumpState.index[1] = outIndex;
1929     }
1930
1931     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1932
1933     /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1934     if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1935         POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1936         *replyLen = msgOut->nlMsg.nlmsgLen;
1937     } else {
1938         /*
1939          * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1940          * it's dump done
1941          */
1942         *replyLen = 0;
1943         /* Free up the dump state, since there's no more data to continue. */
1944         FreeUserDumpState(instance);
1945     }
1946
1947     return STATUS_SUCCESS;
1948 }
1949
1950 static NTSTATUS
1951 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1952             UINT32 *replyLen)
1953 {
1954     NTSTATUS status = STATUS_SUCCESS;
1955     LOCK_STATE_EX lockState;
1956
1957     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1958     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1959     POVS_VPORT_ENTRY vport = NULL;
1960     NL_ERROR nlError = NL_ERROR_SUCCESS;
1961     PCHAR portName = NULL;
1962     UINT32 portNameLen = 0;
1963     UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
1964
1965     static const NL_POLICY ovsVportPolicy[] = {
1966         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1967         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1968                                   .minLen = 2,
1969                                   .maxLen = IFNAMSIZ,
1970                                   .optional = TRUE},
1971     };
1972     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1973
1974     /* input buffer has been validated while validating write dev op. */
1975     ASSERT(usrParamsCtx->inputBuffer != NULL);
1976
1977     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1978         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1979         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1980         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1981         return STATUS_INVALID_PARAMETER;
1982     }
1983
1984     /* Output buffer has been validated while validating transact dev op. */
1985     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1986
1987     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1988     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1989         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1990         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1991
1992         /* the port name is expected to be null-terminated */
1993         ASSERT(portName[portNameLen - 1] == '\0');
1994
1995         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1996     } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1997         portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1998
1999         vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
2000     } else {
2001         nlError = NL_ERROR_INVAL;
2002         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2003         goto Cleanup;
2004     }
2005
2006     if (!vport) {
2007         nlError = NL_ERROR_NODEV;
2008         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2009         goto Cleanup;
2010     }
2011
2012     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2013                                    usrParamsCtx->outputLength,
2014                                    gOvsSwitchContext->dpNo);
2015     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2016
2017     *replyLen = msgOut->nlMsg.nlmsgLen;
2018
2019 Cleanup:
2020     if (nlError != NL_ERROR_SUCCESS) {
2021         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2022             usrParamsCtx->outputBuffer;
2023
2024         NlBuildErrorMsg(msgIn, msgError, nlError);
2025         *replyLen = msgError->nlMsg.nlmsgLen;
2026     }
2027
2028     return STATUS_SUCCESS;
2029 }
2030
2031 /*
2032  * --------------------------------------------------------------------------
2033  *  Command Handler for 'OVS_VPORT_CMD_GET'.
2034  *
2035  *  The function handles the initial call to setup the dump state, as well as
2036  *  subsequent calls to continue dumping data.
2037  * --------------------------------------------------------------------------
2038 */
2039 NTSTATUS
2040 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2041                       UINT32 *replyLen)
2042 {
2043     *replyLen = 0;
2044
2045     switch (usrParamsCtx->devOp) {
2046     case OVS_WRITE_DEV_OP:
2047         return OvsSetupDumpStart(usrParamsCtx);
2048
2049     case OVS_READ_DEV_OP:
2050         return OvsGetVportDumpNext(usrParamsCtx, replyLen);
2051
2052     case OVS_TRANSACTION_DEV_OP:
2053         return OvsGetVport(usrParamsCtx, replyLen);
2054
2055     default:
2056         return STATUS_INVALID_DEVICE_REQUEST;
2057     }
2058
2059 }
2060
2061 static UINT32
2062 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
2063 {
2064     /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
2065     for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
2066         POVS_VPORT_ENTRY vport;
2067
2068         vport = OvsFindVportByPortNo(switchContext, i);
2069         if (!vport) {
2070             return i;
2071         }
2072     }
2073
2074     return OVS_DPPORT_NUMBER_INVALID;
2075 }
2076
2077 /*
2078  * --------------------------------------------------------------------------
2079  *  Command Handler for 'OVS_VPORT_CMD_NEW'.
2080  * --------------------------------------------------------------------------
2081  */
2082 NTSTATUS
2083 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2084                       UINT32 *replyLen)
2085 {
2086     NDIS_STATUS status = STATUS_SUCCESS;
2087     LOCK_STATE_EX lockState;
2088
2089     NL_ERROR nlError = NL_ERROR_SUCCESS;
2090     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2091     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2092     POVS_VPORT_ENTRY vport = NULL;
2093     PCHAR portName;
2094     ULONG portNameLen;
2095     UINT32 portType;
2096     BOOLEAN isBridgeInternal = FALSE;
2097     BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE;
2098     BOOLEAN addInternalPortAsNetdev = FALSE;
2099
2100     static const NL_POLICY ovsVportPolicy[] = {
2101         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2102         [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2103         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2104                                   .optional = FALSE},
2105         [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2106                                         .optional = FALSE },
2107         [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2108     };
2109
2110     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2111
2112     /* input buffer has been validated while validating write dev op. */
2113     ASSERT(usrParamsCtx->inputBuffer != NULL);
2114
2115     /* Output buffer has been validated while validating transact dev op. */
2116     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2117
2118     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2119         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2120         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2121         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2122         return STATUS_INVALID_PARAMETER;
2123     }
2124
2125     portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2126     portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2127     portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2128
2129     /* we are expecting null terminated strings to be passed */
2130     ASSERT(portName[portNameLen - 1] == '\0');
2131
2132     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2133
2134     vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2135     if (vport) {
2136         nlError = NL_ERROR_EXIST;
2137         goto Cleanup;
2138     }
2139
2140     if (portName && portType == OVS_VPORT_TYPE_NETDEV &&
2141         !strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
2142         addInternalPortAsNetdev = TRUE;
2143     }
2144
2145     if (portName && portType == OVS_VPORT_TYPE_INTERNAL &&
2146         strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
2147         isBridgeInternal = TRUE;
2148     }
2149
2150     if (portType == OVS_VPORT_TYPE_INTERNAL && !isBridgeInternal) {
2151         vport = gOvsSwitchContext->internalVport;
2152     } else if (portType == OVS_VPORT_TYPE_NETDEV) {
2153         /* External ports can also be looked up like VIF ports. */
2154         vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
2155     } else {
2156         ASSERT(OvsIsTunnelVportType(portType) ||
2157                (portType == OVS_VPORT_TYPE_INTERNAL && isBridgeInternal));
2158
2159         vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
2160         if (vport == NULL) {
2161             nlError = NL_ERROR_NOMEM;
2162             goto Cleanup;
2163         }
2164         vportAllocated = TRUE;
2165
2166         if (OvsIsTunnelVportType(portType)) {
2167             UINT16 transportPortDest = 0;
2168
2169             switch (portType) {
2170             case OVS_VPORT_TYPE_VXLAN:
2171                 transportPortDest = VXLAN_UDP_PORT;
2172                 break;
2173             case OVS_VPORT_TYPE_STT:
2174                 transportPortDest = STT_TCP_PORT;
2175                 break;
2176             default:
2177                 break;
2178             }
2179
2180             PNL_ATTR attr = NlAttrFindNested(vportAttrs[OVS_VPORT_ATTR_OPTIONS],
2181                                              OVS_TUNNEL_ATTR_DST_PORT);
2182             if (attr) {
2183                 transportPortDest = NlAttrGetU16(attr);
2184             }
2185
2186             status = OvsInitTunnelVport(usrParamsCtx,
2187                                         vport,
2188                                         portType,
2189                                         transportPortDest);
2190
2191             nlError = NlMapStatusToNlErr(status);
2192         } else {
2193             OvsInitBridgeInternalVport(vport);
2194         }
2195
2196         vportInitialized = TRUE;
2197
2198         if (nlError == NL_ERROR_SUCCESS) {
2199             vport->ovsState = OVS_STATE_CONNECTED;
2200             vport->nicState = NdisSwitchNicStateConnected;
2201
2202             /*
2203              * Allow the vport to be deleted, because there is no
2204              * corresponding hyper-v switch part.
2205              */
2206             vport->isAbsentOnHv = TRUE;
2207         } else {
2208             goto Cleanup;
2209         }
2210     }
2211
2212     if (!vport) {
2213         nlError = NL_ERROR_INVAL;
2214         goto Cleanup;
2215     }
2216     if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2217         nlError = NL_ERROR_EXIST;
2218         goto Cleanup;
2219     }
2220
2221     /* Initialize the vport with OVS specific properties. */
2222     if (addInternalPortAsNetdev != TRUE) {
2223         vport->ovsType = portType;
2224     }
2225     if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2226         /*
2227          * XXX: when we implement the limit for ovs port number to be
2228          * MAXUINT16, we'll need to check the port number received from the
2229          * userspace.
2230          */
2231         vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2232     } else {
2233         vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
2234         if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2235             nlError = NL_ERROR_NOMEM;
2236             goto Cleanup;
2237         }
2238     }
2239
2240     /* The ovs port name must be uninitialized. */
2241     ASSERT(vport->ovsName[0] == '\0');
2242     ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2243
2244     RtlCopyMemory(vport->ovsName, portName, portNameLen);
2245     /* if we don't have options, then vport->portOptions will be NULL */
2246     vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2247
2248     /*
2249      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2250      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2251      * it means we have an array of pids, instead of a single pid.
2252      * ATM we assume we have one pid only.
2253      */
2254     vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2255
2256     status = InitOvsVportCommon(gOvsSwitchContext, vport);
2257     ASSERT(status == STATUS_SUCCESS);
2258
2259     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2260                                    usrParamsCtx->outputLength,
2261                                    gOvsSwitchContext->dpNo);
2262
2263     *replyLen = msgOut->nlMsg.nlmsgLen;
2264
2265 Cleanup:
2266     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2267
2268     if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2269         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2270             usrParamsCtx->outputBuffer;
2271
2272         if (vport && vportAllocated == TRUE) {
2273             if (vportInitialized == TRUE) {
2274                 if (OvsIsTunnelVportType(portType)) {
2275                     switch (vport->ovsType) {
2276                     case OVS_VPORT_TYPE_VXLAN:
2277                         OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2278                         break;
2279                     case OVS_VPORT_TYPE_STT:
2280                         OvsCleanupSttTunnel(vport);;
2281                         break;
2282                     default:
2283                         ASSERT(!"Invalid tunnel port type");
2284                     }
2285                 }
2286             }
2287             OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2288         }
2289
2290         NlBuildErrorMsg(msgIn, msgError, nlError);
2291         *replyLen = msgError->nlMsg.nlmsgLen;
2292     }
2293
2294     return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2295 }
2296
2297
2298 /*
2299  * --------------------------------------------------------------------------
2300  *  Command Handler for 'OVS_VPORT_CMD_SET'.
2301  * --------------------------------------------------------------------------
2302  */
2303 NTSTATUS
2304 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2305                       UINT32 *replyLen)
2306 {
2307     NDIS_STATUS status = STATUS_SUCCESS;
2308     LOCK_STATE_EX lockState;
2309
2310     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2311     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2312     POVS_VPORT_ENTRY vport = NULL;
2313     NL_ERROR nlError = NL_ERROR_SUCCESS;
2314
2315     static const NL_POLICY ovsVportPolicy[] = {
2316         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2317         [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
2318         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2319                                   .optional = TRUE },
2320         [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2321                                         .optional = TRUE },
2322         [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
2323                                    .minLen = sizeof(OVS_VPORT_FULL_STATS),
2324                                    .maxLen = sizeof(OVS_VPORT_FULL_STATS),
2325                                    .optional = TRUE },
2326         [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2327     };
2328     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2329
2330     ASSERT(usrParamsCtx->inputBuffer != NULL);
2331
2332     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2333         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2334         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2335         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2336         return STATUS_INVALID_PARAMETER;
2337     }
2338
2339     /* Output buffer has been validated while validating transact dev op. */
2340     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2341
2342     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2343     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2344         PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2345 #ifdef DBG
2346         UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2347 #endif
2348         /* the port name is expected to be null-terminated */
2349         ASSERT(portName[portNameLen - 1] == '\0');
2350
2351         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2352     } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2353         vport = OvsFindVportByPortNo(gOvsSwitchContext,
2354                     NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2355     }
2356
2357     if (!vport) {
2358         nlError = NL_ERROR_NODEV;
2359         goto Cleanup;
2360     }
2361
2362     /*
2363      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2364      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2365      * it means we have an array of pids, instead of a single pid.
2366      * Currently, we support only one pid.
2367      */
2368     if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
2369         vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2370     }
2371
2372     if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
2373         OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2374         if (type != vport->ovsType) {
2375             nlError = NL_ERROR_INVAL;
2376             goto Cleanup;
2377         }
2378     }
2379
2380     if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2381         OVS_LOG_ERROR("Vport options not supported");
2382         nlError = NL_ERROR_NOTSUPP;
2383         goto Cleanup;
2384     }
2385
2386     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2387                                    usrParamsCtx->outputLength,
2388                                    gOvsSwitchContext->dpNo);
2389
2390     *replyLen = msgOut->nlMsg.nlmsgLen;
2391
2392 Cleanup:
2393     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2394
2395     if (nlError != NL_ERROR_SUCCESS) {
2396         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2397             usrParamsCtx->outputBuffer;
2398
2399         NlBuildErrorMsg(msgIn, msgError, nlError);
2400         *replyLen = msgError->nlMsg.nlmsgLen;
2401     }
2402
2403     return STATUS_SUCCESS;
2404 }
2405
2406 /*
2407  * --------------------------------------------------------------------------
2408  *  Command Handler for 'OVS_VPORT_CMD_DEL'.
2409  * --------------------------------------------------------------------------
2410  */
2411 NTSTATUS
2412 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2413                          UINT32 *replyLen)
2414 {
2415     NDIS_STATUS status = STATUS_SUCCESS;
2416     LOCK_STATE_EX lockState;
2417
2418     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2419     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2420     POVS_VPORT_ENTRY vport = NULL;
2421     NL_ERROR nlError = NL_ERROR_SUCCESS;
2422     PSTR portName = NULL;
2423     UINT32 portNameLen = 0;
2424
2425     static const NL_POLICY ovsVportPolicy[] = {
2426         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2427         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2428                                   .optional = TRUE },
2429     };
2430     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2431
2432     ASSERT(usrParamsCtx->inputBuffer != NULL);
2433
2434     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2435         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2436         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2437         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2438         return STATUS_INVALID_PARAMETER;
2439     }
2440
2441     /* Output buffer has been validated while validating transact dev op. */
2442     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2443
2444     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2445     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2446         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2447         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2448
2449         /* the port name is expected to be null-terminated */
2450         ASSERT(portName[portNameLen - 1] == '\0');
2451
2452         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2453     }
2454     else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2455         vport = OvsFindVportByPortNo(gOvsSwitchContext,
2456             NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2457     }
2458
2459     if (!vport) {
2460         nlError = NL_ERROR_NODEV;
2461         goto Cleanup;
2462     }
2463
2464     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2465                                    usrParamsCtx->outputLength,
2466                                    gOvsSwitchContext->dpNo);
2467
2468     *replyLen = msgOut->nlMsg.nlmsgLen;
2469
2470     /*
2471      * Mark the port as deleted from OVS userspace. If the port does not exist
2472      * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
2473      */
2474     status = OvsRemoveAndDeleteVport(usrParamsCtx,
2475                                      gOvsSwitchContext,
2476                                      vport,
2477                                      FALSE,
2478                                      TRUE);
2479     if (status) {
2480         nlError = NlMapStatusToNlErr(status);
2481     }
2482
2483 Cleanup:
2484     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2485
2486     if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2487         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2488             usrParamsCtx->outputBuffer;
2489
2490         NlBuildErrorMsg(msgIn, msgError, nlError);
2491         *replyLen = msgError->nlMsg.nlmsgLen;
2492     }
2493
2494     return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2495 }
2496
2497 static VOID
2498 OvsTunnelVportPendingRemove(PVOID context,
2499                             NTSTATUS status,
2500                             UINT32 *replyLen)
2501 {
2502     POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2503         (POVS_TUNFLT_INIT_CONTEXT) context;
2504     POVS_SWITCH_CONTEXT switchContext = tunnelContext->switchContext;
2505     POVS_VPORT_ENTRY vport = tunnelContext->vport;
2506     POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2507     POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2508     NL_ERROR nlError = NlMapStatusToNlErr(status);
2509     LOCK_STATE_EX lockState;
2510
2511     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
2512
2513     if (msgIn && msgOut) {
2514         /* Check the received status to reply to the caller. */
2515         if (STATUS_SUCCESS == status) {
2516             OvsCreateMsgFromVport(vport,
2517                                   msgIn,
2518                                   msgOut,
2519                                   tunnelContext->outputLength,
2520                                   switchContext->dpNo);
2521
2522             *replyLen = msgOut->nlMsg.nlmsgLen;
2523         } else {
2524             POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)msgOut;
2525
2526             NlBuildErrorMsg(msgIn, msgError, nlError);
2527             *replyLen = msgError->nlMsg.nlmsgLen;
2528         }
2529     }
2530
2531     ASSERT(vport->isAbsentOnHv == TRUE);
2532     ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
2533
2534     /* Remove the port from the relevant lists. */
2535     switchContext->numNonHvVports--;
2536     RemoveEntryList(&vport->ovsNameLink);
2537     RemoveEntryList(&vport->portNoLink);
2538     RemoveEntryList(&vport->tunnelVportLink);
2539
2540     if (vport->priv) {
2541         OvsFreeMemoryWithTag(vport->priv, OVS_VXLAN_POOL_TAG);
2542         vport->priv = NULL;
2543     }
2544
2545     OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2546
2547     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
2548 }
2549
2550 static VOID
2551 OvsTunnelVportPendingInit(PVOID context,
2552                           NTSTATUS status,
2553                           UINT32 *replyLen)
2554 {
2555     POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2556         (POVS_TUNFLT_INIT_CONTEXT) context;
2557     POVS_VPORT_ENTRY vport = tunnelContext->vport;
2558     POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2559     POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2560     PCHAR portName;
2561     ULONG portNameLen = 0;
2562     UINT32 portType = 0;
2563     NL_ERROR nlError = NL_ERROR_SUCCESS;
2564     BOOLEAN error = TRUE;
2565
2566     do {
2567         if (!NT_SUCCESS(status)) {
2568             nlError = NlMapStatusToNlErr(status);
2569             break;
2570         }
2571
2572         static const NL_POLICY ovsVportPolicy[] = {
2573             [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2574             [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2575             [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2576             .optional = FALSE },
2577             [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2578             .optional = FALSE },
2579             [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2580         };
2581
2582         PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2583
2584         /* input buffer has been validated while validating write dev op. */
2585         ASSERT(msgIn != NULL);
2586
2587         /* Output buffer has been validated while validating transact dev op. */
2588         ASSERT(msgOut != NULL && tunnelContext->outputLength >= sizeof *msgOut);
2589
2590         if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2591             NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2592             NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2593             ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2594             nlError = NL_ERROR_INVAL;
2595             break;
2596         }
2597
2598         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2599         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2600         portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2601
2602         if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2603             nlError = NL_ERROR_EXIST;
2604             break;
2605         }
2606
2607         vport->ovsState = OVS_STATE_CONNECTED;
2608         vport->nicState = NdisSwitchNicStateConnected;
2609
2610         /*
2611          * Allow the vport to be deleted, because there is no
2612          * corresponding hyper-v switch part.
2613          */
2614         vport->isAbsentOnHv = TRUE;
2615
2616         if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2617             /*
2618              * XXX: when we implement the limit for OVS port number to be
2619              * MAXUINT16, we'll need to check the port number received from the
2620              * userspace.
2621              */
2622             vport->portNo =
2623                 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2624         } else {
2625             vport->portNo =
2626                 OvsComputeVportNo(gOvsSwitchContext);
2627             if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2628                 nlError = NL_ERROR_NOMEM;
2629                 break;
2630             }
2631         }
2632
2633         /* The ovs port name must be uninitialized. */
2634         ASSERT(vport->ovsName[0] == '\0');
2635         ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2636
2637         RtlCopyMemory(vport->ovsName, portName, portNameLen);
2638         /* if we don't have options, then vport->portOptions will be NULL */
2639         vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2640
2641         /*
2642          * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2643          * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2644          * it means we have an array of pids, instead of a single pid.
2645          * ATM we assume we have one pid only.
2646          */
2647         vport->upcallPid =
2648             NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2649
2650         status = InitOvsVportCommon(gOvsSwitchContext, vport);
2651         ASSERT(status == STATUS_SUCCESS);
2652
2653         OvsCreateMsgFromVport(vport,
2654                               msgIn,
2655                               msgOut,
2656                               tunnelContext->outputLength,
2657                               gOvsSwitchContext->dpNo);
2658
2659         *replyLen = msgOut->nlMsg.nlmsgLen;
2660
2661         error = FALSE;
2662     } while (error);
2663
2664     if (error) {
2665         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) msgOut;
2666
2667         OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2668         OvsFreeMemory(vport);
2669
2670         NlBuildErrorMsg(msgIn, msgError, nlError);
2671         *replyLen = msgError->nlMsg.nlmsgLen;
2672     }
2673 }