summaryrefslogtreecommitdiff
path: root/datapath-windows/ovsext/Vport.c
diff options
context:
space:
mode:
authorSorin Vinturis <svinturis@cloudbasesolutions.com>2015-05-27 16:58:25 +0000
committerBen Pfaff <blp@nicira.com>2015-05-27 12:36:27 -0700
commit5e82ceefd12dfcb954da48d027b47d98dc53676a (patch)
treea13059a1a95de2ffb35b8bd94783cee4338ad484 /datapath-windows/ovsext/Vport.c
parentb6ec827fe0edb5478ac0f580eaa0597b1166a0fb (diff)
downloadopenvswitch-5e82ceefd12dfcb954da48d027b47d98dc53676a.tar.gz
datapath-windows: Support for custom VXLAN tunnel port
The kernel datapath supports only port 4789 for VXLAN tunnel creation. Added support in order to allow for the VXLAN tunnel port to be configurable to any port number set by the userspace. The patch also checks to see if an existing WFP filter, for the necessary UDP tunnel port, is already created before adding a new one. This is a double check, because currently the userspace also verifies this, but it is necessary to avoid future issues. Custom VXLAN tunnel port requires the addition of a new WFP filter with the new UDP tunnel port. The creation of a new WFP filter is triggered in OvsInitVxlanTunnel function and the removal of the WFP filter in OvsCleanupVxlanTunnel function. But the latter functions are running at IRQL = DISPATCH_LEVEL, due to the NDIS RW lock acquisition, and all WFP calls must be running at IRQL = PASSIVE_LEVEL. This is why I have created a system thread which records all filter addition/removal requests into a list for later processing by the system thread. The ThreadStart routine processes all received requests at IRQL = PASSIVE_LEVEL, which is the required IRQL for the necessary WFP calls for adding/removal of the WFP filters. The WFP filter for the default VXLAN port 4789 is not added anymore at filter attach. All WFP filters for the tunnel ports are added when the tunnel ports are initialized and are removed at cleanup. WFP operation status is then reported to userspace. It is necessary that OvsTunnelFilterUninitialize function is called after OvsClearAllSwitchVports in order to allow for the added WFP filters to be removed. OvsTunnelFilterUninitialize function closes the global engine handle used by most of the WFP calls, including filter removal. Signed-off-by: Sorin Vinturis <svinturis@cloudbasesolutions.com> Reported-by: Alin Gabriel Serdean <aserdean@cloudbasesolutions.com> Reported-at: https://github.com/openvswitch/ovs-issues/issues/66 Acked-by: Nithin Raju <nithin@vmware.com> Signed-off-by: Ben Pfaff <blp@nicira.com>
Diffstat (limited to 'datapath-windows/ovsext/Vport.c')
-rw-r--r--datapath-windows/ovsext/Vport.c445
1 files changed, 360 insertions, 85 deletions
diff --git a/datapath-windows/ovsext/Vport.c b/datapath-windows/ovsext/Vport.c
index 1423ace65..66f918906 100644
--- a/datapath-windows/ovsext/Vport.c
+++ b/datapath-windows/ovsext/Vport.c
@@ -47,6 +47,20 @@
#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;
static VOID OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
@@ -69,6 +83,18 @@ static POVS_VPORT_ENTRY OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
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.
@@ -246,7 +272,7 @@ HvDeletePort(POVS_SWITCH_CONTEXT switchContext,
* delete will delete the vport.
*/
if (vport) {
- OvsRemoveAndDeleteVport(switchContext, vport, TRUE, FALSE, NULL);
+ OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
} else {
OVS_LOG_WARN("Vport not present.");
}
@@ -534,13 +560,14 @@ HvDeleteNic(POVS_SWITCH_CONTEXT switchContext,
goto done;
}
+ vport->nicState = NdisSwitchNicStateUnknown;
+ vport->ovsState = OVS_STATE_PORT_CREATED;
+
portNo = vport->portNo;
if (vport->portType == NdisSwitchPortTypeExternal &&
vport->nicIndex != 0) {
- OvsRemoveAndDeleteVport(switchContext, vport, TRUE, FALSE, NULL);
+ 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? */
@@ -850,11 +877,14 @@ OvsInitPhysNicVport(POVS_VPORT_ENTRY physExtVport,
* --------------------------------------------------------------------------
*/
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;
@@ -865,8 +895,26 @@ OvsInitTunnelVport(POVS_VPORT_ENTRY vport,
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;
+ }
default:
ASSERT(0);
}
@@ -1012,7 +1060,6 @@ InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
switch(vport->ovsType) {
case OVS_VPORT_TYPE_VXLAN:
- ASSERT(switchContext->vxlanVport == NULL);
switchContext->vxlanVport = vport;
switchContext->numNonHvVports++;
break;
@@ -1043,6 +1090,64 @@ InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
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);
+ 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);
+ }
+}
/*
* --------------------------------------------------------------------------
@@ -1055,19 +1160,17 @@ InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
* port being removed from OVS userspace.
* --------------------------------------------------------------------------
*/
-VOID
-OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
+NTSTATUS
+OvsRemoveAndDeleteVport(PVOID usrParamsContext,
+ POVS_SWITCH_CONTEXT switchContext,
POVS_VPORT_ENTRY vport,
BOOLEAN hvDelete,
- BOOLEAN ovsDelete,
- BOOLEAN *vportDeallocated)
+ BOOLEAN ovsDelete)
{
+ NTSTATUS status = STATUS_SUCCESS;
+ POVS_USER_PARAMS_CONTEXT usrParamsCtx =
+ (POVS_USER_PARAMS_CONTEXT)usrParamsContext;
BOOLEAN hvSwitchPort = FALSE;
- BOOLEAN deletedOnOvs = FALSE, deletedOnHv = FALSE;
-
- if (vportDeallocated) {
- *vportDeallocated = FALSE;
- }
if (vport->isExternal) {
if (vport->nicIndex == 0) {
@@ -1075,10 +1178,7 @@ OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
switchContext->virtualExternalPortId = 0;
switchContext->virtualExternalVport = NULL;
OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
- if (vportDeallocated) {
- *vportDeallocated = TRUE;
- }
- return;
+ return STATUS_SUCCESS;
} else {
ASSERT(switchContext->numPhysicalNics);
switchContext->numPhysicalNics--;
@@ -1096,9 +1196,38 @@ OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
}
break;
case OVS_VPORT_TYPE_VXLAN:
- OvsCleanupVxlanTunnel(vport);
+ {
+ 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);
+
switchContext->vxlanVport = NULL;
break;
+ }
case OVS_VPORT_TYPE_GRE:
case OVS_VPORT_TYPE_GRE64:
break;
@@ -1108,55 +1237,15 @@ OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
break;
}
- /*
- * '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 (STATUS_SUCCESS == status) {
+ OvsCleanupVportCommon(switchContext,
+ vport,
+ hvSwitchPort,
+ hvDelete,
+ ovsDelete);
}
- 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);
- 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);
- if (vportDeallocated) {
- *vportDeallocated = TRUE;
- }
- }
+ return status;
}
NDIS_STATUS
@@ -1294,7 +1383,7 @@ OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
LIST_FORALL_SAFE(head, link, next) {
POVS_VPORT_ENTRY vport;
vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
- OvsRemoveAndDeleteVport(switchContext, vport, TRUE, TRUE, NULL);
+ OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
}
}
/*
@@ -1302,9 +1391,8 @@ OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
* 'portIdHashArray'.
*/
if (switchContext->virtualExternalVport) {
- OvsRemoveAndDeleteVport(switchContext,
- (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE,
- NULL);
+ OvsRemoveAndDeleteVport(NULL, switchContext,
+ (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE);
}
for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
@@ -1317,7 +1405,7 @@ OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
(vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
vport->isBridgeInternal) || vport->isPresentOnHv == TRUE);
- OvsRemoveAndDeleteVport(switchContext, vport, TRUE, TRUE, NULL);
+ OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
}
}
@@ -1895,7 +1983,7 @@ Cleanup:
/*
* --------------------------------------------------------------------------
- * Command Handler for 'OVS_VPORT_CMD_NEW'.
+ * 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.
@@ -2020,8 +2108,6 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
} else {
ASSERT(OvsIsTunnelVportType(portType) ||
(portType == OVS_VPORT_TYPE_INTERNAL && isBridgeInternal));
- ASSERT(OvsGetTunnelVport(gOvsSwitchContext, portType) == NULL ||
- !OvsIsTunnelVportType(portType));
vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
if (vport == NULL) {
@@ -2031,11 +2117,23 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
vportAllocated = TRUE;
if (OvsIsTunnelVportType(portType)) {
- status = OvsInitTunnelVport(vport, portType, VXLAN_UDP_PORT);
+ UINT16 udpPortDest = VXLAN_UDP_PORT;
+ PNL_ATTR attr = NlAttrFindNested(vportAttrs[OVS_VPORT_ATTR_OPTIONS],
+ OVS_TUNNEL_ATTR_DST_PORT);
+ if (attr) {
+ udpPortDest = NlAttrGetU16(attr);
+ }
+
+ status = OvsInitTunnelVport(usrParamsCtx,
+ vport,
+ portType,
+ udpPortDest);
+
nlError = NlMapStatusToNlErr(status);
} else {
OvsInitBridgeInternalVport(vport);
}
+
vportInitialized = TRUE;
if (nlError == NL_ERROR_SUCCESS) {
@@ -2047,6 +2145,8 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
* corresponding hyper-v switch part.
*/
vport->isPresentOnHv = TRUE;
+ } else {
+ goto Cleanup;
}
}
@@ -2106,14 +2206,14 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
Cleanup:
NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
- if (nlError != NL_ERROR_SUCCESS) {
+ 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)) {
- OvsCleanupVxlanTunnel(vport);
+ OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
}
}
OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
@@ -2123,7 +2223,7 @@ Cleanup:
*replyLen = msgError->nlMsg.nlmsgLen;
}
- return STATUS_SUCCESS;
+ return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
}
@@ -2297,18 +2397,25 @@ OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
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.
*/
- OvsRemoveAndDeleteVport(gOvsSwitchContext, vport, FALSE, TRUE, NULL);
-
- *replyLen = msgOut->nlMsg.nlmsgLen;
+ status = OvsRemoveAndDeleteVport(usrParamsCtx,
+ gOvsSwitchContext,
+ vport,
+ FALSE,
+ TRUE);
+ if (status) {
+ nlError = NlMapStatusToNlErr(status);
+ }
Cleanup:
NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
- if (nlError != NL_ERROR_SUCCESS) {
+ if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
usrParamsCtx->outputBuffer;
@@ -2316,5 +2423,173 @@ Cleanup:
*replyLen = msgError->nlMsg.nlmsgLen;
}
- return STATUS_SUCCESS;
+ 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;
+ }
}