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