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