datapath-windows: OvsFindVportByPortIdAndNicIndex() and external port.
[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 (switchContext->virtualExternalVport &&
556             portId == switchContext->virtualExternalPortId &&
557             index == switchContext->virtualExternalVport->nicIndex) {
558         return (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
559     } else if (switchContext->internalVport &&
560                portId == switchContext->internalPortId &&
561                index == switchContext->internalVport->nicIndex) {
562         return (POVS_VPORT_ENTRY)switchContext->internalVport;
563     } else {
564         PLIST_ENTRY head, link;
565         POVS_VPORT_ENTRY vport;
566         UINT32 hash;
567         hash = OvsJhashWords((UINT32 *)&portId, 1, OVS_HASH_BASIS);
568         head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
569         LIST_FORALL(head, link) {
570             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
571             if (portId == vport->portId && index == vport->nicIndex) {
572                 return vport;
573             }
574         }
575         return NULL;
576     }
577 }
578
579 POVS_VPORT_ENTRY
580 OvsAllocateVport(VOID)
581 {
582     POVS_VPORT_ENTRY vport;
583     vport = (POVS_VPORT_ENTRY)OvsAllocateMemory(sizeof (OVS_VPORT_ENTRY));
584     if (vport == NULL) {
585         return NULL;
586     }
587     RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY));
588     vport->ovsState = OVS_STATE_UNKNOWN;
589     vport->hvDeleted = FALSE;
590     vport->portNo = OVS_DPPORT_NUMBER_INVALID;
591
592     InitializeListHead(&vport->ovsNameLink);
593     InitializeListHead(&vport->portIdLink);
594     InitializeListHead(&vport->portNoLink);
595
596     return vport;
597 }
598
599 static VOID
600 OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
601                           PNDIS_SWITCH_PORT_PARAMETERS portParam)
602 {
603     vport->portType = portParam->PortType;
604     vport->portState = portParam->PortState;
605     vport->portId = portParam->PortId;
606     vport->nicState = NdisSwitchNicStateUnknown;
607     vport->isExternal = FALSE;
608     vport->isBridgeInternal = FALSE;
609
610     switch (vport->portType) {
611     case NdisSwitchPortTypeExternal:
612         vport->isExternal = TRUE;
613         vport->ovsType = OVS_VPORT_TYPE_NETDEV;
614         break;
615     case NdisSwitchPortTypeInternal:
616         vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
617         break;
618     case NdisSwitchPortTypeSynthetic:
619     case NdisSwitchPortTypeEmulated:
620         vport->ovsType = OVS_VPORT_TYPE_NETDEV;
621         break;
622     }
623     RtlCopyMemory(&vport->hvPortName, &portParam->PortName,
624                   sizeof (NDIS_SWITCH_PORT_NAME));
625     /* For external and internal ports, 'portFriendlyName' is overwritten
626      * later. */
627     RtlCopyMemory(&vport->portFriendlyName, &portParam->PortFriendlyName,
628                   sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
629
630     switch (vport->portState) {
631     case NdisSwitchPortStateCreated:
632         vport->ovsState = OVS_STATE_PORT_CREATED;
633         break;
634     case NdisSwitchPortStateTeardown:
635         vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
636         break;
637     case NdisSwitchPortStateDeleted:
638         vport->ovsState = OVS_STATE_PORT_DELETED;
639         break;
640     }
641 }
642
643
644 static VOID
645 OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
646                          POVS_VPORT_ENTRY vport,
647                          PNDIS_SWITCH_NIC_PARAMETERS nicParam)
648 {
649     ASSERT(vport->portId == nicParam->PortId);
650     ASSERT(vport->ovsState == OVS_STATE_PORT_CREATED);
651
652     UNREFERENCED_PARAMETER(switchContext);
653
654     RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
655                   sizeof (nicParam->PermanentMacAddress));
656     RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
657                   sizeof (nicParam->CurrentMacAddress));
658
659     if (nicParam->NicType == NdisSwitchNicTypeSynthetic ||
660         nicParam->NicType == NdisSwitchNicTypeEmulated) {
661         RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
662                       sizeof (nicParam->VMMacAddress));
663         RtlCopyMemory(&vport->vmName, &nicParam->VmName,
664                       sizeof (nicParam->VmName));
665     } else {
666         RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
667                       sizeof (nicParam->NetCfgInstanceId));
668     }
669     RtlCopyMemory(&vport->nicName, &nicParam->NicName,
670                   sizeof (nicParam->NicName));
671     vport->mtu = nicParam->MTU;
672     vport->nicState = nicParam->NicState;
673     vport->nicIndex = nicParam->NicIndex;
674     vport->numaNodeId = nicParam->NumaNodeId;
675
676     switch (vport->nicState) {
677     case NdisSwitchNicStateCreated:
678         vport->ovsState = OVS_STATE_NIC_CREATED;
679         break;
680     case NdisSwitchNicStateConnected:
681         vport->ovsState = OVS_STATE_CONNECTED;
682         break;
683     case NdisSwitchNicStateDisconnected:
684         vport->ovsState = OVS_STATE_NIC_CREATED;
685         break;
686     case NdisSwitchNicStateDeleted:
687         vport->ovsState = OVS_STATE_PORT_CREATED;
688         break;
689     }
690 }
691
692 /*
693  * --------------------------------------------------------------------------
694  * Copies the relevant NDIS port properties from a virtual (pseudo) external
695  * NIC to a physical (real) external NIC.
696  * --------------------------------------------------------------------------
697  */
698 static VOID
699 OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVport,
700                     POVS_VPORT_ENTRY virtExtVport,
701                     UINT32 physNicIndex)
702 {
703     physExtVport->portType = virtExtVport->portType;
704     physExtVport->portState = virtExtVport->portState;
705     physExtVport->portId = virtExtVport->portId;
706     physExtVport->nicState = NdisSwitchNicStateUnknown;
707     physExtVport->ovsType = OVS_VPORT_TYPE_NETDEV;
708     physExtVport->isExternal = TRUE;
709     physExtVport->isBridgeInternal = FALSE;
710     physExtVport->nicIndex = (NDIS_SWITCH_NIC_INDEX)physNicIndex;
711
712     RtlCopyMemory(&physExtVport->hvPortName, &virtExtVport->hvPortName,
713                   sizeof (NDIS_SWITCH_PORT_NAME));
714
715     /* 'portFriendlyName' is overwritten later. */
716     RtlCopyMemory(&physExtVport->portFriendlyName,
717                   &virtExtVport->portFriendlyName,
718                   sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
719
720     physExtVport->ovsState = OVS_STATE_PORT_CREATED;
721 }
722
723 /*
724  * --------------------------------------------------------------------------
725  * Initializes a tunnel vport.
726  * --------------------------------------------------------------------------
727  */
728 NTSTATUS
729 OvsInitTunnelVport(POVS_VPORT_ENTRY vport,
730                    OVS_VPORT_TYPE ovsType,
731                    UINT16 dstPort)
732 {
733     NTSTATUS status = STATUS_SUCCESS;
734
735     vport->isBridgeInternal = FALSE;
736     vport->ovsType = ovsType;
737     vport->ovsState = OVS_STATE_PORT_CREATED;
738     switch (ovsType) {
739     case OVS_VPORT_TYPE_GRE:
740         break;
741     case OVS_VPORT_TYPE_GRE64:
742         break;
743     case OVS_VPORT_TYPE_VXLAN:
744         status = OvsInitVxlanTunnel(vport, dstPort);
745         break;
746     default:
747         ASSERT(0);
748     }
749     return status;
750 }
751
752 /*
753  * --------------------------------------------------------------------------
754  * Initializes a bridge internal vport ie. a port of type
755  * OVS_VPORT_TYPE_INTERNAL but not present on the Hyper-V switch.
756  * --------------------------------------------------------------------------
757  */
758 NTSTATUS
759 OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport)
760 {
761     vport->isBridgeInternal = TRUE;
762     vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
763     /* Mark the status to be connected, since there is no other initialization
764      * for this port. */
765     vport->ovsState = OVS_STATE_CONNECTED;
766     return STATUS_SUCCESS;
767 }
768
769 /*
770  * --------------------------------------------------------------------------
771  * For external vports 'portFriendlyName' provided by Hyper-V is over-written
772  * by synthetic names.
773  * --------------------------------------------------------------------------
774  */
775 static VOID
776 AssignNicNameSpecial(POVS_VPORT_ENTRY vport)
777 {
778     size_t len;
779
780     if (vport->portType == NdisSwitchPortTypeExternal) {
781         if (vport->nicIndex == 0) {
782             ASSERT(vport->nicIndex == 0);
783             RtlStringCbPrintfW(vport->portFriendlyName.String,
784                                IF_MAX_STRING_SIZE,
785                                L"%s.virtualAdapter", OVS_DPPORT_EXTERNAL_NAME_W);
786         } else {
787             RtlStringCbPrintfW(vport->portFriendlyName.String,
788                                IF_MAX_STRING_SIZE,
789                                L"%s.%lu", OVS_DPPORT_EXTERNAL_NAME_W,
790                                (UINT32)vport->nicIndex);
791         }
792     } else {
793         RtlStringCbPrintfW(vport->portFriendlyName.String,
794                            IF_MAX_STRING_SIZE,
795                            L"%s", OVS_DPPORT_INTERNAL_NAME_W);
796     }
797
798     RtlStringCbLengthW(vport->portFriendlyName.String, IF_MAX_STRING_SIZE,
799                        &len);
800     vport->portFriendlyName.Length = (USHORT)len;
801 }
802
803
804 /*
805  * --------------------------------------------------------------------------
806  * Functionality common to any port on the Hyper-V switch. This function is not
807  * to be called for a port that is not on the Hyper-V switch.
808  *
809  * Inserts the port into 'portIdHashArray' and caches the pointer in the
810  * 'switchContext' if needed.
811  *
812  * For external NIC, assigns the name for the NIC.
813  * --------------------------------------------------------------------------
814  */
815 NDIS_STATUS
816 InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
817                   POVS_VPORT_ENTRY vport)
818 {
819     UINT32 hash;
820     ASSERT(vport->portNo == OVS_DPPORT_NUMBER_INVALID);
821
822     switch (vport->portType) {
823     case NdisSwitchPortTypeExternal:
824         /*
825          * Overwrite the 'portFriendlyName' of this external vport. The reason
826          * for having this in common code is to be able to call it from the NDIS
827          * Port callback as well as the NDIS NIC callback.
828          */
829         AssignNicNameSpecial(vport);
830
831         if (vport->nicIndex == 0) {
832             switchContext->virtualExternalPortId = vport->portId;
833             switchContext->virtualExternalVport = vport;
834         } else {
835             switchContext->numPhysicalNics++;
836         }
837         break;
838     case NdisSwitchPortTypeInternal:
839         ASSERT(vport->isBridgeInternal == FALSE);
840
841         /* Overwrite the 'portFriendlyName' of the internal vport. */
842         AssignNicNameSpecial(vport);
843         switchContext->internalPortId = vport->portId;
844         switchContext->internalVport = vport;
845         break;
846     case NdisSwitchPortTypeSynthetic:
847     case NdisSwitchPortTypeEmulated:
848         break;
849     }
850
851     /*
852      * It is important to not insert vport corresponding to virtual external
853      * port into the 'portIdHashArray' since the port should not be exposed to
854      * OVS userspace.
855      */
856     if (vport->portType == NdisSwitchPortTypeExternal &&
857         vport->nicIndex == 0) {
858         return NDIS_STATUS_SUCCESS;
859     }
860
861     /*
862      * NOTE: OvsJhashWords has portId as "1" word. This should be ok, even
863      * though sizeof(NDIS_SWITCH_PORT_ID) = 4, not 2, because the
864      * hyper-v switch seems to use only 2 bytes out of 4.
865      */
866     hash = OvsJhashWords(&vport->portId, 1, OVS_HASH_BASIS);
867     InsertHeadList(&switchContext->portIdHashArray[hash & OVS_VPORT_MASK],
868                    &vport->portIdLink);
869     switchContext->numHvVports++;
870     return NDIS_STATUS_SUCCESS;
871 }
872
873 /*
874  * --------------------------------------------------------------------------
875  * Functionality common to any port added from OVS userspace.
876  *
877  * Inserts the port into 'portIdHashArray', 'ovsPortNameHashArray' and caches
878  * the pointer in the 'switchContext' if needed.
879  * --------------------------------------------------------------------------
880  */
881 NDIS_STATUS
882 InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
883                    POVS_VPORT_ENTRY vport)
884 {
885     UINT32 hash;
886
887     switch(vport->ovsType) {
888     case OVS_VPORT_TYPE_VXLAN:
889         ASSERT(switchContext->vxlanVport == NULL);
890         switchContext->vxlanVport = vport;
891         switchContext->numNonHvVports++;
892         break;
893     case OVS_VPORT_TYPE_INTERNAL:
894         if (vport->isBridgeInternal) {
895             switchContext->numNonHvVports++;
896         }
897     default:
898         break;
899     }
900
901     /*
902      * Insert the port into the hash array of ports: by port number and ovs
903      * and ovs (datapath) port name.
904      * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
905      * portNo is stored in 2 bytes only (max port number = MAXUINT16).
906      */
907     hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
908     InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
909                    &vport->portNoLink);
910
911     hash = OvsJhashBytes(vport->ovsName, strlen(vport->ovsName) + 1,
912                          OVS_HASH_BASIS);
913     InsertHeadList(&gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
914                    &vport->ovsNameLink);
915
916     return STATUS_SUCCESS;
917 }
918
919
920 /*
921  * --------------------------------------------------------------------------
922  * Provides functionality that is partly complementatry to
923  * InitOvsVportCommon()/InitHvVportCommon().
924  * --------------------------------------------------------------------------
925  */
926 VOID
927 OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
928                         POVS_VPORT_ENTRY vport)
929 {
930     BOOLEAN hvSwitchPort = FALSE;
931
932     if (vport->isExternal) {
933         if (vport->nicIndex == 0) {
934             ASSERT(switchContext->numPhysicalNics == 0);
935             switchContext->virtualExternalPortId = 0;
936             switchContext->virtualExternalVport = NULL;
937             OvsFreeMemory(vport);
938             return;
939         } else {
940             ASSERT(switchContext->numPhysicalNics);
941             switchContext->numPhysicalNics--;
942             hvSwitchPort = TRUE;
943         }
944     }
945
946     switch (vport->ovsType) {
947     case OVS_VPORT_TYPE_INTERNAL:
948         if (!vport->isBridgeInternal) {
949             switchContext->internalPortId = 0;
950             switchContext->internalVport = NULL;
951             OvsInternalAdapterDown();
952             hvSwitchPort = TRUE;
953         }
954         break;
955     case OVS_VPORT_TYPE_VXLAN:
956         OvsCleanupVxlanTunnel(vport);
957         switchContext->vxlanVport = NULL;
958         break;
959     case OVS_VPORT_TYPE_GRE:
960     case OVS_VPORT_TYPE_GRE64:
961         break;
962     case OVS_VPORT_TYPE_NETDEV:
963         hvSwitchPort = TRUE;
964     default:
965         break;
966     }
967
968     RemoveEntryList(&vport->ovsNameLink);
969     RemoveEntryList(&vport->portIdLink);
970     RemoveEntryList(&vport->portNoLink);
971     if (hvSwitchPort) {
972         switchContext->numHvVports--;
973     } else {
974         switchContext->numNonHvVports--;
975     }
976     OvsFreeMemory(vport);
977 }
978
979
980 NDIS_STATUS
981 OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
982 {
983     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
984     ULONG arrIndex;
985     PNDIS_SWITCH_PORT_PARAMETERS portParam;
986     PNDIS_SWITCH_PORT_ARRAY portArray = NULL;
987     POVS_VPORT_ENTRY vport;
988
989     OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
990
991     status = OvsGetPortsOnSwitch(switchContext, &portArray);
992     if (status != NDIS_STATUS_SUCCESS) {
993         goto cleanup;
994     }
995
996     for (arrIndex = 0; arrIndex < portArray->NumElements; arrIndex++) {
997          portParam = NDIS_SWITCH_PORT_AT_ARRAY_INDEX(portArray, arrIndex);
998
999          if (portParam->IsValidationPort) {
1000              continue;
1001          }
1002
1003          vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
1004          if (vport == NULL) {
1005              status = NDIS_STATUS_RESOURCES;
1006              goto cleanup;
1007          }
1008          OvsInitVportWithPortParam(vport, portParam);
1009          status = InitHvVportCommon(switchContext, vport);
1010          if (status != NDIS_STATUS_SUCCESS) {
1011              OvsFreeMemory(vport);
1012              goto cleanup;
1013          }
1014     }
1015 cleanup:
1016     if (status != NDIS_STATUS_SUCCESS) {
1017         OvsClearAllSwitchVports(switchContext);
1018     }
1019
1020     if (portArray != NULL) {
1021         OvsFreeMemory(portArray);
1022     }
1023     OVS_LOG_TRACE("Exit: status: %x", status);
1024     return status;
1025 }
1026
1027
1028 NDIS_STATUS
1029 OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext)
1030 {
1031     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1032     PNDIS_SWITCH_NIC_ARRAY nicArray = NULL;
1033     ULONG arrIndex;
1034     PNDIS_SWITCH_NIC_PARAMETERS nicParam;
1035     POVS_VPORT_ENTRY vport;
1036
1037     OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
1038     /*
1039      * Now, get NIC list.
1040      */
1041     status = OvsGetNicsOnSwitch(switchContext, &nicArray);
1042     if (status != NDIS_STATUS_SUCCESS) {
1043         goto cleanup;
1044     }
1045     for (arrIndex = 0; arrIndex < nicArray->NumElements; ++arrIndex) {
1046
1047         nicParam = NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray, arrIndex);
1048
1049         /*
1050          * XXX: Check if the port is configured with a VLAN. Disallow such a
1051          * configuration, since we don't support tag-in-tag.
1052          */
1053
1054         /*
1055          * XXX: Check if the port is connected to a VF. Disconnect the VF in
1056          * such a case.
1057          */
1058
1059         if (nicParam->NicType == NdisSwitchNicTypeExternal &&
1060             nicParam->NicIndex != 0) {
1061             POVS_VPORT_ENTRY virtExtVport =
1062                    (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
1063
1064             vport = OvsAllocateVport();
1065             if (vport) {
1066                 OvsInitPhysNicVport(vport, virtExtVport,
1067                                     nicParam->NicIndex);
1068                 status = InitHvVportCommon(switchContext, vport);
1069                 if (status != NDIS_STATUS_SUCCESS) {
1070                     OvsFreeMemory(vport);
1071                     vport = NULL;
1072                 }
1073             }
1074         } else {
1075             vport = OvsFindVportByPortIdAndNicIndex(switchContext,
1076                                                     nicParam->PortId,
1077                                                     nicParam->NicIndex);
1078         }
1079         if (vport == NULL) {
1080             OVS_LOG_ERROR("Fail to allocate vport");
1081             continue;
1082         }
1083         OvsInitVportWithNicParam(switchContext, vport, nicParam);
1084         if (nicParam->NicType == NdisSwitchNicTypeInternal) {
1085             OvsInternalAdapterUp(vport->portNo, &nicParam->NetCfgInstanceId);
1086         }
1087     }
1088 cleanup:
1089
1090     if (nicArray != NULL) {
1091         OvsFreeMemory(nicArray);
1092     }
1093     OVS_LOG_TRACE("Exit: status: %x", status);
1094     return status;
1095 }
1096
1097 /*
1098  * --------------------------------------------------------------------------
1099  * Deletes ports added from the Hyper-V switch as well as OVS usersapce. The
1100  * function deletes ports in 'portIdHashArray'. This will delete most of the
1101  * ports that are in the 'portNoHashArray' as well. Any remaining ports
1102  * are deleted by walking the the 'portNoHashArray'.
1103  * --------------------------------------------------------------------------
1104  */
1105 VOID
1106 OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
1107 {
1108     for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1109         PLIST_ENTRY head, link, next;
1110
1111         head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
1112         LIST_FORALL_SAFE(head, link, next) {
1113             POVS_VPORT_ENTRY vport;
1114             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1115             OvsRemoveAndDeleteVport(switchContext, vport);
1116         }
1117     }
1118     /*
1119      * Remove 'virtualExternalVport' as well. This port is not part of the
1120      * 'portIdHashArray'.
1121      */
1122     if (switchContext->virtualExternalVport) {
1123         OvsRemoveAndDeleteVport(switchContext,
1124             (POVS_VPORT_ENTRY)switchContext->virtualExternalVport);
1125     }
1126
1127     for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1128         PLIST_ENTRY head, link, next;
1129
1130         head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
1131         LIST_FORALL_SAFE(head, link, next) {
1132             POVS_VPORT_ENTRY vport;
1133             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1134             ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
1135                    (vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
1136                     vport->isBridgeInternal));
1137             OvsRemoveAndDeleteVport(switchContext, vport);
1138         }
1139     }
1140
1141     ASSERT(switchContext->virtualExternalVport == NULL);
1142     ASSERT(switchContext->internalVport == NULL);
1143     ASSERT(switchContext->vxlanVport == NULL);
1144 }
1145
1146
1147 NTSTATUS
1148 OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
1149                                 CHAR *str,
1150                                 UINT16 maxStrLen)
1151 {
1152     ANSI_STRING astr;
1153     UNICODE_STRING ustr;
1154     NTSTATUS status;
1155     UINT32 size;
1156
1157     ustr.Buffer = wStr->String;
1158     ustr.Length = wStr->Length;
1159     ustr.MaximumLength = IF_MAX_STRING_SIZE;
1160
1161     astr.Buffer = str;
1162     astr.MaximumLength = maxStrLen;
1163     astr.Length = 0;
1164
1165     size = RtlUnicodeStringToAnsiSize(&ustr);
1166     if (size > maxStrLen) {
1167         return STATUS_BUFFER_OVERFLOW;
1168     }
1169
1170     status = RtlUnicodeStringToAnsiString(&astr, &ustr, FALSE);
1171
1172     ASSERT(status == STATUS_SUCCESS);
1173     if (status != STATUS_SUCCESS) {
1174         return status;
1175     }
1176     ASSERT(astr.Length <= maxStrLen);
1177     str[astr.Length] = 0;
1178     return STATUS_SUCCESS;
1179 }
1180
1181
1182 /*
1183  * XXX: Get rid of USE_NEW_VPORT_ADD_WORKFLOW while checking in the code for
1184  * new vport add workflow, or set USE_NEW_VPORT_ADD_WORKFLOW to 1.
1185  */
1186 #define USE_NEW_VPORT_ADD_WORKFLOW 1
1187 NTSTATUS
1188 OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
1189                    POVS_VPORT_EXT_INFO extInfo)
1190 {
1191     POVS_VPORT_ENTRY vport;
1192     size_t len;
1193     LOCK_STATE_EX lockState;
1194     NTSTATUS status = STATUS_SUCCESS;
1195     BOOLEAN doConvert = FALSE;
1196
1197     RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO));
1198     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1199                           NDIS_RWL_AT_DISPATCH_LEVEL);
1200     if (vportGet->portNo == 0) {
1201         StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
1202 #if USE_NEW_VPORT_ADD_WORKFLOW == 0
1203         vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name,
1204                                       (UINT32)len);
1205 #else
1206         vport = OvsFindVportByHvName(gOvsSwitchContext, vportGet->name);
1207 #endif
1208     } else {
1209         vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo);
1210     }
1211     if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED &&
1212                           vport->ovsState != OVS_STATE_NIC_CREATED)) {
1213         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1214         NdisReleaseSpinLock(gOvsCtrlLock);
1215         if (vportGet->portNo) {
1216             OVS_LOG_WARN("vport %u does not exist any more", vportGet->portNo);
1217         } else {
1218             OVS_LOG_WARN("vport %s does not exist any more", vportGet->name);
1219         }
1220         status = STATUS_DEVICE_DOES_NOT_EXIST;
1221         goto ext_info_done;
1222     }
1223     extInfo->dpNo = vportGet->dpNo;
1224     extInfo->portNo = vport->portNo;
1225     RtlCopyMemory(extInfo->macAddress, vport->currMacAddress,
1226                   sizeof (vport->currMacAddress));
1227     RtlCopyMemory(extInfo->permMACAddress, vport->permMacAddress,
1228                   sizeof (vport->permMacAddress));
1229     if (vport->ovsType == OVS_VPORT_TYPE_NETDEV) {
1230         RtlCopyMemory(extInfo->vmMACAddress, vport->vmMacAddress,
1231                       sizeof (vport->vmMacAddress));
1232     }
1233     extInfo->nicIndex = vport->nicIndex;
1234     extInfo->portId = vport->portId;
1235     extInfo->type = vport->ovsType;
1236     extInfo->mtu = vport->mtu;
1237     /*
1238      * TO be revisit XXX
1239      */
1240     if (vport->ovsState == OVS_STATE_NIC_CREATED) {
1241        extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_DOWN;
1242     } else if (vport->ovsState == OVS_STATE_CONNECTED) {
1243        extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
1244     } else {
1245        extInfo->status = OVS_EVENT_DISCONNECT;
1246     }
1247     if (extInfo->type == OVS_VPORT_TYPE_NETDEV &&
1248         (vport->ovsState == OVS_STATE_NIC_CREATED  ||
1249          vport->ovsState == OVS_STATE_CONNECTED)) {
1250         doConvert = TRUE;
1251     } else {
1252         extInfo->vmUUID[0] = 0;
1253         extInfo->vifUUID[0] = 0;
1254     }
1255 #if USE_NEW_VPORT_ADD_WORKFLOW == 0
1256     RtlCopyMemory(extInfo->name, vport->ovsName, vport->ovsNameLen + 1);
1257 #endif
1258     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1259     NdisReleaseSpinLock(gOvsCtrlLock);
1260     if (doConvert) {
1261 #if USE_NEW_VPORT_ADD_WORKFLOW == 1
1262         status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName,
1263                                                  extInfo->name,
1264                                                  OVS_MAX_PORT_NAME_LENGTH);
1265         if (status != STATUS_SUCCESS) {
1266             OVS_LOG_INFO("Fail to convert NIC name.");
1267             extInfo->vmUUID[0] = 0;
1268         }
1269 #endif
1270
1271         status = OvsConvertIfCountedStrToAnsiStr(&vport->vmName,
1272                                                  extInfo->vmUUID,
1273                                                  OVS_MAX_VM_UUID_LEN);
1274         if (status != STATUS_SUCCESS) {
1275             OVS_LOG_INFO("Fail to convert VM name.");
1276             extInfo->vmUUID[0] = 0;
1277         }
1278
1279         status = OvsConvertIfCountedStrToAnsiStr(&vport->nicName,
1280                                                  extInfo->vifUUID,
1281                                                  OVS_MAX_VIF_UUID_LEN);
1282         if (status != STATUS_SUCCESS) {
1283             OVS_LOG_INFO("Fail to convert nic UUID");
1284             extInfo->vifUUID[0] = 0;
1285         }
1286         /*
1287          * for now ignore status
1288          */
1289         status = STATUS_SUCCESS;
1290     }
1291
1292 ext_info_done:
1293     return status;
1294 }
1295
1296 /*
1297  * --------------------------------------------------------------------------
1298  *  Command Handler for 'OVS_WIN_NETDEV_CMD_GET'.
1299  * --------------------------------------------------------------------------
1300  */
1301 NTSTATUS
1302 OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1303                        UINT32 *replyLen)
1304 {
1305     NTSTATUS status = STATUS_SUCCESS;
1306     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1307     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1308     NL_ERROR nlError = NL_ERROR_SUCCESS;
1309     OVS_VPORT_GET vportGet;
1310     OVS_VPORT_EXT_INFO info;
1311     LOCK_STATE_EX lockState;
1312
1313     static const NL_POLICY ovsNetdevPolicy[] = {
1314         [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING,
1315                                        .minLen = 2,
1316                                        .maxLen = IFNAMSIZ },
1317     };
1318     PNL_ATTR netdevAttrs[ARRAY_SIZE(ovsNetdevPolicy)];
1319
1320     /* input buffer has been validated while validating transaction dev op. */
1321     ASSERT(usrParamsCtx->inputBuffer != NULL &&
1322            usrParamsCtx->inputLength > sizeof *msgIn);
1323
1324     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1325         return STATUS_INVALID_BUFFER_SIZE;
1326     }
1327
1328     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1329         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1330         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1331         ovsNetdevPolicy, netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
1332         return STATUS_INVALID_PARAMETER;
1333     }
1334
1335     OvsAcquireCtrlLock();
1336     if (!gOvsSwitchContext) {
1337         OvsReleaseCtrlLock();
1338         return STATUS_INVALID_PARAMETER;
1339     }
1340
1341     vportGet.portNo = 0;
1342     RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
1343                   NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
1344
1345     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1346     status = OvsGetExtInfoIoctl(&vportGet, &info);
1347     if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
1348         nlError = NL_ERROR_NODEV;
1349         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1350         OvsReleaseCtrlLock();
1351         goto cleanup;
1352     }
1353     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1354
1355     status = CreateNetlinkMesgForNetdev(&info, msgIn,
1356                  usrParamsCtx->outputBuffer, usrParamsCtx->outputLength,
1357                  gOvsSwitchContext->dpNo);
1358     if (status == STATUS_SUCCESS) {
1359         *replyLen = msgOut->nlMsg.nlmsgLen;
1360     }
1361     OvsReleaseCtrlLock();
1362
1363 cleanup:
1364     if (nlError != NL_ERROR_SUCCESS) {
1365         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1366             usrParamsCtx->outputBuffer;
1367
1368         BuildErrorMsg(msgIn, msgError, nlError);
1369         *replyLen = msgError->nlMsg.nlmsgLen;
1370     }
1371
1372     return STATUS_SUCCESS;
1373 }
1374
1375
1376 /*
1377  * --------------------------------------------------------------------------
1378  *  Utility function to construct an OVS_MESSAGE for the specified vport. The
1379  *  OVS_MESSAGE contains the output of a netdev command.
1380  * --------------------------------------------------------------------------
1381  */
1382 static NTSTATUS
1383 CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
1384                            POVS_MESSAGE msgIn,
1385                            PVOID outBuffer,
1386                            UINT32 outBufLen,
1387                            int dpIfIndex)
1388 {
1389     NL_BUFFER nlBuffer;
1390     BOOLEAN ok;
1391     OVS_MESSAGE msgOut;
1392     PNL_MSG_HDR nlMsg;
1393     UINT32 netdevFlags = 0;
1394
1395     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1396
1397     BuildReplyMsgFromMsgIn(msgIn, &msgOut, 0);
1398     msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1399
1400     ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1401     if (!ok) {
1402         return STATUS_INSUFFICIENT_RESOURCES;
1403     }
1404
1405     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
1406                          info->portNo);
1407     if (!ok) {
1408         return STATUS_INSUFFICIENT_RESOURCES;
1409     }
1410
1411     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
1412     if (!ok) {
1413         return STATUS_INSUFFICIENT_RESOURCES;
1414     }
1415
1416     ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
1417                             info->name);
1418     if (!ok) {
1419         return STATUS_INSUFFICIENT_RESOURCES;
1420     }
1421
1422     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
1423              (PCHAR)info->macAddress, sizeof (info->macAddress));
1424     if (!ok) {
1425         return STATUS_INSUFFICIENT_RESOURCES;
1426     }
1427
1428     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
1429     if (!ok) {
1430         return STATUS_INSUFFICIENT_RESOURCES;
1431     }
1432
1433     if (info->status != OVS_EVENT_CONNECT) {
1434         netdevFlags = OVS_WIN_NETDEV_IFF_UP;
1435     }
1436     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
1437                          netdevFlags);
1438     if (!ok) {
1439         return STATUS_INSUFFICIENT_RESOURCES;
1440     }
1441
1442     /*
1443      * XXX: add netdev_stats when we have the definition available in the
1444      * kernel.
1445      */
1446
1447     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1448     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1449
1450     return STATUS_SUCCESS;
1451 }
1452
1453 static __inline VOID
1454 OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec)
1455 {
1456     while ((!switchContext->isActivated) &&
1457           (!switchContext->isActivateFailed)) {
1458         /* Wait for the switch to be active and
1459          * the list of ports in OVS to be initialized. */
1460         NdisMSleep(sleepMicroSec);
1461     }
1462 }