datapath-windows: Define OVS_DPPORT_NUMBER_INVALID
[cascardo/ovs.git] / datapath-windows / ovsext / Datapath.c
index 3c2f27d..65390b2 100644 (file)
  * Handler for a given netlink command. Not all the parameters are used by all
  * the handlers.
  */
-typedef NTSTATUS (*NetlinkCmdHandler)(PIRP irp, PFILE_OBJECT fileObject,
-                                      PVOID inputBuffer, UINT32 inputLength,
-                                      PVOID outputBuffer, UINT32 outputLength,
-                                      UINT32 *replyLen);
+typedef NTSTATUS(NetlinkCmdHandler)(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                                    UINT32 *replyLen);
 
 typedef struct _NETLINK_CMD {
     UINT16 cmd;
-    NetlinkCmdHandler handler;
+    NetlinkCmdHandler *handler;
     UINT32 supportedDevOp;      /* Supported device operations. */
     BOOLEAN validateDpIndex;    /* Does command require a valid DP argument. */
 } NETLINK_CMD, *PNETLINK_CMD;
@@ -87,20 +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 NTSTATUS OvsGetPidCmdHandler(PIRP irp, PFILE_OBJECT fileObject,
-                                    PVOID inputBuffer, UINT32 inputLength,
-                                    PVOID outputBuffer, UINT32 outputLength,
-                                    UINT32 *replyLen);
+static NetlinkCmdHandler OvsGetPidCmdHandler,
+                         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 HandleDpTransactionCommon(
+                    POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen);
 
 /*
  * The various netlink families, along with the supported commands. Most of
@@ -111,8 +113,26 @@ static NTSTATUS OvsGetPidCmdHandler(PIRP irp, PFILE_OBJECT fileObject,
 
 /* Netlink control family: this is a Windows specific family. */
 NETLINK_CMD nlControlFamilyCmdOps[] = {
-    { OVS_CTRL_CMD_WIN_GET_PID, OvsGetPidCmdHandler,
-      OVS_TRANSACTION_DEV_OP, FALSE }
+    { .cmd             = OVS_CTRL_CMD_WIN_GET_PID,
+      .handler         = OvsGetPidCmdHandler,
+      .supportedDevOp  = OVS_TRANSACTION_DEV_OP,
+      .validateDpIndex = FALSE,
+    },
+    { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ,
+      .handler = OvsPendEventCmdHandler,
+      .supportedDevOp = OVS_WRITE_DEV_OP,
+      .validateDpIndex = TRUE,
+    },
+    { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ,
+      .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_FAMILY nlControlFamilyOps = {
@@ -124,7 +144,35 @@ NETLINK_FAMILY nlControlFamilyOps = {
     .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
 };
 
+/* 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 |
+                         OVS_TRANSACTION_DEV_OP,
+      .validateDpIndex = FALSE
+    },
+    { .cmd             = OVS_DP_CMD_SET,
+      .handler         = OvsSetDpCmdHandler,
+      .supportedDevOp  = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
+                         OVS_TRANSACTION_DEV_OP,
+      .validateDpIndex = TRUE
+    }
+};
 
+NETLINK_FAMILY nlDatapathFamilyOps = {
+    .name     = OVS_DATAPATH_FAMILY,
+    .id       = OVS_WIN_NL_DATAPATH_FAMILY_ID,
+    .version  = OVS_DATAPATH_VERSION,
+    .maxAttr  = OVS_DP_ATTR_MAX,
+    .cmds     = nlDatapathFamilyCmdOps,
+    .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
+};
 
 /* Netlink packet family. */
 /* XXX: Add commands here. */
@@ -137,60 +185,89 @@ NETLINK_FAMILY nlPacketFamilyOps = {
     .opsCount = 0
 };
 
-/* Netlink datapath family. */
-/* XXX: Add commands here. */
-NETLINK_FAMILY nlDatapathFamilyOps = {
-    .name     = OVS_DATAPATH_FAMILY,
-    .id       = OVS_WIN_NL_DATAPATH_FAMILY_ID,
-    .version  = OVS_DATAPATH_VERSION,
-    .maxAttr  = OVS_DP_ATTR_MAX,
-    .cmds     = NULL, /* XXX: placeholder. */
-    .opsCount = 0
+/* Netlink vport family. */
+NETLINK_CMD nlVportFamilyCmdOps[] = {
+    { .cmd = OVS_VPORT_CMD_GET,
+      .handler = OvsGetVportCmdHandler,
+      .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
+                        OVS_TRANSACTION_DEV_OP,
+      .validateDpIndex = TRUE
+    }
 };
 
-/* Netlink vport family. */
-/* XXX: Add commands here. */
 NETLINK_FAMILY nlVportFamilyOps = {
     .name     = OVS_VPORT_FAMILY,
     .id       = OVS_WIN_NL_VPORT_FAMILY_ID,
     .version  = OVS_VPORT_VERSION,
     .maxAttr  = OVS_VPORT_ATTR_MAX,
-    .cmds     = NULL, /* XXX: placeholder. */
-    .opsCount = 0
+    .cmds     = nlVportFamilyCmdOps,
+    .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps)
 };
 
 /* 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)
 };
 
-static NTSTATUS
-MapIrpOutputBuffer(PIRP irp,
-                   UINT32 bufferLength,
-                   UINT32 requiredLength,
-                   PVOID *buffer);
-static NTSTATUS
-ValidateNetlinkCmd(UINT32 devOp,
-                   POVS_MESSAGE ovsMsg,
-                   NETLINK_FAMILY *nlFamilyOps);
-static NTSTATUS
-InvokeNetlinkCmdHandler(PIRP irp,
-                        PFILE_OBJECT fileObject,
-                        UINT32 devOp,
-                        POVS_MESSAGE ovsMsg,
-                        NETLINK_FAMILY *nlFamily,
-                        PVOID inputBuffer,
-                        UINT32 inputLength,
-                        PVOID outputBuffer,
-                        UINT32 outputLength,
-                        UINT32 *replyLen);
+/* 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,
+                                   UINT32 bufferLength,
+                                   UINT32 requiredLength,
+                                   PVOID *buffer);
+static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
+                                   POVS_OPEN_INSTANCE instance,
+                                   POVS_MESSAGE ovsMsg,
+                                   NETLINK_FAMILY *nlFamilyOps);
+static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                                        NETLINK_FAMILY *nlFamilyOps,
+                                        UINT32 *replyLen);
 
 /* Handles to the device object for communication with userspace. */
 NDIS_HANDLE gOvsDeviceHandle;
@@ -218,6 +295,7 @@ DRIVER_DISPATCH OvsDeviceControl;
  * each thread, and at least one descriptor per vport. Revisit this later.
  */
 #define OVS_MAX_OPEN_INSTANCES 512
+#define OVS_SYSTEM_DP_NAME     "ovs-system"
 
 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
 UINT32 ovsNumberOfOpenInstances;
@@ -395,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;
         }
@@ -433,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);
@@ -541,6 +621,7 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject,
     OVS_MESSAGE ovsMsgReadOp;
     POVS_MESSAGE ovsMsg;
     NETLINK_FAMILY *nlFamilyOps;
+    OVS_USER_PARAMS_CONTEXT usrParamsCtx;
 
 #ifdef DBG
     POVS_DEVICE_EXTENSION ovsExt =
@@ -595,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) {
@@ -615,19 +719,28 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject,
          */
         inputBuffer = NULL;
         inputBufferLen = 0;
-        /* Create an NL message for consumption. */
-        ovsMsg = &ovsMsgReadOp;
-        devOp = OVS_READ_DEV_OP;
 
         /*
          * For implementing read (ioctl or otherwise), we need to store some
-         * state in the instance to indicate the previous command. The state can
-         * setup 'ovsMsgReadOp' appropriately.
+         * state in the instance to indicate the command that started the dump
+         * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
+         * that 'ovsMsgReadOp' is needed only in this function to call into the
+         * appropraite handler. The handler itself can access the state in the
+         * instance.
          *
-         * XXX: Support for that will be added as the userspace code evolves.
+         * In the absence of a dump start, return 0 bytes.
          */
-        status = STATUS_NOT_IMPLEMENTED;
-        goto done;
+        if (instance->dumpState.ovsMsg == NULL) {
+            replyLen = 0;
+            status = STATUS_SUCCESS;
+            goto done;
+        }
+        RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
+                      sizeof (ovsMsgReadOp));
+
+        /* Create an NL message for consumption. */
+        ovsMsg = &ovsMsgReadOp;
+        devOp = OVS_READ_DEV_OP;
 
         break;
 
