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