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