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