#include "Event.h"
#include "User.h"
#include "Vxlan.h"
+#include "Stt.h"
#include "IpHelper.h"
#include "Oid.h"
#include "Datapath.h"
#define OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC 100
+/* Context structure used to pass back and forth information to the tunnel
+ * filter threads. */
+typedef struct _OVS_TUNFLT_INIT_CONTEXT {
+ POVS_SWITCH_CONTEXT switchContext;
+ UINT32 outputLength;
+ PVOID outputBuffer;
+ PVOID inputBuffer;
+ POVS_VPORT_ENTRY vport;
+ BOOLEAN hvSwitchPort;
+ BOOLEAN hvDelete;
+ BOOLEAN ovsDelete;
+} OVS_TUNFLT_INIT_CONTEXT, *POVS_TUNFLT_INIT_CONTEXT;
+
+
extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
-extern PNDIS_SPIN_LOCK gOvsCtrlLock;
static VOID OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
PNDIS_SWITCH_PORT_PARAMETERS portParam);
PVOID outBuffer,
UINT32 outBufLen,
int dpIfIndex);
+static POVS_VPORT_ENTRY OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
+ PWSTR wsName, SIZE_T wstrSize);
+static NDIS_STATUS InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
+ POVS_VPORT_ENTRY vport,
+ BOOLEAN newPort);
+static VOID OvsCleanupVportCommon(POVS_SWITCH_CONTEXT switchContext,
+ POVS_VPORT_ENTRY vport,
+ BOOLEAN hvSwitchPort,
+ BOOLEAN hvDelete,
+ BOOLEAN ovsDelete);
+static VOID OvsTunnelVportPendingInit(PVOID context,
+ NTSTATUS status,
+ UINT32 *replyLen);
+static VOID OvsTunnelVportPendingUninit(PVOID context,
+ NTSTATUS status,
+ UINT32 *replyLen);
+
/*
* Functions implemented in relaton to NDIS port manipulation.
POVS_VPORT_ENTRY vport;
LOCK_STATE_EX lockState;
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+ BOOLEAN newPort = FALSE;
VPORT_PORT_ENTER(portParam);
NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
+ /* Lookup by port ID. */
vport = OvsFindVportByPortIdAndNicIndex(switchContext,
portParam->PortId, 0);
- if (vport != NULL && !vport->hvDeleted) {
+ if (vport != NULL) {
+ OVS_LOG_ERROR("Port add failed due to duplicate port name, "
+ "port Id: %u", portParam->PortId);
+ status = STATUS_DATA_NOT_ACCEPTED;
+ goto create_port_done;
+ }
+
+ /*
+ * Lookup by port name to see if this port with this name had been added
+ * (and deleted) previously.
+ */
+ vport = OvsFindVportByHvNameW(gOvsSwitchContext,
+ portParam->PortFriendlyName.String,
+ portParam->PortFriendlyName.Length);
+ if (vport && vport->isPresentOnHv == FALSE) {
+ OVS_LOG_ERROR("Port add failed since a port already exists on "
+ "the specified port Id: %u, ovsName: %s",
+ portParam->PortId, vport->ovsName);
status = STATUS_DATA_NOT_ACCEPTED;
goto create_port_done;
- } else if (!vport) {
+ }
+
+ if (vport != NULL) {
+ ASSERT(vport->isPresentOnHv);
+ ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
+
+ /*
+ * It should be possible to simply just mark this port as "not deleted"
+ * given that the port Id and the name are the same and also provided
+ * that the other properties that we cache have not changed.
+ */
+ if (vport->portType != portParam->PortType) {
+ OVS_LOG_INFO("Port add failed due to PortType change, port Id: %u"
+ " old: %u, new: %u", portParam->PortId,
+ vport->portType, portParam->PortType);
+ status = STATUS_DATA_NOT_ACCEPTED;
+ goto create_port_done;
+ }
+ vport->isPresentOnHv = FALSE;
+ } else {
vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
if (vport == NULL) {
status = NDIS_STATUS_RESOURCES;
goto create_port_done;
}
+ newPort = TRUE;
}
-
OvsInitVportWithPortParam(vport, portParam);
- InitHvVportCommon(switchContext, vport);
+ InitHvVportCommon(switchContext, vport, newPort);
create_port_done:
NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
* Update properties only for NETDEV ports for supprting PS script
* We don't allow changing the names of the internal or external ports
*/
- if (vport == NULL || ( vport->portType != NdisSwitchPortTypeSynthetic) ||
- ( vport->portType != NdisSwitchPortTypeEmulated)) {
+ if (vport == NULL || (( vport->portType != NdisSwitchPortTypeSynthetic) &&
+ ( vport->portType != NdisSwitchPortTypeEmulated))) {
goto update_port_done;
}
vport = OvsFindVportByPortIdAndNicIndex(switchContext,
portParam->PortId, 0);
if (vport) {
- /* add assertion here
- */
+ /* add assertion here */
vport->portState = NdisSwitchPortStateTeardown;
vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
} else {
}
-
VOID
HvDeletePort(POVS_SWITCH_CONTEXT switchContext,
PNDIS_SWITCH_PORT_PARAMETERS portParams)
* delete will delete the vport.
*/
if (vport) {
- if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
- OvsRemoveAndDeleteVport(switchContext, vport);
- } else {
- vport->hvDeleted = TRUE;
- }
+ OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
} else {
OVS_LOG_WARN("Vport not present.");
}
goto add_nic_done;
}
OvsInitPhysNicVport(vport, virtExtVport, nicParam->NicIndex);
- status = InitHvVportCommon(switchContext, vport);
+ status = InitHvVportCommon(switchContext, vport, TRUE);
if (status != NDIS_STATUS_SUCCESS) {
- OvsFreeMemory(vport);
+ OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
goto add_nic_done;
}
}
nicParam->PortId,
nicParam->NicIndex);
if (vport == NULL) {
+ NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
OVS_LOG_WARN("Vport search failed.");
goto update_nic_done;
}
goto done;
}
+ vport->nicState = NdisSwitchNicStateUnknown;
+ vport->ovsState = OVS_STATE_PORT_CREATED;
+
portNo = vport->portNo;
if (vport->portType == NdisSwitchPortTypeExternal &&
vport->nicIndex != 0) {
- OvsRemoveAndDeleteVport(switchContext, vport);
+ OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
}
- vport->nicState = NdisSwitchNicStateUnknown;
- vport->ovsState = OVS_STATE_PORT_CREATED;
NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
/* XXX if portNo != INVALID or always? */
}
+POVS_VPORT_ENTRY
+OvsFindTunnelVportByDstPort(POVS_SWITCH_CONTEXT switchContext,
+ UINT16 dstPort,
+ OVS_VPORT_TYPE ovsPortType)
+{
+ POVS_VPORT_ENTRY vport;
+ PLIST_ENTRY head, link;
+ UINT32 hash = OvsJhashBytes((const VOID *)&dstPort, sizeof(dstPort),
+ OVS_HASH_BASIS);
+ head = &(switchContext->tunnelVportsArray[hash & OVS_VPORT_MASK]);
+ LIST_FORALL(head, link) {
+ vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, tunnelVportLink);
+ if (GetPortFromPriv(vport) == dstPort &&
+ vport->ovsType == ovsPortType) {
+ return vport;
+ }
+ }
+ return NULL;
+}
+
+
POVS_VPORT_ENTRY
OvsFindVportByOvsName(POVS_SWITCH_CONTEXT switchContext,
PSTR name)
/* OvsFindVportByHvName: "name" is assumed to be null-terminated */
POVS_VPORT_ENTRY
-OvsFindVportByHvName(POVS_SWITCH_CONTEXT switchContext,
- PSTR name)
+OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
+ PWSTR wsName, SIZE_T wstrSize)
{
POVS_VPORT_ENTRY vport = NULL;
PLIST_ENTRY head, link;
- /* 'portFriendlyName' is not NUL-terminated. */
- SIZE_T length = strlen(name);
- SIZE_T wstrSize = length * sizeof(WCHAR);
UINT i;
- PWSTR wsName = OvsAllocateMemory(wstrSize);
- if (!wsName) {
- return NULL;
- }
- for (i = 0; i < length; i++) {
- wsName[i] = name[i];
- }
-
for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
head = &(switchContext->portIdHashArray[i]);
LIST_FORALL(head, link) {
}
}
+ /*
+ * Look in the list of ports that were added from the Hyper-V switch and
+ * deleted.
+ */
+ if (vport == NULL) {
+ for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
+ head = &(switchContext->portNoHashArray[i]);
+ LIST_FORALL(head, link) {
+ vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
+ if (vport->portFriendlyName.Length == wstrSize &&
+ RtlEqualMemory(wsName, vport->portFriendlyName.String,
+ vport->portFriendlyName.Length)) {
+ goto Cleanup;
+ }
+
+ vport = NULL;
+ }
+ }
+ }
+
Cleanup:
- OvsFreeMemory(wsName);
+ return vport;
+}
+POVS_VPORT_ENTRY
+OvsFindVportByHvNameA(POVS_SWITCH_CONTEXT switchContext,
+ PSTR name)
+{
+ POVS_VPORT_ENTRY vport = NULL;
+ /* 'portFriendlyName' is not NUL-terminated. */
+ SIZE_T length = strlen(name);
+ SIZE_T wstrSize = length * sizeof(WCHAR);
+ UINT i;
+
+ PWSTR wsName = OvsAllocateMemoryWithTag(wstrSize, OVS_VPORT_POOL_TAG);
+ if (!wsName) {
+ return NULL;
+ }
+ for (i = 0; i < length; i++) {
+ wsName[i] = name[i];
+ }
+ vport = OvsFindVportByHvNameW(switchContext, wsName, wstrSize);
+ OvsFreeMemoryWithTag(wsName, OVS_VPORT_POOL_TAG);
return vport;
}
OvsAllocateVport(VOID)
{
POVS_VPORT_ENTRY vport;
- vport = (POVS_VPORT_ENTRY)OvsAllocateMemory(sizeof (OVS_VPORT_ENTRY));
+ vport = (POVS_VPORT_ENTRY)OvsAllocateMemoryWithTag(
+ sizeof(OVS_VPORT_ENTRY), OVS_VPORT_POOL_TAG);
if (vport == NULL) {
return NULL;
}
RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY));
vport->ovsState = OVS_STATE_UNKNOWN;
- vport->hvDeleted = FALSE;
+ vport->isPresentOnHv = FALSE;
vport->portNo = OVS_DPPORT_NUMBER_INVALID;
InitializeListHead(&vport->ovsNameLink);
* --------------------------------------------------------------------------
*/
NTSTATUS
-OvsInitTunnelVport(POVS_VPORT_ENTRY vport,
+OvsInitTunnelVport(PVOID userContext,
+ POVS_VPORT_ENTRY vport,
OVS_VPORT_TYPE ovsType,
UINT16 dstPort)
{
NTSTATUS status = STATUS_SUCCESS;
+ POVS_USER_PARAMS_CONTEXT usrParamsCtx =
+ (POVS_USER_PARAMS_CONTEXT)userContext;
vport->isBridgeInternal = FALSE;
vport->ovsType = ovsType;
case OVS_VPORT_TYPE_GRE64:
break;
case OVS_VPORT_TYPE_VXLAN:
- status = OvsInitVxlanTunnel(vport, dstPort);
+ {
+ POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
+
+ tunnelContext = OvsAllocateMemory(sizeof(*tunnelContext));
+ if (tunnelContext == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+ tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
+ tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
+ tunnelContext->outputLength = usrParamsCtx->outputLength;
+ tunnelContext->vport = vport;
+
+ status = OvsInitVxlanTunnel(usrParamsCtx->irp,
+ vport,
+ dstPort,
+ OvsTunnelVportPendingInit,
+ (PVOID)tunnelContext);
+ break;
+ }
+ case OVS_VPORT_TYPE_STT:
+ status = OvsInitSttTunnel(vport, dstPort);
break;
default:
ASSERT(0);
* For external NIC, assigns the name for the NIC.
* --------------------------------------------------------------------------
*/
-NDIS_STATUS
+static NDIS_STATUS
InitHvVportCommon(POVS_SWITCH_CONTEXT switchContext,
- POVS_VPORT_ENTRY vport)
+ POVS_VPORT_ENTRY vport,
+ BOOLEAN newPort)
{
UINT32 hash;
- ASSERT(vport->portNo == OVS_DPPORT_NUMBER_INVALID);
switch (vport->portType) {
case NdisSwitchPortTypeExternal:
hash = OvsJhashWords(&vport->portId, 1, OVS_HASH_BASIS);
InsertHeadList(&switchContext->portIdHashArray[hash & OVS_VPORT_MASK],
&vport->portIdLink);
- switchContext->numHvVports++;
+ if (newPort) {
+ switchContext->numHvVports++;
+ }
return NDIS_STATUS_SUCCESS;
}
* --------------------------------------------------------------------------
* Functionality common to any port added from OVS userspace.
*
- * Inserts the port into 'portIdHashArray', 'ovsPortNameHashArray' and caches
- * the pointer in the 'switchContext' if needed.
+ * Inserts the port into 'portNoHashArray', 'ovsPortNameHashArray' and in
+ * 'tunnelVportsArray' if appropriate.
* --------------------------------------------------------------------------
*/
NDIS_STATUS
switch(vport->ovsType) {
case OVS_VPORT_TYPE_VXLAN:
- ASSERT(switchContext->vxlanVport == NULL);
- switchContext->vxlanVport = vport;
+ case OVS_VPORT_TYPE_STT:
+ {
+ UINT16 dstPort = GetPortFromPriv(vport);
+ hash = OvsJhashBytes(&dstPort,
+ sizeof(dstPort),
+ OVS_HASH_BASIS);
+ InsertHeadList(
+ &gOvsSwitchContext->tunnelVportsArray[hash & OVS_VPORT_MASK],
+ &vport->tunnelVportLink);
switchContext->numNonHvVports++;
break;
+ }
case OVS_VPORT_TYPE_INTERNAL:
if (vport->isBridgeInternal) {
switchContext->numNonHvVports++;
hash = OvsJhashBytes(vport->ovsName, strlen(vport->ovsName) + 1,
OVS_HASH_BASIS);
- InsertHeadList(&gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
- &vport->ovsNameLink);
+ InsertHeadList(
+ &gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
+ &vport->ovsNameLink);
return STATUS_SUCCESS;
}
+static VOID
+OvsCleanupVportCommon(POVS_SWITCH_CONTEXT switchContext,
+ POVS_VPORT_ENTRY vport,
+ BOOLEAN hvSwitchPort,
+ BOOLEAN hvDelete,
+ BOOLEAN ovsDelete)
+{
+ BOOLEAN deletedOnOvs = FALSE;
+ BOOLEAN deletedOnHv = FALSE;
+
+ /*
+ * 'hvDelete' == TRUE indicates that the port should be removed from the
+ * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
+ * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
+ *
+ * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
+ */
+ if (vport->isPresentOnHv == TRUE) {
+ deletedOnHv = TRUE;
+ }
+ if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
+ deletedOnOvs = TRUE;
+ }
+
+ if (hvDelete && !deletedOnHv) {
+ vport->isPresentOnHv = TRUE;
+
+ /* Remove the port from the relevant lists. */
+ RemoveEntryList(&vport->portIdLink);
+ InitializeListHead(&vport->portIdLink);
+ deletedOnHv = TRUE;
+ }
+ if (ovsDelete && !deletedOnOvs) {
+ vport->portNo = OVS_DPPORT_NUMBER_INVALID;
+ vport->ovsName[0] = '\0';
+
+ /* Remove the port from the relevant lists. */
+ RemoveEntryList(&vport->ovsNameLink);
+ InitializeListHead(&vport->ovsNameLink);
+ RemoveEntryList(&vport->portNoLink);
+ InitializeListHead(&vport->portNoLink);
+ if (OVS_VPORT_TYPE_VXLAN == vport->ovsType ||
+ OVS_VPORT_TYPE_STT == vport->ovsType) {
+ RemoveEntryList(&vport->tunnelVportLink);
+ InitializeListHead(&vport->tunnelVportLink);
+ }
+
+ deletedOnOvs = TRUE;
+ }
+
+ /*
+ * Deallocate the port if it has been deleted on the Hyper-V switch as well
+ * as OVS userspace.
+ */
+ if (deletedOnHv && deletedOnOvs) {
+ if (hvSwitchPort) {
+ switchContext->numHvVports--;
+ }
+ else {
+ switchContext->numNonHvVports--;
+ }
+ OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
+ }
+}
/*
* --------------------------------------------------------------------------
* Provides functionality that is partly complementatry to
* InitOvsVportCommon()/InitHvVportCommon().
+ *
+ * 'hvDelete' indicates if caller is removing the vport as a result of the
+ * port being removed on the Hyper-V switch.
+ * 'ovsDelete' indicates if caller is removing the vport as a result of the
+ * port being removed from OVS userspace.
* --------------------------------------------------------------------------
*/
-VOID
-OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
- POVS_VPORT_ENTRY vport)
+NTSTATUS
+OvsRemoveAndDeleteVport(PVOID usrParamsContext,
+ POVS_SWITCH_CONTEXT switchContext,
+ POVS_VPORT_ENTRY vport,
+ BOOLEAN hvDelete,
+ BOOLEAN ovsDelete)
{
+ NTSTATUS status = STATUS_SUCCESS;
+ POVS_USER_PARAMS_CONTEXT usrParamsCtx =
+ (POVS_USER_PARAMS_CONTEXT)usrParamsContext;
BOOLEAN hvSwitchPort = FALSE;
if (vport->isExternal) {
ASSERT(switchContext->numPhysicalNics == 0);
switchContext->virtualExternalPortId = 0;
switchContext->virtualExternalVport = NULL;
- OvsFreeMemory(vport);
- return;
+ OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
+ return STATUS_SUCCESS;
} else {
ASSERT(switchContext->numPhysicalNics);
switchContext->numPhysicalNics--;
}
break;
case OVS_VPORT_TYPE_VXLAN:
- OvsCleanupVxlanTunnel(vport);
- switchContext->vxlanVport = NULL;
+ {
+ POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
+ PIRP irp = NULL;
+
+ tunnelContext = OvsAllocateMemory(sizeof(*tunnelContext));
+ if (tunnelContext == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+ RtlZeroMemory(tunnelContext, sizeof(*tunnelContext));
+
+ tunnelContext->switchContext = switchContext;
+ tunnelContext->hvSwitchPort = hvSwitchPort;
+ tunnelContext->hvDelete = hvDelete;
+ tunnelContext->ovsDelete = ovsDelete;
+ tunnelContext->vport = vport;
+
+ if (usrParamsCtx) {
+ tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
+ tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
+ tunnelContext->outputLength = usrParamsCtx->outputLength;
+ irp = usrParamsCtx->irp;
+ }
+
+ status = OvsCleanupVxlanTunnel(irp,
+ vport,
+ OvsTunnelVportPendingUninit,
+ tunnelContext);
+ break;
+ }
+ case OVS_VPORT_TYPE_STT:
+ OvsCleanupSttTunnel(vport);
break;
case OVS_VPORT_TYPE_GRE:
case OVS_VPORT_TYPE_GRE64:
break;
}
- RemoveEntryList(&vport->ovsNameLink);
- RemoveEntryList(&vport->portIdLink);
- RemoveEntryList(&vport->portNoLink);
- if (hvSwitchPort) {
- switchContext->numHvVports--;
- } else {
- switchContext->numNonHvVports--;
+ if (STATUS_SUCCESS == status) {
+ OvsCleanupVportCommon(switchContext,
+ vport,
+ hvSwitchPort,
+ hvDelete,
+ ovsDelete);
}
- OvsFreeMemory(vport);
-}
+ return status;
+}
NDIS_STATUS
OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
goto cleanup;
}
OvsInitVportWithPortParam(vport, portParam);
- status = InitHvVportCommon(switchContext, vport);
+ status = InitHvVportCommon(switchContext, vport, TRUE);
if (status != NDIS_STATUS_SUCCESS) {
- OvsFreeMemory(vport);
+ OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
goto cleanup;
}
}
+
cleanup:
if (status != NDIS_STATUS_SUCCESS) {
OvsClearAllSwitchVports(switchContext);
}
- if (portArray != NULL) {
- OvsFreeMemory(portArray);
- }
+ OvsFreeSwitchPortsArray(portArray);
+
OVS_LOG_TRACE("Exit: status: %x", status);
+
return status;
}
if (vport) {
OvsInitPhysNicVport(vport, virtExtVport,
nicParam->NicIndex);
- status = InitHvVportCommon(switchContext, vport);
+ status = InitHvVportCommon(switchContext, vport, TRUE);
if (status != NDIS_STATUS_SUCCESS) {
- OvsFreeMemory(vport);
+ OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
vport = NULL;
}
}
}
cleanup:
- if (nicArray != NULL) {
- OvsFreeMemory(nicArray);
- }
+ OvsFreeSwitchNicsArray(nicArray);
+
OVS_LOG_TRACE("Exit: status: %x", status);
return status;
}
LIST_FORALL_SAFE(head, link, next) {
POVS_VPORT_ENTRY vport;
vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
- OvsRemoveAndDeleteVport(switchContext, vport);
+ OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
}
}
+
/*
* Remove 'virtualExternalVport' as well. This port is not part of the
* 'portIdHashArray'.
*/
if (switchContext->virtualExternalVport) {
- OvsRemoveAndDeleteVport(switchContext,
- (POVS_VPORT_ENTRY)switchContext->virtualExternalVport);
+ OvsRemoveAndDeleteVport(NULL, switchContext,
+ (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE);
}
+
for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
PLIST_ENTRY head, link, next;
-
head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
LIST_FORALL_SAFE(head, link, next) {
POVS_VPORT_ENTRY vport;
vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
(vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
- vport->isBridgeInternal));
- OvsRemoveAndDeleteVport(switchContext, vport);
+ vport->isBridgeInternal) || vport->isPresentOnHv == TRUE);
+ OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
}
}
ASSERT(switchContext->virtualExternalVport == NULL);
ASSERT(switchContext->internalVport == NULL);
- ASSERT(switchContext->vxlanVport == NULL);
}
return STATUS_SUCCESS;
}
-
+/*
+ * --------------------------------------------------------------------------
+ * Utility function that populates a 'OVS_VPORT_EXT_INFO' structure for the
+ * specified vport.
+ * --------------------------------------------------------------------------
+ */
NTSTATUS
OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
POVS_VPORT_EXT_INFO extInfo)
BOOLEAN doConvert = FALSE;
RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO));
- NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
- NDIS_RWL_AT_DISPATCH_LEVEL);
+ NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
if (vportGet->portNo == 0) {
StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
- vport = OvsFindVportByHvName(gOvsSwitchContext, vportGet->name);
+ vport = OvsFindVportByHvNameA(gOvsSwitchContext, vportGet->name);
+ if (vport != NULL) {
+ /* If the port is not a Hyper-V port and it has been added earlier,
+ * we'll find it in 'ovsPortNameHashArray'. */
+ vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name);
+ }
} else {
vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo);
}
extInfo->vifUUID[0] = 0;
}
NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
- NdisReleaseSpinLock(gOvsCtrlLock);
if (doConvert) {
status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName,
extInfo->name,
return STATUS_INVALID_PARAMETER;
}
- OvsAcquireCtrlLock();
-
vportGet.portNo = 0;
RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
status = OvsGetExtInfoIoctl(&vportGet, &info);
if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
nlError = NL_ERROR_NODEV;
- OvsReleaseCtrlLock();
goto cleanup;
}
if (status == STATUS_SUCCESS) {
*replyLen = msgOut->nlMsg.nlmsgLen;
}
- OvsReleaseCtrlLock();
cleanup:
if (nlError != NL_ERROR_SUCCESS) {
POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
usrParamsCtx->outputBuffer;
- BuildErrorMsg(msgIn, msgError, nlError);
+ NlBuildErrorMsg(msgIn, msgError, nlError);
*replyLen = msgError->nlMsg.nlmsgLen;
}
{
NL_BUFFER nlBuffer;
BOOLEAN ok;
- OVS_MESSAGE msgOut;
PNL_MSG_HDR nlMsg;
UINT32 netdevFlags = 0;
NlBufInit(&nlBuffer, outBuffer, outBufLen);
- BuildReplyMsgFromMsgIn(msgIn, &msgOut, 0);
- msgOut.ovsHdr.dp_ifindex = dpIfIndex;
-
- ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
+ ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
+ msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
+ msgIn->genlMsg.cmd, msgIn->genlMsg.version,
+ dpIfIndex);
if (!ok) {
- return STATUS_INSUFFICIENT_RESOURCES;
+ return STATUS_INVALID_BUFFER_SIZE;
}
ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
info->portNo);
if (!ok) {
- return STATUS_INSUFFICIENT_RESOURCES;
+ return STATUS_INVALID_BUFFER_SIZE;
}
ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
if (!ok) {
- return STATUS_INSUFFICIENT_RESOURCES;
+ return STATUS_INVALID_BUFFER_SIZE;
}
ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
info->name);
if (!ok) {
- return STATUS_INSUFFICIENT_RESOURCES;
+ return STATUS_INVALID_BUFFER_SIZE;
}
ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
(PCHAR)info->macAddress, sizeof (info->macAddress));
if (!ok) {
- return STATUS_INSUFFICIENT_RESOURCES;
+ return STATUS_INVALID_BUFFER_SIZE;
}
ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
if (!ok) {
- return STATUS_INSUFFICIENT_RESOURCES;
+ return STATUS_INVALID_BUFFER_SIZE;
}
if (info->status != OVS_EVENT_CONNECT) {
ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
netdevFlags);
if (!ok) {
- return STATUS_INSUFFICIENT_RESOURCES;
+ return STATUS_INVALID_BUFFER_SIZE;
}
/*
NdisMSleep(sleepMicroSec);
}
}
+
+static NTSTATUS
+OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
+ POVS_MESSAGE msgIn,
+ PVOID outBuffer,
+ UINT32 outBufLen,
+ int dpIfIndex)
+{
+ NL_BUFFER nlBuffer;
+ OVS_VPORT_FULL_STATS vportStats;
+ BOOLEAN ok;
+ PNL_MSG_HDR nlMsg;
+
+ NlBufInit(&nlBuffer, outBuffer, outBufLen);
+
+ ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
+ msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
+ msgIn->genlMsg.cmd, msgIn->genlMsg.version,
+ dpIfIndex);
+ if (!ok) {
+ return STATUS_INVALID_BUFFER_SIZE;
+ }
+
+ ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
+ if (!ok) {
+ return STATUS_INVALID_BUFFER_SIZE;
+ }
+
+ ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
+ if (!ok) {
+ return STATUS_INVALID_BUFFER_SIZE;
+ }
+
+ ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
+ if (!ok) {
+ return STATUS_INVALID_BUFFER_SIZE;
+ }
+
+ /*
+ * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
+ * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
+ * it means we have an array of pids, instead of a single pid.
+ * ATM we assume we have one pid only.
+ */
+
+ ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
+ vport->upcallPid);
+ if (!ok) {
+ return STATUS_INVALID_BUFFER_SIZE;
+ }
+
+ /*stats*/
+ vportStats.rxPackets = vport->stats.rxPackets;
+ vportStats.rxBytes = vport->stats.rxBytes;
+ vportStats.txPackets = vport->stats.txPackets;
+ vportStats.txBytes = vport->stats.txBytes;
+ vportStats.rxErrors = vport->errStats.rxErrors;
+ vportStats.txErrors = vport->errStats.txErrors;
+ vportStats.rxDropped = vport->errStats.rxDropped;
+ vportStats.txDropped = vport->errStats.txDropped;
+
+ ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
+ (PCHAR)&vportStats,
+ sizeof(OVS_VPORT_FULL_STATS));
+ if (!ok) {
+ return STATUS_INVALID_BUFFER_SIZE;
+ }
+
+ /*
+ * XXX: when vxlan udp dest port becomes configurable, we will also need
+ * to add vport options
+ */
+
+ nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
+ nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+ UINT32 *replyLen)
+{
+ POVS_MESSAGE msgIn;
+ POVS_OPEN_INSTANCE instance =
+ (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
+ LOCK_STATE_EX lockState;
+ UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
+
+ /*
+ * XXX: this function shares some code with other dump command(s).
+ * In the future, we will need to refactor the dump functions
+ */
+
+ ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
+
+ if (instance->dumpState.ovsMsg == NULL) {
+ ASSERT(FALSE);
+ return STATUS_INVALID_DEVICE_STATE;
+ }
+
+ /* Output buffer has been validated while validating read dev op. */
+ ASSERT(usrParamsCtx->outputBuffer != NULL);
+
+ msgIn = instance->dumpState.ovsMsg;
+
+ /*
+ * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
+ * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
+ * it means we have an array of pids, instead of a single pid.
+ * ATM we assume we have one pid only.
+ */
+ NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
+
+ if (gOvsSwitchContext->numHvVports > 0 ||
+ gOvsSwitchContext->numNonHvVports > 0) {
+ /* inBucket: the bucket, used for lookup */
+ UINT32 inBucket = instance->dumpState.index[0];
+ /* inIndex: index within the given bucket, used for lookup */
+ UINT32 inIndex = instance->dumpState.index[1];
+ /* the bucket to be used for the next dump operation */
+ UINT32 outBucket = 0;
+ /* the index within the outBucket to be used for the next dump */
+ UINT32 outIndex = 0;
+
+ for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
+ PLIST_ENTRY head, link;
+ head = &(gOvsSwitchContext->portNoHashArray[i]);
+ POVS_VPORT_ENTRY vport = NULL;
+
+ outIndex = 0;
+ LIST_FORALL(head, link) {
+
+ /*
+ * if one or more dumps were previously done on this same bucket,
+ * inIndex will be > 0, so we'll need to reply with the
+ * inIndex + 1 vport from the bucket.
+ */
+ if (outIndex >= inIndex) {
+ vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
+
+ ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
+ OvsCreateMsgFromVport(vport, msgIn,
+ usrParamsCtx->outputBuffer,
+ usrParamsCtx->outputLength,
+ gOvsSwitchContext->dpNo);
+ ++outIndex;
+ break;
+ }
+
+ ++outIndex;
+ }
+
+ if (vport) {
+ break;
+ }
+
+ /*
+ * if no vport was found above, check the next bucket, beginning
+ * with the first (i.e. index 0) elem from within that bucket
+ */
+ inIndex = 0;
+ }
+
+ outBucket = i;
+
+ /* XXX: what about NLMSG_DONE (as msg type)? */
+ instance->dumpState.index[0] = outBucket;
+ instance->dumpState.index[1] = outIndex;
+ }
+
+ NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
+
+ /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
+ if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
+ POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
+ *replyLen = msgOut->nlMsg.nlmsgLen;
+ } else {
+ /*
+ * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
+ * it's dump done
+ */
+ *replyLen = 0;
+ /* Free up the dump state, since there's no more data to continue. */
+ FreeUserDumpState(instance);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+ UINT32 *replyLen)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ LOCK_STATE_EX lockState;
+
+ POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
+ POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
+ POVS_VPORT_ENTRY vport = NULL;
+ NL_ERROR nlError = NL_ERROR_SUCCESS;
+ PCHAR portName = NULL;
+ UINT32 portNameLen = 0;
+ UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID;
+
+ static const NL_POLICY ovsVportPolicy[] = {
+ [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
+ [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
+ .minLen = 2,
+ .maxLen = IFNAMSIZ,
+ .optional = TRUE},
+ };
+ PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
+
+ /* input buffer has been validated while validating write dev op. */
+ ASSERT(usrParamsCtx->inputBuffer != NULL);
+
+ if (!NlAttrParse((PNL_MSG_HDR)msgIn,
+ NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
+ NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
+ ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ /* Output buffer has been validated while validating transact dev op. */
+ ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
+
+ NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
+ if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
+ portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
+ portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
+
+ /* the port name is expected to be null-terminated */
+ ASSERT(portName[portNameLen - 1] == '\0');
+
+ vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
+ } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
+ portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
+
+ vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
+ } else {
+ nlError = NL_ERROR_INVAL;
+ NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
+ goto Cleanup;
+ }
+
+ if (!vport) {
+ nlError = NL_ERROR_NODEV;
+ NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
+ goto Cleanup;
+ }
+
+ status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
+ usrParamsCtx->outputLength,
+ gOvsSwitchContext->dpNo);
+ NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
+
+ *replyLen = msgOut->nlMsg.nlmsgLen;
+
+Cleanup:
+ if (nlError != NL_ERROR_SUCCESS) {
+ POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
+ usrParamsCtx->outputBuffer;
+
+ NlBuildErrorMsg(msgIn, msgError, nlError);
+ *replyLen = msgError->nlMsg.nlmsgLen;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * Command Handler for 'OVS_VPORT_CMD_GET'.
+ *
+ * The function handles the initial call to setup the dump state, as well as
+ * subsequent calls to continue dumping data.
+ * --------------------------------------------------------------------------
+*/
+NTSTATUS
+OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+ UINT32 *replyLen)
+{
+ *replyLen = 0;
+
+ switch (usrParamsCtx->devOp) {
+ case OVS_WRITE_DEV_OP:
+ return OvsSetupDumpStart(usrParamsCtx);
+
+ case OVS_READ_DEV_OP:
+ return OvsGetVportDumpNext(usrParamsCtx, replyLen);
+
+ case OVS_TRANSACTION_DEV_OP:
+ return OvsGetVport(usrParamsCtx, replyLen);
+
+ default:
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+}
+
+static UINT32
+OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext)
+{
+ /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */
+ for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) {
+ POVS_VPORT_ENTRY vport;
+
+ vport = OvsFindVportByPortNo(switchContext, i);
+ if (!vport) {
+ return i;
+ }
+ }
+
+ return OVS_DPPORT_NUMBER_INVALID;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * Command Handler for 'OVS_VPORT_CMD_NEW'.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+ UINT32 *replyLen)
+{
+ NDIS_STATUS status = STATUS_SUCCESS;
+ LOCK_STATE_EX lockState;
+
+ NL_ERROR nlError = NL_ERROR_SUCCESS;
+ POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
+ POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
+ POVS_VPORT_ENTRY vport = NULL;
+ PCHAR portName;
+ ULONG portNameLen;
+ UINT32 portType;
+ BOOLEAN isBridgeInternal = FALSE;
+ BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE;
+ BOOLEAN addInternalPortAsNetdev = FALSE;
+
+ static const NL_POLICY ovsVportPolicy[] = {
+ [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
+ [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
+ [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
+ .optional = FALSE},
+ [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
+ .optional = FALSE },
+ [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
+ };
+
+ PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
+
+ /* input buffer has been validated while validating write dev op. */
+ ASSERT(usrParamsCtx->inputBuffer != NULL);
+
+ /* Output buffer has been validated while validating transact dev op. */
+ ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
+
+ if (!NlAttrParse((PNL_MSG_HDR)msgIn,
+ NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
+ NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
+ ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
+ portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
+ portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
+
+ /* we are expecting null terminated strings to be passed */
+ ASSERT(portName[portNameLen - 1] == '\0');
+
+ NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
+
+ vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
+ if (vport) {
+ nlError = NL_ERROR_EXIST;
+ goto Cleanup;
+ }
+
+ if (portName && portType == OVS_VPORT_TYPE_NETDEV &&
+ !strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
+ addInternalPortAsNetdev = TRUE;
+ }
+
+ if (portName && portType == OVS_VPORT_TYPE_INTERNAL &&
+ strcmp(OVS_DPPORT_INTERNAL_NAME_A, portName)) {
+ isBridgeInternal = TRUE;
+ }
+
+ if (portType == OVS_VPORT_TYPE_INTERNAL && !isBridgeInternal) {
+ vport = gOvsSwitchContext->internalVport;
+ } else if (portType == OVS_VPORT_TYPE_NETDEV) {
+ /* External ports can also be looked up like VIF ports. */
+ vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
+ } else {
+ ASSERT(OvsIsTunnelVportType(portType) ||
+ (portType == OVS_VPORT_TYPE_INTERNAL && isBridgeInternal));
+
+ vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
+ if (vport == NULL) {
+ nlError = NL_ERROR_NOMEM;
+ goto Cleanup;
+ }
+ vportAllocated = TRUE;
+
+ if (OvsIsTunnelVportType(portType)) {
+ UINT16 transportPortDest = 0;
+
+ switch (vport->ovsType) {
+ case OVS_VPORT_TYPE_VXLAN:
+ transportPortDest = VXLAN_UDP_PORT;
+ break;
+ case OVS_VPORT_TYPE_STT:
+ transportPortDest = STT_TCP_PORT;
+ break;
+ default:
+ break;
+ }
+
+ PNL_ATTR attr = NlAttrFindNested(vportAttrs[OVS_VPORT_ATTR_OPTIONS],
+ OVS_TUNNEL_ATTR_DST_PORT);
+ if (attr) {
+ transportPortDest = NlAttrGetU16(attr);
+ }
+
+ status = OvsInitTunnelVport(usrParamsCtx,
+ vport,
+ portType,
+ transportPortDest);
+
+ nlError = NlMapStatusToNlErr(status);
+ } else {
+ OvsInitBridgeInternalVport(vport);
+ }
+
+ vportInitialized = TRUE;
+
+ if (nlError == NL_ERROR_SUCCESS) {
+ vport->ovsState = OVS_STATE_CONNECTED;
+ vport->nicState = NdisSwitchNicStateConnected;
+
+ /*
+ * Allow the vport to be deleted, because there is no
+ * corresponding hyper-v switch part.
+ */
+ vport->isPresentOnHv = TRUE;
+ } else {
+ goto Cleanup;
+ }
+ }
+
+ if (!vport) {
+ nlError = NL_ERROR_INVAL;
+ goto Cleanup;
+ }
+ if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
+ nlError = NL_ERROR_EXIST;
+ goto Cleanup;
+ }
+
+ /* Initialize the vport with OVS specific properties. */
+ if (addInternalPortAsNetdev != TRUE) {
+ vport->ovsType = portType;
+ }
+ if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
+ /*
+ * XXX: when we implement the limit for ovs port number to be
+ * MAXUINT16, we'll need to check the port number received from the
+ * userspace.
+ */
+ vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
+ } else {
+ vport->portNo = OvsComputeVportNo(gOvsSwitchContext);
+ if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
+ nlError = NL_ERROR_NOMEM;
+ goto Cleanup;
+ }
+ }
+
+ /* The ovs port name must be uninitialized. */
+ ASSERT(vport->ovsName[0] == '\0');
+ ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
+
+ RtlCopyMemory(vport->ovsName, portName, portNameLen);
+ /* if we don't have options, then vport->portOptions will be NULL */
+ vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
+
+ /*
+ * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
+ * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
+ * it means we have an array of pids, instead of a single pid.
+ * ATM we assume we have one pid only.
+ */
+ vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
+
+ status = InitOvsVportCommon(gOvsSwitchContext, vport);
+ ASSERT(status == STATUS_SUCCESS);
+
+ status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
+ usrParamsCtx->outputLength,
+ gOvsSwitchContext->dpNo);
+
+ *replyLen = msgOut->nlMsg.nlmsgLen;
+
+Cleanup:
+ NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
+
+ if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
+ POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
+ usrParamsCtx->outputBuffer;
+
+ if (vport && vportAllocated == TRUE) {
+ if (vportInitialized == TRUE) {
+ if (OvsIsTunnelVportType(portType)) {
+ switch (vport->ovsType) {
+ case OVS_VPORT_TYPE_VXLAN:
+ OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
+ break;
+ case OVS_VPORT_TYPE_STT:
+ OvsCleanupSttTunnel(vport);;
+ break;
+ default:
+ ASSERT(!"Invalid tunnel port type");
+ }
+ }
+ }
+ OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
+ }
+
+ NlBuildErrorMsg(msgIn, msgError, nlError);
+ *replyLen = msgError->nlMsg.nlmsgLen;
+ }
+
+ return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ * Command Handler for 'OVS_VPORT_CMD_SET'.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+ UINT32 *replyLen)
+{
+ NDIS_STATUS status = STATUS_SUCCESS;
+ LOCK_STATE_EX lockState;
+
+ POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
+ POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
+ POVS_VPORT_ENTRY vport = NULL;
+ NL_ERROR nlError = NL_ERROR_SUCCESS;
+
+ static const NL_POLICY ovsVportPolicy[] = {
+ [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
+ [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE },
+ [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
+ .optional = TRUE },
+ [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
+ .optional = TRUE },
+ [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
+ .minLen = sizeof(OVS_VPORT_FULL_STATS),
+ .maxLen = sizeof(OVS_VPORT_FULL_STATS),
+ .optional = TRUE },
+ [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
+ };
+ PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
+
+ ASSERT(usrParamsCtx->inputBuffer != NULL);
+
+ if (!NlAttrParse((PNL_MSG_HDR)msgIn,
+ NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
+ NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
+ ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ /* Output buffer has been validated while validating transact dev op. */
+ ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
+
+ NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
+ if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
+ PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
+#ifdef DBG
+ UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
+#endif
+ /* the port name is expected to be null-terminated */
+ ASSERT(portName[portNameLen - 1] == '\0');
+
+ vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
+ } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
+ vport = OvsFindVportByPortNo(gOvsSwitchContext,
+ NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
+ }
+
+ if (!vport) {
+ nlError = NL_ERROR_NODEV;
+ goto Cleanup;
+ }
+
+ /*
+ * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
+ * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
+ * it means we have an array of pids, instead of a single pid.
+ * Currently, we support only one pid.
+ */
+ if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
+ vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
+ }
+
+ if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
+ OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
+ if (type != vport->ovsType) {
+ nlError = NL_ERROR_INVAL;
+ goto Cleanup;
+ }
+ }
+
+ if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
+ OVS_LOG_ERROR("Vport options not supported");
+ nlError = NL_ERROR_NOTSUPP;
+ goto Cleanup;
+ }
+
+ status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
+ usrParamsCtx->outputLength,
+ gOvsSwitchContext->dpNo);
+
+ *replyLen = msgOut->nlMsg.nlmsgLen;
+
+Cleanup:
+ NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
+
+ if (nlError != NL_ERROR_SUCCESS) {
+ POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
+ usrParamsCtx->outputBuffer;
+
+ NlBuildErrorMsg(msgIn, msgError, nlError);
+ *replyLen = msgError->nlMsg.nlmsgLen;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * Command Handler for 'OVS_VPORT_CMD_DEL'.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+ UINT32 *replyLen)
+{
+ NDIS_STATUS status = STATUS_SUCCESS;
+ LOCK_STATE_EX lockState;
+
+ POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
+ POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
+ POVS_VPORT_ENTRY vport = NULL;
+ NL_ERROR nlError = NL_ERROR_SUCCESS;
+ PSTR portName = NULL;
+ UINT32 portNameLen = 0;
+
+ static const NL_POLICY ovsVportPolicy[] = {
+ [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
+ [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
+ .optional = TRUE },
+ };
+ PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
+
+ ASSERT(usrParamsCtx->inputBuffer != NULL);
+
+ if (!NlAttrParse((PNL_MSG_HDR)msgIn,
+ NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
+ NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
+ ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ /* Output buffer has been validated while validating transact dev op. */
+ ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
+
+ NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
+ if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
+ portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
+ portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
+
+ /* the port name is expected to be null-terminated */
+ ASSERT(portName[portNameLen - 1] == '\0');
+
+ vport = OvsFindVportByOvsName(gOvsSwitchContext, portName);
+ }
+ else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
+ vport = OvsFindVportByPortNo(gOvsSwitchContext,
+ NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
+ }
+
+ if (!vport) {
+ nlError = NL_ERROR_NODEV;
+ goto Cleanup;
+ }
+
+ status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
+ usrParamsCtx->outputLength,
+ gOvsSwitchContext->dpNo);
+
+ *replyLen = msgOut->nlMsg.nlmsgLen;
+
+ /*
+ * Mark the port as deleted from OVS userspace. If the port does not exist
+ * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
+ */
+ status = OvsRemoveAndDeleteVport(usrParamsCtx,
+ gOvsSwitchContext,
+ vport,
+ FALSE,
+ TRUE);
+ if (status) {
+ nlError = NlMapStatusToNlErr(status);
+ }
+
+Cleanup:
+ NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
+
+ if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
+ POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
+ usrParamsCtx->outputBuffer;
+
+ NlBuildErrorMsg(msgIn, msgError, nlError);
+ *replyLen = msgError->nlMsg.nlmsgLen;
+ }
+
+ return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
+}
+
+static VOID
+OvsTunnelVportPendingUninit(PVOID context,
+ NTSTATUS status,
+ UINT32 *replyLen)
+{
+ POVS_TUNFLT_INIT_CONTEXT tunnelContext =
+ (POVS_TUNFLT_INIT_CONTEXT) context;
+ POVS_SWITCH_CONTEXT switchContext = tunnelContext->switchContext;
+ POVS_VPORT_ENTRY vport = tunnelContext->vport;
+ POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
+ POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
+ NL_ERROR nlError = NlMapStatusToNlErr(status);
+ LOCK_STATE_EX lockState;
+
+ NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
+
+ if (msgIn && msgOut) {
+ /* Check the received status to reply to the caller. */
+ if (STATUS_SUCCESS == status) {
+ OvsCreateMsgFromVport(vport,
+ msgIn,
+ msgOut,
+ tunnelContext->outputLength,
+ switchContext->dpNo);
+
+ *replyLen = msgOut->nlMsg.nlmsgLen;
+ } else {
+ POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)msgOut;
+
+ NlBuildErrorMsg(msgIn, msgError, nlError);
+ *replyLen = msgError->nlMsg.nlmsgLen;
+ }
+ }
+
+ OvsCleanupVportCommon(switchContext,
+ vport,
+ tunnelContext->hvSwitchPort,
+ tunnelContext->hvDelete,
+ tunnelContext->ovsDelete);
+
+ NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
+}
+
+static VOID
+OvsTunnelVportPendingInit(PVOID context,
+ NTSTATUS status,
+ UINT32 *replyLen)
+{
+ POVS_TUNFLT_INIT_CONTEXT tunnelContext =
+ (POVS_TUNFLT_INIT_CONTEXT) context;
+ POVS_VPORT_ENTRY vport = tunnelContext->vport;
+ POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
+ POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
+ PCHAR portName;
+ ULONG portNameLen = 0;
+ UINT32 portType = 0;
+ NL_ERROR nlError = NL_ERROR_SUCCESS;
+ BOOLEAN error = TRUE;
+
+ do {
+ if (!NT_SUCCESS(status)) {
+ nlError = NlMapStatusToNlErr(status);
+ break;
+ }
+
+ static const NL_POLICY ovsVportPolicy[] = {
+ [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
+ [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
+ [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
+ .optional = FALSE },
+ [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
+ .optional = FALSE },
+ [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
+ };
+
+ PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
+
+ /* input buffer has been validated while validating write dev op. */
+ ASSERT(msgIn != NULL);
+
+ /* Output buffer has been validated while validating transact dev op. */
+ ASSERT(msgOut != NULL && tunnelContext->outputLength >= sizeof *msgOut);
+
+ if (!NlAttrParse((PNL_MSG_HDR)msgIn,
+ NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
+ NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
+ ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
+ nlError = NL_ERROR_INVAL;
+ break;
+ }
+
+ portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
+ portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
+ portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
+
+ if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
+ nlError = NL_ERROR_EXIST;
+ break;
+ }
+
+ vport->ovsState = OVS_STATE_CONNECTED;
+ vport->nicState = NdisSwitchNicStateConnected;
+
+ /*
+ * Allow the vport to be deleted, because there is no
+ * corresponding hyper-v switch part.
+ */
+ vport->isPresentOnHv = TRUE;
+
+ if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
+ /*
+ * XXX: when we implement the limit for OVS port number to be
+ * MAXUINT16, we'll need to check the port number received from the
+ * userspace.
+ */
+ vport->portNo =
+ NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
+ } else {
+ vport->portNo =
+ OvsComputeVportNo(gOvsSwitchContext);
+ if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
+ nlError = NL_ERROR_NOMEM;
+ break;
+ }
+ }
+
+ /* The ovs port name must be uninitialized. */
+ ASSERT(vport->ovsName[0] == '\0');
+ ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
+
+ RtlCopyMemory(vport->ovsName, portName, portNameLen);
+ /* if we don't have options, then vport->portOptions will be NULL */
+ vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
+
+ /*
+ * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
+ * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
+ * it means we have an array of pids, instead of a single pid.
+ * ATM we assume we have one pid only.
+ */
+ vport->upcallPid =
+ NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
+
+ status = InitOvsVportCommon(gOvsSwitchContext, vport);
+ ASSERT(status == STATUS_SUCCESS);
+
+ OvsCreateMsgFromVport(vport,
+ msgIn,
+ msgOut,
+ tunnelContext->outputLength,
+ gOvsSwitchContext->dpNo);
+
+ *replyLen = msgOut->nlMsg.nlmsgLen;
+
+ error = FALSE;
+ } while (error);
+
+ if (error) {
+ POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) msgOut;
+
+ OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
+ OvsFreeMemory(vport);
+
+ NlBuildErrorMsg(msgIn, msgError, nlError);
+ *replyLen = msgError->nlMsg.nlmsgLen;
+ }
+}