datapath-windows: Process tunnel filter requests iteratively
[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, ARRAY_SIZE(ovsNetdevPolicy),
1638         netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
1639         return STATUS_INVALID_PARAMETER;
1640     }
1641
1642     vportGet.portNo = 0;
1643     RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
1644                   NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
1645
1646     status = OvsGetExtInfoIoctl(&vportGet, &info);
1647     if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
1648         nlError = NL_ERROR_NODEV;
1649         goto cleanup;
1650     }
1651
1652     status = CreateNetlinkMesgForNetdev(&info, msgIn,
1653                  usrParamsCtx->outputBuffer, usrParamsCtx->outputLength,
1654                  gOvsSwitchContext->dpNo);
1655     if (status == STATUS_SUCCESS) {
1656         *replyLen = msgOut->nlMsg.nlmsgLen;
1657     }
1658
1659 cleanup:
1660     if (nlError != NL_ERROR_SUCCESS) {
1661         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1662             usrParamsCtx->outputBuffer;
1663
1664         NlBuildErrorMsg(msgIn, msgError, nlError);
1665         *replyLen = msgError->nlMsg.nlmsgLen;
1666     }
1667
1668     return STATUS_SUCCESS;
1669 }
1670
1671
1672 /*
1673  * --------------------------------------------------------------------------
1674  *  Utility function to construct an OVS_MESSAGE for the specified vport. The
1675  *  OVS_MESSAGE contains the output of a netdev command.
1676  * --------------------------------------------------------------------------
1677  */
1678 static NTSTATUS
1679 CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
1680                            POVS_MESSAGE msgIn,
1681                            PVOID outBuffer,
1682                            UINT32 outBufLen,
1683                            int dpIfIndex)
1684 {
1685     NL_BUFFER nlBuffer;
1686     BOOLEAN ok;
1687     PNL_MSG_HDR nlMsg;
1688     UINT32 netdevFlags = 0;
1689
1690     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1691
1692     ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1693                       msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1694                       msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1695                       dpIfIndex);
1696     if (!ok) {
1697         return STATUS_INVALID_BUFFER_SIZE;
1698     }
1699
1700     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
1701                          info->portNo);
1702     if (!ok) {
1703         return STATUS_INVALID_BUFFER_SIZE;
1704     }
1705
1706     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
1707     if (!ok) {
1708         return STATUS_INVALID_BUFFER_SIZE;
1709     }
1710
1711     ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
1712                             info->name);
1713     if (!ok) {
1714         return STATUS_INVALID_BUFFER_SIZE;
1715     }
1716
1717     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
1718              (PCHAR)info->macAddress, sizeof (info->macAddress));
1719     if (!ok) {
1720         return STATUS_INVALID_BUFFER_SIZE;
1721     }
1722
1723     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
1724     if (!ok) {
1725         return STATUS_INVALID_BUFFER_SIZE;
1726     }
1727
1728     if (info->status != OVS_EVENT_CONNECT) {
1729         netdevFlags = OVS_WIN_NETDEV_IFF_UP;
1730     }
1731     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
1732                          netdevFlags);
1733     if (!ok) {
1734         return STATUS_INVALID_BUFFER_SIZE;
1735     }
1736
1737     /*
1738      * XXX: add netdev_stats when we have the definition available in the
1739      * kernel.
1740      */
1741
1742     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1743     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1744
1745     return STATUS_SUCCESS;
1746 }
1747
1748 static __inline VOID
1749 OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec)
1750 {
1751     while ((!switchContext->isActivated) &&
1752           (!switchContext->isActivateFailed)) {
1753         /* Wait for the switch to be active and
1754          * the list of ports in OVS to be initialized. */
1755         NdisMSleep(sleepMicroSec);
1756     }
1757 }
1758
1759 static NTSTATUS
1760 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1761                       POVS_MESSAGE msgIn,
1762                       PVOID outBuffer,
1763                       UINT32 outBufLen,
1764                       int dpIfIndex)
1765 {
1766     NL_BUFFER nlBuffer;
1767     OVS_VPORT_FULL_STATS vportStats;
1768     BOOLEAN ok;
1769     PNL_MSG_HDR nlMsg;
1770
1771     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1772
1773     ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1774                       msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1775                       msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1776                       dpIfIndex);
1777     if (!ok) {
1778         return STATUS_INVALID_BUFFER_SIZE;
1779     }
1780
1781     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1782     if (!ok) {
1783         return STATUS_INVALID_BUFFER_SIZE;
1784     }
1785
1786     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1787     if (!ok) {
1788         return STATUS_INVALID_BUFFER_SIZE;
1789     }
1790
1791     ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1792     if (!ok) {
1793         return STATUS_INVALID_BUFFER_SIZE;
1794     }
1795
1796     /*
1797      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1798      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1799      * it means we have an array of pids, instead of a single pid.
1800      * ATM we assume we have one pid only.
1801     */
1802
1803     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1804                          vport->upcallPid);
1805     if (!ok) {
1806         return STATUS_INVALID_BUFFER_SIZE;
1807     }
1808
1809     /*stats*/
1810     vportStats.rxPackets = vport->stats.rxPackets;
1811     vportStats.rxBytes = vport->stats.rxBytes;
1812     vportStats.txPackets = vport->stats.txPackets;
1813     vportStats.txBytes = vport->stats.txBytes;
1814     vportStats.rxErrors = vport->errStats.rxErrors;
1815     vportStats.txErrors = vport->errStats.txErrors;
1816     vportStats.rxDropped = vport->errStats.rxDropped;
1817     vportStats.txDropped = vport->errStats.txDropped;
1818
1819     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1820                             (PCHAR)&vportStats,
1821                             sizeof(OVS_VPORT_FULL_STATS));
1822     if (!ok) {
1823         return STATUS_INVALID_BUFFER_SIZE;
1824     }
1825
1826     /*
1827      * XXX: when vxlan udp dest port becomes configurable, we will also need
1828      * to add vport options
1829     */
1830
1831     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1832     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1833
1834     return STATUS_SUCCESS;
1835 }
1836
1837 static NTSTATUS
1838 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1839                     UINT32 *replyLen)
1840 {
1841     POVS_MESSAGE msgIn;
1842     POVS_OPEN_INSTANCE instance =
1843         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1844     LOCK_STATE_EX lockState;
1845     UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1846
1847     /*
1848      * XXX: this function shares some code with other dump command(s).
1849      * In the future, we will need to refactor the dump functions
1850     */
1851
1852     ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1853
1854     if (instance->dumpState.ovsMsg == NULL) {
1855         ASSERT(FALSE);
1856         return STATUS_INVALID_DEVICE_STATE;
1857     }
1858
1859     /* Output buffer has been validated while validating read dev op. */
1860     ASSERT(usrParamsCtx->outputBuffer != NULL);
1861
1862     msgIn = instance->dumpState.ovsMsg;
1863
1864     /*
1865      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1866      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1867      * it means we have an array of pids, instead of a single pid.
1868      * ATM we assume we have one pid only.
1869     */
1870     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1871
1872     if (gOvsSwitchContext->numHvVports > 0 ||
1873             gOvsSwitchContext->numNonHvVports > 0) {
1874         /* inBucket: the bucket, used for lookup */
1875         UINT32 inBucket = instance->dumpState.index[0];
1876         /* inIndex: index within the given bucket, used for lookup */
1877         UINT32 inIndex = instance->dumpState.index[1];
1878         /* the bucket to be used for the next dump operation */
1879         UINT32 outBucket = 0;
1880         /* the index within the outBucket to be used for the next dump */
1881         UINT32 outIndex = 0;
1882
1883         for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1884             PLIST_ENTRY head, link;
1885             head = &(gOvsSwitchContext->portNoHashArray[i]);
1886             POVS_VPORT_ENTRY vport = NULL;
1887
1888             outIndex = 0;
1889             LIST_FORALL(head, link) {
1890
1891                 /*
1892                  * if one or more dumps were previously done on this same bucket,
1893                  * inIndex will be > 0, so we'll need to reply with the
1894                  * inIndex + 1 vport from the bucket.
1895                 */
1896                 if (outIndex >= inIndex) {
1897                     vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1898
1899                     ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
1900                     OvsCreateMsgFromVport(vport, msgIn,
1901                                           usrParamsCtx->outputBuffer,
1902                                           usrParamsCtx->outputLength,
1903                                           gOvsSwitchContext->dpNo);
1904                     ++outIndex;
1905                     break;
1906                 }
1907
1908                 ++outIndex;
1909             }
1910
1911             if (vport) {
1912                 break;
1913             }
1914
1915             /*
1916              * if no vport was found above, check the next bucket, beginning
1917              * with the first (i.e. index 0) elem from within that bucket
1918             */
1919             inIndex = 0;
1920         }
1921
1922         outBucket = i;
1923
1924         /* XXX: what about NLMSG_DONE (as msg type)? */
1925         instance->dumpState.index[0] = outBucket;
1926         instance->dumpState.index[1] = outIndex;
1927     }
1928
1929     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1930
1931     /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1932     if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1933         POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1934         *replyLen = msgOut->nlMsg.nlmsgLen;
1935     } else {
1936         /*
1937          * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1938          * it's dump done
1939          */
1940         *replyLen = 0;
1941         /* Free up the dump state, since there's no more data to continue. */
1942         FreeUserDumpState(instance);
1943     }
1944
1945     return STATUS_SUCCESS;
1946 }
1947
1948 static NTSTATUS
1949 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1950             UINT32 *replyLen)
1951 {
1952     NTSTATUS status = STATUS_SUCCESS;
1953     LOCK_STATE_EX lockState;
1954
1955     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1956     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1957     POVS_VPORT_ENTRY vport = NULL;
1958     NL_ERROR nlError = NL_ERROR_SUCCESS;
1959     PCHAR portName = NULL;
1960     UINT32 portNameLen = 0;
1961     UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
1962
1963     static const NL_POLICY ovsVportPolicy[] = {
1964         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1965         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1966                                   .minLen = 2,
1967                                   .maxLen = IFNAMSIZ,
1968                                   .optional = TRUE},
1969     };
1970     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1971
1972     /* input buffer has been validated while validating write dev op. */
1973     ASSERT(usrParamsCtx->inputBuffer != NULL);
1974
1975     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1976         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1977         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1978         ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
1979         vportAttrs, ARRAY_SIZE(vportAttrs))) {
1980         return STATUS_INVALID_PARAMETER;
1981     }
1982
1983     /* Output buffer has been validated while validating transact dev op. */
1984     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1985
1986     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1987     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1988         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1989         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1990
1991         /* the port name is expected to be null-terminated */
1992         ASSERT(portName[portNameLen - 1] == '\0');
1993
1994         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1995     } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1996         portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1997
1998         vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
1999     } else {
2000         nlError = NL_ERROR_INVAL;
2001         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2002         goto Cleanup;
2003     }
2004
2005     if (!vport) {
2006         nlError = NL_ERROR_NODEV;
2007         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2008         goto Cleanup;
2009     }
2010
2011     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2012                                    usrParamsCtx->outputLength,
2013                                    gOvsSwitchContext->dpNo);
2014     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2015
2016     *replyLen = msgOut->nlMsg.nlmsgLen;
2017
2018 Cleanup:
2019     if (nlError != NL_ERROR_SUCCESS) {
2020         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2021             usrParamsCtx->outputBuffer;
2022
2023         NlBuildErrorMsg(msgIn, msgError, nlError);
2024         *replyLen = msgError->nlMsg.nlmsgLen;
2025     }
2026
2027     return STATUS_SUCCESS;
2028 }
2029
2030 /*
2031  * --------------------------------------------------------------------------
2032  *  Command Handler for 'OVS_VPORT_CMD_GET'.
2033  *
2034  *  The function handles the initial call to setup the dump state, as well as
2035  *  subsequent calls to continue dumping data.
2036  * --------------------------------------------------------------------------
2037 */
2038 NTSTATUS
2039 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2040                       UINT32 *replyLen)
2041 {
2042     *replyLen = 0;
2043
2044     switch (usrParamsCtx->devOp) {
2045     case OVS_WRITE_DEV_OP:
2046         return OvsSetupDumpStart(usrParamsCtx);
2047
2048     case OVS_READ_DEV_OP:
2049         return OvsGetVportDumpNext(usrParamsCtx, replyLen);
2050
2051     case OVS_TRANSACTION_DEV_OP:
2052         return OvsGetVport(usrParamsCtx, replyLen);
2053
2054     default:
2055         return STATUS_INVALID_DEVICE_REQUEST;
2056     }
2057
2058 }
2059
2060 static UINT32
2061 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
2062 {
2063     /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
2064     for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
2065         POVS_VPORT_ENTRY vport;
2066
2067         vport = OvsFindVportByPortNo(switchContext, i);
2068         if (!vport) {
2069             return i;
2070         }
2071     }
2072
2073     return OVS_DPPORT_NUMBER_INVALID;
2074 }
2075
2076 /*
2077  * --------------------------------------------------------------------------
2078  *  Command Handler for 'OVS_VPORT_CMD_NEW'.
2079  * --------------------------------------------------------------------------
2080  */
2081 NTSTATUS
2082 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2083                       UINT32 *replyLen)
2084 {
2085     NDIS_STATUS status = STATUS_SUCCESS;
2086     LOCK_STATE_EX lockState;
2087
2088     NL_ERROR nlError = NL_ERROR_SUCCESS;
2089     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2090     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2091     POVS_VPORT_ENTRY vport = NULL;
2092     PCHAR portName;
2093     ULONG portNameLen;
2094     UINT32 portType;
2095     BOOLEAN isBridgeInternal = FALSE;
2096     BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE;
2097     BOOLEAN addInternalPortAsNetdev = FALSE;
2098
2099     static const NL_POLICY ovsVportPolicy[] = {
2100         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2101         [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2102         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2103                                   .optional = FALSE},
2104         [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2105                                         .optional = FALSE },
2106         [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2107     };
2108
2109     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2110
2111     /* input buffer has been validated while validating write dev op. */
2112     ASSERT(usrParamsCtx->inputBuffer != NULL);
2113
2114     /* Output buffer has been validated while validating transact dev op. */
2115     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2116
2117     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2118         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2119         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2120         ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2121         vportAttrs, ARRAY_SIZE(vportAttrs))) {
2122         return STATUS_INVALID_PARAMETER;
2123     }
2124
2125     portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2126     portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2127     portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2128
2129     /* we are expecting null terminated strings to be passed */
2130     ASSERT(portName[portNameLen - 1] == '\0');
2131
2132     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2133
2134     vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2135     if (vport) {
2136         nlError = NL_ERROR_EXIST;
2137         goto Cleanup;
2138     }
2139
2140     if (portName && portType == OVS_VPORT_TYPE_NETDEV &&
2141         !strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
2142         addInternalPortAsNetdev = TRUE;
2143     }
2144
2145     if (portName && portType == OVS_VPORT_TYPE_INTERNAL &&
2146         strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
2147         isBridgeInternal = TRUE;
2148     }
2149
2150     if (portType == OVS_VPORT_TYPE_INTERNAL && !isBridgeInternal) {
2151         vport = gOvsSwitchContext->internalVport;
2152     } else if (portType == OVS_VPORT_TYPE_NETDEV) {
2153         /* External ports can also be looked up like VIF ports. */
2154         vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
2155     } else {
2156         ASSERT(OvsIsTunnelVportType(portType) ||
2157                (portType == OVS_VPORT_TYPE_INTERNAL && isBridgeInternal));
2158
2159         vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
2160         if (vport == NULL) {
2161             nlError = NL_ERROR_NOMEM;
2162             goto Cleanup;
2163         }
2164         vportAllocated = TRUE;
2165
2166         if (OvsIsTunnelVportType(portType)) {
2167             UINT16 transportPortDest = 0;
2168
2169             switch (portType) {
2170             case OVS_VPORT_TYPE_VXLAN:
2171                 transportPortDest = VXLAN_UDP_PORT;
2172                 break;
2173             case OVS_VPORT_TYPE_STT:
2174                 transportPortDest = STT_TCP_PORT;
2175                 break;
2176             default:
2177                 break;
2178             }
2179
2180             PNL_ATTR attr = NlAttrFindNested(vportAttrs[OVS_VPORT_ATTR_OPTIONS],
2181                                              OVS_TUNNEL_ATTR_DST_PORT);
2182             if (attr) {
2183                 transportPortDest = NlAttrGetU16(attr);
2184             }
2185
2186             status = OvsInitTunnelVport(usrParamsCtx,
2187                                         vport,
2188                                         portType,
2189                                         transportPortDest);
2190
2191             nlError = NlMapStatusToNlErr(status);
2192         } else {
2193             OvsInitBridgeInternalVport(vport);
2194         }
2195
2196         vportInitialized = TRUE;
2197
2198         if (nlError == NL_ERROR_SUCCESS) {
2199             vport->ovsState = OVS_STATE_CONNECTED;
2200             vport->nicState = NdisSwitchNicStateConnected;
2201
2202             /*
2203              * Allow the vport to be deleted, because there is no
2204              * corresponding hyper-v switch part.
2205              */
2206             vport->isAbsentOnHv = TRUE;
2207         } else {
2208             goto Cleanup;
2209         }
2210     }
2211
2212     if (!vport) {
2213         nlError = NL_ERROR_INVAL;
2214         goto Cleanup;
2215     }
2216     if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2217         nlError = NL_ERROR_EXIST;
2218         goto Cleanup;
2219     }
2220
2221     /* Initialize the vport with OVS specific properties. */
2222     if (addInternalPortAsNetdev != TRUE) {
2223         vport->ovsType = portType;
2224     }
2225     if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2226         /*
2227          * XXX: when we implement the limit for ovs port number to be
2228          * MAXUINT16, we'll need to check the port number received from the
2229          * userspace.
2230          */
2231         vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2232     } else {
2233         vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
2234         if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2235             nlError = NL_ERROR_NOMEM;
2236             goto Cleanup;
2237         }
2238     }
2239
2240     /* The ovs port name must be uninitialized. */
2241     ASSERT(vport->ovsName[0] == '\0');
2242     ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2243
2244     RtlCopyMemory(vport->ovsName, portName, portNameLen);
2245     /* if we don't have options, then vport->portOptions will be NULL */
2246     vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2247
2248     /*
2249      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2250      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2251      * it means we have an array of pids, instead of a single pid.
2252      * ATM we assume we have one pid only.
2253      */
2254     vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2255
2256     status = InitOvsVportCommon(gOvsSwitchContext, vport);
2257     ASSERT(status == STATUS_SUCCESS);
2258
2259     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2260                                    usrParamsCtx->outputLength,
2261                                    gOvsSwitchContext->dpNo);
2262
2263     *replyLen = msgOut->nlMsg.nlmsgLen;
2264
2265 Cleanup:
2266     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2267
2268     if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2269         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2270             usrParamsCtx->outputBuffer;
2271
2272         if (vport && vportAllocated == TRUE) {
2273             if (vportInitialized == TRUE) {
2274                 if (OvsIsTunnelVportType(portType)) {
2275                     switch (vport->ovsType) {
2276                     case OVS_VPORT_TYPE_VXLAN:
2277                         OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2278                         break;
2279                     case OVS_VPORT_TYPE_STT:
2280                         OvsCleanupSttTunnel(vport);
2281                         break;
2282                     default:
2283                         ASSERT(!"Invalid tunnel port type");
2284                     }
2285                 }
2286             }
2287             OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2288         }
2289
2290         NlBuildErrorMsg(msgIn, msgError, nlError);
2291         *replyLen = msgError->nlMsg.nlmsgLen;
2292     }
2293
2294     return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2295 }
2296
2297
2298 /*
2299  * --------------------------------------------------------------------------
2300  *  Command Handler for 'OVS_VPORT_CMD_SET'.
2301  * --------------------------------------------------------------------------
2302  */
2303 NTSTATUS
2304 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2305                       UINT32 *replyLen)
2306 {
2307     NDIS_STATUS status = STATUS_SUCCESS;
2308     LOCK_STATE_EX lockState;
2309
2310     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2311     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2312     POVS_VPORT_ENTRY vport = NULL;
2313     NL_ERROR nlError = NL_ERROR_SUCCESS;
2314
2315     static const NL_POLICY ovsVportPolicy[] = {
2316         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2317         [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
2318         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2319                                   .optional = TRUE },
2320         [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2321                                         .optional = TRUE },
2322         [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
2323                                    .minLen = sizeof(OVS_VPORT_FULL_STATS),
2324                                    .maxLen = sizeof(OVS_VPORT_FULL_STATS),
2325                                    .optional = TRUE },
2326         [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2327     };
2328     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2329
2330     ASSERT(usrParamsCtx->inputBuffer != NULL);
2331
2332     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2333         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2334         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2335         ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2336         vportAttrs, ARRAY_SIZE(vportAttrs))) {
2337         return STATUS_INVALID_PARAMETER;
2338     }
2339
2340     /* Output buffer has been validated while validating transact dev op. */
2341     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2342
2343     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2344     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2345         PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2346 #ifdef DBG
2347         UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2348 #endif
2349         /* the port name is expected to be null-terminated */
2350         ASSERT(portName[portNameLen - 1] == '\0');
2351
2352         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2353     } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2354         vport = OvsFindVportByPortNo(gOvsSwitchContext,
2355                     NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2356     }
2357
2358     if (!vport) {
2359         nlError = NL_ERROR_NODEV;
2360         goto Cleanup;
2361     }
2362
2363     /*
2364      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2365      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2366      * it means we have an array of pids, instead of a single pid.
2367      * Currently, we support only one pid.
2368      */
2369     if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
2370         vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2371     }
2372
2373     if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
2374         OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2375         if (type != vport->ovsType) {
2376             nlError = NL_ERROR_INVAL;
2377             goto Cleanup;
2378         }
2379     }
2380
2381     if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2382         OVS_LOG_ERROR("Vport options not supported");
2383         nlError = NL_ERROR_NOTSUPP;
2384         goto Cleanup;
2385     }
2386
2387     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2388                                    usrParamsCtx->outputLength,
2389                                    gOvsSwitchContext->dpNo);
2390
2391     *replyLen = msgOut->nlMsg.nlmsgLen;
2392
2393 Cleanup:
2394     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2395
2396     if (nlError != NL_ERROR_SUCCESS) {
2397         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2398             usrParamsCtx->outputBuffer;
2399
2400         NlBuildErrorMsg(msgIn, msgError, nlError);
2401         *replyLen = msgError->nlMsg.nlmsgLen;
2402     }
2403
2404     return STATUS_SUCCESS;
2405 }
2406
2407 /*
2408  * --------------------------------------------------------------------------
2409  *  Command Handler for 'OVS_VPORT_CMD_DEL'.
2410  * --------------------------------------------------------------------------
2411  */
2412 NTSTATUS
2413 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2414                          UINT32 *replyLen)
2415 {
2416     NDIS_STATUS status = STATUS_SUCCESS;
2417     LOCK_STATE_EX lockState;
2418
2419     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2420     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2421     POVS_VPORT_ENTRY vport = NULL;
2422     NL_ERROR nlError = NL_ERROR_SUCCESS;
2423     PSTR portName = NULL;
2424     UINT32 portNameLen = 0;
2425
2426     static const NL_POLICY ovsVportPolicy[] = {
2427         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2428         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2429                                   .optional = TRUE },
2430     };
2431     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2432
2433     ASSERT(usrParamsCtx->inputBuffer != NULL);
2434
2435     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2436         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2437         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2438         ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2439         vportAttrs, ARRAY_SIZE(vportAttrs))) {
2440         return STATUS_INVALID_PARAMETER;
2441     }
2442
2443     /* Output buffer has been validated while validating transact dev op. */
2444     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2445
2446     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2447     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2448         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2449         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2450
2451         /* the port name is expected to be null-terminated */
2452         ASSERT(portName[portNameLen - 1] == '\0');
2453
2454         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2455     }
2456     else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2457         vport = OvsFindVportByPortNo(gOvsSwitchContext,
2458             NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2459     }
2460
2461     if (!vport) {
2462         nlError = NL_ERROR_NODEV;
2463         goto Cleanup;
2464     }
2465
2466     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2467                                    usrParamsCtx->outputLength,
2468                                    gOvsSwitchContext->dpNo);
2469
2470     *replyLen = msgOut->nlMsg.nlmsgLen;
2471
2472     /*
2473      * Mark the port as deleted from OVS userspace. If the port does not exist
2474      * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
2475      */
2476     status = OvsRemoveAndDeleteVport(usrParamsCtx,
2477                                      gOvsSwitchContext,
2478                                      vport,
2479                                      FALSE,
2480                                      TRUE);
2481     if (status) {
2482         nlError = NlMapStatusToNlErr(status);
2483     }
2484
2485 Cleanup:
2486     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2487
2488     if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2489         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2490             usrParamsCtx->outputBuffer;
2491
2492         NlBuildErrorMsg(msgIn, msgError, nlError);
2493         *replyLen = msgError->nlMsg.nlmsgLen;
2494     }
2495
2496     return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2497 }
2498
2499 static VOID
2500 OvsTunnelVportPendingRemove(PVOID context,
2501                             NTSTATUS status,
2502                             UINT32 *replyLen)
2503 {
2504     POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2505         (POVS_TUNFLT_INIT_CONTEXT) context;
2506     POVS_SWITCH_CONTEXT switchContext = tunnelContext->switchContext;
2507     POVS_VPORT_ENTRY vport = tunnelContext->vport;
2508     POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2509     POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2510     NL_ERROR nlError = NlMapStatusToNlErr(status);
2511     LOCK_STATE_EX lockState;
2512
2513     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
2514
2515     if (msgIn && msgOut) {
2516         /* Check the received status to reply to the caller. */
2517         if (STATUS_SUCCESS == status) {
2518             OvsCreateMsgFromVport(vport,
2519                                   msgIn,
2520                                   msgOut,
2521                                   tunnelContext->outputLength,
2522                                   switchContext->dpNo);
2523
2524             *replyLen = msgOut->nlMsg.nlmsgLen;
2525         } else {
2526             POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)msgOut;
2527
2528             NlBuildErrorMsg(msgIn, msgError, nlError);
2529             *replyLen = msgError->nlMsg.nlmsgLen;
2530         }
2531     }
2532
2533     ASSERT(vport->isAbsentOnHv == TRUE);
2534     ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
2535
2536     /* Remove the port from the relevant lists. */
2537     switchContext->numNonHvVports--;
2538     RemoveEntryList(&vport->ovsNameLink);
2539     RemoveEntryList(&vport->portNoLink);
2540     RemoveEntryList(&vport->tunnelVportLink);
2541
2542     if (vport->priv) {
2543         OvsFreeMemoryWithTag(vport->priv, OVS_VXLAN_POOL_TAG);
2544         vport->priv = NULL;
2545     }
2546
2547     OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2548
2549     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
2550 }
2551
2552 static VOID
2553 OvsTunnelVportPendingInit(PVOID context,
2554                           NTSTATUS status,
2555                           UINT32 *replyLen)
2556 {
2557     POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2558         (POVS_TUNFLT_INIT_CONTEXT) context;
2559     POVS_VPORT_ENTRY vport = tunnelContext->vport;
2560     POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2561     POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2562     PCHAR portName;
2563     ULONG portNameLen = 0;
2564     UINT32 portType = 0;
2565     NL_ERROR nlError = NL_ERROR_SUCCESS;
2566     BOOLEAN error = TRUE;
2567
2568     do {
2569         if (!NT_SUCCESS(status)) {
2570             nlError = NlMapStatusToNlErr(status);
2571             break;
2572         }
2573
2574         static const NL_POLICY ovsVportPolicy[] = {
2575             [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2576             [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2577             [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2578             .optional = FALSE },
2579             [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2580             .optional = FALSE },
2581             [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2582         };
2583
2584         PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2585
2586         /* input buffer has been validated while validating write dev op. */
2587         ASSERT(msgIn != NULL);
2588
2589         /* Output buffer has been validated while validating transact dev op. */
2590         ASSERT(msgOut != NULL && tunnelContext->outputLength >= sizeof *msgOut);
2591
2592         if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2593             NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2594             NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2595             ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
2596             vportAttrs, ARRAY_SIZE(vportAttrs))) {
2597             nlError = NL_ERROR_INVAL;
2598             break;
2599         }
2600
2601         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2602         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2603         portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2604
2605         if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2606             nlError = NL_ERROR_EXIST;
2607             break;
2608         }
2609
2610         vport->ovsState = OVS_STATE_CONNECTED;
2611         vport->nicState = NdisSwitchNicStateConnected;
2612
2613         /*
2614          * Allow the vport to be deleted, because there is no
2615          * corresponding hyper-v switch part.
2616          */
2617         vport->isAbsentOnHv = TRUE;
2618
2619         if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2620             /*
2621              * XXX: when we implement the limit for OVS port number to be
2622              * MAXUINT16, we'll need to check the port number received from the
2623              * userspace.
2624              */
2625             vport->portNo =
2626                 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2627         } else {
2628             vport->portNo =
2629                 OvsComputeVportNo(gOvsSwitchContext);
2630             if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2631                 nlError = NL_ERROR_NOMEM;
2632                 break;
2633             }
2634         }
2635
2636         /* The ovs port name must be uninitialized. */
2637         ASSERT(vport->ovsName[0] == '\0');
2638         ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2639
2640         RtlCopyMemory(vport->ovsName, portName, portNameLen);
2641         /* if we don't have options, then vport->portOptions will be NULL */
2642         vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2643
2644         /*
2645          * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2646          * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2647          * it means we have an array of pids, instead of a single pid.
2648          * ATM we assume we have one pid only.
2649          */
2650         vport->upcallPid =
2651             NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2652
2653         status = InitOvsVportCommon(gOvsSwitchContext, vport);
2654         ASSERT(status == STATUS_SUCCESS);
2655
2656         OvsCreateMsgFromVport(vport,
2657                               msgIn,
2658                               msgOut,
2659                               tunnelContext->outputLength,
2660                               gOvsSwitchContext->dpNo);
2661
2662         *replyLen = msgOut->nlMsg.nlmsgLen;
2663
2664         error = FALSE;
2665     } while (error);
2666
2667     if (error) {
2668         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) msgOut;
2669
2670         OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2671         OvsFreeMemory(vport);
2672
2673         NlBuildErrorMsg(msgIn, msgError, nlError);
2674         *replyLen = msgError->nlMsg.nlmsgLen;
2675     }
2676 }