#include "NetProto.h"
#include "Flow.h"
#include "User.h"
+#include "Vxlan.h"
#ifdef OVS_DBG_MOD
#undef OVS_DBG_MOD
OvsNewDpCmdHandler,
OvsGetDpCmdHandler,
OvsSetDpCmdHandler,
- OvsGetVportCmdHandler;
+ OvsGetVportCmdHandler,
+ OvsNewVportCmdHandler;
NetlinkCmdHandler OvsGetNetdevCmdHandler;
.supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
OVS_TRANSACTION_DEV_OP,
.validateDpIndex = TRUE
+ },
+ { .cmd = OVS_VPORT_CMD_NEW,
+ .handler = OvsNewVportCmdHandler,
+ .supportedDevOp = OVS_TRANSACTION_DEV_OP,
+ .validateDpIndex = TRUE
}
};
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 },
NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
- vport = OvsFindVportByOvsName(gOvsSwitchContext,
- NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]),
- NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]) - 1);
+ 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]));
+ portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
+
+ vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
} else {
nlError = NL_ERROR_INVAL;
NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
}
+
+
+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;
+}
+
+static 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;
+ UINT32 hash;
+
+ 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);
+
+ if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
+ return STATUS_INVALID_BUFFER_SIZE;
+ }
+
+ 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;
+ }
+
+ OvsAcquireCtrlLock();
+ if (!gOvsSwitchContext) {
+ OvsReleaseCtrlLock();
+ return STATUS_INVALID_PARAMETER;
+ }
+ OvsReleaseCtrlLock();
+
+ 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 (portType == OVS_VPORT_TYPE_INTERNAL) {
+ vport = gOvsSwitchContext->internalVport;
+ } else if (portType == OVS_VPORT_TYPE_NETDEV) {
+ if (!strcmp(portName, "external")) {
+ vport = gOvsSwitchContext->externalVport;
+ } else {
+ vport = OvsFindVportByHvName(gOvsSwitchContext, portName);
+ }
+ } else {
+ /* XXX: change when other tunneling ports are added */
+ ASSERT(portType == OVS_VPORT_TYPE_VXLAN);
+
+ if (gOvsSwitchContext->vxlanVport) {
+ nlError = NL_ERROR_EXIST;
+ goto Cleanup;
+ }
+
+ vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
+ if (vport == NULL) {
+ nlError = NL_ERROR_NOMEM;
+ goto Cleanup;
+ }
+
+ vport->ovsState = OVS_STATE_PORT_CREATED;
+
+ /*
+ * XXX: when we allow configuring the vxlan udp port, we should read
+ * this from vport->options instead!
+ */
+ nlError = OvsInitVxlanTunnel(vport, VXLAN_UDP_PORT);
+ if (nlError != NL_ERROR_SUCCESS) {
+ goto Cleanup;
+ }
+ }
+
+ if (!vport) {
+ nlError = NL_ERROR_INVAL;
+ goto Cleanup;
+ }
+
+ if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
+ nlError = NL_ERROR_EXIST;
+ goto Cleanup;
+ }
+
+ /* Fill the data in vport */
+ 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]);
+
+ if (vport->ovsType == OVS_VPORT_TYPE_VXLAN) {
+ gOvsSwitchContext->vxlanVport = vport;
+ } else if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
+ gOvsSwitchContext->internalVport = vport;
+ gOvsSwitchContext->internalPortId = vport->portId;
+ } else if (vport->ovsType == OVS_VPORT_TYPE_NETDEV &&
+ vport->isExternal) {
+ gOvsSwitchContext->externalVport = vport;
+ gOvsSwitchContext->externalPortId = vport->portId;
+ }
+
+ /*
+ * insert the port into the hash array of ports: by port number and ovs
+ * and ovs (datapath) port name.
+ * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the
+ * portNo is stored in 2 bytes only (max port number = MAXUINT16).
+ */
+ hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS);
+ InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK],
+ &vport->portNoLink);
+
+ hash = OvsJhashBytes(vport->ovsName, portNameLen, OVS_HASH_BASIS);
+ InsertHeadList(&gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
+ &vport->ovsNameLink);
+
+ 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;
+
+ if (vport && vport->ovsType == OVS_VPORT_TYPE_VXLAN) {
+ OvsRemoveAndDeleteVport(gOvsSwitchContext, vport);
+ }
+
+ BuildErrorMsg(msgIn, msgError, nlError);
+ *replyLen = msgError->nlMsg.nlmsgLen;
+ }
+
+ return STATUS_SUCCESS;
+}
+
/*
* --------------------------------------------------------------------------
* Utility function to map the output buffer in an IRP. The buffer is assumed
extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
extern PNDIS_SPIN_LOCK gOvsCtrlLock;
-static POVS_VPORT_ENTRY OvsAllocateVport(VOID);
static VOID OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
PNDIS_SWITCH_PORT_PARAMETERS portParam);
static VOID OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
virtVport, UINT32 nicIndex);
static VOID OvsInitPhysNicVport(POVS_VPORT_ENTRY vport, POVS_VPORT_ENTRY
virtVport, UINT32 nicIndex);
-static NDIS_STATUS OvsInitVportCommon(POVS_SWITCH_CONTEXT switchContext,
- POVS_VPORT_ENTRY vport);
-static VOID OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
- POVS_VPORT_ENTRY vport);
static __inline VOID OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext,
ULONG sleepMicroSec);
static NTSTATUS OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
vport = OvsFindVportByPortIdAndNicIndex(switchContext,
portParam->PortId, 0);
- if (vport != NULL) {
+ if (vport != NULL && !vport->hvDeleted) {
status = STATUS_DATA_NOT_ACCEPTED;
goto create_port_done;
+ } else if (!vport) {
+ vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
+ if (vport == NULL) {
+ status = NDIS_STATUS_RESOURCES;
+ goto create_port_done;
+ }
}
- vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
- if (vport == NULL) {
- status = NDIS_STATUS_RESOURCES;
- goto create_port_done;
- }
+
OvsInitVportWithPortParam(vport, portParam);
OvsInitVportCommon(switchContext, vport);
POVS_VPORT_ENTRY
OvsFindVportByOvsName(POVS_SWITCH_CONTEXT switchContext,
- CHAR *name,
- UINT32 length)
+ PSTR name)
{
POVS_VPORT_ENTRY vport;
PLIST_ENTRY head, link;
- UINT32 hash = OvsJhashBytes((const VOID *)name, length, OVS_HASH_BASIS);
+ UINT32 hash;
+ SIZE_T length = strlen(name) + 1;
+
+ hash = OvsJhashBytes((const VOID *)name, length, OVS_HASH_BASIS);
head = &(switchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK]);
+
LIST_FORALL(head, link) {
vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, ovsNameLink);
- if (vport->ovsNameLen == length &&
- RtlEqualMemory(name, vport->ovsName, length)) {
+ if (!strcmp(name, vport->ovsName)) {
return vport;
}
}
+
return NULL;
}
}
}
-static POVS_VPORT_ENTRY
+POVS_VPORT_ENTRY
OvsAllocateVport(VOID)
{
POVS_VPORT_ENTRY vport;
}
RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY));
vport->ovsState = OVS_STATE_UNKNOWN;
+ vport->hvDeleted = FALSE;
vport->portNo = OVS_DPPORT_NUMBER_INVALID;
InitializeListHead(&vport->ovsNameLink);
vport->ovsState = OVS_STATE_PORT_CREATED;
}
-static NDIS_STATUS
+
+NDIS_STATUS
OvsInitVportCommon(POVS_SWITCH_CONTEXT switchContext,
POVS_VPORT_ENTRY vport)
{
return NDIS_STATUS_SUCCESS;
}
-
-static VOID
+VOID
OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
POVS_VPORT_ENTRY vport)
{
}
}
-NTSTATUS
-OvsInitTunnelVport(POVS_VPORT_ENTRY vport,
- POVS_VPORT_ADD_REQUEST addReq)
-{
- size_t len;
- NTSTATUS status = STATUS_SUCCESS;
-
- vport->ovsType = addReq->type;
- vport->ovsState = OVS_STATE_PORT_CREATED;
- RtlCopyMemory(vport->ovsName, addReq->name, OVS_MAX_PORT_NAME_LENGTH);
- vport->ovsName[OVS_MAX_PORT_NAME_LENGTH - 1] = 0;
- StringCbLengthA(vport->ovsName, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
- vport->ovsNameLen = (UINT32)len;
- switch (addReq->type) {
- case OVS_VPORT_TYPE_GRE:
- break;
- case OVS_VPORT_TYPE_GRE64:
- break;
- case OVS_VPORT_TYPE_VXLAN:
- status = OvsInitVxlanTunnel(vport, addReq);
- break;
- default:
- ASSERT(0);
- }
- return status;
-}
NTSTATUS
OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
* XXX: Get rid of USE_NEW_VPORT_ADD_WORKFLOW while checking in the code for
* new vport add workflow, or set USE_NEW_VPORT_ADD_WORKFLOW to 1.
*/
-#define USE_NEW_VPORT_ADD_WORKFLOW 0
+#define USE_NEW_VPORT_ADD_WORKFLOW 1
NTSTATUS
OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
POVS_VPORT_EXT_INFO extInfo)
#include "PacketIO.h"
#include "Flow.h"
#include "PacketParser.h"
-#include "Checksum.h"
#pragma warning( push )
#pragma warning( disable:4127 )
/* Move to a header file */
extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
-NTSTATUS
+/*
+ * udpDestPort: the vxlan is set as payload to a udp frame. If the destination
+ * port of an udp frame is udpDestPort, we understand it to be vxlan.
+*/
+NL_ERROR
OvsInitVxlanTunnel(POVS_VPORT_ENTRY vport,
- POVS_VPORT_ADD_REQUEST addReq)
+ UINT16 udpDestPort)
{
POVS_VXLAN_VPORT vxlanPort;
- NTSTATUS status = STATUS_SUCCESS;
-
- ASSERT(addReq->type == OVS_VPORT_TYPE_VXLAN);
+ NTSTATUS status;
vxlanPort = OvsAllocateMemory(sizeof (*vxlanPort));
if (vxlanPort == NULL) {
- status = STATUS_INSUFFICIENT_RESOURCES;
- } else {
- RtlZeroMemory(vxlanPort, sizeof (*vxlanPort));
- vxlanPort->dstPort = addReq->dstPort;
- /*
- * since we are installing the WFP filter before the port is created
- * We need to check if it is the same number
- * XXX should be removed later
- */
- ASSERT(vxlanPort->dstPort == VXLAN_UDP_PORT);
- vport->priv = (PVOID)vxlanPort;
+ return NL_ERROR_NOMEM;
}
- return status;
+
+ RtlZeroMemory(vxlanPort, sizeof(*vxlanPort));
+ vxlanPort->dstPort = udpDestPort;
+ /*
+ * since we are installing the WFP filter before the port is created
+ * We need to check if it is the same number
+ * XXX should be removed later
+ */
+ ASSERT(vxlanPort->dstPort == VXLAN_UDP_PORT);
+ vport->priv = (PVOID)vxlanPort;
+
+ status = OvsInitVportCommon(gOvsSwitchContext, vport);
+ ASSERT(status == NDIS_STATUS_SUCCESS);
+
+ vport->ovsState = OVS_STATE_CONNECTED;
+ vport->nicState = NdisSwitchNicStateConnected;
+ /*
+ * allow the vport to be deleted, because there is no hyper-v switch part
+ */
+ vport->hvDeleted = TRUE;
+
+ return NL_ERROR_SUCCESS;
}