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