913954561e367d50a5d84a596804c74bcf4909f5
[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 "Stt.h"
25 #include "IpHelper.h"
26 #include "Oid.h"
27 #include "Datapath.h"
28
29 #ifdef OVS_DBG_MOD
30 #undef OVS_DBG_MOD
31 #endif
32 #define OVS_DBG_MOD OVS_DBG_VPORT
33 #include "Debug.h"
34
35 #define VPORT_NIC_ENTER(_nic) \
36     OVS_LOG_TRACE("Enter: PortId: %x, NicIndex: %d", _nic->PortId, \
37                                                      _nic->NicIndex)
38
39 #define VPORT_NIC_EXIT(_nic) \
40     OVS_LOG_TRACE("Exit: PortId: %x, NicIndex: %d", _nic->PortId, \
41                                                     _nic->NicIndex)
42
43 #define VPORT_PORT_ENTER(_port) \
44     OVS_LOG_TRACE("Enter: PortId: %x", _port->PortId)
45
46 #define VPORT_PORT_EXIT(_port) \
47     OVS_LOG_TRACE("Exit: PortId: %x", _port->PortId)
48
49 #define OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC    100
50
51 /* Context structure used to pass back and forth information to the tunnel
52  * filter threads. */
53 typedef struct _OVS_TUNFLT_INIT_CONTEXT {
54     POVS_SWITCH_CONTEXT switchContext;
55     UINT32 outputLength;
56     PVOID outputBuffer;
57     PVOID inputBuffer;
58     POVS_VPORT_ENTRY vport;
59     BOOLEAN hvSwitchPort;
60     BOOLEAN hvDelete;
61     BOOLEAN ovsDelete;
62 } OVS_TUNFLT_INIT_CONTEXT, *POVS_TUNFLT_INIT_CONTEXT;
63
64
65 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
66
67 static VOID OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
68                 PNDIS_SWITCH_PORT_PARAMETERS portParam);
69 static VOID OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
70                 POVS_VPORT_ENTRY vport, PNDIS_SWITCH_NIC_PARAMETERS nicParam);
71 static VOID OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVPort,
72                 POVS_VPORT_ENTRY virtExtVport, UINT32 nicIndex);
73 static __inline VOID OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext,
74                                      ULONG sleepMicroSec);
75 static NTSTATUS OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
76                                    POVS_VPORT_EXT_INFO extInfo);
77 static NTSTATUS CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
78                                            POVS_MESSAGE msgIn,
79                                            PVOID outBuffer,
80                                            UINT32 outBufLen,
81                                            int dpIfIndex);
82 static POVS_VPORT_ENTRY OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
83                                               PWSTR wsName, SIZE_T wstrSize);
84 static NDIS_STATUS InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
85                                      POVS_VPORT_ENTRY vport,
86                                      BOOLEAN newPort);
87 static VOID OvsCleanupVportCommon(POVS_SWITCH_CONTEXT switchContext,
88                                   POVS_VPORT_ENTRY vport,
89                                   BOOLEAN hvSwitchPort,
90                                   BOOLEAN hvDelete,
91                                   BOOLEAN ovsDelete);
92 static VOID OvsTunnelVportPendingInit(PVOID context,
93                                       NTSTATUS status,
94                                       UINT32 *replyLen);
95 static VOID OvsTunnelVportPendingUninit(PVOID context,
96                                         NTSTATUS status,
97                                         UINT32 *replyLen);
98
99
100 /*
101  * Functions implemented in relaton to NDIS port manipulation.
102  */
103 NDIS_STATUS
104 HvCreatePort(POVS_SWITCH_CONTEXT switchContext,
105              PNDIS_SWITCH_PORT_PARAMETERS portParam)
106 {
107     POVS_VPORT_ENTRY vport;
108     LOCK_STATE_EX lockState;
109     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
110     BOOLEAN newPort = FALSE;
111
112     VPORT_PORT_ENTER(portParam);
113
114     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
115     /* Lookup by port ID. */
116     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
117                                             portParam->PortId, 0);
118     if (vport != NULL) {
119         OVS_LOG_ERROR("Port add failed due to duplicate port name, "
120                       "port Id: %u", portParam->PortId);
121         status = STATUS_DATA_NOT_ACCEPTED;
122         goto create_port_done;
123     }
124
125     /*
126      * Lookup by port name to see if this port with this name had been added
127      * (and deleted) previously.
128      */
129     vport = OvsFindVportByHvNameW(gOvsSwitchContext,
130                                   portParam->PortFriendlyName.String,
131                                   portParam->PortFriendlyName.Length);
132     if (vport && vport->isPresentOnHv == FALSE) {
133         OVS_LOG_ERROR("Port add failed since a port already exists on "
134                       "the specified port Id: %u, ovsName: %s",
135                       portParam->PortId, vport->ovsName);
136         status = STATUS_DATA_NOT_ACCEPTED;
137         goto create_port_done;
138     }
139
140     if (vport != NULL) {
141         ASSERT(vport->isPresentOnHv);
142         ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
143
144         /*
145          * It should be possible to simply just mark this port as "not deleted"
146          * given that the port Id and the name are the same and also provided
147          * that the other properties that we cache have not changed.
148          */
149         if (vport->portType != portParam->PortType) {
150             OVS_LOG_INFO("Port add failed due to PortType change, port Id: %u"
151                          " old: %u, new: %u", portParam->PortId,
152                          vport->portType, portParam->PortType);
153             status = STATUS_DATA_NOT_ACCEPTED;
154             goto create_port_done;
155         }
156         vport->isPresentOnHv = FALSE;
157     } else {
158         vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
159         if (vport == NULL) {
160             status = NDIS_STATUS_RESOURCES;
161             goto create_port_done;
162         }
163         newPort = TRUE;
164     }
165     OvsInitVportWithPortParam(vport, portParam);
166     InitHvVportCommon(switchContext, vport, newPort);
167
168 create_port_done:
169     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
170     VPORT_PORT_EXIT(portParam);
171     return status;
172 }
173
174
175 /*
176  * Function updating the port properties
177  */
178 NDIS_STATUS
179 HvUpdatePort(POVS_SWITCH_CONTEXT switchContext,
180              PNDIS_SWITCH_PORT_PARAMETERS portParam)
181 {
182     POVS_VPORT_ENTRY vport;
183     LOCK_STATE_EX lockState;
184     OVS_VPORT_STATE ovsState;
185     NDIS_SWITCH_NIC_STATE nicState;
186
187     VPORT_PORT_ENTER(portParam);
188
189     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
190     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
191                                             portParam->PortId, 0);
192     /*
193      * Update properties only for NETDEV ports for supprting PS script
194      * We don't allow changing the names of the internal or external ports
195      */
196     if (vport == NULL || (( vport->portType != NdisSwitchPortTypeSynthetic) &&
197         ( vport->portType != NdisSwitchPortTypeEmulated))) {
198         goto update_port_done;
199     }
200
201     /* Store the nic and the OVS states as Nic Create won't be called */
202     ovsState = vport->ovsState;
203     nicState = vport->nicState;
204     
205     /*
206      * Currently only the port friendly name is being updated
207      * Make sure that no other properties are changed
208      */
209     ASSERT(portParam->PortId == vport->portId);
210     ASSERT(portParam->PortState == vport->portState);
211     ASSERT(portParam->PortType == vport->portType);
212
213     /*
214      * Call the set parameters function the handle all properties
215      * change in a single place in case future version supports change of
216      * other properties
217      */
218     OvsInitVportWithPortParam(vport, portParam);
219     /* Retore the nic and OVS states */
220     vport->nicState = nicState;
221     vport->ovsState = ovsState;
222
223 update_port_done:
224     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
225     VPORT_PORT_EXIT(portParam);
226
227     /* Must always return success */
228     return NDIS_STATUS_SUCCESS;
229 }
230
231 VOID
232 HvTeardownPort(POVS_SWITCH_CONTEXT switchContext,
233                PNDIS_SWITCH_PORT_PARAMETERS portParam)
234 {
235     POVS_VPORT_ENTRY vport;
236     LOCK_STATE_EX lockState;
237
238     VPORT_PORT_ENTER(portParam);
239
240     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
241     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
242                                             portParam->PortId, 0);
243     if (vport) {
244         /* add assertion here */
245         vport->portState = NdisSwitchPortStateTeardown;
246         vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
247     } else {
248         OVS_LOG_WARN("Vport not present.");
249     }
250     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
251
252     VPORT_PORT_EXIT(portParam);
253 }
254
255
256 VOID
257 HvDeletePort(POVS_SWITCH_CONTEXT switchContext,
258              PNDIS_SWITCH_PORT_PARAMETERS portParams)
259 {
260     POVS_VPORT_ENTRY vport;
261     LOCK_STATE_EX lockState;
262
263     VPORT_PORT_ENTER(portParams);
264
265     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
266     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
267                                             portParams->PortId, 0);
268
269     /*
270      * XXX: we can only destroy and remove the port if its datapath port
271      * counterpart was deleted. If the datapath port counterpart is present,
272      * we only mark the vport for deletion, so that a netlink command vport
273      * delete will delete the vport.
274     */
275     if (vport) {
276         OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
277     } else {
278         OVS_LOG_WARN("Vport not present.");
279     }
280     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
281
282     VPORT_PORT_EXIT(portParams);
283 }
284
285
286 /*
287  * Functions implemented in relaton to NDIS NIC manipulation.
288  */
289 NDIS_STATUS
290 HvCreateNic(POVS_SWITCH_CONTEXT switchContext,
291             PNDIS_SWITCH_NIC_PARAMETERS nicParam)
292 {
293     POVS_VPORT_ENTRY vport;
294     UINT32 portNo = 0;
295     UINT32 event = 0;
296     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
297
298     LOCK_STATE_EX lockState;
299
300     VPORT_NIC_ENTER(nicParam);
301
302     /* Wait for lists to be initialized. */
303     OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
304
305     if (!switchContext->isActivated) {
306         OVS_LOG_WARN("Switch is not activated yet.");
307         /* Veto the creation of nic */
308         status = NDIS_STATUS_NOT_SUPPORTED;
309         goto done;
310     }
311
312     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
313     vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId, 0);
314     if (vport == NULL) {
315         OVS_LOG_ERROR("Create NIC without Switch Port,"
316                       " PortId: %x, NicIndex: %d",
317                       nicParam->PortId, nicParam->NicIndex);
318         status = NDIS_STATUS_INVALID_PARAMETER;
319         goto add_nic_done;
320     }
321
322     if (nicParam->NicType == NdisSwitchNicTypeExternal &&
323         nicParam->NicIndex != 0) {
324         POVS_VPORT_ENTRY virtExtVport =
325             (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
326
327         vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
328         if (vport == NULL) {
329             status = NDIS_STATUS_RESOURCES;
330             goto add_nic_done;
331         }
332         OvsInitPhysNicVport(vport, virtExtVport, nicParam->NicIndex);
333         status = InitHvVportCommon(switchContext, vport, TRUE);
334         if (status != NDIS_STATUS_SUCCESS) {
335             OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
336             goto add_nic_done;
337         }
338     }
339     OvsInitVportWithNicParam(switchContext, vport, nicParam);
340     portNo = vport->portNo;
341     if (vport->ovsState == OVS_STATE_CONNECTED) {
342         event = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
343     } else if (vport->ovsState == OVS_STATE_NIC_CREATED) {
344         event = OVS_EVENT_CONNECT;
345     }
346
347 add_nic_done:
348     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
349     if (portNo != OVS_DPPORT_NUMBER_INVALID && event) {
350         OvsPostEvent(portNo, event);
351     }
352
353 done:
354     VPORT_NIC_EXIT(nicParam);
355     OVS_LOG_TRACE("Exit: status %8x.\n", status);
356
357     return status;
358 }
359
360
361 /* Mark already created NIC as connected. */
362 VOID
363 HvConnectNic(POVS_SWITCH_CONTEXT switchContext,
364              PNDIS_SWITCH_NIC_PARAMETERS nicParam)
365 {
366     LOCK_STATE_EX lockState;
367     POVS_VPORT_ENTRY vport;
368     UINT32 portNo = 0;
369
370     VPORT_NIC_ENTER(nicParam);
371
372     /* Wait for lists to be initialized. */
373     OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
374
375     if (!switchContext->isActivated) {
376         OVS_LOG_WARN("Switch is not activated yet.");
377         goto done;
378     }
379
380     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
381     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
382                                             nicParam->PortId,
383                                             nicParam->NicIndex);
384
385     if (!vport) {
386         OVS_LOG_WARN("Vport not present.");
387         NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
388         ASSERT(0);
389         goto done;
390     }
391
392     vport->ovsState = OVS_STATE_CONNECTED;
393     vport->nicState = NdisSwitchNicStateConnected;
394     portNo = vport->portNo;
395
396     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
397
398     /* XXX only if portNo != INVALID or always? */
399     OvsPostEvent(portNo, OVS_EVENT_LINK_UP);
400
401     if (nicParam->NicType == NdisSwitchNicTypeInternal) {
402         OvsInternalAdapterUp(portNo, &nicParam->NetCfgInstanceId);
403     }
404
405 done:
406     VPORT_NIC_EXIT(nicParam);
407 }
408
409 VOID
410 HvUpdateNic(POVS_SWITCH_CONTEXT switchContext,
411             PNDIS_SWITCH_NIC_PARAMETERS nicParam)
412 {
413     POVS_VPORT_ENTRY vport;
414     LOCK_STATE_EX lockState;
415
416     UINT32 status = 0, portNo = 0;
417
418     VPORT_NIC_ENTER(nicParam);
419
420     /* Wait for lists to be initialized. */
421     OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
422
423     if (!switchContext->isActivated) {
424         OVS_LOG_WARN("Switch is not activated yet.");
425         goto update_nic_done;
426     }
427
428     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
429     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
430                                             nicParam->PortId,
431                                             nicParam->NicIndex);
432     if (vport == NULL) {
433         NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
434         OVS_LOG_WARN("Vport search failed.");
435         goto update_nic_done;
436     }
437     switch (nicParam->NicType) {
438     case NdisSwitchNicTypeExternal:
439     case NdisSwitchNicTypeInternal:
440         RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
441                       sizeof (GUID));
442         break;
443     case NdisSwitchNicTypeSynthetic:
444     case NdisSwitchNicTypeEmulated:
445         if (!RtlEqualMemory(vport->vmMacAddress, nicParam->VMMacAddress,
446                            sizeof (vport->vmMacAddress))) {
447             status |= OVS_EVENT_MAC_CHANGE;
448             RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
449                           sizeof (vport->vmMacAddress));
450         }
451         break;
452     default:
453         ASSERT(0);
454     }
455     if (!RtlEqualMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
456                         sizeof (vport->permMacAddress))) {
457         RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
458                       sizeof (vport->permMacAddress));
459         status |= OVS_EVENT_MAC_CHANGE;
460     }
461     if (!RtlEqualMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
462                         sizeof (vport->currMacAddress))) {
463         RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
464                       sizeof (vport->currMacAddress));
465         status |= OVS_EVENT_MAC_CHANGE;
466     }
467
468     if (vport->mtu != nicParam->MTU) {
469         vport->mtu = nicParam->MTU;
470         status |= OVS_EVENT_MTU_CHANGE;
471     }
472     vport->numaNodeId = nicParam->NumaNodeId;
473     portNo = vport->portNo;
474
475     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
476     if (status && portNo) {
477         OvsPostEvent(portNo, status);
478     }
479 update_nic_done:
480     VPORT_NIC_EXIT(nicParam);
481 }
482
483
484 VOID
485 HvDisconnectNic(POVS_SWITCH_CONTEXT switchContext,
486                 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
487 {
488     POVS_VPORT_ENTRY vport;
489     UINT32 portNo = 0;
490     LOCK_STATE_EX lockState;
491     BOOLEAN isInternalPort = FALSE;
492
493     VPORT_NIC_ENTER(nicParam);
494
495     /* Wait for lists to be initialized. */
496     OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
497
498     if (!switchContext->isActivated) {
499         OVS_LOG_WARN("Switch is not activated yet.");
500         goto done;
501     }
502
503     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
504     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
505                                             nicParam->PortId,
506                                             nicParam->NicIndex);
507
508     if (!vport) {
509         OVS_LOG_WARN("Vport not present.");
510         NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
511         goto done;
512     }
513
514     vport->nicState = NdisSwitchNicStateDisconnected;
515     vport->ovsState = OVS_STATE_NIC_CREATED;
516     portNo = vport->portNo;
517
518     if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
519         isInternalPort = TRUE;
520     }
521
522     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
523
524     /* XXX if portNo != INVALID or always? */
525     OvsPostEvent(portNo, OVS_EVENT_LINK_DOWN);
526
527     if (isInternalPort) {
528         OvsInternalAdapterDown();
529     }
530
531 done:
532     VPORT_NIC_EXIT(nicParam);
533 }
534
535
536 VOID
537 HvDeleteNic(POVS_SWITCH_CONTEXT switchContext,
538             PNDIS_SWITCH_NIC_PARAMETERS nicParam)
539 {
540     LOCK_STATE_EX lockState;
541     POVS_VPORT_ENTRY vport;
542     UINT32 portNo = 0;
543
544     VPORT_NIC_ENTER(nicParam);
545     /* Wait for lists to be initialized. */
546     OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC);
547
548     if (!switchContext->isActivated) {
549         OVS_LOG_WARN("Switch is not activated yet.");
550         goto done;
551     }
552
553     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
554     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
555                                             nicParam->PortId,
556                                             nicParam->NicIndex);
557
558     if (!vport) {
559         OVS_LOG_WARN("Vport not present.");
560         NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
561         goto done;
562     }
563
564     vport->nicState = NdisSwitchNicStateUnknown;
565     vport->ovsState = OVS_STATE_PORT_CREATED;
566
567     portNo = vport->portNo;
568     if (vport->portType == NdisSwitchPortTypeExternal &&
569         vport->nicIndex != 0) {
570         OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
571     }
572
573     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
574     /* XXX if portNo != INVALID or always? */
575     OvsPostEvent(portNo, OVS_EVENT_DISCONNECT);
576
577 done:
578     VPORT_NIC_EXIT(nicParam);
579 }
580
581
582 /*
583  * OVS Vport related functionality.
584  */
585 POVS_VPORT_ENTRY
586 OvsFindVportByPortNo(POVS_SWITCH_CONTEXT switchContext,
587                      UINT32 portNo)
588 {
589     POVS_VPORT_ENTRY vport;
590     PLIST_ENTRY head, link;
591     UINT32 hash = OvsJhashBytes((const VOID *)&portNo, sizeof(portNo),
592                                 OVS_HASH_BASIS);
593     head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
594     LIST_FORALL(head, link) {
595         vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
596         if (vport->portNo == portNo) {
597             return vport;
598         }
599     }
600     return NULL;
601 }
602
603
604 POVS_VPORT_ENTRY
605 OvsFindTunnelVportByDstPort(POVS_SWITCH_CONTEXT switchContext,
606                             UINT16 dstPort,
607                             OVS_VPORT_TYPE ovsPortType)
608 {
609     POVS_VPORT_ENTRY vport;
610     PLIST_ENTRY head, link;
611     UINT32 hash = OvsJhashBytes((const VOID *)&dstPort, sizeof(dstPort),
612                                 OVS_HASH_BASIS);
613     head = &(switchContext->tunnelVportsArray[hash & OVS_VPORT_MASK]);
614     LIST_FORALL(head, link) {
615         vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, tunnelVportLink);
616         if (GetPortFromPriv(vport) == dstPort &&
617             vport->ovsType == ovsPortType) {
618             return vport;
619         }
620     }
621     return NULL;
622 }
623
624
625 POVS_VPORT_ENTRY
626 OvsFindVportByOvsName(POVS_SWITCH_CONTEXT switchContext,
627                       PSTR name)
628 {
629     POVS_VPORT_ENTRY vport;
630     PLIST_ENTRY head, link;
631     UINT32 hash;
632     SIZE_T length = strlen(name) + 1;
633
634     hash = OvsJhashBytes((const VOID *)name, length, OVS_HASH_BASIS);
635     head = &(switchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK]);
636
637     LIST_FORALL(head, link) {
638         vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, ovsNameLink);
639         if (!strcmp(name, vport->ovsName)) {
640             return vport;
641         }
642     }
643
644     return NULL;
645 }
646
647 /* OvsFindVportByHvName: "name" is assumed to be null-terminated */
648 POVS_VPORT_ENTRY
649 OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
650                       PWSTR wsName, SIZE_T wstrSize)
651 {
652     POVS_VPORT_ENTRY vport = NULL;
653     PLIST_ENTRY head, link;
654     UINT i;
655
656     for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
657         head = &(switchContext->portIdHashArray[i]);
658         LIST_FORALL(head, link) {
659             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
660
661             /*
662              * NOTE about portFriendlyName:
663              * If the string is NULL-terminated, the Length member does not
664              * include the terminating NULL character.
665              */
666             if (vport->portFriendlyName.Length == wstrSize &&
667                 RtlEqualMemory(wsName, vport->portFriendlyName.String,
668                                vport->portFriendlyName.Length)) {
669                 goto Cleanup;
670             }
671
672             vport = NULL;
673         }
674     }
675
676     /*
677      * Look in the list of ports that were added from the Hyper-V switch and
678      * deleted.
679      */
680     if (vport == NULL) {
681         for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
682             head = &(switchContext->portNoHashArray[i]);
683             LIST_FORALL(head, link) {
684                 vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
685                 if (vport->portFriendlyName.Length == wstrSize &&
686                     RtlEqualMemory(wsName, vport->portFriendlyName.String,
687                                    vport->portFriendlyName.Length)) {
688                     goto Cleanup;
689                 }
690
691                 vport = NULL;
692             }
693         }
694     }
695
696 Cleanup:
697     return vport;
698 }
699
700 POVS_VPORT_ENTRY
701 OvsFindVportByHvNameA(POVS_SWITCH_CONTEXT switchContext,
702                       PSTR name)
703 {
704     POVS_VPORT_ENTRY vport = NULL;
705     /* 'portFriendlyName' is not NUL-terminated. */
706     SIZE_T length = strlen(name);
707     SIZE_T wstrSize = length * sizeof(WCHAR);
708     UINT i;
709
710     PWSTR wsName = OvsAllocateMemoryWithTag(wstrSize, OVS_VPORT_POOL_TAG);
711     if (!wsName) {
712         return NULL;
713     }
714     for (i = 0; i < length; i++) {
715         wsName[i] = name[i];
716     }
717     vport = OvsFindVportByHvNameW(switchContext, wsName, wstrSize);
718     OvsFreeMemoryWithTag(wsName, OVS_VPORT_POOL_TAG);
719     return vport;
720 }
721
722 POVS_VPORT_ENTRY
723 OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT switchContext,
724                                 NDIS_SWITCH_PORT_ID portId,
725                                 NDIS_SWITCH_NIC_INDEX index)
726 {
727     if (switchContext->virtualExternalVport &&
728             portId == switchContext->virtualExternalPortId &&
729             index == switchContext->virtualExternalVport->nicIndex) {
730         return (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
731     } else if (switchContext->internalVport &&
732                portId == switchContext->internalPortId &&
733                index == switchContext->internalVport->nicIndex) {
734         return (POVS_VPORT_ENTRY)switchContext->internalVport;
735     } else {
736         PLIST_ENTRY head, link;
737         POVS_VPORT_ENTRY vport;
738         UINT32 hash;
739         hash = OvsJhashWords((UINT32 *)&portId, 1, OVS_HASH_BASIS);
740         head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
741         LIST_FORALL(head, link) {
742             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
743             if (portId == vport->portId && index == vport->nicIndex) {
744                 return vport;
745             }
746         }
747         return NULL;
748     }
749 }
750
751 POVS_VPORT_ENTRY
752 OvsAllocateVport(VOID)
753 {
754     POVS_VPORT_ENTRY vport;
755     vport = (POVS_VPORT_ENTRY)OvsAllocateMemoryWithTag(
756         sizeof(OVS_VPORT_ENTRY), OVS_VPORT_POOL_TAG);
757     if (vport == NULL) {
758         return NULL;
759     }
760     RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY));
761     vport->ovsState = OVS_STATE_UNKNOWN;
762     vport->isPresentOnHv = FALSE;
763     vport->portNo = OVS_DPPORT_NUMBER_INVALID;
764
765     InitializeListHead(&vport->ovsNameLink);
766     InitializeListHead(&vport->portIdLink);
767     InitializeListHead(&vport->portNoLink);
768
769     return vport;
770 }
771
772 static VOID
773 OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
774                           PNDIS_SWITCH_PORT_PARAMETERS portParam)
775 {
776     vport->portType = portParam->PortType;
777     vport->portState = portParam->PortState;
778     vport->portId = portParam->PortId;
779     vport->nicState = NdisSwitchNicStateUnknown;
780     vport->isExternal = FALSE;
781     vport->isBridgeInternal = FALSE;
782
783     switch (vport->portType) {
784     case NdisSwitchPortTypeExternal:
785         vport->isExternal = TRUE;
786         vport->ovsType = OVS_VPORT_TYPE_NETDEV;
787         break;
788     case NdisSwitchPortTypeInternal:
789         vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
790         break;
791     case NdisSwitchPortTypeSynthetic:
792     case NdisSwitchPortTypeEmulated:
793         vport->ovsType = OVS_VPORT_TYPE_NETDEV;
794         break;
795     }
796     RtlCopyMemory(&vport->hvPortName, &portParam->PortName,
797                   sizeof (NDIS_SWITCH_PORT_NAME));
798     /* For external and internal ports, 'portFriendlyName' is overwritten
799      * later. */
800     RtlCopyMemory(&vport->portFriendlyName, &portParam->PortFriendlyName,
801                   sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
802
803     switch (vport->portState) {
804     case NdisSwitchPortStateCreated:
805         vport->ovsState = OVS_STATE_PORT_CREATED;
806         break;
807     case NdisSwitchPortStateTeardown:
808         vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
809         break;
810     case NdisSwitchPortStateDeleted:
811         vport->ovsState = OVS_STATE_PORT_DELETED;
812         break;
813     }
814 }
815
816
817 static VOID
818 OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
819                          POVS_VPORT_ENTRY vport,
820                          PNDIS_SWITCH_NIC_PARAMETERS nicParam)
821 {
822     ASSERT(vport->portId == nicParam->PortId);
823     ASSERT(vport->ovsState == OVS_STATE_PORT_CREATED);
824
825     UNREFERENCED_PARAMETER(switchContext);
826
827     RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
828                   sizeof (nicParam->PermanentMacAddress));
829     RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
830                   sizeof (nicParam->CurrentMacAddress));
831
832     if (nicParam->NicType == NdisSwitchNicTypeSynthetic ||
833         nicParam->NicType == NdisSwitchNicTypeEmulated) {
834         RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
835                       sizeof (nicParam->VMMacAddress));
836         RtlCopyMemory(&vport->vmName, &nicParam->VmName,
837                       sizeof (nicParam->VmName));
838     } else {
839         RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
840                       sizeof (nicParam->NetCfgInstanceId));
841     }
842     RtlCopyMemory(&vport->nicName, &nicParam->NicName,
843                   sizeof (nicParam->NicName));
844     vport->mtu = nicParam->MTU;
845     vport->nicState = nicParam->NicState;
846     vport->nicIndex = nicParam->NicIndex;
847     vport->numaNodeId = nicParam->NumaNodeId;
848
849     switch (vport->nicState) {
850     case NdisSwitchNicStateCreated:
851         vport->ovsState = OVS_STATE_NIC_CREATED;
852         break;
853     case NdisSwitchNicStateConnected:
854         vport->ovsState = OVS_STATE_CONNECTED;
855         break;
856     case NdisSwitchNicStateDisconnected:
857         vport->ovsState = OVS_STATE_NIC_CREATED;
858         break;
859     case NdisSwitchNicStateDeleted:
860         vport->ovsState = OVS_STATE_PORT_CREATED;
861         break;
862     }
863 }
864
865 /*
866  * --------------------------------------------------------------------------
867  * Copies the relevant NDIS port properties from a virtual (pseudo) external
868  * NIC to a physical (real) external NIC.
869  * --------------------------------------------------------------------------
870  */
871 static VOID
872 OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVport,
873                     POVS_VPORT_ENTRY virtExtVport,
874                     UINT32 physNicIndex)
875 {
876     physExtVport->portType = virtExtVport->portType;
877     physExtVport->portState = virtExtVport->portState;
878     physExtVport->portId = virtExtVport->portId;
879     physExtVport->nicState = NdisSwitchNicStateUnknown;
880     physExtVport->ovsType = OVS_VPORT_TYPE_NETDEV;
881     physExtVport->isExternal = TRUE;
882     physExtVport->isBridgeInternal = FALSE;
883     physExtVport->nicIndex = (NDIS_SWITCH_NIC_INDEX)physNicIndex;
884
885     RtlCopyMemory(&physExtVport->hvPortName, &virtExtVport->hvPortName,
886                   sizeof (NDIS_SWITCH_PORT_NAME));
887
888     /* 'portFriendlyName' is overwritten later. */
889     RtlCopyMemory(&physExtVport->portFriendlyName,
890                   &virtExtVport->portFriendlyName,
891                   sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
892
893     physExtVport->ovsState = OVS_STATE_PORT_CREATED;
894 }
895
896 /*
897  * --------------------------------------------------------------------------
898  * Initializes a tunnel vport.
899  * --------------------------------------------------------------------------
900  */
901 NTSTATUS
902 OvsInitTunnelVport(PVOID userContext,
903                    POVS_VPORT_ENTRY vport,
904                    OVS_VPORT_TYPE ovsType,
905                    UINT16 dstPort)
906 {
907     NTSTATUS status = STATUS_SUCCESS;
908     POVS_USER_PARAMS_CONTEXT usrParamsCtx =
909         (POVS_USER_PARAMS_CONTEXT)userContext;
910
911     vport->isBridgeInternal = FALSE;
912     vport->ovsType = ovsType;
913     vport->ovsState = OVS_STATE_PORT_CREATED;
914     switch (ovsType) {
915     case OVS_VPORT_TYPE_GRE:
916         break;
917     case OVS_VPORT_TYPE_GRE64:
918         break;
919     case OVS_VPORT_TYPE_VXLAN:
920     {
921         POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
922
923         tunnelContext = OvsAllocateMemory(sizeof(*tunnelContext));
924         if (tunnelContext == NULL) {
925             status = STATUS_INSUFFICIENT_RESOURCES;
926             break;
927         }
928         tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
929         tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
930         tunnelContext->outputLength = usrParamsCtx->outputLength;
931         tunnelContext->vport = vport;
932
933         status = OvsInitVxlanTunnel(usrParamsCtx->irp,
934                                     vport,
935                                     dstPort,
936                                     OvsTunnelVportPendingInit,
937                                     (PVOID)tunnelContext);
938         break;
939     }
940     case OVS_VPORT_TYPE_STT:
941         status = OvsInitSttTunnel(vport, dstPort);
942         break;
943     default:
944         ASSERT(0);
945     }
946     return status;
947 }
948
949 /*
950  * --------------------------------------------------------------------------
951  * Initializes a bridge internal vport ie. a port of type
952  * OVS_VPORT_TYPE_INTERNAL but not present on the Hyper-V switch.
953  * --------------------------------------------------------------------------
954  */
955 NTSTATUS
956 OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport)
957 {
958     vport->isBridgeInternal = TRUE;
959     vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
960     /* Mark the status to be connected, since there is no other initialization
961      * for this port. */
962     vport->ovsState = OVS_STATE_CONNECTED;
963     return STATUS_SUCCESS;
964 }
965
966 /*
967  * --------------------------------------------------------------------------
968  * For external vports 'portFriendlyName' provided by Hyper-V is over-written
969  * by synthetic names.
970  * --------------------------------------------------------------------------
971  */
972 static VOID
973 AssignNicNameSpecial(POVS_VPORT_ENTRY vport)
974 {
975     size_t len;
976
977     if (vport->portType == NdisSwitchPortTypeExternal) {
978         if (vport->nicIndex == 0) {
979             ASSERT(vport->nicIndex == 0);
980             RtlStringCbPrintfW(vport->portFriendlyName.String,
981                                IF_MAX_STRING_SIZE,
982                                L"%s.virtualAdapter", OVS_DPPORT_EXTERNAL_NAME_W);
983         } else {
984             RtlStringCbPrintfW(vport->portFriendlyName.String,
985                                IF_MAX_STRING_SIZE,
986                                L"%s.%lu", OVS_DPPORT_EXTERNAL_NAME_W,
987                                (UINT32)vport->nicIndex);
988         }
989     } else {
990         RtlStringCbPrintfW(vport->portFriendlyName.String,
991                            IF_MAX_STRING_SIZE,
992                            L"%s", OVS_DPPORT_INTERNAL_NAME_W);
993     }
994
995     RtlStringCbLengthW(vport->portFriendlyName.String, IF_MAX_STRING_SIZE,
996                        &len);
997     vport->portFriendlyName.Length = (USHORT)len;
998 }
999
1000
1001 /*
1002  * --------------------------------------------------------------------------
1003  * Functionality common to any port on the Hyper-V switch. This function is not
1004  * to be called for a port that is not on the Hyper-V switch.
1005  *
1006  * Inserts the port into 'portIdHashArray' and caches the pointer in the
1007  * 'switchContext' if needed.
1008  *
1009  * For external NIC, assigns the name for the NIC.
1010  * --------------------------------------------------------------------------
1011  */
1012 static NDIS_STATUS
1013 InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
1014                   POVS_VPORT_ENTRY vport,
1015                   BOOLEAN newPort)
1016 {
1017     UINT32 hash;
1018
1019     switch (vport->portType) {
1020     case NdisSwitchPortTypeExternal:
1021         /*
1022          * Overwrite the 'portFriendlyName' of this external vport. The reason
1023          * for having this in common code is to be able to call it from the NDIS
1024          * Port callback as well as the NDIS NIC callback.
1025          */
1026         AssignNicNameSpecial(vport);
1027
1028         if (vport->nicIndex == 0) {
1029             switchContext->virtualExternalPortId = vport->portId;
1030             switchContext->virtualExternalVport = vport;
1031         } else {
1032             switchContext->numPhysicalNics++;
1033         }
1034         break;
1035     case NdisSwitchPortTypeInternal:
1036         ASSERT(vport->isBridgeInternal == FALSE);
1037
1038         /* Overwrite the 'portFriendlyName' of the internal vport. */
1039         AssignNicNameSpecial(vport);
1040         switchContext->internalPortId = vport->portId;
1041         switchContext->internalVport = vport;
1042         break;
1043     case NdisSwitchPortTypeSynthetic:
1044     case NdisSwitchPortTypeEmulated:
1045         break;
1046     }
1047
1048     /*
1049      * It is important to not insert vport corresponding to virtual external
1050      * port into the 'portIdHashArray' since the port should not be exposed to
1051      * OVS userspace.
1052      */
1053     if (vport->portType == NdisSwitchPortTypeExternal &&
1054         vport->nicIndex == 0) {
1055         return NDIS_STATUS_SUCCESS;
1056     }
1057
1058     /*
1059      * NOTE: OvsJhashWords has portId as "1" word. This should be ok, even
1060      * though sizeof(NDIS_SWITCH_PORT_ID) = 4, not 2, because the
1061      * hyper-v switch seems to use only 2 bytes out of 4.
1062      */
1063     hash = OvsJhashWords(&vport->portId, 1, OVS_HASH_BASIS);
1064     InsertHeadList(&switchContext->portIdHashArray[hash & OVS_VPORT_MASK],
1065                    &vport->portIdLink);
1066     if (newPort) {
1067         switchContext->numHvVports++;
1068     }
1069     return NDIS_STATUS_SUCCESS;
1070 }
1071
1072 /*
1073  * --------------------------------------------------------------------------
1074  * Functionality common to any port added from OVS userspace.
1075  *
1076  * Inserts the port into 'portNoHashArray', 'ovsPortNameHashArray' and in
1077  * 'tunnelVportsArray' if appropriate.
1078  * --------------------------------------------------------------------------
1079  */
1080 NDIS_STATUS
1081 InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
1082                    POVS_VPORT_ENTRY vport)
1083 {
1084     UINT32 hash;
1085
1086     switch(vport->ovsType) {
1087     case OVS_VPORT_TYPE_VXLAN:
1088     case OVS_VPORT_TYPE_STT:
1089     {
1090         UINT16 dstPort = GetPortFromPriv(vport);
1091         hash = OvsJhashBytes(&dstPort,
1092                              sizeof(dstPort),
1093                              OVS_HASH_BASIS);
1094         InsertHeadList(
1095             &gOvsSwitchContext->tunnelVportsArray[hash & OVS_VPORT_MASK],
1096             &vport->tunnelVportLink);
1097         switchContext->numNonHvVports++;
1098         break;
1099     }
1100     case OVS_VPORT_TYPE_INTERNAL:
1101         if (vport->isBridgeInternal) {
1102             switchContext->numNonHvVports++;
1103         }
1104     default:
1105         break;
1106     }
1107
1108     /*
1109      * Insert the port into the hash array of ports: by port number and ovs
1110      * and ovs (datapath) port name.
1111      * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
1112      * portNo is stored in 2 bytes only (max port number = MAXUINT16).
1113      */
1114     hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
1115     InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
1116                    &vport->portNoLink);
1117
1118     hash = OvsJhashBytes(vport->ovsName, strlen(vport->ovsName) + 1,
1119                          OVS_HASH_BASIS);
1120     InsertHeadList(
1121         &gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
1122         &vport->ovsNameLink);
1123
1124     return STATUS_SUCCESS;
1125 }
1126
1127 static VOID
1128 OvsCleanupVportCommon(POVS_SWITCH_CONTEXT switchContext,
1129                       POVS_VPORT_ENTRY vport,
1130                       BOOLEAN hvSwitchPort,
1131                       BOOLEAN hvDelete,
1132                       BOOLEAN ovsDelete)
1133 {
1134     BOOLEAN deletedOnOvs = FALSE;
1135     BOOLEAN deletedOnHv = FALSE;
1136
1137     /*
1138      * 'hvDelete' == TRUE indicates that the port should be removed from the
1139      * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
1140      * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
1141      *
1142      * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
1143      */
1144     if (vport->isPresentOnHv == TRUE) {
1145         deletedOnHv = TRUE;
1146     }
1147     if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
1148         deletedOnOvs = TRUE;
1149     }
1150
1151     if (hvDelete && !deletedOnHv) {
1152         vport->isPresentOnHv = TRUE;
1153
1154         /* Remove the port from the relevant lists. */
1155         RemoveEntryList(&vport->portIdLink);
1156         InitializeListHead(&vport->portIdLink);
1157         deletedOnHv = TRUE;
1158     }
1159     if (ovsDelete && !deletedOnOvs) {
1160         vport->portNo = OVS_DPPORT_NUMBER_INVALID;
1161         vport->ovsName[0] = '\0';
1162
1163         /* Remove the port from the relevant lists. */
1164         RemoveEntryList(&vport->ovsNameLink);
1165         InitializeListHead(&vport->ovsNameLink);
1166         RemoveEntryList(&vport->portNoLink);
1167         InitializeListHead(&vport->portNoLink);
1168         if (OVS_VPORT_TYPE_VXLAN == vport->ovsType ||
1169             OVS_VPORT_TYPE_STT == vport->ovsType) {
1170             RemoveEntryList(&vport->tunnelVportLink);
1171             InitializeListHead(&vport->tunnelVportLink);
1172         }
1173
1174         deletedOnOvs = TRUE;
1175     }
1176
1177     /*
1178      * Deallocate the port if it has been deleted on the Hyper-V switch as well
1179      * as OVS userspace.
1180      */
1181     if (deletedOnHv && deletedOnOvs) {
1182         if (hvSwitchPort) {
1183             switchContext->numHvVports--;
1184         }
1185         else {
1186             switchContext->numNonHvVports--;
1187         }
1188         OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1189     }
1190 }
1191
1192 /*
1193  * --------------------------------------------------------------------------
1194  * Provides functionality that is partly complementatry to
1195  * InitOvsVportCommon()/InitHvVportCommon().
1196  *
1197  * 'hvDelete' indicates if caller is removing the vport as a result of the
1198  * port being removed on the Hyper-V switch.
1199  * 'ovsDelete' indicates if caller is removing the vport as a result of the
1200  * port being removed from OVS userspace.
1201  * --------------------------------------------------------------------------
1202  */
1203 NTSTATUS
1204 OvsRemoveAndDeleteVport(PVOID usrParamsContext,
1205                         POVS_SWITCH_CONTEXT switchContext,
1206                         POVS_VPORT_ENTRY vport,
1207                         BOOLEAN hvDelete,
1208                         BOOLEAN ovsDelete)
1209 {
1210     NTSTATUS status = STATUS_SUCCESS;
1211     POVS_USER_PARAMS_CONTEXT usrParamsCtx =
1212         (POVS_USER_PARAMS_CONTEXT)usrParamsContext;
1213     BOOLEAN hvSwitchPort = FALSE;
1214
1215     if (vport->isExternal) {
1216         if (vport->nicIndex == 0) {
1217             ASSERT(switchContext->numPhysicalNics == 0);
1218             switchContext->virtualExternalPortId = 0;
1219             switchContext->virtualExternalVport = NULL;
1220             OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1221             return STATUS_SUCCESS;
1222         } else {
1223             ASSERT(switchContext->numPhysicalNics);
1224             switchContext->numPhysicalNics--;
1225             hvSwitchPort = TRUE;
1226         }
1227     }
1228
1229     switch (vport->ovsType) {
1230     case OVS_VPORT_TYPE_INTERNAL:
1231         if (!vport->isBridgeInternal) {
1232             switchContext->internalPortId = 0;
1233             switchContext->internalVport = NULL;
1234             OvsInternalAdapterDown();
1235             hvSwitchPort = TRUE;
1236         }
1237         break;
1238     case OVS_VPORT_TYPE_VXLAN:
1239     {
1240         POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
1241         PIRP irp = NULL;
1242
1243         tunnelContext = OvsAllocateMemory(sizeof(*tunnelContext));
1244         if (tunnelContext == NULL) {
1245             status = STATUS_INSUFFICIENT_RESOURCES;
1246             break;
1247         }
1248         RtlZeroMemory(tunnelContext, sizeof(*tunnelContext));
1249
1250         tunnelContext->switchContext = switchContext;
1251         tunnelContext->hvSwitchPort = hvSwitchPort;
1252         tunnelContext->hvDelete = hvDelete;
1253         tunnelContext->ovsDelete = ovsDelete;
1254         tunnelContext->vport = vport;
1255
1256         if (usrParamsCtx) {
1257             tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
1258             tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
1259             tunnelContext->outputLength = usrParamsCtx->outputLength;
1260             irp = usrParamsCtx->irp;
1261         }
1262
1263         status = OvsCleanupVxlanTunnel(irp,
1264                                        vport,
1265                                        OvsTunnelVportPendingUninit,
1266                                        tunnelContext);
1267         break;
1268     }
1269     case OVS_VPORT_TYPE_STT:
1270         OvsCleanupSttTunnel(vport);
1271         break;
1272     case OVS_VPORT_TYPE_GRE:
1273     case OVS_VPORT_TYPE_GRE64:
1274         break;
1275     case OVS_VPORT_TYPE_NETDEV:
1276         hvSwitchPort = TRUE;
1277     default:
1278         break;
1279     }
1280
1281     if (STATUS_SUCCESS == status) {
1282         OvsCleanupVportCommon(switchContext,
1283                               vport,
1284                               hvSwitchPort,
1285                               hvDelete,
1286                               ovsDelete);
1287     }
1288
1289     return status;
1290 }
1291
1292 NDIS_STATUS
1293 OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
1294 {
1295     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1296     ULONG arrIndex;
1297     PNDIS_SWITCH_PORT_PARAMETERS portParam;
1298     PNDIS_SWITCH_PORT_ARRAY portArray = NULL;
1299     POVS_VPORT_ENTRY vport;
1300
1301     OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
1302
1303     status = OvsGetPortsOnSwitch(switchContext, &portArray);
1304     if (status != NDIS_STATUS_SUCCESS) {
1305         goto cleanup;
1306     }
1307
1308     for (arrIndex = 0; arrIndex < portArray->NumElements; arrIndex++) {
1309          portParam = NDIS_SWITCH_PORT_AT_ARRAY_INDEX(portArray, arrIndex);
1310
1311          if (portParam->IsValidationPort) {
1312              continue;
1313          }
1314
1315          vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
1316          if (vport == NULL) {
1317              status = NDIS_STATUS_RESOURCES;
1318              goto cleanup;
1319          }
1320          OvsInitVportWithPortParam(vport, portParam);
1321          status = InitHvVportCommon(switchContext, vport, TRUE);
1322          if (status != NDIS_STATUS_SUCCESS) {
1323              OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1324              goto cleanup;
1325          }
1326     }
1327
1328 cleanup:
1329     if (status != NDIS_STATUS_SUCCESS) {
1330         OvsClearAllSwitchVports(switchContext);
1331     }
1332
1333     OvsFreeSwitchPortsArray(portArray);
1334
1335     OVS_LOG_TRACE("Exit: status: %x", status);
1336
1337     return status;
1338 }
1339
1340
1341 NDIS_STATUS
1342 OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext)
1343 {
1344     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1345     PNDIS_SWITCH_NIC_ARRAY nicArray = NULL;
1346     ULONG arrIndex;
1347     PNDIS_SWITCH_NIC_PARAMETERS nicParam;
1348     POVS_VPORT_ENTRY vport;
1349
1350     OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
1351     /*
1352      * Now, get NIC list.
1353      */
1354     status = OvsGetNicsOnSwitch(switchContext, &nicArray);
1355     if (status != NDIS_STATUS_SUCCESS) {
1356         goto cleanup;
1357     }
1358     for (arrIndex = 0; arrIndex < nicArray->NumElements; ++arrIndex) {
1359
1360         nicParam = NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray, arrIndex);
1361
1362         /*
1363          * XXX: Check if the port is configured with a VLAN. Disallow such a
1364          * configuration, since we don't support tag-in-tag.
1365          */
1366
1367         /*
1368          * XXX: Check if the port is connected to a VF. Disconnect the VF in
1369          * such a case.
1370          */
1371
1372         if (nicParam->NicType == NdisSwitchNicTypeExternal &&
1373             nicParam->NicIndex != 0) {
1374             POVS_VPORT_ENTRY virtExtVport =
1375                    (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
1376
1377             vport = OvsAllocateVport();
1378             if (vport) {
1379                 OvsInitPhysNicVport(vport, virtExtVport,
1380                                     nicParam->NicIndex);
1381                 status = InitHvVportCommon(switchContext, vport, TRUE);
1382                 if (status != NDIS_STATUS_SUCCESS) {
1383                     OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
1384                     vport = NULL;
1385                 }
1386             }
1387         } else {
1388             vport = OvsFindVportByPortIdAndNicIndex(switchContext,
1389                                                     nicParam->PortId,
1390                                                     nicParam->NicIndex);
1391         }
1392         if (vport == NULL) {
1393             OVS_LOG_ERROR("Fail to allocate vport");
1394             continue;
1395         }
1396         OvsInitVportWithNicParam(switchContext, vport, nicParam);
1397         if (nicParam->NicType == NdisSwitchNicTypeInternal) {
1398             OvsInternalAdapterUp(vport->portNo, &nicParam->NetCfgInstanceId);
1399         }
1400     }
1401 cleanup:
1402
1403     OvsFreeSwitchNicsArray(nicArray);
1404
1405     OVS_LOG_TRACE("Exit: status: %x", status);
1406     return status;
1407 }
1408
1409 /*
1410  * --------------------------------------------------------------------------
1411  * Deletes ports added from the Hyper-V switch as well as OVS usersapce. The
1412  * function deletes ports in 'portIdHashArray'. This will delete most of the
1413  * ports that are in the 'portNoHashArray' as well. Any remaining ports
1414  * are deleted by walking the the 'portNoHashArray'.
1415  * --------------------------------------------------------------------------
1416  */
1417 VOID
1418 OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
1419 {
1420     for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1421         PLIST_ENTRY head, link, next;
1422
1423         head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
1424         LIST_FORALL_SAFE(head, link, next) {
1425             POVS_VPORT_ENTRY vport;
1426             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
1427             OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
1428         }
1429     }
1430
1431     /*
1432      * Remove 'virtualExternalVport' as well. This port is not part of the
1433      * 'portIdHashArray'.
1434      */
1435     if (switchContext->virtualExternalVport) {
1436         OvsRemoveAndDeleteVport(NULL, switchContext,
1437             (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE);
1438     }
1439
1440
1441     for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
1442         PLIST_ENTRY head, link, next;
1443         head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
1444         LIST_FORALL_SAFE(head, link, next) {
1445             POVS_VPORT_ENTRY vport;
1446             vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1447             ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
1448                    (vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
1449                     vport->isBridgeInternal) || vport->isPresentOnHv == TRUE);
1450             OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
1451         }
1452     }
1453
1454     ASSERT(switchContext->virtualExternalVport == NULL);
1455     ASSERT(switchContext->internalVport == NULL);
1456 }
1457
1458
1459 NTSTATUS
1460 OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
1461                                 CHAR *str,
1462                                 UINT16 maxStrLen)
1463 {
1464     ANSI_STRING astr;
1465     UNICODE_STRING ustr;
1466     NTSTATUS status;
1467     UINT32 size;
1468
1469     ustr.Buffer = wStr->String;
1470     ustr.Length = wStr->Length;
1471     ustr.MaximumLength = IF_MAX_STRING_SIZE;
1472
1473     astr.Buffer = str;
1474     astr.MaximumLength = maxStrLen;
1475     astr.Length = 0;
1476
1477     size = RtlUnicodeStringToAnsiSize(&ustr);
1478     if (size > maxStrLen) {
1479         return STATUS_BUFFER_OVERFLOW;
1480     }
1481
1482     status = RtlUnicodeStringToAnsiString(&astr, &ustr, FALSE);
1483
1484     ASSERT(status == STATUS_SUCCESS);
1485     if (status != STATUS_SUCCESS) {
1486         return status;
1487     }
1488     ASSERT(astr.Length <= maxStrLen);
1489     str[astr.Length] = 0;
1490     return STATUS_SUCCESS;
1491 }
1492
1493 /*
1494  * --------------------------------------------------------------------------
1495  * Utility function that populates a 'OVS_VPORT_EXT_INFO' structure for the
1496  * specified vport.
1497  * --------------------------------------------------------------------------
1498  */
1499 NTSTATUS
1500 OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
1501                    POVS_VPORT_EXT_INFO extInfo)
1502 {
1503     POVS_VPORT_ENTRY vport;
1504     size_t len;
1505     LOCK_STATE_EX lockState;
1506     NTSTATUS status = STATUS_SUCCESS;
1507     BOOLEAN doConvert = FALSE;
1508
1509     RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO));
1510     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1511     if (vportGet->portNo == 0) {
1512         StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
1513         vport = OvsFindVportByHvNameA(gOvsSwitchContext, vportGet->name);
1514         if (vport != NULL) {
1515             /* If the port is not a Hyper-V port and it has been added earlier,
1516              * we'll find it in 'ovsPortNameHashArray'. */
1517             vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name);
1518         }
1519     } else {
1520         vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo);
1521     }
1522     if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED &&
1523                           vport->ovsState != OVS_STATE_NIC_CREATED)) {
1524         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1525         if (vportGet->portNo) {
1526             OVS_LOG_WARN("vport %u does not exist any more", vportGet->portNo);
1527         } else {
1528             OVS_LOG_WARN("vport %s does not exist any more", vportGet->name);
1529         }
1530         status = STATUS_DEVICE_DOES_NOT_EXIST;
1531         goto ext_info_done;
1532     }
1533     extInfo->dpNo = vportGet->dpNo;
1534     extInfo->portNo = vport->portNo;
1535     RtlCopyMemory(extInfo->macAddress, vport->currMacAddress,
1536                   sizeof (vport->currMacAddress));
1537     RtlCopyMemory(extInfo->permMACAddress, vport->permMacAddress,
1538                   sizeof (vport->permMacAddress));
1539     if (vport->ovsType == OVS_VPORT_TYPE_NETDEV) {
1540         RtlCopyMemory(extInfo->vmMACAddress, vport->vmMacAddress,
1541                       sizeof (vport->vmMacAddress));
1542     }
1543     extInfo->nicIndex = vport->nicIndex;
1544     extInfo->portId = vport->portId;
1545     extInfo->type = vport->ovsType;
1546     extInfo->mtu = vport->mtu;
1547     /*
1548      * TO be revisit XXX
1549      */
1550     if (vport->ovsState == OVS_STATE_NIC_CREATED) {
1551        extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_DOWN;
1552     } else if (vport->ovsState == OVS_STATE_CONNECTED) {
1553        extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
1554     } else {
1555        extInfo->status = OVS_EVENT_DISCONNECT;
1556     }
1557     if (extInfo->type == OVS_VPORT_TYPE_NETDEV &&
1558         (vport->ovsState == OVS_STATE_NIC_CREATED  ||
1559          vport->ovsState == OVS_STATE_CONNECTED)) {
1560         doConvert = TRUE;
1561     } else {
1562         extInfo->vmUUID[0] = 0;
1563         extInfo->vifUUID[0] = 0;
1564     }
1565     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1566     if (doConvert) {
1567         status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName,
1568                                                  extInfo->name,
1569                                                  OVS_MAX_PORT_NAME_LENGTH);
1570         if (status != STATUS_SUCCESS) {
1571             OVS_LOG_INFO("Fail to convert NIC name.");
1572             extInfo->vmUUID[0] = 0;
1573         }
1574
1575         status = OvsConvertIfCountedStrToAnsiStr(&vport->vmName,
1576                                                  extInfo->vmUUID,
1577                                                  OVS_MAX_VM_UUID_LEN);
1578         if (status != STATUS_SUCCESS) {
1579             OVS_LOG_INFO("Fail to convert VM name.");
1580             extInfo->vmUUID[0] = 0;
1581         }
1582
1583         status = OvsConvertIfCountedStrToAnsiStr(&vport->nicName,
1584                                                  extInfo->vifUUID,
1585                                                  OVS_MAX_VIF_UUID_LEN);
1586         if (status != STATUS_SUCCESS) {
1587             OVS_LOG_INFO("Fail to convert nic UUID");
1588             extInfo->vifUUID[0] = 0;
1589         }
1590         /*
1591          * for now ignore status
1592          */
1593         status = STATUS_SUCCESS;
1594     }
1595
1596 ext_info_done:
1597     return status;
1598 }
1599
1600 /*
1601  * --------------------------------------------------------------------------
1602  *  Command Handler for 'OVS_WIN_NETDEV_CMD_GET'.
1603  * --------------------------------------------------------------------------
1604  */
1605 NTSTATUS
1606 OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1607                        UINT32 *replyLen)
1608 {
1609     NTSTATUS status = STATUS_SUCCESS;
1610     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1611     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1612     NL_ERROR nlError = NL_ERROR_SUCCESS;
1613     OVS_VPORT_GET vportGet;
1614     OVS_VPORT_EXT_INFO info;
1615
1616     static const NL_POLICY ovsNetdevPolicy[] = {
1617         [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING,
1618                                        .minLen = 2,
1619                                        .maxLen = IFNAMSIZ },
1620     };
1621     PNL_ATTR netdevAttrs[ARRAY_SIZE(ovsNetdevPolicy)];
1622
1623     /* input buffer has been validated while validating transaction dev op. */
1624     ASSERT(usrParamsCtx->inputBuffer != NULL &&
1625            usrParamsCtx->inputLength > sizeof *msgIn);
1626
1627     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1628         return STATUS_INVALID_BUFFER_SIZE;
1629     }
1630
1631     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1632         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1633         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1634         ovsNetdevPolicy, netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
1635         return STATUS_INVALID_PARAMETER;
1636     }
1637
1638     vportGet.portNo = 0;
1639     RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
1640                   NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
1641
1642     status = OvsGetExtInfoIoctl(&vportGet, &info);
1643     if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
1644         nlError = NL_ERROR_NODEV;
1645         goto cleanup;
1646     }
1647
1648     status = CreateNetlinkMesgForNetdev(&info, msgIn,
1649                  usrParamsCtx->outputBuffer, usrParamsCtx->outputLength,
1650                  gOvsSwitchContext->dpNo);
1651     if (status == STATUS_SUCCESS) {
1652         *replyLen = msgOut->nlMsg.nlmsgLen;
1653     }
1654
1655 cleanup:
1656     if (nlError != NL_ERROR_SUCCESS) {
1657         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1658             usrParamsCtx->outputBuffer;
1659
1660         NlBuildErrorMsg(msgIn, msgError, nlError);
1661         *replyLen = msgError->nlMsg.nlmsgLen;
1662     }
1663
1664     return STATUS_SUCCESS;
1665 }
1666
1667
1668 /*
1669  * --------------------------------------------------------------------------
1670  *  Utility function to construct an OVS_MESSAGE for the specified vport. The
1671  *  OVS_MESSAGE contains the output of a netdev command.
1672  * --------------------------------------------------------------------------
1673  */
1674 static NTSTATUS
1675 CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
1676                            POVS_MESSAGE msgIn,
1677                            PVOID outBuffer,
1678                            UINT32 outBufLen,
1679                            int dpIfIndex)
1680 {
1681     NL_BUFFER nlBuffer;
1682     BOOLEAN ok;
1683     PNL_MSG_HDR nlMsg;
1684     UINT32 netdevFlags = 0;
1685
1686     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1687
1688     ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1689                       msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1690                       msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1691                       dpIfIndex);
1692     if (!ok) {
1693         return STATUS_INVALID_BUFFER_SIZE;
1694     }
1695
1696     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
1697                          info->portNo);
1698     if (!ok) {
1699         return STATUS_INVALID_BUFFER_SIZE;
1700     }
1701
1702     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
1703     if (!ok) {
1704         return STATUS_INVALID_BUFFER_SIZE;
1705     }
1706
1707     ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
1708                             info->name);
1709     if (!ok) {
1710         return STATUS_INVALID_BUFFER_SIZE;
1711     }
1712
1713     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
1714              (PCHAR)info->macAddress, sizeof (info->macAddress));
1715     if (!ok) {
1716         return STATUS_INVALID_BUFFER_SIZE;
1717     }
1718
1719     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
1720     if (!ok) {
1721         return STATUS_INVALID_BUFFER_SIZE;
1722     }
1723
1724     if (info->status != OVS_EVENT_CONNECT) {
1725         netdevFlags = OVS_WIN_NETDEV_IFF_UP;
1726     }
1727     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
1728                          netdevFlags);
1729     if (!ok) {
1730         return STATUS_INVALID_BUFFER_SIZE;
1731     }
1732
1733     /*
1734      * XXX: add netdev_stats when we have the definition available in the
1735      * kernel.
1736      */
1737
1738     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1739     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1740
1741     return STATUS_SUCCESS;
1742 }
1743
1744 static __inline VOID
1745 OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec)
1746 {
1747     while ((!switchContext->isActivated) &&
1748           (!switchContext->isActivateFailed)) {
1749         /* Wait for the switch to be active and
1750          * the list of ports in OVS to be initialized. */
1751         NdisMSleep(sleepMicroSec);
1752     }
1753 }
1754
1755 static NTSTATUS
1756 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1757                       POVS_MESSAGE msgIn,
1758                       PVOID outBuffer,
1759                       UINT32 outBufLen,
1760                       int dpIfIndex)
1761 {
1762     NL_BUFFER nlBuffer;
1763     OVS_VPORT_FULL_STATS vportStats;
1764     BOOLEAN ok;
1765     PNL_MSG_HDR nlMsg;
1766
1767     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1768
1769     ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
1770                       msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
1771                       msgIn->genlMsg.cmd, msgIn->genlMsg.version,
1772                       dpIfIndex);
1773     if (!ok) {
1774         return STATUS_INVALID_BUFFER_SIZE;
1775     }
1776
1777     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1778     if (!ok) {
1779         return STATUS_INVALID_BUFFER_SIZE;
1780     }
1781
1782     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1783     if (!ok) {
1784         return STATUS_INVALID_BUFFER_SIZE;
1785     }
1786
1787     ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1788     if (!ok) {
1789         return STATUS_INVALID_BUFFER_SIZE;
1790     }
1791
1792     /*
1793      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1794      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1795      * it means we have an array of pids, instead of a single pid.
1796      * ATM we assume we have one pid only.
1797     */
1798
1799     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1800                          vport->upcallPid);
1801     if (!ok) {
1802         return STATUS_INVALID_BUFFER_SIZE;
1803     }
1804
1805     /*stats*/
1806     vportStats.rxPackets = vport->stats.rxPackets;
1807     vportStats.rxBytes = vport->stats.rxBytes;
1808     vportStats.txPackets = vport->stats.txPackets;
1809     vportStats.txBytes = vport->stats.txBytes;
1810     vportStats.rxErrors = vport->errStats.rxErrors;
1811     vportStats.txErrors = vport->errStats.txErrors;
1812     vportStats.rxDropped = vport->errStats.rxDropped;
1813     vportStats.txDropped = vport->errStats.txDropped;
1814
1815     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1816                             (PCHAR)&vportStats,
1817                             sizeof(OVS_VPORT_FULL_STATS));
1818     if (!ok) {
1819         return STATUS_INVALID_BUFFER_SIZE;
1820     }
1821
1822     /*
1823      * XXX: when vxlan udp dest port becomes configurable, we will also need
1824      * to add vport options
1825     */
1826
1827     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1828     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1829
1830     return STATUS_SUCCESS;
1831 }
1832
1833 static NTSTATUS
1834 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1835                     UINT32 *replyLen)
1836 {
1837     POVS_MESSAGE msgIn;
1838     POVS_OPEN_INSTANCE instance =
1839         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1840     LOCK_STATE_EX lockState;
1841     UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1842
1843     /*
1844      * XXX: this function shares some code with other dump command(s).
1845      * In the future, we will need to refactor the dump functions
1846     */
1847
1848     ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1849
1850     if (instance->dumpState.ovsMsg == NULL) {
1851         ASSERT(FALSE);
1852         return STATUS_INVALID_DEVICE_STATE;
1853     }
1854
1855     /* Output buffer has been validated while validating read dev op. */
1856     ASSERT(usrParamsCtx->outputBuffer != NULL);
1857
1858     msgIn = instance->dumpState.ovsMsg;
1859
1860     /*
1861      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1862      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1863      * it means we have an array of pids, instead of a single pid.
1864      * ATM we assume we have one pid only.
1865     */
1866     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1867
1868     if (gOvsSwitchContext->numHvVports > 0 ||
1869             gOvsSwitchContext->numNonHvVports > 0) {
1870         /* inBucket: the bucket, used for lookup */
1871         UINT32 inBucket = instance->dumpState.index[0];
1872         /* inIndex: index within the given bucket, used for lookup */
1873         UINT32 inIndex = instance->dumpState.index[1];
1874         /* the bucket to be used for the next dump operation */
1875         UINT32 outBucket = 0;
1876         /* the index within the outBucket to be used for the next dump */
1877         UINT32 outIndex = 0;
1878
1879         for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1880             PLIST_ENTRY head, link;
1881             head = &(gOvsSwitchContext->portNoHashArray[i]);
1882             POVS_VPORT_ENTRY vport = NULL;
1883
1884             outIndex = 0;
1885             LIST_FORALL(head, link) {
1886
1887                 /*
1888                  * if one or more dumps were previously done on this same bucket,
1889                  * inIndex will be > 0, so we'll need to reply with the
1890                  * inIndex + 1 vport from the bucket.
1891                 */
1892                 if (outIndex >= inIndex) {
1893                     vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
1894
1895                     ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
1896                     OvsCreateMsgFromVport(vport, msgIn,
1897                                           usrParamsCtx->outputBuffer,
1898                                           usrParamsCtx->outputLength,
1899                                           gOvsSwitchContext->dpNo);
1900                     ++outIndex;
1901                     break;
1902                 }
1903
1904                 ++outIndex;
1905             }
1906
1907             if (vport) {
1908                 break;
1909             }
1910
1911             /*
1912              * if no vport was found above, check the next bucket, beginning
1913              * with the first (i.e. index 0) elem from within that bucket
1914             */
1915             inIndex = 0;
1916         }
1917
1918         outBucket = i;
1919
1920         /* XXX: what about NLMSG_DONE (as msg type)? */
1921         instance->dumpState.index[0] = outBucket;
1922         instance->dumpState.index[1] = outIndex;
1923     }
1924
1925     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1926
1927     /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1928     if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1929         POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1930         *replyLen = msgOut->nlMsg.nlmsgLen;
1931     } else {
1932         /*
1933          * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1934          * it's dump done
1935          */
1936         *replyLen = 0;
1937         /* Free up the dump state, since there's no more data to continue. */
1938         FreeUserDumpState(instance);
1939     }
1940
1941     return STATUS_SUCCESS;
1942 }
1943
1944 static NTSTATUS
1945 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1946             UINT32 *replyLen)
1947 {
1948     NTSTATUS status = STATUS_SUCCESS;
1949     LOCK_STATE_EX lockState;
1950
1951     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1952     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1953     POVS_VPORT_ENTRY vport = NULL;
1954     NL_ERROR nlError = NL_ERROR_SUCCESS;
1955     PCHAR portName = NULL;
1956     UINT32 portNameLen = 0;
1957     UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
1958
1959     static const NL_POLICY ovsVportPolicy[] = {
1960         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1961         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1962                                   .minLen = 2,
1963                                   .maxLen = IFNAMSIZ,
1964                                   .optional = TRUE},
1965     };
1966     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1967
1968     /* input buffer has been validated while validating write dev op. */
1969     ASSERT(usrParamsCtx->inputBuffer != NULL);
1970
1971     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1972         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1973         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1974         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1975         return STATUS_INVALID_PARAMETER;
1976     }
1977
1978     /* Output buffer has been validated while validating transact dev op. */
1979     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1980
1981     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1982     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1983         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
1984         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
1985
1986         /* the port name is expected to be null-terminated */
1987         ASSERT(portName[portNameLen - 1] == '\0');
1988
1989         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
1990     } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1991         portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
1992
1993         vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
1994     } else {
1995         nlError = NL_ERROR_INVAL;
1996         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1997         goto Cleanup;
1998     }
1999
2000     if (!vport) {
2001         nlError = NL_ERROR_NODEV;
2002         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2003         goto Cleanup;
2004     }
2005
2006     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2007                                    usrParamsCtx->outputLength,
2008                                    gOvsSwitchContext->dpNo);
2009     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2010
2011     *replyLen = msgOut->nlMsg.nlmsgLen;
2012
2013 Cleanup:
2014     if (nlError != NL_ERROR_SUCCESS) {
2015         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2016             usrParamsCtx->outputBuffer;
2017
2018         NlBuildErrorMsg(msgIn, msgError, nlError);
2019         *replyLen = msgError->nlMsg.nlmsgLen;
2020     }
2021
2022     return STATUS_SUCCESS;
2023 }
2024
2025 /*
2026  * --------------------------------------------------------------------------
2027  *  Command Handler for 'OVS_VPORT_CMD_GET'.
2028  *
2029  *  The function handles the initial call to setup the dump state, as well as
2030  *  subsequent calls to continue dumping data.
2031  * --------------------------------------------------------------------------
2032 */
2033 NTSTATUS
2034 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2035                       UINT32 *replyLen)
2036 {
2037     *replyLen = 0;
2038
2039     switch (usrParamsCtx->devOp) {
2040     case OVS_WRITE_DEV_OP:
2041         return OvsSetupDumpStart(usrParamsCtx);
2042
2043     case OVS_READ_DEV_OP:
2044         return OvsGetVportDumpNext(usrParamsCtx, replyLen);
2045
2046     case OVS_TRANSACTION_DEV_OP:
2047         return OvsGetVport(usrParamsCtx, replyLen);
2048
2049     default:
2050         return STATUS_INVALID_DEVICE_REQUEST;
2051     }
2052
2053 }
2054
2055 static UINT32
2056 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
2057 {
2058     /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
2059     for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
2060         POVS_VPORT_ENTRY vport;
2061
2062         vport = OvsFindVportByPortNo(switchContext, i);
2063         if (!vport) {
2064             return i;
2065         }
2066     }
2067
2068     return OVS_DPPORT_NUMBER_INVALID;
2069 }
2070
2071 /*
2072  * --------------------------------------------------------------------------
2073  *  Command Handler for 'OVS_VPORT_CMD_NEW'.
2074  * --------------------------------------------------------------------------
2075  */
2076 NTSTATUS
2077 OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2078                       UINT32 *replyLen)
2079 {
2080     NDIS_STATUS status = STATUS_SUCCESS;
2081     LOCK_STATE_EX lockState;
2082
2083     NL_ERROR nlError = NL_ERROR_SUCCESS;
2084     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2085     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2086     POVS_VPORT_ENTRY vport = NULL;
2087     PCHAR portName;
2088     ULONG portNameLen;
2089     UINT32 portType;
2090     BOOLEAN isBridgeInternal = FALSE;
2091     BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE;
2092     BOOLEAN addInternalPortAsNetdev = FALSE;
2093
2094     static const NL_POLICY ovsVportPolicy[] = {
2095         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2096         [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2097         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2098                                   .optional = FALSE},
2099         [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2100                                         .optional = FALSE },
2101         [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2102     };
2103
2104     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2105
2106     /* input buffer has been validated while validating write dev op. */
2107     ASSERT(usrParamsCtx->inputBuffer != NULL);
2108
2109     /* Output buffer has been validated while validating transact dev op. */
2110     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2111
2112     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2113         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2114         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2115         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2116         return STATUS_INVALID_PARAMETER;
2117     }
2118
2119     portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2120     portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2121     portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2122
2123     /* we are expecting null terminated strings to be passed */
2124     ASSERT(portName[portNameLen - 1] == '\0');
2125
2126     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2127
2128     vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2129     if (vport) {
2130         nlError = NL_ERROR_EXIST;
2131         goto Cleanup;
2132     }
2133
2134     if (portName && portType == OVS_VPORT_TYPE_NETDEV &&
2135         !strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
2136         addInternalPortAsNetdev = TRUE;
2137     }
2138
2139     if (portName && portType == OVS_VPORT_TYPE_INTERNAL &&
2140         strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
2141         isBridgeInternal = TRUE;
2142     }
2143
2144     if (portType == OVS_VPORT_TYPE_INTERNAL && !isBridgeInternal) {
2145         vport = gOvsSwitchContext->internalVport;
2146     } else if (portType == OVS_VPORT_TYPE_NETDEV) {
2147         /* External ports can also be looked up like VIF ports. */
2148         vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
2149     } else {
2150         ASSERT(OvsIsTunnelVportType(portType) ||
2151                (portType == OVS_VPORT_TYPE_INTERNAL && isBridgeInternal));
2152
2153         vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
2154         if (vport == NULL) {
2155             nlError = NL_ERROR_NOMEM;
2156             goto Cleanup;
2157         }
2158         vportAllocated = TRUE;
2159
2160         if (OvsIsTunnelVportType(portType)) {
2161             UINT16 transportPortDest = 0;
2162
2163             switch (vport->ovsType) {
2164             case OVS_VPORT_TYPE_VXLAN:
2165                 transportPortDest = VXLAN_UDP_PORT;
2166                 break;
2167             case OVS_VPORT_TYPE_STT:
2168                 transportPortDest = STT_TCP_PORT;
2169                 break;
2170             default:
2171                 break;
2172             }
2173
2174             PNL_ATTR attr = NlAttrFindNested(vportAttrs[OVS_VPORT_ATTR_OPTIONS],
2175                                              OVS_TUNNEL_ATTR_DST_PORT);
2176             if (attr) {
2177                 transportPortDest = NlAttrGetU16(attr);
2178             }
2179
2180             status = OvsInitTunnelVport(usrParamsCtx,
2181                                         vport,
2182                                         portType,
2183                                         transportPortDest);
2184
2185             nlError = NlMapStatusToNlErr(status);
2186         } else {
2187             OvsInitBridgeInternalVport(vport);
2188         }
2189
2190         vportInitialized = TRUE;
2191
2192         if (nlError == NL_ERROR_SUCCESS) {
2193             vport->ovsState = OVS_STATE_CONNECTED;
2194             vport->nicState = NdisSwitchNicStateConnected;
2195
2196             /*
2197              * Allow the vport to be deleted, because there is no
2198              * corresponding hyper-v switch part.
2199              */
2200             vport->isPresentOnHv = TRUE;
2201         } else {
2202             goto Cleanup;
2203         }
2204     }
2205
2206     if (!vport) {
2207         nlError = NL_ERROR_INVAL;
2208         goto Cleanup;
2209     }
2210     if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2211         nlError = NL_ERROR_EXIST;
2212         goto Cleanup;
2213     }
2214
2215     /* Initialize the vport with OVS specific properties. */
2216     if (addInternalPortAsNetdev != TRUE) {
2217         vport->ovsType = portType;
2218     }
2219     if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2220         /*
2221          * XXX: when we implement the limit for ovs port number to be
2222          * MAXUINT16, we'll need to check the port number received from the
2223          * userspace.
2224          */
2225         vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2226     } else {
2227         vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
2228         if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2229             nlError = NL_ERROR_NOMEM;
2230             goto Cleanup;
2231         }
2232     }
2233
2234     /* The ovs port name must be uninitialized. */
2235     ASSERT(vport->ovsName[0] == '\0');
2236     ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2237
2238     RtlCopyMemory(vport->ovsName, portName, portNameLen);
2239     /* if we don't have options, then vport->portOptions will be NULL */
2240     vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2241
2242     /*
2243      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2244      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2245      * it means we have an array of pids, instead of a single pid.
2246      * ATM we assume we have one pid only.
2247      */
2248     vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2249
2250     status = InitOvsVportCommon(gOvsSwitchContext, vport);
2251     ASSERT(status == STATUS_SUCCESS);
2252
2253     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2254                                    usrParamsCtx->outputLength,
2255                                    gOvsSwitchContext->dpNo);
2256
2257     *replyLen = msgOut->nlMsg.nlmsgLen;
2258
2259 Cleanup:
2260     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2261
2262     if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2263         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2264             usrParamsCtx->outputBuffer;
2265
2266         if (vport && vportAllocated == TRUE) {
2267             if (vportInitialized == TRUE) {
2268                 if (OvsIsTunnelVportType(portType)) {
2269                     switch (vport->ovsType) {
2270                     case OVS_VPORT_TYPE_VXLAN:
2271                         OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2272                         break;
2273                     case OVS_VPORT_TYPE_STT:
2274                         OvsCleanupSttTunnel(vport);;
2275                         break;
2276                     default:
2277                         ASSERT(!"Invalid tunnel port type");
2278                     }
2279                 }
2280             }
2281             OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
2282         }
2283
2284         NlBuildErrorMsg(msgIn, msgError, nlError);
2285         *replyLen = msgError->nlMsg.nlmsgLen;
2286     }
2287
2288     return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2289 }
2290
2291
2292 /*
2293  * --------------------------------------------------------------------------
2294  *  Command Handler for 'OVS_VPORT_CMD_SET'.
2295  * --------------------------------------------------------------------------
2296  */
2297 NTSTATUS
2298 OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2299                       UINT32 *replyLen)
2300 {
2301     NDIS_STATUS status = STATUS_SUCCESS;
2302     LOCK_STATE_EX lockState;
2303
2304     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2305     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2306     POVS_VPORT_ENTRY vport = NULL;
2307     NL_ERROR nlError = NL_ERROR_SUCCESS;
2308
2309     static const NL_POLICY ovsVportPolicy[] = {
2310         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2311         [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
2312         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2313                                   .optional = TRUE },
2314         [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2315                                         .optional = TRUE },
2316         [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
2317                                    .minLen = sizeof(OVS_VPORT_FULL_STATS),
2318                                    .maxLen = sizeof(OVS_VPORT_FULL_STATS),
2319                                    .optional = TRUE },
2320         [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2321     };
2322     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2323
2324     ASSERT(usrParamsCtx->inputBuffer != NULL);
2325
2326     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2327         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2328         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2329         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2330         return STATUS_INVALID_PARAMETER;
2331     }
2332
2333     /* Output buffer has been validated while validating transact dev op. */
2334     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2335
2336     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2337     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2338         PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2339 #ifdef DBG
2340         UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2341 #endif
2342         /* the port name is expected to be null-terminated */
2343         ASSERT(portName[portNameLen - 1] == '\0');
2344
2345         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2346     } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2347         vport = OvsFindVportByPortNo(gOvsSwitchContext,
2348                     NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2349     }
2350
2351     if (!vport) {
2352         nlError = NL_ERROR_NODEV;
2353         goto Cleanup;
2354     }
2355
2356     /*
2357      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2358      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2359      * it means we have an array of pids, instead of a single pid.
2360      * Currently, we support only one pid.
2361      */
2362     if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
2363         vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2364     }
2365
2366     if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
2367         OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2368         if (type != vport->ovsType) {
2369             nlError = NL_ERROR_INVAL;
2370             goto Cleanup;
2371         }
2372     }
2373
2374     if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
2375         OVS_LOG_ERROR("Vport options not supported");
2376         nlError = NL_ERROR_NOTSUPP;
2377         goto Cleanup;
2378     }
2379
2380     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2381                                    usrParamsCtx->outputLength,
2382                                    gOvsSwitchContext->dpNo);
2383
2384     *replyLen = msgOut->nlMsg.nlmsgLen;
2385
2386 Cleanup:
2387     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2388
2389     if (nlError != NL_ERROR_SUCCESS) {
2390         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2391             usrParamsCtx->outputBuffer;
2392
2393         NlBuildErrorMsg(msgIn, msgError, nlError);
2394         *replyLen = msgError->nlMsg.nlmsgLen;
2395     }
2396
2397     return STATUS_SUCCESS;
2398 }
2399
2400 /*
2401  * --------------------------------------------------------------------------
2402  *  Command Handler for 'OVS_VPORT_CMD_DEL'.
2403  * --------------------------------------------------------------------------
2404  */
2405 NTSTATUS
2406 OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
2407                          UINT32 *replyLen)
2408 {
2409     NDIS_STATUS status = STATUS_SUCCESS;
2410     LOCK_STATE_EX lockState;
2411
2412     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
2413     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
2414     POVS_VPORT_ENTRY vport = NULL;
2415     NL_ERROR nlError = NL_ERROR_SUCCESS;
2416     PSTR portName = NULL;
2417     UINT32 portNameLen = 0;
2418
2419     static const NL_POLICY ovsVportPolicy[] = {
2420         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2421         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2422                                   .optional = TRUE },
2423     };
2424     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2425
2426     ASSERT(usrParamsCtx->inputBuffer != NULL);
2427
2428     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2429         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2430         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2431         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2432         return STATUS_INVALID_PARAMETER;
2433     }
2434
2435     /* Output buffer has been validated while validating transact dev op. */
2436     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
2437
2438     NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
2439     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
2440         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2441         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2442
2443         /* the port name is expected to be null-terminated */
2444         ASSERT(portName[portNameLen - 1] == '\0');
2445
2446         vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
2447     }
2448     else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2449         vport = OvsFindVportByPortNo(gOvsSwitchContext,
2450             NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
2451     }
2452
2453     if (!vport) {
2454         nlError = NL_ERROR_NODEV;
2455         goto Cleanup;
2456     }
2457
2458     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
2459                                    usrParamsCtx->outputLength,
2460                                    gOvsSwitchContext->dpNo);
2461
2462     *replyLen = msgOut->nlMsg.nlmsgLen;
2463
2464     /*
2465      * Mark the port as deleted from OVS userspace. If the port does not exist
2466      * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
2467      */
2468     status = OvsRemoveAndDeleteVport(usrParamsCtx,
2469                                      gOvsSwitchContext,
2470                                      vport,
2471                                      FALSE,
2472                                      TRUE);
2473     if (status) {
2474         nlError = NlMapStatusToNlErr(status);
2475     }
2476
2477 Cleanup:
2478     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
2479
2480     if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
2481         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
2482             usrParamsCtx->outputBuffer;
2483
2484         NlBuildErrorMsg(msgIn, msgError, nlError);
2485         *replyLen = msgError->nlMsg.nlmsgLen;
2486     }
2487
2488     return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
2489 }
2490
2491 static VOID
2492 OvsTunnelVportPendingUninit(PVOID context,
2493                             NTSTATUS status,
2494                             UINT32 *replyLen)
2495 {
2496     POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2497         (POVS_TUNFLT_INIT_CONTEXT) context;
2498     POVS_SWITCH_CONTEXT switchContext = tunnelContext->switchContext;
2499     POVS_VPORT_ENTRY vport = tunnelContext->vport;
2500     POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2501     POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2502     NL_ERROR nlError = NlMapStatusToNlErr(status);
2503     LOCK_STATE_EX lockState;
2504
2505     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
2506
2507     if (msgIn && msgOut) {
2508         /* Check the received status to reply to the caller. */
2509         if (STATUS_SUCCESS == status) {
2510             OvsCreateMsgFromVport(vport,
2511                                   msgIn,
2512                                   msgOut,
2513                                   tunnelContext->outputLength,
2514                                   switchContext->dpNo);
2515
2516             *replyLen = msgOut->nlMsg.nlmsgLen;
2517         } else {
2518             POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)msgOut;
2519
2520             NlBuildErrorMsg(msgIn, msgError, nlError);
2521             *replyLen = msgError->nlMsg.nlmsgLen;
2522         }
2523     }
2524
2525     OvsCleanupVportCommon(switchContext,
2526                           vport,
2527                           tunnelContext->hvSwitchPort,
2528                           tunnelContext->hvDelete,
2529                           tunnelContext->ovsDelete);
2530
2531     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
2532 }
2533
2534 static VOID
2535 OvsTunnelVportPendingInit(PVOID context,
2536                           NTSTATUS status,
2537                           UINT32 *replyLen)
2538 {
2539     POVS_TUNFLT_INIT_CONTEXT tunnelContext =
2540         (POVS_TUNFLT_INIT_CONTEXT) context;
2541     POVS_VPORT_ENTRY vport = tunnelContext->vport;
2542     POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
2543     POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
2544     PCHAR portName;
2545     ULONG portNameLen = 0;
2546     UINT32 portType = 0;
2547     NL_ERROR nlError = NL_ERROR_SUCCESS;
2548     BOOLEAN error = TRUE;
2549
2550     do {
2551         if (!NT_SUCCESS(status)) {
2552             nlError = NlMapStatusToNlErr(status);
2553             break;
2554         }
2555
2556         static const NL_POLICY ovsVportPolicy[] = {
2557             [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
2558             [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
2559             [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
2560             .optional = FALSE },
2561             [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
2562             .optional = FALSE },
2563             [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
2564         };
2565
2566         PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
2567
2568         /* input buffer has been validated while validating write dev op. */
2569         ASSERT(msgIn != NULL);
2570
2571         /* Output buffer has been validated while validating transact dev op. */
2572         ASSERT(msgOut != NULL && tunnelContext->outputLength >= sizeof *msgOut);
2573
2574         if (!NlAttrParse((PNL_MSG_HDR)msgIn,
2575             NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
2576             NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
2577             ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
2578             nlError = NL_ERROR_INVAL;
2579             break;
2580         }
2581
2582         portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
2583         portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
2584         portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
2585
2586         if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
2587             nlError = NL_ERROR_EXIST;
2588             break;
2589         }
2590
2591         vport->ovsState = OVS_STATE_CONNECTED;
2592         vport->nicState = NdisSwitchNicStateConnected;
2593
2594         /*
2595          * Allow the vport to be deleted, because there is no
2596          * corresponding hyper-v switch part.
2597          */
2598         vport->isPresentOnHv = TRUE;
2599
2600         if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
2601             /*
2602              * XXX: when we implement the limit for OVS port number to be
2603              * MAXUINT16, we'll need to check the port number received from the
2604              * userspace.
2605              */
2606             vport->portNo =
2607                 NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
2608         } else {
2609             vport->portNo =
2610                 OvsComputeVportNo(gOvsSwitchContext);
2611             if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
2612                 nlError = NL_ERROR_NOMEM;
2613                 break;
2614             }
2615         }
2616
2617         /* The ovs port name must be uninitialized. */
2618         ASSERT(vport->ovsName[0] == '\0');
2619         ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
2620
2621         RtlCopyMemory(vport->ovsName, portName, portNameLen);
2622         /* if we don't have options, then vport->portOptions will be NULL */
2623         vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
2624
2625         /*
2626          * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
2627          * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
2628          * it means we have an array of pids, instead of a single pid.
2629          * ATM we assume we have one pid only.
2630          */
2631         vport->upcallPid =
2632             NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
2633
2634         status = InitOvsVportCommon(gOvsSwitchContext, vport);
2635         ASSERT(status == STATUS_SUCCESS);
2636
2637         OvsCreateMsgFromVport(vport,
2638                               msgIn,
2639                               msgOut,
2640                               tunnelContext->outputLength,
2641                               gOvsSwitchContext->dpNo);
2642
2643         *replyLen = msgOut->nlMsg.nlmsgLen;
2644
2645         error = FALSE;
2646     } while (error);
2647
2648     if (error) {
2649         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) msgOut;
2650
2651         OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
2652         OvsFreeMemory(vport);
2653
2654         NlBuildErrorMsg(msgIn, msgError, nlError);
2655         *replyLen = msgError->nlMsg.nlmsgLen;
2656     }
2657 }