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