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