UINT16 opsCount;
} NETLINK_FAMILY, *PNETLINK_FAMILY;
-/*
- * Device operations to tag netlink commands with. This is a bitmask since it is
- * possible that a particular command can be invoked via different device
- * operations.
- */
-#define OVS_READ_DEV_OP (1 << 0)
-#define OVS_WRITE_DEV_OP (1 << 1)
-#define OVS_TRANSACTION_DEV_OP (1 << 2)
-
/* Handlers for the various netlink commands. */
static NetlinkCmdHandler OvsGetPidCmdHandler,
- OvsGetDpCmdHandler,
OvsPendEventCmdHandler,
OvsSubscribeEventCmdHandler,
+ OvsReadEventCmdHandler,
+ OvsNewDpCmdHandler,
+ OvsGetDpCmdHandler,
OvsSetDpCmdHandler,
OvsGetVportCmdHandler;
+NetlinkCmdHandler OvsGetNetdevCmdHandler;
+
static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
UINT32 *replyLen);
static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
UINT32 *replyLen);
-static NTSTATUS HandleDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
- UINT32 *replyLen);
+static NTSTATUS HandleDpTransactionCommon(
+ POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen);
/*
* The various netlink families, along with the supported commands. Most of
.handler = OvsSubscribeEventCmdHandler,
.supportedDevOp = OVS_WRITE_DEV_OP,
.validateDpIndex = TRUE,
+ },
+ { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY,
+ .handler = OvsReadEventCmdHandler,
+ .supportedDevOp = OVS_READ_EVENT_DEV_OP,
+ .validateDpIndex = FALSE,
}
};
/* Netlink datapath family. */
NETLINK_CMD nlDatapathFamilyCmdOps[] = {
+ { .cmd = OVS_DP_CMD_NEW,
+ .handler = OvsNewDpCmdHandler,
+ .supportedDevOp = OVS_TRANSACTION_DEV_OP,
+ .validateDpIndex = FALSE
+ },
{ .cmd = OVS_DP_CMD_GET,
.handler = OvsGetDpCmdHandler,
.supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
};
/* Netlink flow family. */
-/* XXX: Add commands here. */
+
+NETLINK_CMD nlFlowFamilyCmdOps[] = {
+ { .cmd = OVS_FLOW_CMD_NEW,
+ .handler = OvsFlowNlCmdHandler,
+ .supportedDevOp = OVS_TRANSACTION_DEV_OP,
+ .validateDpIndex = TRUE
+ },
+ { .cmd = OVS_FLOW_CMD_SET,
+ .handler = OvsFlowNlCmdHandler,
+ .supportedDevOp = OVS_TRANSACTION_DEV_OP,
+ .validateDpIndex = TRUE
+ },
+ { .cmd = OVS_FLOW_CMD_DEL,
+ .handler = OvsFlowNlCmdHandler,
+ .supportedDevOp = OVS_TRANSACTION_DEV_OP,
+ .validateDpIndex = TRUE
+ },
+ { .cmd = OVS_FLOW_CMD_GET,
+ .handler = OvsFlowNlGetCmdHandler,
+ .supportedDevOp = OVS_TRANSACTION_DEV_OP |
+ OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
+ .validateDpIndex = TRUE
+ },
+};
+
NETLINK_FAMILY nlFLowFamilyOps = {
.name = OVS_FLOW_FAMILY,
.id = OVS_WIN_NL_FLOW_FAMILY_ID,
.version = OVS_FLOW_VERSION,
.maxAttr = OVS_FLOW_ATTR_MAX,
- .cmds = NULL, /* XXX: placeholder. */
- .opsCount = 0
+ .cmds = nlFlowFamilyCmdOps,
+ .opsCount = ARRAY_SIZE(nlFlowFamilyCmdOps)
+};
+
+/* Netlink netdev family. */
+NETLINK_CMD nlNetdevFamilyCmdOps[] = {
+ { .cmd = OVS_WIN_NETDEV_CMD_GET,
+ .handler = OvsGetNetdevCmdHandler,
+ .supportedDevOp = OVS_TRANSACTION_DEV_OP,
+ .validateDpIndex = FALSE
+ },
+};
+
+NETLINK_FAMILY nlNetdevFamilyOps = {
+ .name = OVS_WIN_NETDEV_FAMILY,
+ .id = OVS_WIN_NL_NETDEV_FAMILY_ID,
+ .version = OVS_WIN_NETDEV_VERSION,
+ .maxAttr = OVS_WIN_NETDEV_ATTR_MAX,
+ .cmds = nlNetdevFamilyCmdOps,
+ .opsCount = ARRAY_SIZE(nlNetdevFamilyCmdOps)
};
static NTSTATUS MapIrpOutputBuffer(PIRP irp,
static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
NETLINK_FAMILY *nlFamilyOps,
UINT32 *replyLen);
-static NTSTATUS OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx);
-
/* Handles to the device object for communication with userspace. */
NDIS_HANDLE gOvsDeviceHandle;
for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
if (ovsOpenInstanceArray[i] == NULL) {
ovsOpenInstanceArray[i] = instance;
+ ovsNumberOfOpenInstances++;
instance->cookie = i;
break;
}
fileObject->FsContext = NULL;
ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
ovsOpenInstanceArray[instance->cookie] = NULL;
+ ovsNumberOfOpenInstances--;
OvsReleaseCtrlLock();
ASSERT(instance->eventQueue == NULL);
ASSERT (instance->packetQueue == NULL);
devOp = OVS_TRANSACTION_DEV_OP;
break;
+ case OVS_IOCTL_READ_EVENT:
+ /* This IOCTL is used to read events */
+ if (outputBufferLen != 0) {
+ status = MapIrpOutputBuffer(irp, outputBufferLen,
+ sizeof *ovsMsg, &outputBuffer);
+ if (status != STATUS_SUCCESS) {
+ goto done;
+ }
+ ASSERT(outputBuffer);
+ } else {
+ status = STATUS_NDIS_INVALID_LENGTH;
+ goto done;
+ }
+ inputBuffer = NULL;
+ inputBufferLen = 0;
+
+ ovsMsg = &ovsMsgReadOp;
+ ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
+ /* An "artificial" command so we can use NL family function table*/
+ ovsMsg->genlMsg.cmd = OVS_CTRL_CMD_EVENT_NOTIFY;
+ devOp = OVS_READ_DEV_OP;
+ break;
+
case OVS_IOCTL_READ:
/* Output buffer is mandatory. */
if (outputBufferLen != 0) {
case OVS_WIN_NL_DATAPATH_FAMILY_ID:
nlFamilyOps = &nlDatapathFamilyOps;
break;
- case OVS_WIN_NL_PACKET_FAMILY_ID:
case OVS_WIN_NL_FLOW_FAMILY_ID:
+ nlFamilyOps = &nlFLowFamilyOps;
+ break;
+ case OVS_WIN_NL_PACKET_FAMILY_ID:
status = STATUS_NOT_IMPLEMENTED;
goto done;
case OVS_WIN_NL_VPORT_FAMILY_ID:
nlFamilyOps = &nlVportFamilyOps;
break;
+ case OVS_WIN_NL_NETDEV_FAMILY_ID:
+ nlFamilyOps = &nlNetdevFamilyOps;
+ break;
default:
status = STATUS_INVALID_PARAMETER;
goto done;
OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
PNL_MSG_HDR nlMsg;
- /* XXX: Add API for nlBuf->bufRemLen. */
- ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof *msgIn);
+ ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && NlBufRemLen(nlBuf) >= sizeof *msgIn);
msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
(POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
- rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),policy, attrs, 2);
+ rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
+ NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, 2);
if (!rc) {
status = STATUS_INVALID_PARAMETER;
goto done;
return status;
}
+/*
+ * --------------------------------------------------------------------------
+ * Command Handler for 'OVS_DP_CMD_NEW'.
+ * --------------------------------------------------------------------------
+ */
+static NTSTATUS
+OvsNewDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+ UINT32 *replyLen)
+{
+ return HandleDpTransactionCommon(usrParamsCtx, replyLen);
+}
/*
* --------------------------------------------------------------------------
UINT32 *replyLen)
{
if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
- return HandleGetDpTransaction(usrParamsCtx, replyLen);
+ return HandleDpTransactionCommon(usrParamsCtx, replyLen);
} else {
return HandleGetDpDump(usrParamsCtx, replyLen);
}
HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
UINT32 *replyLen)
{
- return HandleDpTransaction(usrParamsCtx, replyLen);
+ return HandleDpTransactionCommon(usrParamsCtx, replyLen);
}
OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
UINT32 *replyLen)
{
- return HandleDpTransaction(usrParamsCtx, replyLen);
+ return HandleDpTransactionCommon(usrParamsCtx, replyLen);
}
/*
* --------------------------------------------------------------------------
- * Function for handling transaction based 'OVS_DP_CMD_GET' and
- * 'OVS_DP_CMD_SET' commands.
+ * Function for handling transaction based 'OVS_DP_CMD_NEW', 'OVS_DP_CMD_GET'
+ * and 'OVS_DP_CMD_SET' commands.
+ *
+ * 'OVS_DP_CMD_NEW' is implemented to keep userspace code happy. Creation of a
+ * new datapath is not supported currently.
* --------------------------------------------------------------------------
*/
static NTSTATUS
-HandleDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
- UINT32 *replyLen)
+HandleDpTransactionCommon(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+ UINT32 *replyLen)
{
POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
NTSTATUS status = STATUS_SUCCESS;
NL_BUFFER nlBuf;
+ NL_ERROR nlError = NL_ERROR_SUCCESS;
static const NL_POLICY ovsDatapathSetPolicy[] = {
[OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
[OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
/* Parse any attributes in the request. */
- if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
+ if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET ||
+ usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
if (!NlAttrParse((PNL_MSG_HDR)msgIn,
NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
+ NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
return STATUS_INVALID_PARAMETER;
}
!OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
OVS_SYSTEM_DP_NAME)) {
OvsReleaseCtrlLock();
- status = STATUS_NOT_FOUND;
+
+ /* Creation of new datapaths is not supported. */
+ if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
+ nlError = NL_ERROR_NOTSUPP;
+ goto cleanup;
+ }
+
+ nlError = NL_ERROR_NODEV;
goto cleanup;
}
} else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
OvsReleaseCtrlLock();
- status = STATUS_NOT_FOUND;
+ nlError = NL_ERROR_NODEV;
+ goto cleanup;
+ }
+
+ if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
+ OvsReleaseCtrlLock();
+ nlError = NL_ERROR_EXIST;
goto cleanup;
}
*replyLen = NlBufSize(&nlBuf);
cleanup:
- return status;
+ if (nlError != NL_ERROR_SUCCESS) {
+ POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
+ usrParamsCtx->outputBuffer;
+
+ BuildErrorMsg(msgIn, msgError, nlError);
+ *replyLen = msgError->nlMsg.nlmsgLen;
+ }
+
+ return STATUS_SUCCESS;
}
-static NTSTATUS
+NTSTATUS
OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
{
POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
static VOID
BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
- UINT32 length, UINT16 flags)
+ UINT32 length, UINT16 flags)
{
msgOut->nlMsg.nlmsgType = type;
msgOut->nlMsg.nlmsgFlags = flags;
msgOut->nlMsg.nlmsgLen = length;
msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
- msgOut->genlMsg.version = nlDatapathFamilyOps.version;
+ msgOut->genlMsg.version = msgIn->genlMsg.version;
msgOut->genlMsg.reserved = 0;
}
-static VOID
+/*
+ * XXX: should move out these functions to a Netlink.c or to a OvsMessage.c
+ * or even make them inlined functions in Datapath.h. Can be done after the
+ * first sprint once we have more code to refactor.
+ */
+VOID
BuildReplyMsgFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
{
BuildMsgOut(msgIn, msgOut, msgIn->nlMsg.nlmsgType, sizeof(OVS_MESSAGE),
flags);
}
-static VOID
+VOID
BuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode)
{
BuildMsgOut(msgIn, (POVS_MESSAGE)msgOut, NLMSG_ERROR,
* 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);
+ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+ NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
+ NDIS_RWL_AT_DISPATCH_LEVEL);
if (gOvsSwitchContext->numVports > 0) {
/* inBucket: the bucket, used for lookup */
if (outIndex >= inIndex) {
vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portLink);
- if (vport->portNo != 0) {
+ if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
OvsCreateMsgFromVport(vport, msgIn,
usrParamsCtx->outputBuffer,
usrParamsCtx->outputLength,
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;
}
}
OvsReleaseCtrlLock();
+ NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
vport = OvsFindVportByOvsName(gOvsSwitchContext,
NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]),
NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
} else {
nlError = NL_ERROR_INVAL;
+ NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
goto Cleanup;
}
if (!vport) {
nlError = NL_ERROR_NODEV;
+ NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
goto Cleanup;
}
- NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
usrParamsCtx->outputLength,
gOvsSwitchContext->dpNo);
return STATUS_SUCCESS;
}
+/*
+ * --------------------------------------------------------------------------
+ * Utility function to fill up information about the state of a port in a reply
+ * to* userspace.
+ * Assumes that 'gOvsCtrlLock' lock is acquired.
+ * --------------------------------------------------------------------------
+ */
+static NTSTATUS
+OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+ POVS_EVENT_ENTRY eventEntry,
+ PNL_BUFFER nlBuf)
+{
+ NTSTATUS status;
+ BOOLEAN rc;
+ OVS_MESSAGE msgOutTmp;
+ PNL_MSG_HDR nlMsg;
+ POVS_VPORT_ENTRY vport;
+
+ ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
+
+ msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
+ msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */
+
+ /* driver intiated messages should have zerp seq number*/
+ msgOutTmp.nlMsg.nlmsgSeq = 0;
+ msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
+
+ msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
+ msgOutTmp.genlMsg.reserved = 0;
+
+ /* we don't have netdev yet, treat link up/down a adding/removing a port*/
+ if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
+ msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
+ } else if (eventEntry->status &
+ (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
+ msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
+ } else {
+ ASSERT(FALSE);
+ return STATUS_UNSUCCESSFUL;
+ }
+ msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
+
+ rc = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
+ if (!rc) {
+ status = STATUS_INVALID_BUFFER_SIZE;
+ goto cleanup;
+ }
+
+ vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
+ if (!vport) {
+ status = STATUS_DEVICE_DOES_NOT_EXIST;
+ goto cleanup;
+ }
+
+ rc = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) ||
+ NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) ||
+ NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
+ if (!rc) {
+ status = STATUS_INVALID_BUFFER_SIZE;
+ goto cleanup;
+ }
+
+ /* XXXX Should we add the port stats attributes?*/
+ nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
+ nlMsg->nlmsgLen = NlBufSize(nlBuf);
+ status = STATUS_SUCCESS;
+
+cleanup:
+ return status;
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ * Handler for reading events from the driver event queue. This handler is
+ * executed when user modes issues a socket receive on a socket assocaited
+ * with the MC group for events.
+ * XXX user mode should read multiple events in one system call
+ * --------------------------------------------------------------------------
+ */
+static NTSTATUS
+OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+ UINT32 *replyLen)
+{
+#ifdef DBG
+ POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
+ POVS_OPEN_INSTANCE instance =
+ (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
+#endif
+ NL_BUFFER nlBuf;
+ NTSTATUS status;
+ OVS_EVENT_ENTRY eventEntry;
+
+ ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
+
+ /* Should never read events with a dump socket */
+ ASSERT(instance->dumpState.ovsMsg == NULL);
+
+ /* Must have an event queue */
+ ASSERT(instance->eventQueue != NULL);
+
+ /* Output buffer has been validated while validating read dev op. */
+ ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
+
+ NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
+
+ OvsAcquireCtrlLock();
+ if (!gOvsSwitchContext) {
+ status = STATUS_SUCCESS;
+ goto cleanup;
+ }
+
+ /* remove an event entry from the event queue */
+ status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
+ if (status != STATUS_SUCCESS) {
+ goto cleanup;
+ }
+
+ status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
+ if (status == NDIS_STATUS_SUCCESS) {
+ *replyLen = NlBufSize(&nlBuf);
+ }
+
+cleanup:
+ OvsReleaseCtrlLock();
+ return status;
+}
#endif /* OVS_USE_NL_INTERFACE */