datapath-windows: Changes to InitOvsVportCommon()
[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 "IpHelper.h"
25 #include "Oid.h"
26 #include "Datapath.h"
27
28 #ifdef OVS_DBG_MOD
29 #undef OVS_DBG_MOD
30 #endif
31 #define OVS_DBG_MOD OVS_DBG_VPORT
32 #include "Debug.h"
33
34 #define VPORT_NIC_ENTER(_nic) \
35     OVS_LOG_TRACE("Enter: PortId: %x, NicIndex: %d", _nic->PortId, \
36                                                      _nic->NicIndex)
37
38 #define VPORT_NIC_EXIT(_nic) \
39     OVS_LOG_TRACE("Exit: PortId: %x, NicIndex: %d", _nic->PortId, \
40                                                     _nic->NicIndex)
41
42 #define VPORT_PORT_ENTER(_port) \
43     OVS_LOG_TRACE("Enter: PortId: %x", _port->PortId)
44
45 #define VPORT_PORT_EXIT(_port) \
46     OVS_LOG_TRACE("Exit: PortId: %x", _port->PortId)
47
48 #define OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC    100
49
50 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
51 extern PNDIS_SPIN_LOCK gOvsCtrlLock;
52
53 static VOID OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
54                 PNDIS_SWITCH_PORT_PARAMETERS portParam);
55 static VOID OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
56                 POVS_VPORT_ENTRY vport, PNDIS_SWITCH_NIC_PARAMETERS nicParam);
57 static VOID OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVPort,
58                 POVS_VPORT_ENTRY virtExtVport, UINT32 nicIndex);
59 static __inline VOID OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext,
60                                      ULONG sleepMicroSec);
61 static NTSTATUS OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
62                                    POVS_VPORT_EXT_INFO extInfo);
63 static NTSTATUS CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
64                                            POVS_MESSAGE msgIn,
65                                            PVOID outBuffer,
66                                            UINT32 outBufLen,
67                                            int dpIfIndex);
68 static POVS_VPORT_ENTRY OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
69                                               PWSTR wsName, SIZE_T wstrSize);
70 static NDIS_STATUS InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
71                                      POVS_VPORT_ENTRY vport,
72                                      BOOLEAN newPort);
73
74 /*
75  * Functions implemented in relaton to NDIS port manipulation.
76  */
77 NDIS_STATUS
78 HvCreatePort(POVS_SWITCH_CONTEXT switchContext,
79              PNDIS_SWITCH_PORT_PARAMETERS portParam)
80 {
81     POVS_VPORT_ENTRY vport;
82     LOCK_STATE_EX lockState;
83     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
84
85     VPORT_PORT_ENTER(portParam);
86
87     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
88     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
89                                             portParam->PortId, 0);
90     if (vport != NULL && !vport->hvDeleted) {
91         status = STATUS_DATA_NOT_ACCEPTED;
92         goto create_port_done;
93     } else if (!vport) {
94         vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
95         if (vport == NULL) {
96             status = NDIS_STATUS_RESOURCES;
97             goto create_port_done;
98         }
99     }
100
101     OvsInitVportWithPortParam(vport, portParam);
102     /* XXX: Dummy argument to InitHvVportCommon(). */
103     InitHvVportCommon(switchContext, vport, TRUE);
104
105 create_port_done:
106     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
107     VPORT_PORT_EXIT(portParam);
108     return status;
109 }
110
111
112 /*
113  * Function updating the port properties
114  */
115 NDIS_STATUS
116 HvUpdatePort(POVS_SWITCH_CONTEXT switchContext,
117              PNDIS_SWITCH_PORT_PARAMETERS portParam)
118 {
119     POVS_VPORT_ENTRY vport;
120     LOCK_STATE_EX lockState;
121     OVS_VPORT_STATE ovsState;
122     NDIS_SWITCH_NIC_STATE nicState;
123
124     VPORT_PORT_ENTER(portParam);
125
126     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
127     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
128                                             portParam->PortId, 0);
129     /*
130      * Update properties only for NETDEV ports for supprting PS script
131      * We don't allow changing the names of the internal or external ports
132      */
133     if (vport == NULL || ( vport->portType != NdisSwitchPortTypeSynthetic) || 
134         ( vport->portType != NdisSwitchPortTypeEmulated)) {
135         goto update_port_done;
136     }
137
138     /* Store the nic and the OVS states as Nic Create won't be called */
139     ovsState = vport->ovsState;
140     nicState = vport->nicState;
141     
142     /*
143      * Currently only the port friendly name is being updated
144      * Make sure that no other properties are changed
145      */
146     ASSERT(portParam->PortId == vport->portId);
147     ASSERT(portParam->PortState == vport->portState);
148     ASSERT(portParam->PortType == vport->portType);
149
150     /*
151      * Call the set parameters function the handle all properties
152      * change in a single place in case future version supports change of
153      * other properties
154      */
155     OvsInitVportWithPortParam(vport, portParam);
156     /* Retore the nic and OVS states */
157     vport->nicState = nicState;
158     vport->ovsState = ovsState;
159
160 update_port_done:
161     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
162     VPORT_PORT_EXIT(portParam);
163
164     /* Must always return success */
165     return NDIS_STATUS_SUCCESS;
166 }
167
168 VOID
169 HvTeardownPort(POVS_SWITCH_CONTEXT switchContext,
170                PNDIS_SWITCH_PORT_PARAMETERS portParam)
171 {
172     POVS_VPORT_ENTRY vport;
173     LOCK_STATE_EX lockState;
174
175     VPORT_PORT_ENTER(portParam);
176
177     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
178     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
179                                             portParam->PortId, 0);
180     if (vport) {
181         /* add assertion here */
182         vport->portState = NdisSwitchPortStateTeardown;
183         vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
184     } else {
185         OVS_LOG_WARN("Vport not present.");
186     }
187     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
188
189     VPORT_PORT_EXIT(portParam);
190 }
191
192
193 VOID
194 HvDeletePort(POVS_SWITCH_CONTEXT switchContext,
195              PNDIS_SWITCH_PORT_PARAMETERS portParams)
196 {
197     POVS_VPORT_ENTRY vport;
198     LOCK_STATE_EX lockState;
199
200     VPORT_PORT_ENTER(portParams);
201
202     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
203     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
204                                             portParams->PortId, 0);
205
206     /*
207      * XXX: we can only destroy and remove the port if its datapath port
208      * counterpart was deleted. If the datapath port counterpart is present,
209      * we only mark the vport for deletion, so that a netlink command vport
210      * delete will delete the vport.
211     */
212     if (vport) {
213         OvsRemoveAndDeleteVport(switchContext, vport, TRUE, FALSE, NULL);
214     } else {
215         OVS_LOG_WARN("Vport not present.");
216     }
217     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
218
219     VPORT_PORT_EXIT(portParams);
220 }
221
222
223 /*
224  * Functions implemented in relaton to NDIS NIC manipulation.
225  */
226 NDIS_STATUS
227 HvCreateNic(POVS_SWITCH_CONTEXT switchContext,
228             PNDIS_SWITCH_NIC_PARAMETERS nicParam)
229 {
230     POVS_VPORT_ENTRY vport;
231     UINT32 portNo = 0;
232     UINT32 event = 0;
233     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
234
235     LOCK_STATE_EX lockState;
236
237     VPORT_NIC_ENTER(nicParam);
238
239     /* Wait for lists to be initialized. */
240     OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
241
242     if (!switchContext->isActivated) {
243         OVS_LOG_WARN("Switch is not activated yet.");
244         /* Veto the creation of nic */
245         status = NDIS_STATUS_NOT_SUPPORTED;
246         goto done;
247     }
248
249     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
250     vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId, 0);
251     if (vport == NULL) {
252         OVS_LOG_ERROR("Create NIC without Switch Port,"
253                       " PortId: %x, NicIndex: %d",
254                       nicParam->PortId, nicParam->NicIndex);
255         status = NDIS_STATUS_INVALID_PARAMETER;
256         goto add_nic_done;
257     }
258
259     if (nicParam->NicType == NdisSwitchNicTypeExternal &&
260         nicParam->NicIndex != 0) {
261         POVS_VPORT_ENTRY virtExtVport =
262             (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
263
264         vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
265         if (vport == NULL) {
266             status = NDIS_STATUS_RESOURCES;
267             goto add_nic_done;
268         }
269         OvsInitPhysNicVport(vport, virtExtVport, nicParam->NicIndex);
270         status = InitHvVportCommon(switchContext, vport, TRUE);
271         if (status != NDIS_STATUS_SUCCESS) {
272             OvsFreeMemory(vport);
273             goto add_nic_done;
274         }
275     }
276     OvsInitVportWithNicParam(switchContext, vport, nicParam);
277     portNo = vport->portNo;
278     if (vport->ovsState == OVS_STATE_CONNECTED) {
279         event = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
280     } else if (vport->ovsState == OVS_STATE_NIC_CREATED) {
281         event = OVS_EVENT_CONNECT;
282     }
283
284 add_nic_done:
285     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
286     if (portNo != OVS_DPPORT_NUMBER_INVALID && event) {
287         OvsPostEvent(portNo, event);
288     }
289
290 done:
291     VPORT_NIC_EXIT(nicParam);
292     OVS_LOG_TRACE("Exit: status %8x.\n", status);
293
294     return status;
295 }
296
297
298 /* Mark already created NIC as connected. */
299 VOID
300 HvConnectNic(POVS_SWITCH_CONTEXT switchContext,
301              PNDIS_SWITCH_NIC_PARAMETERS nicParam)
302 {
303     LOCK_STATE_EX lockState;
304     POVS_VPORT_ENTRY vport;
305     UINT32 portNo = 0;
306
307     VPORT_NIC_ENTER(nicParam);
308
309     /* Wait for lists to be initialized. */
310     OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
311
312     if (!switchContext->isActivated) {
313         OVS_LOG_WARN("Switch is not activated yet.");
314         goto done;
315     }
316
317     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
318     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
319                                             nicParam->PortId,
320                                             nicParam->NicIndex);
321
322     if (!vport) {
323         OVS_LOG_WARN("Vport not present.");
324         NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
325         ASSERT(0);
326         goto done;
327     }
328
329     vport->ovsState = OVS_STATE_CONNECTED;
330     vport->nicState = NdisSwitchNicStateConnected;
331     portNo = vport->portNo;
332
333     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
334
335     /* XXX only if portNo != INVALID or always? */
336     OvsPostEvent(portNo, OVS_EVENT_LINK_UP);
337
338     if (nicParam->NicType == NdisSwitchNicTypeInternal) {
339         OvsInternalAdapterUp(portNo, &nicParam->NetCfgInstanceId);
340     }
341
342 done:
343     VPORT_NIC_EXIT(nicParam);
344 }
345
346 VOID
347 HvUpdateNic(POVS_SWITCH_CONTEXT switchContext,
348             PNDIS_SWITCH_NIC_PARAMETERS nicParam)
349 {
350     POVS_VPORT_ENTRY vport;
351     LOCK_STATE_EX lockState;
352
353     UINT32 status = 0, portNo = 0;
354
355     VPORT_NIC_ENTER(nicParam);
356
357     /* Wait for lists to be initialized. */
358     OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
359
360     if (!switchContext->isActivated) {
361         OVS_LOG_WARN("Switch is not activated yet.");
362         goto update_nic_done;
363     }
364
365     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
366     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
367                                             nicParam->PortId,
368                                             nicParam->NicIndex);
369     if (vport == NULL) {
370         OVS_LOG_WARN("Vport search failed.");
371         goto update_nic_done;
372     }
373     switch (nicParam->NicType) {
374     case NdisSwitchNicTypeExternal:
375     case NdisSwitchNicTypeInternal:
376         RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
377                       sizeof (GUID));
378         break;
379     case NdisSwitchNicTypeSynthetic:
380     case NdisSwitchNicTypeEmulated:
381         if (!RtlEqualMemory(vport->vmMacAddress, nicParam->VMMacAddress,
382                            sizeof (vport->vmMacAddress))) {
383             status |= OVS_EVENT_MAC_CHANGE;
384             RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
385                           sizeof (vport->vmMacAddress));
386         }
387         break;
388     default:
389         ASSERT(0);
390     }
391     if (!RtlEqualMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
392                         sizeof (vport->permMacAddress))) {
393         RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
394                       sizeof (vport->permMacAddress));
395         status |= OVS_EVENT_MAC_CHANGE;
396     }
397     if (!RtlEqualMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
398                         sizeof (vport->currMacAddress))) {
399         RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
400                       sizeof (vport->currMacAddress));
401         status |= OVS_EVENT_MAC_CHANGE;
402     }
403
404     if (vport->mtu != nicParam->MTU) {
405         vport->mtu = nicParam->MTU;
406         status |= OVS_EVENT_MTU_CHANGE;
407     }
408     vport->numaNodeId = nicParam->NumaNodeId;
409     portNo = vport->portNo;
410
411     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
412     if (status && portNo) {
413         OvsPostEvent(portNo, status);
414     }
415 update_nic_done:
416     VPORT_NIC_EXIT(nicParam);
417 }
418
419
420 VOID
421 HvDisconnectNic(POVS_SWITCH_CONTEXT switchContext,
422                 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
423 {
424     POVS_VPORT_ENTRY vport;
425     UINT32 portNo = 0;
426     LOCK_STATE_EX lockState;
427     BOOLEAN isInternalPort = FALSE;
428
429     VPORT_NIC_ENTER(nicParam);
430
431     /* Wait for lists to be initialized. */
432     OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
433
434     if (!switchContext->isActivated) {
435         OVS_LOG_WARN("Switch is not activated yet.");
436         goto done;
437     }
438
439     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
440     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
441                                             nicParam->PortId,
442                                             nicParam->NicIndex);
443
444     if (!vport) {
445         OVS_LOG_WARN("Vport not present.");
446         NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
447         goto done;
448     }
449
450     vport->nicState = NdisSwitchNicStateDisconnected;
451     vport->ovsState = OVS_STATE_NIC_CREATED;
452     portNo = vport->portNo;
453
454     if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
455         isInternalPort = TRUE;
456     }
457
458     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
459
460     /* XXX if portNo != INVALID or always? */
461     OvsPostEvent(portNo, OVS_EVENT_LINK_DOWN);
462
463     if (isInternalPort) {
464         OvsInternalAdapterDown();
465     }
466
467 done:
468     VPORT_NIC_EXIT(nicParam);
469 }
470
471
472 VOID
473 HvDeleteNic(POVS_SWITCH_CONTEXT switchContext,
474             PNDIS_SWITCH_NIC_PARAMETERS nicParam)
475 {
476     LOCK_STATE_EX lockState;
477     POVS_VPORT_ENTRY vport;
478     UINT32 portNo = 0;
479
480     VPORT_NIC_ENTER(nicParam);
481     /* Wait for lists to be initialized. */
482     OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
483
484     if (!switchContext->isActivated) {
485         OVS_LOG_WARN("Switch is not activated yet.");
486         goto done;
487     }
488
489     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
490     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
491                                             nicParam->PortId,
492                                             nicParam->NicIndex);
493
494     if (!vport) {
495         OVS_LOG_WARN("Vport not present.");
496         NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
497         goto done;
498     }
499
500     portNo = vport->portNo;
501     if (vport->portType == NdisSwitchPortTypeExternal &&
502         vport->nicIndex != 0) {
503         OvsRemoveAndDeleteVport(switchContext, vport, TRUE, FALSE, NULL);
504     }
505     vport->nicState = NdisSwitchNicStateUnknown;
506     vport->ovsState = OVS_STATE_PORT_CREATED;
507
508     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
509     /* XXX if portNo != INVALID or always? */
510     OvsPostEvent(portNo, OVS_EVENT_DISCONNECT);
511
512 done:
513     VPORT_NIC_EXIT(nicParam);
514 }
515
516
517 /*
518  * OVS Vport related functionality.
519  */
520 POVS_VPORT_ENTRY
521 OvsFindVportByPortNo(POVS_SWITCH_CONTEXT switchContext,
522                      UINT32 portNo)
523 {
524     POVS_VPORT_ENTRY vport;
525     PLIST_ENTRY head, link;
526     UINT32 hash = OvsJhashBytes((const VOID *)&portNo, sizeof(portNo),
527                                 OVS_HASH_BASIS);
528     head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
529     LIST_FORALL(head, link) {
530         vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
531         if (vport->portNo == portNo) {
532             return vport;
533         }
534     }
535     return NULL;
536 }
537
538
539 POVS_VPORT_ENTRY
540 OvsFindVportByOvsName(POVS_SWITCH_CONTEXT switchContext,
541                       PSTR name)
542 {
543     POVS_VPORT_ENTRY vport;
544     PLIST_ENTRY head, link;
545     UINT32 hash;
546     SIZE_T length = strlen(name) + 1;
547
548     hash = OvsJhashBytes((const VOID *)name, length, OVS_HASH_BASIS);
549     head = &(switchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK]);
550
551     LIST_FORALL(head, link) {
552         vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, ovsNameLink);
553         if (!strcmp(name, vport->ovsName)) {
554             return vport;
555         }
556     }
557
558     return NULL;
559 }
560
561 /* OvsFindVportByHvName: "name" is assumed to be null-terminated */
562 POVS_VPORT_ENTRY
563 OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
564                       PWSTR wsName, SIZE_T wstrSize)
565 {
566     POVS_VPORT_ENTRY vport = NULL;
567     PLIST_ENTRY head, link;
568     UINT i;
569
570     for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
571         head = &(switchContext->portIdHashArray[i]);
572         LIST_FORALL(head, link) {
573             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
574
575             /*
576              * NOTE about portFriendlyName:
577              * If the string is NULL-terminated, the Length member does not
578              * include the terminating NULL character.
579              */
580             if (vport->portFriendlyName.Length == wstrSize &&
581                 RtlEqualMemory(wsName, vport->portFriendlyName.String,
582                                vport->portFriendlyName.Length)) {
583                 goto Cleanup;
584             }
585
586             vport = NULL;
587         }
588     }
589
590 Cleanup:
591     OvsFreeMemory(wsName);
592
593     return vport;
594 }
595
596 POVS_VPORT_ENTRY
597 OvsFindVportByHvNameA(POVS_SWITCH_CONTEXT switchContext,
598                       PSTR name)
599 {
600     POVS_VPORT_ENTRY vport = NULL;
601     /* 'portFriendlyName' is not NUL-terminated. */
602     SIZE_T length = strlen(name);
603     SIZE_T wstrSize = length * sizeof(WCHAR);
604     UINT i;
605
606     PWSTR wsName = OvsAllocateMemory(wstrSize);
607     if (!wsName) {
608         return NULL;
609     }
610     for (i = 0; i < length; i++) {
611         wsName[i] = name[i];
612     }
613     vport = OvsFindVportByHvNameW(switchContext, wsName, wstrSize);
614     OvsFreeMemory(wsName);
615     return vport;
616 }
617
618 POVS_VPORT_ENTRY
619 OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT switchContext,
620                                 NDIS_SWITCH_PORT_ID portId,
621                                 NDIS_SWITCH_NIC_INDEX index)
622 {
623     if (switchContext->virtualExternalVport &&
624             portId == switchContext->virtualExternalPortId &&
625             index == switchContext->virtualExternalVport->nicIndex) {
626         return (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
627     } else if (switchContext->internalVport &&
628                portId == switchContext->internalPortId &&
629                index == switchContext->internalVport->nicIndex) {
630         return (POVS_VPORT_ENTRY)switchContext->internalVport;
631     } else {
632         PLIST_ENTRY head, link;
633         POVS_VPORT_ENTRY vport;
634         UINT32 hash;
635         hash = OvsJhashWords((UINT32 *)&portId, 1, OVS_HASH_BASIS);
636         head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
637         LIST_FORALL(head, link) {
638             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
639             if (portId == vport->portId && index == vport->nicIndex) {
640                 return vport;
641             }
642         }
643         return NULL;
644     }
645 }
646
647 POVS_VPORT_ENTRY
648 OvsAllocateVport(VOID)
649 {
650     POVS_VPORT_ENTRY vport;
651     vport = (POVS_VPORT_ENTRY)OvsAllocateMemory(sizeof (OVS_VPORT_ENTRY));
652     if (vport == NULL) {
653         return NULL;
654     }
655     RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY));
656     vport->ovsState = OVS_STATE_UNKNOWN;
657     vport->hvDeleted = FALSE;
658     vport->portNo = OVS_DPPORT_NUMBER_INVALID;
659
660     InitializeListHead(&vport->ovsNameLink);
661     InitializeListHead(&vport->portIdLink);
662     InitializeListHead(&vport->portNoLink);
663
664     return vport;
665 }
666
667 static VOID
668 OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
669                           PNDIS_SWITCH_PORT_PARAMETERS portParam)
670 {
671     vport->portType = portParam->PortType;
672     vport->portState = portParam->PortState;
673     vport->portId = portParam->PortId;
674     vport->nicState = NdisSwitchNicStateUnknown;
675     vport->isExternal = FALSE;
676     vport->isBridgeInternal = FALSE;
677
678     switch (vport->portType) {
679     case NdisSwitchPortTypeExternal:
680         vport->isExternal = TRUE;
681         vport->ovsType = OVS_VPORT_TYPE_NETDEV;
682         break;
683     case NdisSwitchPortTypeInternal:
684         vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
685         break;
686     case NdisSwitchPortTypeSynthetic:
687     case NdisSwitchPortTypeEmulated:
688         vport->ovsType = OVS_VPORT_TYPE_NETDEV;
689         break;
690     }
691     RtlCopyMemory(&vport->hvPortName, &portParam->PortName,
692                   sizeof (NDIS_SWITCH_PORT_NAME));
693     /* For external and internal ports, 'portFriendlyName' is overwritten
694      * later. */
695     RtlCopyMemory(&vport->portFriendlyName, &portParam->PortFriendlyName,
696                   sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
697
698     switch (vport->portState) {
699     case NdisSwitchPortStateCreated:
700         vport->ovsState = OVS_STATE_PORT_CREATED;
701         break;
702     case NdisSwitchPortStateTeardown:
703         vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
704         break;
705     case NdisSwitchPortStateDeleted:
706         vport->ovsState = OVS_STATE_PORT_DELETED;
707         break;
708     }
709 }
710
711
712 static VOID
713 OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
714                          POVS_VPORT_ENTRY vport,
715                          PNDIS_SWITCH_NIC_PARAMETERS nicParam)
716 {
717     ASSERT(vport->portId == nicParam->PortId);
718     ASSERT(vport->ovsState == OVS_STATE_PORT_CREATED);
719
720     UNREFERENCED_PARAMETER(switchContext);
721
722     RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
723                   sizeof (nicParam->PermanentMacAddress));
724     RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
725                   sizeof (nicParam->CurrentMacAddress));
726
727     if (nicParam->NicType == NdisSwitchNicTypeSynthetic ||
728         nicParam->NicType == NdisSwitchNicTypeEmulated) {
729         RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
730                       sizeof (nicParam->VMMacAddress));
731         RtlCopyMemory(&vport->vmName, &nicParam->VmName,
732                       sizeof (nicParam->VmName));
733     } else {
734         RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
735                       sizeof (nicParam->NetCfgInstanceId));
736     }
737     RtlCopyMemory(&vport->nicName, &nicParam->NicName,
738                   sizeof (nicParam->NicName));
739     vport->mtu = nicParam->MTU;
740     vport->nicState = nicParam->NicState;
741     vport->nicIndex = nicParam->NicIndex;
742     vport->numaNodeId = nicParam->NumaNodeId;
743
744     switch (vport->nicState) {
745     case NdisSwitchNicStateCreated:
746         vport->ovsState = OVS_STATE_NIC_CREATED;
747         break;
748     case NdisSwitchNicStateConnected:
749         vport->ovsState = OVS_STATE_CONNECTED;
750         break;
751     case NdisSwitchNicStateDisconnected:
752         vport->ovsState = OVS_STATE_NIC_CREATED;
753         break;
754     case NdisSwitchNicStateDeleted:
755         vport->ovsState = OVS_STATE_PORT_CREATED;
756         break;
757     }
758 }
759
760 /*
761  * --------------------------------------------------------------------------
762  * Copies the relevant NDIS port properties from a virtual (pseudo) external
763  * NIC to a physical (real) external NIC.
764  * --------------------------------------------------------------------------
765  */
766 static VOID
767 OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVport,
768                     POVS_VPORT_ENTRY virtExtVport,
769                     UINT32 physNicIndex)
770 {
771     physExtVport->portType = virtExtVport->portType;
772     physExtVport->portState = virtExtVport->portState;
773     physExtVport->portId = virtExtVport->portId;
774     physExtVport->nicState = NdisSwitchNicStateUnknown;
775     physExtVport->ovsType = OVS_VPORT_TYPE_NETDEV;
776     physExtVport->isExternal = TRUE;
777     physExtVport->isBridgeInternal = FALSE;
778     physExtVport->nicIndex = (NDIS_SWITCH_NIC_INDEX)physNicIndex;
779
780     RtlCopyMemory(&physExtVport->hvPortName, &virtExtVport->hvPortName,
781                   sizeof (NDIS_SWITCH_PORT_NAME));
782
783     /* 'portFriendlyName' is overwritten later. */
784     RtlCopyMemory(&physExtVport->portFriendlyName,
785                   &virtExtVport->portFriendlyName,
786                   sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
787
788     physExtVport->ovsState = OVS_STATE_PORT_CREATED;
789 }
790
791 /*
792  * --------------------------------------------------------------------------
793  * Initializes a tunnel vport.
794  * --------------------------------------------------------------------------
795  */
796 NTSTATUS
797 OvsInitTunnelVport(POVS_VPORT_ENTRY vport,
798                    OVS_VPORT_TYPE ovsType,
799                    UINT16 dstPort)
800 {
801     NTSTATUS status = STATUS_SUCCESS;
802
803     vport->isBridgeInternal = FALSE;
804     vport->ovsType = ovsType;
805     vport->ovsState = OVS_STATE_PORT_CREATED;
806     switch (ovsType) {
807     case OVS_VPORT_TYPE_GRE:
808         break;
809     case OVS_VPORT_TYPE_GRE64:
810         break;
811     case OVS_VPORT_TYPE_VXLAN:
812         status = OvsInitVxlanTunnel(vport, dstPort);
813         break;
814     default:
815         ASSERT(0);
816     }
817     return status;
818 }
819
820 /*
821  * --------------------------------------------------------------------------
822  * Initializes a bridge internal vport ie. a port of type
823  * OVS_VPORT_TYPE_INTERNAL but not present on the Hyper-V switch.
824  * --------------------------------------------------------------------------
825  */
826 NTSTATUS
827 OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport)
828 {
829     vport->isBridgeInternal = TRUE;
830     vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
831     /* Mark the status to be connected, since there is no other initialization
832      * for this port. */
833     vport->ovsState = OVS_STATE_CONNECTED;
834     return STATUS_SUCCESS;
835 }
836
837 /*
838  * --------------------------------------------------------------------------
839  * For external vports 'portFriendlyName' provided by Hyper-V is over-written
840  * by synthetic names.
841  * --------------------------------------------------------------------------
842  */
843 static VOID
844 AssignNicNameSpecial(POVS_VPORT_ENTRY vport)
845 {
846     size_t len;
847
848     if (vport->portType == NdisSwitchPortTypeExternal) {
849         if (vport->nicIndex == 0) {
850             ASSERT(vport->nicIndex == 0);
851             RtlStringCbPrintfW(vport->portFriendlyName.String,
852                                IF_MAX_STRING_SIZE,
853                                L"%s.virtualAdapter", OVS_DPPORT_EXTERNAL_NAME_W);
854         } else {
855             RtlStringCbPrintfW(vport->portFriendlyName.String,
856                                IF_MAX_STRING_SIZE,
857                                L"%s.%lu", OVS_DPPORT_EXTERNAL_NAME_W,
858                                (UINT32)vport->nicIndex);
859         }
860     } else {
861         RtlStringCbPrintfW(vport->portFriendlyName.String,
862                            IF_MAX_STRING_SIZE,
863                            L"%s", OVS_DPPORT_INTERNAL_NAME_W);
864     }
865
866     RtlStringCbLengthW(vport->portFriendlyName.String, IF_MAX_STRING_SIZE,
867                        &len);
868     vport->portFriendlyName.Length = (USHORT)len;
869 }
870
871
872 /*
873  * --------------------------------------------------------------------------
874  * Functionality common to any port on the Hyper-V switch. This function is not
875  * to be called for a port that is not on the Hyper-V switch.
876  *
877  * Inserts the port into 'portIdHashArray' and caches the pointer in the
878  * 'switchContext' if needed.
879  *
880  * For external NIC, assigns the name for the NIC.
881  * --------------------------------------------------------------------------
882  */
883 static NDIS_STATUS
884 InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
885                   POVS_VPORT_ENTRY vport,
886                   BOOLEAN newPort)
887 {
888     UINT32 hash;
889
890     switch (vport->portType) {
891     case NdisSwitchPortTypeExternal:
892         /*
893          * Overwrite the 'portFriendlyName' of this external vport. The reason
894          * for having this in common code is to be able to call it from the NDIS
895          * Port callback as well as the NDIS NIC callback.
896          */
897         AssignNicNameSpecial(vport);
898
899         if (vport->nicIndex == 0) {
900             switchContext->virtualExternalPortId = vport->portId;
901             switchContext->virtualExternalVport = vport;
902         } else {
903             switchContext->numPhysicalNics++;
904         }
905         break;
906     case NdisSwitchPortTypeInternal:
907         ASSERT(vport->isBridgeInternal == FALSE);
908
909         /* Overwrite the 'portFriendlyName' of the internal vport. */
910         AssignNicNameSpecial(vport);
911         switchContext->internalPortId = vport->portId;
912         switchContext->internalVport = vport;
913         break;
914     case NdisSwitchPortTypeSynthetic:
915     case NdisSwitchPortTypeEmulated:
916         break;
917     }
918
919     /*
920      * It is important to not insert vport corresponding to virtual external
921      * port into the 'portIdHashArray' since the port should not be exposed to
922      * OVS userspace.
923      */
924     if (vport->portType == NdisSwitchPortTypeExternal &&
925         vport->nicIndex == 0) {
926         return NDIS_STATUS_SUCCESS;
927     }
928
929     /*
930      * NOTE: OvsJhashWords has portId as "1" word. This should be ok, even
931      * though sizeof(NDIS_SWITCH_PORT_ID) = 4, not 2, because the
932      * hyper-v switch seems to use only 2 bytes out of 4.
933      */
934     hash = OvsJhashWords(&vport->portId, 1, OVS_HASH_BASIS);
935     InsertHeadList(&switchContext->portIdHashArray[hash & OVS_VPORT_MASK],
936                    &vport->portIdLink);
937     if (newPort) {
938         switchContext->numHvVports++;
939     }
940     return NDIS_STATUS_SUCCESS;
941 }
942
943 /*
944  * --------------------------------------------------------------------------
945  * Functionality common to any port added from OVS userspace.
946  *
947  * Inserts the port into 'portIdHashArray', 'ovsPortNameHashArray' and caches
948  * the pointer in the 'switchContext' if needed.
949  * --------------------------------------------------------------------------
950  */
951 NDIS_STATUS
952 InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
953                    POVS_VPORT_ENTRY vport)
954 {
955     UINT32 hash;
956
957     switch(vport->ovsType) {
958     case OVS_VPORT_TYPE_VXLAN:
959         ASSERT(switchContext->vxlanVport == NULL);
960         switchContext->vxlanVport = vport;
961         switchContext->numNonHvVports++;
962         break;
963     case OVS_VPORT_TYPE_INTERNAL:
964         if (vport->isBridgeInternal) {
965             switchContext->numNonHvVports++;
966         }
967     default:
968         break;
969     }
970
971     /*
972      * Insert the port into the hash array of ports: by port number and ovs
973      * and ovs (datapath) port name.
974      * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
975      * portNo is stored in 2 bytes only (max port number = MAXUINT16).
976      */
977     hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
978     InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
979                    &vport->portNoLink);
980
981     hash = OvsJhashBytes(vport->ovsName, strlen(vport->ovsName) + 1,
982                          OVS_HASH_BASIS);
983     InsertHeadList(
984         &gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
985         &vport->ovsNameLink);
986
987     return STATUS_SUCCESS;
988 }
989
990
991 /*
992  * --------------------------------------------------------------------------
993  * Provides functionality that is partly complementatry to
994  * InitOvsVportCommon()/InitHvVportCommon().
995  *
996  * 'hvDelete' indicates if caller is removing the vport as a result of the
997  * port being removed on the Hyper-V switch.
998  * 'ovsDelete' indicates if caller is removing the vport as a result of the
999  * port being removed from OVS userspace.
1000  * --------------------------------------------------------------------------
1001  */
1002 VOID
1003 OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
1004                         POVS_VPORT_ENTRY vport,
1005                         BOOLEAN hvDelete,
1006                         BOOLEAN ovsDelete,
1007                         BOOLEAN *vportDeallocated)
1008 {
1009     BOOLEAN hvSwitchPort = FALSE;
1010     BOOLEAN deletedOnOvs = FALSE, deletedOnHv = FALSE;
1011
1012     if (vportDeallocated) {
1013         *vportDeallocated = FALSE;
1014     }
1015
1016     if (vport->isExternal) {
1017         if (vport->nicIndex == 0) {
1018             ASSERT(switchContext->numPhysicalNics == 0);
1019             switchContext->virtualExternalPortId = 0;
1020             switchContext->virtualExternalVport = NULL;
1021             OvsFreeMemory(vport);
1022             if (vportDeallocated) {
1023                 *vportDeallocated = TRUE;
1024             }
1025             return;
1026         } else {
1027             ASSERT(switchContext->numPhysicalNics);
1028             switchContext->numPhysicalNics--;
1029             hvSwitchPort = TRUE;
1030         }
1031     }
1032
1033     switch (vport->ovsType) {
1034     case OVS_VPORT_TYPE_INTERNAL:
1035         if (!vport->isBridgeInternal) {
1036             switchContext->internalPortId = 0;
1037             switchContext->internalVport = NULL;
1038             OvsInternalAdapterDown();
1039             hvSwitchPort = TRUE;
1040         }
1041         break;
1042     case OVS_VPORT_TYPE_VXLAN:
1043         OvsCleanupVxlanTunnel(vport);
1044         switchContext->vxlanVport = NULL;
1045         break;
1046     case OVS_VPORT_TYPE_GRE:
1047     case OVS_VPORT_TYPE_GRE64:
1048         break;
1049     case OVS_VPORT_TYPE_NETDEV:
1050         hvSwitchPort = TRUE;
1051     default:
1052         break;
1053     }
1054
1055     /*
1056      * 'hvDelete' == TRUE indicates that the port should be removed from the
1057      * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
1058      * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
1059      *
1060      * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
1061      */
1062     if (vport->hvDeleted == TRUE) {
1063         deletedOnHv = TRUE;
1064     }
1065     if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1066         deletedOnOvs = TRUE;
1067     }
1068
1069     if (hvDelete && !deletedOnHv) {
1070         vport->hvDeleted = TRUE;
1071
1072         /* Remove the port from the relevant lists. */
1073         RemoveEntryList(&vport->portIdLink);
1074         InitializeListHead(&vport->portIdLink);
1075         deletedOnHv = TRUE;
1076     }
1077     if (ovsDelete && !deletedOnOvs) {
1078         vport->portNo = OVS_DPPORT_NUMBER_INVALID;
1079         vport->ovsName[0] = '\0';
1080
1081         /* Remove the port from the relevant lists. */
1082         RemoveEntryList(&vport->ovsNameLink);
1083         InitializeListHead(&vport->ovsNameLink);
1084         RemoveEntryList(&vport->portNoLink);
1085         InitializeListHead(&vport->portNoLink);
1086         deletedOnOvs = TRUE;
1087     }
1088
1089     /*
1090      * Deallocate the port if it has been deleted on the Hyper-V switch as well
1091      * as OVS userspace.
1092      */
1093     if (deletedOnHv && deletedOnOvs) {
1094         if (hvSwitchPort) {
1095             switchContext->numHvVports--;
1096         } else {
1097             switchContext->numNonHvVports--;
1098         }
1099         OvsFreeMemory(vport);
1100         if (vportDeallocated) {
1101             *vportDeallocated = TRUE;
1102         }
1103     }
1104 }
1105
1106 NDIS_STATUS
1107 OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
1108 {
1109     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1110     ULONG arrIndex;
1111     PNDIS_SWITCH_PORT_PARAMETERS portParam;
1112     PNDIS_SWITCH_PORT_ARRAY portArray = NULL;
1113     POVS_VPORT_ENTRY vport;
1114
1115     OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
1116
1117     status = OvsGetPortsOnSwitch(switchContext, &portArray);
1118     if (status != NDIS_STATUS_SUCCESS) {
1119         goto cleanup;
1120     }
1121
1122     for (arrIndex = 0; arrIndex < portArray->NumElements; arrIndex++) {
1123          portParam = NDIS_SWITCH_PORT_AT_ARRAY_INDEX(portArray, arrIndex);
1124
1125          if (portParam->IsValidationPort) {
1126              continue;
1127          }
1128
1129          vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
1130          if (vport == NULL) {
1131              status = NDIS_STATUS_RESOURCES;
1132              goto cleanup;
1133          }
1134          OvsInitVportWithPortParam(vport, portParam);
1135          status = InitHvVportCommon(switchContext, vport, TRUE);
1136          if (status != NDIS_STATUS_SUCCESS) {
1137              OvsFreeMemory(vport);
1138              goto cleanup;
1139          }
1140     }
1141 cleanup:
1142     if (status != NDIS_STATUS_SUCCESS) {
1143         OvsClearAllSwitchVports(switchContext);
1144     }
1145
1146     if (portArray != NULL) {
1147         OvsFreeMemory(portArray);
1148     }
1149     OVS_LOG_TRACE("Exit: status: %x", status);
1150     return status;
1151 }
1152
1153
1154 NDIS_STATUS
1155 OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext)
1156 {
1157     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1158     PNDIS_SWITCH_NIC_ARRAY nicArray = NULL;
1159     ULONG arrIndex;
1160     PNDIS_SWITCH_NIC_PARAMETERS nicParam;
1161     POVS_VPORT_ENTRY vport;
1162
1163     OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
1164     /*
1165      * Now, get NIC list.
1166      */
1167     status = OvsGetNicsOnSwitch(switchContext, &nicArray);
1168     if (status != NDIS_STATUS_SUCCESS) {
1169         goto cleanup;
1170     }
1171     for (arrIndex = 0; arrIndex < nicArray->NumElements; ++arrIndex) {
1172
1173         nicParam = NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray, arrIndex);
1174
1175         /*
1176          * XXX: Check if the port is configured with a VLAN. Disallow such a
1177          * configuration, since we don't support tag-in-tag.
1178          */
1179
1180         /*
1181          * XXX: Check if the port is connected to a VF. Disconnect the VF in
1182          * such a case.
1183          */
1184
1185         if (nicParam->NicType == NdisSwitchNicTypeExternal &&
1186             nicParam->NicIndex != 0) {
1187             POVS_VPORT_ENTRY virtExtVport =
1188                    (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
1189
1190             vport = OvsAllocateVport();
1191             if (vport) {
1192                 OvsInitPhysNicVport(vport, virtExtVport,
1193                                     nicParam->NicIndex);
1194                 status = InitHvVportCommon(switchContext, vport, TRUE);
1195                 if (status != NDIS_STATUS_SUCCESS) {
1196                     OvsFreeMemory(vport);
1197                     vport = NULL;
1198                 }
1199             }
1200         } else {
1201             vport = OvsFindVportByPortIdAndNicIndex(switchContext,
1202                                                     nicParam->PortId,
1203                                                     nicParam->NicIndex);
1204         }
1205         if (vport == NULL) {
1206             OVS_LOG_ERROR("Fail to allocate vport");
1207             continue;
1208         }
1209         OvsInitVportWithNicParam(switchContext, vport, nicParam);
1210         if (nicParam->NicType == NdisSwitchNicTypeInternal) {
1211             OvsInternalAdapterUp(vport->portNo, &nicParam->NetCfgInstanceId);
1212         }
1213     }
1214 cleanup:
1215
1216     if (nicArray != NULL) {
1217         OvsFreeMemory(nicArray);
1218     }
1219     OVS_LOG_TRACE("Exit: status: %x", status);
1220     return status;
1221 }
1222
1223 /*
1224  * --------------------------------------------------------------------------
1225  * Deletes ports added from the Hyper-V switch as well as OVS usersapce. The
1226  * function deletes ports in 'portIdHashArray'. This will delete most of the
1227  * ports that are in the 'portNoHashArray' as well. Any remaining ports
1228  * are deleted by walking the the 'portNoHashArray'.
1229  * --------------------------------------------------------------------------
1230  */
1231 VOID
1232 OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
1233 {
1234     for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1235         PLIST_ENTRY head, link, next;
1236
1237         head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
1238         LIST_FORALL_SAFE(head, link, next) {
1239             POVS_VPORT_ENTRY vport;
1240             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1241             OvsRemoveAndDeleteVport(switchContext, vport, TRUE, TRUE, NULL);
1242         }
1243     }
1244     /*
1245      * Remove 'virtualExternalVport' as well. This port is not part of the
1246      * 'portIdHashArray'.
1247      */
1248     if (switchContext->virtualExternalVport) {
1249         OvsRemoveAndDeleteVport(switchContext,
1250             (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE,
1251             NULL);
1252     }
1253
1254     for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1255         PLIST_ENTRY head, link, next;
1256
1257         head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
1258         LIST_FORALL_SAFE(head, link, next) {
1259             POVS_VPORT_ENTRY vport;
1260             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1261             ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
1262                    (vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
1263                     vport->isBridgeInternal) || vport->hvDeleted == TRUE);
1264             OvsRemoveAndDeleteVport(switchContext, vport, TRUE, TRUE, NULL);
1265         }
1266     }
1267
1268     ASSERT(switchContext->virtualExternalVport == NULL);
1269     ASSERT(switchContext->internalVport == NULL);
1270     ASSERT(switchContext->vxlanVport == NULL);
1271 }
1272
1273
1274 NTSTATUS
1275 OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
1276                                 CHAR *str,
1277                                 UINT16 maxStrLen)
1278 {
1279     ANSI_STRING astr;
1280     UNICODE_STRING ustr;
1281     NTSTATUS status;
1282     UINT32 size;
1283
1284     ustr.Buffer = wStr->String;
1285     ustr.Length = wStr->Length;
1286     ustr.MaximumLength = IF_MAX_STRING_SIZE;
1287
1288     astr.Buffer = str;
1289     astr.MaximumLength = maxStrLen;
1290     astr.Length = 0;
1291
1292     size = RtlUnicodeStringToAnsiSize(&ustr);
1293     if (size > maxStrLen) {
1294         return STATUS_BUFFER_OVERFLOW;
1295     }
1296
1297     status = RtlUnicodeStringToAnsiString(&astr, &ustr, FALSE);
1298
1299     ASSERT(status == STATUS_SUCCESS);
1300     if (status != STATUS_SUCCESS) {
1301         return status;
1302     }
1303     ASSERT(astr.Length <= maxStrLen);
1304     str[astr.Length] = 0;
1305     return STATUS_SUCCESS;
1306 }
1307
1308 /*
1309  * --------------------------------------------------------------------------
1310  * Utility function that populates a 'OVS_VPORT_EXT_INFO' structure for the
1311  * specified vport.
1312  *
1313  * Assumes that 'gOvsCtrlLock' is held.
1314  * --------------------------------------------------------------------------
1315  */
1316 NTSTATUS
1317 OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
1318                    POVS_VPORT_EXT_INFO extInfo)
1319 {
1320     POVS_VPORT_ENTRY vport;
1321     size_t len;
1322     LOCK_STATE_EX lockState;
1323     NTSTATUS status = STATUS_SUCCESS;
1324     BOOLEAN doConvert = FALSE;
1325
1326     RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO));
1327     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1328     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1329                           NDIS_RWL_AT_DISPATCH_LEVEL);
1330     if (vportGet->portNo == 0) {
1331         StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
1332         vport = OvsFindVportByHvNameA(gOvsSwitchContext, vportGet->name);
1333         if (vport != NULL) {
1334             /* If the port is not a Hyper-V port and it has been added earlier,
1335              * we'll find it in 'ovsPortNameHashArray'. */
1336             vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name);
1337         }
1338     } else {
1339         vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo);
1340     }
1341     if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED &&
1342                           vport->ovsState != OVS_STATE_NIC_CREATED)) {
1343         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1344         if (vportGet->portNo) {
1345             OVS_LOG_WARN("vport %u does not exist any more", vportGet->portNo);
1346         } else {
1347             OVS_LOG_WARN("vport %s does not exist any more", vportGet->name);
1348         }
1349         status = STATUS_DEVICE_DOES_NOT_EXIST;
1350         goto ext_info_done;
1351     }
1352     extInfo->dpNo = vportGet->dpNo;
1353     extInfo->portNo = vport->portNo;
1354     RtlCopyMemory(extInfo->macAddress, vport->currMacAddress,
1355                   sizeof (vport->currMacAddress));
1356     RtlCopyMemory(extInfo->permMACAddress, vport->permMacAddress,
1357                   sizeof (vport->permMacAddress));
1358     if (vport->ovsType == OVS_VPORT_TYPE_NETDEV) {
1359         RtlCopyMemory(extInfo->vmMACAddress, vport->vmMacAddress,
1360                       sizeof (vport->vmMacAddress));
1361     }
1362     extInfo->nicIndex = vport->nicIndex;
1363     extInfo->portId = vport->portId;
1364     extInfo->type = vport->ovsType;
1365     extInfo->mtu = vport->mtu;
1366     /*
1367      * TO be revisit XXX
1368      */
1369     if (vport->ovsState == OVS_STATE_NIC_CREATED) {
1370        extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_DOWN;
1371     } else if (vport->ovsState == OVS_STATE_CONNECTED) {
1372        extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
1373     } else {
1374        extInfo->status = OVS_EVENT_DISCONNECT;
1375     }
1376     if (extInfo->type == OVS_VPORT_TYPE_NETDEV &&
1377         (vport->ovsState == OVS_STATE_NIC_CREATED  ||
1378          vport->ovsState == OVS_STATE_CONNECTED)) {
1379         doConvert = TRUE;
1380     } else {
1381         extInfo->vmUUID[0] = 0;
1382         extInfo->vifUUID[0] = 0;
1383     }
1384     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1385     if (doConvert) {
1386         status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName,
1387                                                  extInfo->name,
1388                                                  OVS_MAX_PORT_NAME_LENGTH);
1389         if (status != STATUS_SUCCESS) {
1390             OVS_LOG_INFO("Fail to convert NIC name.");
1391             extInfo->vmUUID[0] = 0;
1392         }
1393
1394         status = OvsConvertIfCountedStrToAnsiStr(&vport->vmName,
1395                                                  extInfo->vmUUID,
1396                                                  OVS_MAX_VM_UUID_LEN);
1397         if (status != STATUS_SUCCESS) {
1398             OVS_LOG_INFO("Fail to convert VM name.");
1399             extInfo->vmUUID[0] = 0;
1400         }
1401
1402         status = OvsConvertIfCountedStrToAnsiStr(&vport->nicName,
1403                                                  extInfo->vifUUID,
1404                                                  OVS_MAX_VIF_UUID_LEN);
1405         if (status != STATUS_SUCCESS) {
1406             OVS_LOG_INFO("Fail to convert nic UUID");
1407             extInfo->vifUUID[0] = 0;
1408         }
1409         /*
1410          * for now ignore status
1411          */
1412         status = STATUS_SUCCESS;
1413     }
1414
1415 ext_info_done:
1416     return status;
1417 }
1418
1419 /*
1420  * --------------------------------------------------------------------------
1421  *  Command Handler for 'OVS_WIN_NETDEV_CMD_GET'.
1422  * --------------------------------------------------------------------------
1423  */
1424 NTSTATUS
1425 OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1426                        UINT32 *replyLen)
1427 {
1428     NTSTATUS status = STATUS_SUCCESS;
1429     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1430     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1431     NL_ERROR nlError = NL_ERROR_SUCCESS;
1432     OVS_VPORT_GET vportGet;
1433     OVS_VPORT_EXT_INFO info;
1434
1435     static const NL_POLICY ovsNetdevPolicy[] = {
1436         [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING,
1437                                        .minLen = 2,
1438                                        .maxLen = IFNAMSIZ },
1439     };
1440     PNL_ATTR netdevAttrs[ARRAY_SIZE(ovsNetdevPolicy)];
1441
1442     /* input buffer has been validated while validating transaction dev op. */
1443     ASSERT(usrParamsCtx->inputBuffer != NULL &&
1444            usrParamsCtx->inputLength > sizeof *msgIn);
1445
1446     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1447         return STATUS_INVALID_BUFFER_SIZE;
1448     }
1449
1450     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1451         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1452         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1453         ovsNetdevPolicy, netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
1454         return STATUS_INVALID_PARAMETER;
1455     }
1456
1457     OvsAcquireCtrlLock();
1458
1459     vportGet.portNo = 0;
1460     RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
1461                   NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
1462
1463     status = OvsGetExtInfoIoctl(&vportGet, &info);
1464     if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
1465         nlError = NL_ERROR_NODEV;
1466         OvsReleaseCtrlLock();
1467         goto cleanup;
1468     }
1469
1470     status = CreateNetlinkMesgForNetdev(&info, msgIn,
1471                  usrParamsCtx->outputBuffer, usrParamsCtx->outputLength,
1472                  gOvsSwitchContext->dpNo);
1473     if (status == STATUS_SUCCESS) {
1474         *replyLen = msgOut->nlMsg.nlmsgLen;
1475     }
1476     OvsReleaseCtrlLock();
1477
1478 cleanup:
1479     if (nlError != NL_ERROR_SUCCESS) {
1480         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1481             usrParamsCtx->outputBuffer;
1482
1483         BuildErrorMsg(msgIn, msgError, nlError);
1484         *replyLen = msgError->nlMsg.nlmsgLen;
1485     }
1486
1487     return STATUS_SUCCESS;
1488 }
1489
1490
1491 /*
1492  * --------------------------------------------------------------------------
1493  *  Utility function to construct an OVS_MESSAGE for the specified vport. The
1494  *  OVS_MESSAGE contains the output of a netdev command.
1495  * --------------------------------------------------------------------------
1496  */
1497 static NTSTATUS
1498 CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
1499                            POVS_MESSAGE msgIn,
1500                            PVOID outBuffer,
1501                            UINT32 outBufLen,
1502                            int dpIfIndex)
1503 {
1504     NL_BUFFER nlBuffer;
1505     BOOLEAN ok;
1506     OVS_MESSAGE msgOut;
1507     PNL_MSG_HDR nlMsg;
1508     UINT32 netdevFlags = 0;
1509
1510     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1511
1512     BuildReplyMsgFromMsgIn(msgIn, &msgOut, 0);
1513     msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1514
1515     ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1516     if (!ok) {
1517         return STATUS_INSUFFICIENT_RESOURCES;
1518     }
1519
1520     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
1521                          info->portNo);
1522     if (!ok) {
1523         return STATUS_INSUFFICIENT_RESOURCES;
1524     }
1525
1526     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
1527     if (!ok) {
1528         return STATUS_INSUFFICIENT_RESOURCES;
1529     }
1530
1531     ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
1532                             info->name);
1533     if (!ok) {
1534         return STATUS_INSUFFICIENT_RESOURCES;
1535     }
1536
1537     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
1538              (PCHAR)info->macAddress, sizeof (info->macAddress));
1539     if (!ok) {
1540         return STATUS_INSUFFICIENT_RESOURCES;
1541     }
1542
1543     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
1544     if (!ok) {
1545         return STATUS_INSUFFICIENT_RESOURCES;
1546     }
1547
1548     if (info->status != OVS_EVENT_CONNECT) {
1549         netdevFlags = OVS_WIN_NETDEV_IFF_UP;
1550     }
1551     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
1552                          netdevFlags);
1553     if (!ok) {
1554         return STATUS_INSUFFICIENT_RESOURCES;
1555     }
1556
1557     /*
1558      * XXX: add netdev_stats when we have the definition available in the
1559      * kernel.
1560      */
1561
1562     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1563     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1564
1565     return STATUS_SUCCESS;
1566 }
1567
1568 static __inline VOID
1569 OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec)
1570 {
1571     while ((!switchContext->isActivated) &&
1572           (!switchContext->isActivateFailed)) {
1573         /* Wait for the switch to be active and
1574          * the list of ports in OVS to be initialized. */
1575         NdisMSleep(sleepMicroSec);
1576     }
1577 }