datapath-windows: Define OVS_DPPORT_NUMBER_INVALID
[cascardo/ovs.git] / datapath-windows / ovsext / Datapath.c
index b038026..65390b2 100644 (file)
@@ -85,29 +85,24 @@ typedef struct _NETLINK_FAMILY {
     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
@@ -132,6 +127,11 @@ NETLINK_CMD nlControlFamilyCmdOps[] = {
       .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,
     }
 };
 
@@ -146,6 +146,11 @@ NETLINK_FAMILY nlControlFamilyOps = {
 
 /* 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 |
@@ -200,14 +205,56 @@ NETLINK_FAMILY nlVportFamilyOps = {
 };
 
 /* 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,
@@ -221,8 +268,6 @@ static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
 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;
@@ -428,6 +473,7 @@ OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
     for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
         if (ovsOpenInstanceArray[i] == NULL) {
             ovsOpenInstanceArray[i] = instance;
+            ovsNumberOfOpenInstances++;
             instance->cookie = i;
             break;
         }
@@ -466,6 +512,7 @@ OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
     fileObject->FsContext = NULL;
     ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
     ovsOpenInstanceArray[instance->cookie] = NULL;
+    ovsNumberOfOpenInstances--;
     OvsReleaseCtrlLock();
     ASSERT(instance->eventQueue == NULL);
     ASSERT (instance->packetQueue == NULL);
@@ -629,6 +676,29 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject,
         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) {
@@ -698,13 +768,18 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject,
     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;
@@ -873,8 +948,7 @@ OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
     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: ? */
@@ -959,7 +1033,8 @@ OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
         (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;
@@ -977,6 +1052,17 @@ done:
     return status;
 }
 
+/*
+ * --------------------------------------------------------------------------
+ *  Command Handler for 'OVS_DP_CMD_NEW'.
+ * --------------------------------------------------------------------------
+ */
+static NTSTATUS
+OvsNewDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                   UINT32 *replyLen)
+{
+    return HandleDpTransactionCommon(usrParamsCtx, replyLen);
+}
 
 /*
  * --------------------------------------------------------------------------
@@ -993,7 +1079,7 @@ OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
                    UINT32 *replyLen)
 {
     if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
-        return HandleGetDpTransaction(usrParamsCtx, replyLen);
+        return HandleDpTransactionCommon(usrParamsCtx, replyLen);
     } else {
         return HandleGetDpDump(usrParamsCtx, replyLen);
     }
@@ -1008,7 +1094,7 @@ static NTSTATUS
 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
                        UINT32 *replyLen)
 {
-    return HandleDpTransaction(usrParamsCtx, replyLen);
+    return HandleDpTransactionCommon(usrParamsCtx, replyLen);
 }
 
 
@@ -1086,23 +1172,27 @@ static NTSTATUS
 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 },
@@ -1114,9 +1204,11 @@ HandleDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
     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;
         }
@@ -1143,12 +1235,25 @@ HandleDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
             !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;
     }
 
@@ -1158,11 +1263,19 @@ HandleDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
     *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;
@@ -1191,7 +1304,7 @@ OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
 
 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;
@@ -1200,18 +1313,23 @@ BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
     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,
@@ -1342,8 +1460,9 @@ OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
      * 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 */
@@ -1371,7 +1490,7 @@ OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
                 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,
@@ -1425,6 +1544,87 @@ OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
     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;
+
+    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;
+    }
+
+    if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
+        return STATUS_INVALID_BUFFER_SIZE;
+    }
+
+    OvsAcquireCtrlLock();
+    if (!gOvsSwitchContext) {
+        OvsReleaseCtrlLock();
+        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]),
+            NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]) - 1);
+    } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
+        vport = OvsFindVportByPortNo(gOvsSwitchContext,
+            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;
+    }
+
+    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;
+
+        BuildErrorMsg(msgIn, msgError, nlError);
+        *replyLen = msgError->nlMsg.nlmsgLen;
+    }
+
+    return STATUS_SUCCESS;
+}
+
 /*
  * --------------------------------------------------------------------------
  *  Handler for the get vport command. The function handles the initial call to
@@ -1435,15 +1635,19 @@ static NTSTATUS
 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
                       UINT32 *replyLen)
 {
+    *replyLen = 0;
+
     switch (usrParamsCtx->devOp)
     {
     case OVS_WRITE_DEV_OP:
-        *replyLen = 0;
         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;
     }
@@ -1485,4 +1689,131 @@ MapIrpOutputBuffer(PIRP irp,
     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 */