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