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