@@ -652,13 +765,21 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject,
     case OVS_WIN_NL_CTRL_FAMILY_ID:
         nlFamilyOps = &nlControlFamilyOps;
         break;
-    case OVS_WIN_NL_PACKET_FAMILY_ID:
     case OVS_WIN_NL_DATAPATH_FAMILY_ID:
+        nlFamilyOps = &nlDatapathFamilyOps;
+        break;
     case OVS_WIN_NL_FLOW_FAMILY_ID:
-    case OVS_WIN_NL_VPORT_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;
@@ -669,17 +790,18 @@ OvsDeviceControl(PDEVICE_OBJECT deviceObject,
      * previously.
      */
     if (devOp != OVS_READ_DEV_OP) {
-        status = ValidateNetlinkCmd(devOp, ovsMsg, nlFamilyOps);
+        status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
         if (status != STATUS_SUCCESS) {
             goto done;
         }
     }
 
-    status = InvokeNetlinkCmdHandler(irp, fileObject, devOp,
-                                     ovsMsg, nlFamilyOps,
-                                     inputBuffer, inputBufferLen,
-                                     outputBuffer, outputBufferLen,
-                                     &replyLen);
+    InitUserParamsCtx(irp, instance, devOp, ovsMsg,
+                      inputBuffer, inputBufferLen,
+                      outputBuffer, outputBufferLen,
+                      &usrParamsCtx);
+
+    status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
 
 done:
     KeMemoryBarrier();
@@ -696,6 +818,7 @@ done:
  */
 static NTSTATUS
 ValidateNetlinkCmd(UINT32 devOp,
+                   POVS_OPEN_INSTANCE instance,
                    POVS_MESSAGE ovsMsg,
                    NETLINK_FAMILY *nlFamilyOps)
 {
@@ -728,6 +851,14 @@ ValidateNetlinkCmd(UINT32 devOp,
                 OvsReleaseCtrlLock();
             }
 
+            /* Validate the PID. */
+            if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
+                if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
+                    status = STATUS_INVALID_PARAMETER;
+                    goto done;
+                }
+            }
+
             status = STATUS_SUCCESS;
             break;
         }
