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