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