a46d49fdac2dc03ae48a6e0756a2a90b75520b81
[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 NTSTATUS
1183 OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
1184                    POVS_VPORT_EXT_INFO extInfo)
1185 {
1186     POVS_VPORT_ENTRY vport;
1187     size_t len;
1188     LOCK_STATE_EX lockState;
1189     NTSTATUS status = STATUS_SUCCESS;
1190     BOOLEAN doConvert = FALSE;
1191
1192     RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO));
1193     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1194                           NDIS_RWL_AT_DISPATCH_LEVEL);
1195     if (vportGet->portNo == 0) {
1196         StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
1197         vport = OvsFindVportByHvName(gOvsSwitchContext, vportGet->name);
1198     } else {
1199         vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo);
1200     }
1201     if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED &&
1202                           vport->ovsState != OVS_STATE_NIC_CREATED)) {
1203         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1204         if (vportGet->portNo) {
1205             OVS_LOG_WARN("vport %u does not exist any more", vportGet->portNo);
1206         } else {
1207             OVS_LOG_WARN("vport %s does not exist any more", vportGet->name);
1208         }
1209         status = STATUS_DEVICE_DOES_NOT_EXIST;
1210         goto ext_info_done;
1211     }
1212     extInfo->dpNo = vportGet->dpNo;
1213     extInfo->portNo = vport->portNo;
1214     RtlCopyMemory(extInfo->macAddress, vport->currMacAddress,
1215                   sizeof (vport->currMacAddress));
1216     RtlCopyMemory(extInfo->permMACAddress, vport->permMacAddress,
1217                   sizeof (vport->permMacAddress));
1218     if (vport->ovsType == OVS_VPORT_TYPE_NETDEV) {
1219         RtlCopyMemory(extInfo->vmMACAddress, vport->vmMacAddress,
1220                       sizeof (vport->vmMacAddress));
1221     }
1222     extInfo->nicIndex = vport->nicIndex;
1223     extInfo->portId = vport->portId;
1224     extInfo->type = vport->ovsType;
1225     extInfo->mtu = vport->mtu;
1226     /*
1227      * TO be revisit XXX
1228      */
1229     if (vport->ovsState == OVS_STATE_NIC_CREATED) {
1230        extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_DOWN;
1231     } else if (vport->ovsState == OVS_STATE_CONNECTED) {
1232        extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
1233     } else {
1234        extInfo->status = OVS_EVENT_DISCONNECT;
1235     }
1236     if (extInfo->type == OVS_VPORT_TYPE_NETDEV &&
1237         (vport->ovsState == OVS_STATE_NIC_CREATED  ||
1238          vport->ovsState == OVS_STATE_CONNECTED)) {
1239         doConvert = TRUE;
1240     } else {
1241         extInfo->vmUUID[0] = 0;
1242         extInfo->vifUUID[0] = 0;
1243     }
1244     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1245     NdisReleaseSpinLock(gOvsCtrlLock);
1246     if (doConvert) {
1247         status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName,
1248                                                  extInfo->name,
1249                                                  OVS_MAX_PORT_NAME_LENGTH);
1250         if (status != STATUS_SUCCESS) {
1251             OVS_LOG_INFO("Fail to convert NIC name.");
1252             extInfo->vmUUID[0] = 0;
1253         }
1254
1255         status = OvsConvertIfCountedStrToAnsiStr(&vport->vmName,
1256                                                  extInfo->vmUUID,
1257                                                  OVS_MAX_VM_UUID_LEN);
1258         if (status != STATUS_SUCCESS) {
1259             OVS_LOG_INFO("Fail to convert VM name.");
1260             extInfo->vmUUID[0] = 0;
1261         }
1262
1263         status = OvsConvertIfCountedStrToAnsiStr(&vport->nicName,
1264                                                  extInfo->vifUUID,
1265                                                  OVS_MAX_VIF_UUID_LEN);
1266         if (status != STATUS_SUCCESS) {
1267             OVS_LOG_INFO("Fail to convert nic UUID");
1268             extInfo->vifUUID[0] = 0;
1269         }
1270         /*
1271          * for now ignore status
1272          */
1273         status = STATUS_SUCCESS;
1274     }
1275
1276 ext_info_done:
1277     return status;
1278 }
1279
1280 /*
1281  * --------------------------------------------------------------------------
1282  *  Command Handler for 'OVS_WIN_NETDEV_CMD_GET'.
1283  * --------------------------------------------------------------------------
1284  */
1285 NTSTATUS
1286 OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1287                        UINT32 *replyLen)
1288 {
1289     NTSTATUS status = STATUS_SUCCESS;
1290     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1291     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1292     NL_ERROR nlError = NL_ERROR_SUCCESS;
1293     OVS_VPORT_GET vportGet;
1294     OVS_VPORT_EXT_INFO info;
1295
1296     static const NL_POLICY ovsNetdevPolicy[] = {
1297         [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING,
1298                                        .minLen = 2,
1299                                        .maxLen = IFNAMSIZ },
1300     };
1301     PNL_ATTR netdevAttrs[ARRAY_SIZE(ovsNetdevPolicy)];
1302
1303     /* input buffer has been validated while validating transaction dev op. */
1304     ASSERT(usrParamsCtx->inputBuffer != NULL &&
1305            usrParamsCtx->inputLength > sizeof *msgIn);
1306
1307     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1308         return STATUS_INVALID_BUFFER_SIZE;
1309     }
1310
1311     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1312         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1313         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1314         ovsNetdevPolicy, netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
1315         return STATUS_INVALID_PARAMETER;
1316     }
1317
1318     OvsAcquireCtrlLock();
1319     if (!gOvsSwitchContext) {
1320         OvsReleaseCtrlLock();
1321         return STATUS_INVALID_PARAMETER;
1322     }
1323
1324     vportGet.portNo = 0;
1325     RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
1326                   NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
1327
1328     status = OvsGetExtInfoIoctl(&vportGet, &info);
1329     if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
1330         nlError = NL_ERROR_NODEV;
1331         OvsReleaseCtrlLock();
1332         goto cleanup;
1333     }
1334
1335     status = CreateNetlinkMesgForNetdev(&info, msgIn,
1336                  usrParamsCtx->outputBuffer, usrParamsCtx->outputLength,
1337                  gOvsSwitchContext->dpNo);
1338     if (status == STATUS_SUCCESS) {
1339         *replyLen = msgOut->nlMsg.nlmsgLen;
1340     }
1341     OvsReleaseCtrlLock();
1342
1343 cleanup:
1344     if (nlError != NL_ERROR_SUCCESS) {
1345         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1346             usrParamsCtx->outputBuffer;
1347
1348         BuildErrorMsg(msgIn, msgError, nlError);
1349         *replyLen = msgError->nlMsg.nlmsgLen;
1350     }
1351
1352     return STATUS_SUCCESS;
1353 }
1354
1355
1356 /*
1357  * --------------------------------------------------------------------------
1358  *  Utility function to construct an OVS_MESSAGE for the specified vport. The
1359  *  OVS_MESSAGE contains the output of a netdev command.
1360  * --------------------------------------------------------------------------
1361  */
1362 static NTSTATUS
1363 CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
1364                            POVS_MESSAGE msgIn,
1365                            PVOID outBuffer,
1366                            UINT32 outBufLen,
1367                            int dpIfIndex)
1368 {
1369     NL_BUFFER nlBuffer;
1370     BOOLEAN ok;
1371     OVS_MESSAGE msgOut;
1372     PNL_MSG_HDR nlMsg;
1373     UINT32 netdevFlags = 0;
1374
1375     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1376
1377     BuildReplyMsgFromMsgIn(msgIn, &msgOut, 0);
1378     msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1379
1380     ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1381     if (!ok) {
1382         return STATUS_INSUFFICIENT_RESOURCES;
1383     }
1384
1385     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
1386                          info->portNo);
1387     if (!ok) {
1388         return STATUS_INSUFFICIENT_RESOURCES;
1389     }
1390
1391     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
1392     if (!ok) {
1393         return STATUS_INSUFFICIENT_RESOURCES;
1394     }
1395
1396     ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
1397                             info->name);
1398     if (!ok) {
1399         return STATUS_INSUFFICIENT_RESOURCES;
1400     }
1401
1402     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
1403              (PCHAR)info->macAddress, sizeof (info->macAddress));
1404     if (!ok) {
1405         return STATUS_INSUFFICIENT_RESOURCES;
1406     }
1407
1408     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
1409     if (!ok) {
1410         return STATUS_INSUFFICIENT_RESOURCES;
1411     }
1412
1413     if (info->status != OVS_EVENT_CONNECT) {
1414         netdevFlags = OVS_WIN_NETDEV_IFF_UP;
1415     }
1416     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
1417                          netdevFlags);
1418     if (!ok) {
1419         return STATUS_INSUFFICIENT_RESOURCES;
1420     }
1421
1422     /*
1423      * XXX: add netdev_stats when we have the definition available in the
1424      * kernel.
1425      */
1426
1427     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1428     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1429
1430     return STATUS_SUCCESS;
1431 }
1432
1433 static __inline VOID
1434 OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec)
1435 {
1436     while ((!switchContext->isActivated) &&
1437           (!switchContext->isActivateFailed)) {
1438         /* Wait for the switch to be active and
1439          * the list of ports in OVS to be initialized. */
1440         NdisMSleep(sleepMicroSec);
1441     }
1442 }