@@ -743,28 +874,20 @@ done:
  * --------------------------------------------------------------------------
  */
 static NTSTATUS
-InvokeNetlinkCmdHandler(PIRP irp,
-                        PFILE_OBJECT fileObject,
-                        UINT32 devOp,
-                        OVS_MESSAGE *ovsMsg,
+InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
                         NETLINK_FAMILY *nlFamilyOps,
-                        PVOID inputBuffer,
-                        UINT32 inputLength,
-                        PVOID outputBuffer,
-                        UINT32 outputLength,
                         UINT32 *replyLen)
 {
     NTSTATUS status = STATUS_INVALID_PARAMETER;
     UINT16 i;
 
-    UNREFERENCED_PARAMETER(devOp);
-
     for (i = 0; i < nlFamilyOps->opsCount; i++) {
-        if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
-            status = nlFamilyOps->cmds[i].handler(irp, fileObject,
-                                                inputBuffer, inputLength,
-                                                outputBuffer, outputLength,
-                                                replyLen);
+        if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
+            NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
+            ASSERT(handler);
+            if (handler) {
+                status = handler(usrParamsCtx, replyLen);
+            }
             break;
         }
     }
@@ -772,9 +895,10 @@ InvokeNetlinkCmdHandler(PIRP irp,
     return status;
 }
 
-
 /*
  * --------------------------------------------------------------------------
+ *  Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
+ *
  *  Each handle on the device is assigned a unique PID when the handle is
  *  created. On platforms that support netlink natively, the PID is available
  *  to userspace when the netlink socket is created. However, without native
@@ -785,24 +909,15 @@ InvokeNetlinkCmdHandler(PIRP irp,
  * --------------------------------------------------------------------------
  */
 static NTSTATUS
