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