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