-OvsGetPidCmdHandler(PIRP irp,
-                    PFILE_OBJECT fileObject,
-                    PVOID inputBuffer,
-                    UINT32 inputLength,
-                    PVOID outputBuffer,
-                    UINT32 outputLength,
+OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
                     UINT32 *replyLen)
 {
-    UNREFERENCED_PARAMETER(irp);
-    UNREFERENCED_PARAMETER(fileObject);
-    UNREFERENCED_PARAMETER(inputBuffer);
-    UNREFERENCED_PARAMETER(inputLength);
+    POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
+    POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
 
-    POVS_MESSAGE msgIn = (POVS_MESSAGE)inputBuffer;
-    POVS_MESSAGE msgOut = (POVS_MESSAGE)outputBuffer;
-
-    if (outputLength >= sizeof *msgOut) {
-        POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
+    if (usrParamsCtx->outputLength >= sizeof *msgOut) {
+        POVS_OPEN_INSTANCE instance =
+            (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
 
         RtlZeroMemory(msgOut, sizeof *msgOut);
         msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
@@ -813,9 +928,731 @@ OvsGetPidCmdHandler(PIRP irp,
         return STATUS_NDIS_INVALID_LENGTH;
     }
 
-    return NDIS_STATUS_SUCCESS;
+    return STATUS_SUCCESS;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * Utility function to fill up information about the datapath in a reply to
+ * userspace.
+ * Assumes that 'gOvsCtrlLock' lock is acquired.
+ * --------------------------------------------------------------------------
+ */
+static NTSTATUS
+OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
+              POVS_MESSAGE msgIn,
+              PNL_BUFFER nlBuf)
+{
+    BOOLEAN writeOk;
+    OVS_MESSAGE msgOutTmp;
+    OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
+    PNL_MSG_HDR nlMsg;
+
+    ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && NlBufRemLen(nlBuf) >= sizeof *msgIn);
+
+    msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
+    msgOutTmp.nlMsg.nlmsgFlags = 0;  /* XXX: ? */
+    msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
+    msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
+
+    msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
+    msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
+    msgOutTmp.genlMsg.reserved = 0;
+
+    msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
+
+    writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
+    if (writeOk) {
+        writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
+                                     OVS_SYSTEM_DP_NAME);
+    }
+    if (writeOk) {
+        OVS_DP_STATS dpStats;
+
+        dpStats.n_hit = datapath->hits;
+        dpStats.n_missed = datapath->misses;
+        dpStats.n_lost = datapath->lost;
+        dpStats.n_flows = datapath->nFlows;
+        writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
+                                     (PCHAR)&dpStats, sizeof dpStats);
+    }
+    nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
+    nlMsg->nlmsgLen = NlBufSize(nlBuf);
+
+    return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * Handler for queueing an IRP used for event notification. The IRP is
+ * completed when a port state changes. STATUS_PENDING is returned on
+ * success. User mode keep a pending IRP at all times.
+ * --------------------------------------------------------------------------
+ */
+static NTSTATUS
+OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                       UINT32 *replyLen)
+{
+    NDIS_STATUS status;
+
+    UNREFERENCED_PARAMETER(replyLen);
+
+    POVS_OPEN_INSTANCE instance =
+        (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
+    POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
+    OVS_EVENT_POLL poll;
+
+    poll.dpNo = msgIn->ovsHdr.dp_ifindex;
+    status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
+                               &poll, sizeof poll);
+    return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *  Handler for the subscription for the event queue
+ * --------------------------------------------------------------------------
+ */
+static NTSTATUS
+OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                            UINT32 *replyLen)
+{
+    NDIS_STATUS status;
+    OVS_EVENT_SUBSCRIBE request;
+    BOOLEAN rc;
+    UINT8 join;
+    PNL_ATTR attrs[2];
+    const NL_POLICY policy[] =  {
+        [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
+        [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
+        };
+
+    UNREFERENCED_PARAMETER(replyLen);
+
+    POVS_OPEN_INSTANCE instance =
+        (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
+    POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
+
+    rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
+         NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, 2);
+    if (!rc) {
+        status = STATUS_INVALID_PARAMETER;
+        goto done;
+    }
+
+    /* XXX Ignore the MC group for now */
+    join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
+    request.dpNo = msgIn->ovsHdr.dp_ifindex;
+    request.subscribe = join;
+    request.mask = OVS_EVENT_MASK_ALL;
+
+    status = OvsSubscribeEventIoctl(instance->fileObject, &request,
+                                    sizeof request);
+done:
+    return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *  Command Handler for 'OVS_DP_CMD_NEW'.
+ * --------------------------------------------------------------------------
+ */
+static NTSTATUS
+OvsNewDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                   UINT32 *replyLen)
+{
+    return HandleDpTransactionCommon(usrParamsCtx, replyLen);
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *  Command Handler for 'OVS_DP_CMD_GET'.
+ *
+ *  The function handles both the dump based as well as the transaction based
+ *  'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
+ *  call to setup dump state, as well as subsequent calls to continue dumping
+ *  data.
+ * --------------------------------------------------------------------------
+ */
+static NTSTATUS
+OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                   UINT32 *replyLen)
+{
+    if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
+        return HandleDpTransactionCommon(usrParamsCtx, replyLen);
+    } else {
+        return HandleGetDpDump(usrParamsCtx, replyLen);
+    }
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *  Function for handling the transaction based 'OVS_DP_CMD_GET' command.
+ * --------------------------------------------------------------------------
+ */
+static NTSTATUS
+HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                       UINT32 *replyLen)
+{
+    return HandleDpTransactionCommon(usrParamsCtx, replyLen);
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ *  Function for handling the dump-based 'OVS_DP_CMD_GET' command.
+ * --------------------------------------------------------------------------
+ */
+static NTSTATUS
+HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                UINT32 *replyLen)
+{
+    POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
+    POVS_OPEN_INSTANCE instance =
+        (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
+
+    if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
+        *replyLen = 0;
+        OvsSetupDumpStart(usrParamsCtx);
+    } else {
+        NL_BUFFER nlBuf;
+        NTSTATUS status;
+        POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
+
+        ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
+
+        if (instance->dumpState.ovsMsg == NULL) {
+            ASSERT(FALSE);
+            return STATUS_INVALID_DEVICE_STATE;
+        }
+
+        /* Dump state must have been deleted after previous dump operation. */
+        ASSERT(instance->dumpState.index[0] == 0);
+        /* 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) {
+            /* Treat this as a dump done. */
+            OvsReleaseCtrlLock();
+            *replyLen = 0;
+            FreeUserDumpState(instance);
+            return STATUS_SUCCESS;
+        }
+        status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
+        OvsReleaseCtrlLock();
+
+        if (status != STATUS_SUCCESS) {
+            *replyLen = 0;
+            FreeUserDumpState(instance);
+            return status;
+        }
+
+        /* Increment the dump index. */
+        instance->dumpState.index[0] = 1;
+        *replyLen = msgOut->nlMsg.nlmsgLen;
+
+        /* Free up the dump state, since there's no more data to continue. */
+        FreeUserDumpState(instance);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ *  Command Handler for 'OVS_DP_CMD_SET'.
+ * --------------------------------------------------------------------------
+ */
+static NTSTATUS
+OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                   UINT32 *replyLen)
+{
+    return HandleDpTransactionCommon(usrParamsCtx, replyLen);
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *  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
+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 },
+        [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
+    };
+    PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
+
+    /* input buffer has been validated while validating write dev op. */
+    ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
+
+    /* Parse any attributes in the request. */
+    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;
+        }
+
+        /*
+        * XXX: Not clear at this stage if there's any role for the
+        * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
+        * from userspace.
+        */
+
+    } else {
+        RtlZeroMemory(dpAttrs, sizeof dpAttrs);
+    }
+
+    /* Output buffer is optional for OVS_TRANSACTION_DEV_OP. */
+    if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
+        return STATUS_NDIS_INVALID_LENGTH;
+    }
+    NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
+
+    OvsAcquireCtrlLock();
+    if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
+        if (!gOvsSwitchContext &&
+            !OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
+                              OVS_SYSTEM_DP_NAME)) {
+            OvsReleaseCtrlLock();
+
+            /* 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();
+        nlError = NL_ERROR_NODEV;
+        goto cleanup;
+    }
+
+    if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) {
+        OvsReleaseCtrlLock();
+        nlError = NL_ERROR_EXIST;
+        goto cleanup;
+    }
+
+    status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
+    OvsReleaseCtrlLock();
+
+    *replyLen = NlBufSize(&nlBuf);
+
+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;
+}
+
+
+NTSTATUS
+OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
+{
+    POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
+    POVS_OPEN_INSTANCE instance =
+        (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
+
+    /* input buffer has been validated while validating write dev op. */
+    ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
+
+    /* A write operation that does not indicate dump start is invalid. */
+    if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
+        return STATUS_INVALID_PARAMETER;
+    }
+    /* XXX: Handle other NLM_F_* flags in the future. */
+
+    /*
+     * This operation should be setting up the dump state. If there's any
+     * previous state, clear it up so as to set it up afresh.
+     */
+    if (instance->dumpState.ovsMsg != NULL) {
+        FreeUserDumpState(instance);
+    }
+
+    return InitUserDumpState(instance, msgIn);
+}
+
+static VOID
+BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
+            UINT32 length, UINT16 flags)
+{
+    msgOut->nlMsg.nlmsgType = type;
+    msgOut->nlMsg.nlmsgFlags = flags;
+    msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
+    msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
+    msgOut->nlMsg.nlmsgLen = length;
+
+    msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
+    msgOut->genlMsg.version = msgIn->genlMsg.version;
+    msgOut->genlMsg.reserved = 0;
+}
+
+/*
+ * 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);
+}
+
+VOID
+BuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode)
+{
+    BuildMsgOut(msgIn, (POVS_MESSAGE)msgOut, NLMSG_ERROR,
+                sizeof(OVS_MESSAGE_ERROR), 0);
+
+    msgOut->errorMsg.error = errorCode;
+    msgOut->errorMsg.nlMsg = msgIn->nlMsg;
+}
+
+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;
+    OVS_MESSAGE msgOut;
+    PNL_MSG_HDR nlMsg;
+
+    NlBufInit(&nlBuffer, outBuffer, outBufLen);
+
+    BuildReplyMsgFromMsgIn(msgIn, &msgOut, NLM_F_MULTI);
+    msgOut.ovsHdr.dp_ifindex = dpIfIndex;
+
+    ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
+    if (!ok) {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
+    if (!ok) {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
+    if (!ok) {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
+    if (!ok) {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /*
+     * 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_INSUFFICIENT_RESOURCES;
+    }
+
+    /*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_INSUFFICIENT_RESOURCES;
+    }
+
+    /*
+     * 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;
+
+    OvsAcquireCtrlLock();
+    if (!gOvsSwitchContext) {
+        /* Treat this as a dump done. */
+        OvsReleaseCtrlLock();
+        *replyLen = 0;
+        FreeUserDumpState(instance);
+        return STATUS_SUCCESS;
+    }
+
+    /*
+     * 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.
+    */
+    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+    NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
+                          NDIS_RWL_AT_DISPATCH_LEVEL);
+
+    if (gOvsSwitchContext->numVports > 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->portHashArray[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, portLink);
+
+                    if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
+                        OvsCreateMsgFromVport(vport, msgIn,
+                                              usrParamsCtx->outputBuffer,
+                                              usrParamsCtx->outputLength,
+                                              gOvsSwitchContext->dpNo);
+                        ++outIndex;
+                        break;
+                    } else {
+                        vport = NULL;
+                    }
+                }
+
+                ++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);
+
+    OvsReleaseCtrlLock();
+
+    /* 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;
+
+    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
+ *  setup the dump state, as well as subsequent calls to continue dumping data.
+ * --------------------------------------------------------------------------
+*/
+static 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;
+    }
+
+}
 
 /*
  * --------------------------------------------------------------------------
@@ -852,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 */