datapath-windows: Add GRE TEB support for windows datapath
[cascardo/ovs.git] / datapath-windows / ovsext / Vport.c
index 0e8776e..7b0103d 100644 (file)
  */
 
 #include "precomp.h"
+
+#include "Datapath.h"
+#include "Event.h"
+#include "Gre.h"
+#include "IpHelper.h"
 #include "Jhash.h"
+#include "Oid.h"
+#include "Stt.h"
 #include "Switch.h"
-#include "Vport.h"
-#include "Event.h"
 #include "User.h"
+#include "Vport.h"
 #include "Vxlan.h"
-#include "IpHelper.h"
-#include "Oid.h"
-#include "Datapath.h"
 
 #ifdef OVS_DBG_MOD
 #undef OVS_DBG_MOD
 
 #define OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC    100
 
+/* Context structure used to pass back and forth information to the tunnel
+ * filter threads. */
+typedef struct _OVS_TUNFLT_INIT_CONTEXT {
+    POVS_SWITCH_CONTEXT switchContext;
+    UINT32 outputLength;
+    PVOID outputBuffer;
+    PVOID inputBuffer;
+    POVS_VPORT_ENTRY vport;
+    BOOLEAN hvSwitchPort;
+    BOOLEAN hvDelete;
+    BOOLEAN ovsDelete;
+} OVS_TUNFLT_INIT_CONTEXT, *POVS_TUNFLT_INIT_CONTEXT;
+
+
 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
-extern PNDIS_SPIN_LOCK gOvsCtrlLock;
 
-static UINT32 OvsGetVportNo(POVS_SWITCH_CONTEXT switchContext, UINT32 nicIndex,
-                            OVS_VPORT_TYPE ovsType,
-                            BOOLEAN isExternal);
-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,
                 POVS_VPORT_ENTRY vport, PNDIS_SWITCH_NIC_PARAMETERS nicParam);
-static VOID OvsInitPhysNicVport(POVS_VPORT_ENTRY vport, POVS_VPORT_ENTRY
-                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 VOID OvsCopyPortParamsFromVport(POVS_VPORT_ENTRY vport,
+                                       PNDIS_SWITCH_PORT_PARAMETERS portParam);
 static __inline VOID OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext,
                                      ULONG sleepMicroSec);
 static NTSTATUS OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
@@ -75,34 +81,97 @@ static NTSTATUS CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
                                            PVOID outBuffer,
                                            UINT32 outBufLen,
                                            int dpIfIndex);
+static POVS_VPORT_ENTRY OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
+                                              PWSTR wsName, SIZE_T wstrSize);
+static VOID UpdateSwitchCtxWithVport(POVS_SWITCH_CONTEXT switchContext,
+                                     POVS_VPORT_ENTRY vport, BOOLEAN newPort);
+static NTSTATUS OvsRemoveTunnelVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                                     POVS_SWITCH_CONTEXT switchContext,
+                                     POVS_VPORT_ENTRY vport,
+                                     BOOLEAN hvDelete,
+                                     BOOLEAN ovsDelete);
+static VOID OvsTunnelVportPendingInit(PVOID context,
+                                      NTSTATUS status,
+                                      UINT32 *replyLen);
+static VOID OvsTunnelVportPendingRemove(PVOID context,
+                                        NTSTATUS status,
+                                        UINT32 *replyLen);
+static NTSTATUS GetNICAlias(GUID *netCfgInstanceId,
+                            IF_COUNTED_STRING *portFriendlyName);
 
 /*
- * Functions implemented in relaton to NDIS port manipulation.
+ * --------------------------------------------------------------------------
+ *  Creates a Vport entry for a Hyper-V switch port. 'nicIndex' is typically
+ *  associated with a NIC than a port. We use it here for the special case
+ *  where we need to create a Vport for an external NIC with NicIndex > 0.
+ * --------------------------------------------------------------------------
  */
 NDIS_STATUS
-OvsCreatePort(POVS_SWITCH_CONTEXT switchContext,
-              PNDIS_SWITCH_PORT_PARAMETERS portParam)
+HvCreatePort(POVS_SWITCH_CONTEXT switchContext,
+             PNDIS_SWITCH_PORT_PARAMETERS portParam,
+             NDIS_SWITCH_NIC_INDEX nicIndex)
 {
     POVS_VPORT_ENTRY vport;
     LOCK_STATE_EX lockState;
     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+    BOOLEAN newPort = FALSE;
 
     VPORT_PORT_ENTER(portParam);
 
     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
+    /* Lookup by port ID. */
     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
-                                            portParam->PortId, 0);
+                                            portParam->PortId, nicIndex);
     if (vport != NULL) {
+        OVS_LOG_ERROR("Port add failed due to duplicate port name, "
+                      "port Id: %u", portParam->PortId);
         status = STATUS_DATA_NOT_ACCEPTED;
         goto create_port_done;
     }
-    vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
-    if (vport == NULL) {
-        status = NDIS_STATUS_RESOURCES;
+
+    /*
+     * Lookup by port name to see if this port with this name had been added
+     * (and deleted) previously.
+     */
+    vport = OvsFindVportByHvNameW(gOvsSwitchContext,
+                                  portParam->PortFriendlyName.String,
+                                  portParam->PortFriendlyName.Length);
+    if (vport && vport->isAbsentOnHv == FALSE) {
+        OVS_LOG_ERROR("Port add failed since a port already exists on "
+                      "the specified port Id: %u, ovsName: %s",
+                      portParam->PortId, vport->ovsName);
+        status = STATUS_DATA_NOT_ACCEPTED;
         goto create_port_done;
     }
+
+    if (vport != NULL) {
+        ASSERT(vport->isAbsentOnHv);
+        ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
+
+        /*
+         * It should be possible to simply just mark this port as "not deleted"
+         * given that the port Id and the name are the same and also provided
+         * that the other properties that we cache have not changed.
+         */
+        if (vport->portType != portParam->PortType) {
+            OVS_LOG_INFO("Port add failed due to PortType change, port Id: %u"
+                         " old: %u, new: %u", portParam->PortId,
+                         vport->portType, portParam->PortType);
+            status = STATUS_DATA_NOT_ACCEPTED;
+            goto create_port_done;
+        }
+        vport->isAbsentOnHv = FALSE;
+    } else {
+        vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
+        if (vport == NULL) {
+            status = NDIS_STATUS_RESOURCES;
+            goto create_port_done;
+        }
+        newPort = TRUE;
+    }
     OvsInitVportWithPortParam(vport, portParam);
-    OvsInitVportCommon(switchContext, vport);
+    vport->nicIndex = nicIndex;
+    UpdateSwitchCtxWithVport(switchContext, vport, newPort);
 
 create_port_done:
     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
@@ -110,9 +179,72 @@ create_port_done:
     return status;
 }
 
+
+/*
+ * --------------------------------------------------------------------------
+ * Function to process updates to a port on the Hyper-Vs witch.
+ * --------------------------------------------------------------------------
+ */
+NDIS_STATUS
+HvUpdatePort(POVS_SWITCH_CONTEXT switchContext,
+             PNDIS_SWITCH_PORT_PARAMETERS portParam)
+{
+    POVS_VPORT_ENTRY vport;
+    LOCK_STATE_EX lockState;
+    OVS_VPORT_STATE ovsState;
+    NDIS_SWITCH_NIC_STATE nicState;
+
+    VPORT_PORT_ENTER(portParam);
+
+    NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
+    vport = OvsFindVportByPortIdAndNicIndex(switchContext,
+                                            portParam->PortId, 0);
+    /*
+     * Update properties only for NETDEV ports for supprting PS script
+     */
+    if (vport == NULL) {
+        goto update_port_done;
+    }
+
+    /* Store the nic and the OVS states as Nic Create won't be called */
+    ovsState = vport->ovsState;
+    nicState = vport->nicState;
+
+    /*
+     * Currently only the port friendly name is being updated
+     * Make sure that no other properties are changed
+     */
+    ASSERT(portParam->PortId == vport->portId);
+    ASSERT(portParam->PortState == vport->portState);
+    ASSERT(portParam->PortType == vport->portType);
+
+    /*
+     * Call the set parameters function the handle all properties
+     * change in a single place in case future version supports change of
+     * other properties
+     */
+    OvsInitVportWithPortParam(vport, portParam);
+    /* Retore the nic and OVS states */
+    vport->nicState = nicState;
+    vport->ovsState = ovsState;
+
+update_port_done:
+    NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
+    VPORT_PORT_EXIT(portParam);
+
+    /* Must always return success */
+    return NDIS_STATUS_SUCCESS;
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ * Function to process teardown of a port on the Hyper-V switch.
+ * --------------------------------------------------------------------------
+ */
 VOID
-OvsTeardownPort(POVS_SWITCH_CONTEXT switchContext,
-                PNDIS_SWITCH_PORT_PARAMETERS portParam)
+HvTeardownPort(POVS_SWITCH_CONTEXT switchContext,
+               PNDIS_SWITCH_PORT_PARAMETERS portParam)
 {
     POVS_VPORT_ENTRY vport;
     LOCK_STATE_EX lockState;
@@ -123,8 +255,7 @@ OvsTeardownPort(POVS_SWITCH_CONTEXT switchContext,
     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
                                             portParam->PortId, 0);
     if (vport) {
-        /* add assertion here
-         */
+        /* add assertion here */
         vport->portState = NdisSwitchPortStateTeardown;
         vport->ovsState = OVS_STATE_PORT_TEAR_DOWN;
     } else {
@@ -135,43 +266,55 @@ OvsTeardownPort(POVS_SWITCH_CONTEXT switchContext,
     VPORT_PORT_EXIT(portParam);
 }
 
-
-
+/*
+ * --------------------------------------------------------------------------
+ * Function to process deletion of a port on the Hyper-V switch.
+ * --------------------------------------------------------------------------
+ */
 VOID
-OvsDeletePort(POVS_SWITCH_CONTEXT switchContext,
-              PNDIS_SWITCH_PORT_PARAMETERS portParam)
+HvDeletePort(POVS_SWITCH_CONTEXT switchContext,
+             PNDIS_SWITCH_PORT_PARAMETERS portParams)
 {
     POVS_VPORT_ENTRY vport;
     LOCK_STATE_EX lockState;
 
-    VPORT_PORT_ENTER(portParam);
+    VPORT_PORT_ENTER(portParams);
 
     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
-                                            portParam->PortId, 0);
+                                            portParams->PortId, 0);
+
+    /*
+     * XXX: we can only destroy and remove the port if its datapath port
+     * counterpart was deleted. If the datapath port counterpart is present,
+     * we only mark the vport for deletion, so that a netlink command vport
+     * delete will delete the vport.
+    */
     if (vport) {
-        OvsRemoveAndDeleteVport(switchContext, vport);
+        OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
     } else {
         OVS_LOG_WARN("Vport not present.");
     }
     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
 
-    VPORT_PORT_EXIT(portParam);
+    VPORT_PORT_EXIT(portParams);
 }
 
 
 /*
- * Functions implemented in relaton to NDIS NIC manipulation.
+ * --------------------------------------------------------------------------
+ * Function to process addition of a NIC connection on the Hyper-V switch.
+ * XXX: Posting an event to DPIF is incorrect here. However, it might be useful
+ * to post an event to netdev-windows.c.
+ * --------------------------------------------------------------------------
  */
 NDIS_STATUS
-OvsCreateNic(POVS_SWITCH_CONTEXT switchContext,
-             PNDIS_SWITCH_NIC_PARAMETERS nicParam)
+HvCreateNic(POVS_SWITCH_CONTEXT switchContext,
+            PNDIS_SWITCH_NIC_PARAMETERS nicParam)
 {
     POVS_VPORT_ENTRY vport;
-    UINT32 portNo = 0;
-    UINT32 event = 0;
     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
-
+    IF_COUNTED_STRING portFriendlyName = {0};
     LOCK_STATE_EX lockState;
 
     VPORT_NIC_ENTER(nicParam);
@@ -186,8 +329,38 @@ OvsCreateNic(POVS_SWITCH_CONTEXT switchContext,
         goto done;
     }
 
+    if (OvsIsInternalNIC(nicParam->NicType) ||
+        OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) {
+        GetNICAlias(&nicParam->NetCfgInstanceId, &portFriendlyName);
+    }
+
     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
-    vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId, 0);
+    /*
+     * There can be one or more NICs for the external port. We create a vport
+     * structure for each such NIC, and each NIC inherits a lot of properties
+     * from the parent external port.
+     */
+    if (OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) {
+        NDIS_SWITCH_PORT_PARAMETERS portParam;
+        POVS_VPORT_ENTRY virtExtVport =
+            (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
+
+        ASSERT(virtExtVport);
+        ASSERT(OvsFindVportByPortIdAndNicIndex(switchContext,
+                                               nicParam->PortId,
+                                               nicParam->NicIndex) == NULL);
+        OvsCopyPortParamsFromVport(virtExtVport, &portParam);
+        NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
+        status = HvCreatePort(switchContext, &portParam,
+                              nicParam->NicIndex);
+        NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
+        if (status != NDIS_STATUS_SUCCESS) {
+            goto add_nic_done;
+        }
+    }
+
+    vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId,
+                                            nicParam->NicIndex);
     if (vport == NULL) {
         OVS_LOG_ERROR("Create NIC without Switch Port,"
                       " PortId: %x, NicIndex: %d",
@@ -195,36 +368,15 @@ OvsCreateNic(POVS_SWITCH_CONTEXT switchContext,
         status = NDIS_STATUS_INVALID_PARAMETER;
         goto add_nic_done;
     }
-
-    if (nicParam->NicType == NdisSwitchNicTypeExternal &&
-        nicParam->NicIndex != 0) {
-        POVS_VPORT_ENTRY virtVport =
-            (POVS_VPORT_ENTRY)switchContext->externalVport;
-        vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
-        if (vport == NULL) {
-            status = NDIS_STATUS_RESOURCES;
-            goto add_nic_done;
-        }
-        OvsInitPhysNicVport(vport, virtVport, nicParam->NicIndex);
-        status = OvsInitVportCommon(switchContext, vport);
-        if (status != NDIS_STATUS_SUCCESS) {
-            OvsFreeMemory(vport);
-            goto add_nic_done;
-        }
-    }
     OvsInitVportWithNicParam(switchContext, vport, nicParam);
-    portNo = vport->portNo;
-    if (vport->ovsState == OVS_STATE_CONNECTED) {
-        event = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP;
-    } else if (vport->ovsState == OVS_STATE_NIC_CREATED) {
-        event = OVS_EVENT_CONNECT;
+    if (OvsIsInternalNIC(nicParam->NicType) ||
+        OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) {
+        RtlCopyMemory(&vport->portFriendlyName, &portFriendlyName,
+                      sizeof portFriendlyName);
     }
 
 add_nic_done:
     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
-    if (portNo && event) {
-        OvsPostEvent(portNo, event);
-    }
 
 done:
     VPORT_NIC_EXIT(nicParam);
@@ -233,15 +385,18 @@ done:
     return status;
 }
 
-
-/* Mark already created NIC as connected. */
+/*
+ * --------------------------------------------------------------------------
+ * Function to process connection event of a NIC on the Hyper-V switch.
+ * --------------------------------------------------------------------------
+ */
 VOID
-OvsConnectNic(POVS_SWITCH_CONTEXT switchContext,
-              PNDIS_SWITCH_NIC_PARAMETERS nicParam)
+HvConnectNic(POVS_SWITCH_CONTEXT switchContext,
+             PNDIS_SWITCH_NIC_PARAMETERS nicParam)
 {
     LOCK_STATE_EX lockState;
     POVS_VPORT_ENTRY vport;
-    UINT32 portNo = 0;
+    UINT32 portNo;
 
     VPORT_NIC_ENTER(nicParam);
 
@@ -271,24 +426,30 @@ OvsConnectNic(POVS_SWITCH_CONTEXT switchContext,
 
     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
 
-    OvsPostEvent(portNo, OVS_EVENT_LINK_UP);
-
     if (nicParam->NicType == NdisSwitchNicTypeInternal) {
-        OvsInternalAdapterUp(portNo, &nicParam->NetCfgInstanceId);
+        OvsInternalAdapterUp(&nicParam->NetCfgInstanceId);
     }
 
 done:
     VPORT_NIC_EXIT(nicParam);
 }
 
+
+/*
+ * --------------------------------------------------------------------------
+ * Function to process updates to a NIC on the Hyper-V switch.
+ * --------------------------------------------------------------------------
+ */
 VOID
-OvsUpdateNic(POVS_SWITCH_CONTEXT switchContext,
-             PNDIS_SWITCH_NIC_PARAMETERS nicParam)
+HvUpdateNic(POVS_SWITCH_CONTEXT switchContext,
+            PNDIS_SWITCH_NIC_PARAMETERS nicParam)
 {
     POVS_VPORT_ENTRY vport;
     LOCK_STATE_EX lockState;
-
-    UINT32 status = 0, portNo = 0;
+    UINT32 event = 0;
+    IF_COUNTED_STRING portFriendlyName = {0};
+    BOOLEAN nameChanged = FALSE;
+    BOOLEAN aliasLookup = FALSE;
 
     VPORT_NIC_ENTER(nicParam);
 
@@ -300,11 +461,19 @@ OvsUpdateNic(POVS_SWITCH_CONTEXT switchContext,
         goto update_nic_done;
     }
 
+    /* GetNICAlias() must be called outside of a lock. */
+    if (nicParam->NicType == NdisSwitchNicTypeInternal ||
+        OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) {
+        GetNICAlias(&nicParam->NetCfgInstanceId, &portFriendlyName);
+        aliasLookup = TRUE;
+    }
+
     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
     vport = OvsFindVportByPortIdAndNicIndex(switchContext,
                                             nicParam->PortId,
                                             nicParam->NicIndex);
     if (vport == NULL) {
+        NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
         OVS_LOG_WARN("Vport search failed.");
         goto update_nic_done;
     }
@@ -313,12 +482,21 @@ OvsUpdateNic(POVS_SWITCH_CONTEXT switchContext,
     case NdisSwitchNicTypeInternal:
         RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId,
                       sizeof (GUID));
+        if (aliasLookup) {
+            if (RtlCompareMemory(&vport->portFriendlyName,
+                    &portFriendlyName, vport->portFriendlyName.Length) !=
+                    vport->portFriendlyName.Length) {
+                RtlCopyMemory(&vport->portFriendlyName, &portFriendlyName,
+                    sizeof portFriendlyName);
+                nameChanged = TRUE;
+            }
+        }
         break;
     case NdisSwitchNicTypeSynthetic:
     case NdisSwitchNicTypeEmulated:
         if (!RtlEqualMemory(vport->vmMacAddress, nicParam->VMMacAddress,
                            sizeof (vport->vmMacAddress))) {
-            status |= OVS_EVENT_MAC_CHANGE;
+            event |= OVS_EVENT_MAC_CHANGE;
             RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress,
                           sizeof (vport->vmMacAddress));
         }
@@ -330,39 +508,59 @@ OvsUpdateNic(POVS_SWITCH_CONTEXT switchContext,
                         sizeof (vport->permMacAddress))) {
         RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress,
                       sizeof (vport->permMacAddress));
-        status |= OVS_EVENT_MAC_CHANGE;
+        event |= OVS_EVENT_MAC_CHANGE;
     }
     if (!RtlEqualMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
                         sizeof (vport->currMacAddress))) {
         RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress,
                       sizeof (vport->currMacAddress));
-        status |= OVS_EVENT_MAC_CHANGE;
+        event |= OVS_EVENT_MAC_CHANGE;
     }
 
     if (vport->mtu != nicParam->MTU) {
         vport->mtu = nicParam->MTU;
-        status |= OVS_EVENT_MTU_CHANGE;
+        event |= OVS_EVENT_MTU_CHANGE;
     }
     vport->numaNodeId = nicParam->NumaNodeId;
-    portNo = vport->portNo;
 
-    NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
-    if (status && portNo) {
-        OvsPostEvent(portNo, status);
+    if (nameChanged) {
+        OVS_EVENT_ENTRY event;
+        event.portNo = vport->portNo;
+        event.ovsType = vport->ovsType;
+        event.upcallPid = vport->upcallPid;
+        RtlCopyMemory(&event.ovsName, &vport->ovsName, sizeof event.ovsName);
+        event.type = OVS_EVENT_LINK_DOWN;
+        OvsRemoveAndDeleteVport(NULL, switchContext, vport, FALSE, TRUE);
+        OvsPostEvent(&event);
     }
+
+    NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
+
+    /*
+     * XXX: Not sure what kind of event to post here. DPIF is not interested in
+     * changes to MAC address. Netdev-windows might be intrested, though.
+     * That said, if the name chagnes, not clear what kind of event to be
+     * posted. We might have to delete the vport, and have userspace recreate
+     * it.
+     */
+
 update_nic_done:
     VPORT_NIC_EXIT(nicParam);
 }
 
-
+/*
+ * --------------------------------------------------------------------------
+ * Function to process disconnect event of a NIC on the Hyper-V switch.
+ * --------------------------------------------------------------------------
+ */
 VOID
-OvsDisconnectNic(POVS_SWITCH_CONTEXT switchContext,
-                 PNDIS_SWITCH_NIC_PARAMETERS nicParam)
+HvDisconnectNic(POVS_SWITCH_CONTEXT switchContext,
+                PNDIS_SWITCH_NIC_PARAMETERS nicParam)
 {
     POVS_VPORT_ENTRY vport;
-    UINT32 portNo = 0;
     LOCK_STATE_EX lockState;
     BOOLEAN isInternalPort = FALSE;
+    OVS_EVENT_ENTRY event;
 
     VPORT_NIC_ENTER(nicParam);
 
@@ -387,15 +585,26 @@ OvsDisconnectNic(POVS_SWITCH_CONTEXT switchContext,
 
     vport->nicState = NdisSwitchNicStateDisconnected;
     vport->ovsState = OVS_STATE_NIC_CREATED;
-    portNo = vport->portNo;
 
     if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) {
         isInternalPort = TRUE;
     }
 
-    NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
+    event.portNo = vport->portNo;
+    event.ovsType = vport->ovsType;
+    event.upcallPid = vport->upcallPid;
+    RtlCopyMemory(&event.ovsName, &vport->ovsName, sizeof event.ovsName);
+    event.type = OVS_EVENT_LINK_DOWN;
 
-    OvsPostEvent(portNo, OVS_EVENT_LINK_DOWN);
+    /*
+     * Delete the port from the hash tables accessible to userspace. After this
+     * point, userspace should not be able to access this port.
+     */
+    if (OvsIsRealExternalVport(vport)) {
+        OvsRemoveAndDeleteVport(NULL, switchContext, vport, FALSE, TRUE);
+        OvsPostEvent(&event);
+    }
+    NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
 
     if (isInternalPort) {
         OvsInternalAdapterDown();
@@ -405,14 +614,17 @@ done:
     VPORT_NIC_EXIT(nicParam);
 }
 
-
+/*
+ * --------------------------------------------------------------------------
+ * Function to process delete event of a NIC on the Hyper-V switch.
+ * --------------------------------------------------------------------------
+ */
 VOID
-OvsDeleteNic(POVS_SWITCH_CONTEXT switchContext,
-             PNDIS_SWITCH_NIC_PARAMETERS nicParam)
+HvDeleteNic(POVS_SWITCH_CONTEXT switchContext,
+            PNDIS_SWITCH_NIC_PARAMETERS nicParam)
 {
     LOCK_STATE_EX lockState;
     POVS_VPORT_ENTRY vport;
-    UINT32 portNo = 0;
 
     VPORT_NIC_ENTER(nicParam);
     /* Wait for lists to be initialized. */
@@ -434,22 +646,20 @@ OvsDeleteNic(POVS_SWITCH_CONTEXT switchContext,
         goto done;
     }
 
-    portNo = vport->portNo;
-    if (vport->portType == NdisSwitchPortTypeExternal &&
-        vport->nicIndex != 0) {
-        OvsRemoveAndDeleteVport(switchContext, vport);
-    }
     vport->nicState = NdisSwitchNicStateUnknown;
     vport->ovsState = OVS_STATE_PORT_CREATED;
 
+    if (OvsIsRealExternalVport(vport)) {
+        /* This vport was created in HvCreateNic(). */
+        OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE);
+    }
+
     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
-    OvsPostEvent(portNo, OVS_EVENT_DISCONNECT);
 
 done:
     VPORT_NIC_EXIT(nicParam);
 }
 
-
 /*
  * OVS Vport related functionality.
  */
@@ -457,67 +667,178 @@ POVS_VPORT_ENTRY
 OvsFindVportByPortNo(POVS_SWITCH_CONTEXT switchContext,
                      UINT32 portNo)
 {
-    if (OVS_VPORT_INDEX(portNo) < OVS_MAX_VPORT_ARRAY_SIZE) {
-        if (OVS_IS_VPORT_ENTRY_NULL(switchContext, OVS_VPORT_INDEX(portNo))) {
-            return NULL;
-        } else {
-            POVS_VPORT_ENTRY vport;
-            vport = (POVS_VPORT_ENTRY)
-                     switchContext->vportArray[OVS_VPORT_INDEX(portNo)];
-            return vport->portNo == portNo ? vport : NULL;
+    POVS_VPORT_ENTRY vport;
+    PLIST_ENTRY head, link;
+    UINT32 hash = OvsJhashBytes((const VOID *)&portNo, sizeof(portNo),
+                                OVS_HASH_BASIS);
+    head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
+    LIST_FORALL(head, link) {
+        vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
+        if (vport->portNo == portNo) {
+            return vport;
+        }
+    }
+    return NULL;
+}
+
+
+POVS_VPORT_ENTRY
+OvsFindTunnelVportByDstPort(POVS_SWITCH_CONTEXT switchContext,
+                            UINT16 dstPort,
+                            OVS_VPORT_TYPE ovsPortType)
+{
+    POVS_VPORT_ENTRY vport;
+    PLIST_ENTRY head, link;
+    UINT32 hash = OvsJhashBytes((const VOID *)&dstPort, sizeof(dstPort),
+                                OVS_HASH_BASIS);
+    head = &(switchContext->tunnelVportsArray[hash & OVS_VPORT_MASK]);
+    LIST_FORALL(head, link) {
+        vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, tunnelVportLink);
+        if (GetPortFromPriv(vport) == dstPort &&
+            vport->ovsType == ovsPortType) {
+            return vport;
         }
     }
     return NULL;
 }
 
+POVS_VPORT_ENTRY
+OvsFindTunnelVportByPortType(POVS_SWITCH_CONTEXT switchContext,
+                             OVS_VPORT_TYPE ovsPortType)
+{
+    POVS_VPORT_ENTRY vport;
+    PLIST_ENTRY head, link;
+    UINT16 dstPort = 0;
+    UINT32 hash = OvsJhashBytes((const VOID *)&dstPort, sizeof(dstPort),
+                                OVS_HASH_BASIS);
+    head = &(switchContext->tunnelVportsArray[hash & OVS_VPORT_MASK]);
+    LIST_FORALL(head, link) {
+        vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, tunnelVportLink);
+        if (vport->ovsType == ovsPortType) {
+            return vport;
+        }
+    }
+    return NULL;
+}
 
 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);
-    head = &(switchContext->nameHashArray[hash & OVS_VPORT_MASK]);
+    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, nameLink);
-        if (vport->ovsNameLen == length &&
-            RtlEqualMemory(name, vport->ovsName, length)) {
+        vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, ovsNameLink);
+        if (!strcmp(name, vport->ovsName)) {
             return vport;
         }
     }
+
     return NULL;
 }
 
+/* OvsFindVportByHvName: "name" is assumed to be null-terminated */
+POVS_VPORT_ENTRY
+OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
+                      PWSTR wsName, SIZE_T wstrSize)
+{
+    POVS_VPORT_ENTRY vport = NULL;
+    PLIST_ENTRY head, link;
+    UINT i;
+
+    for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
+        head = &(switchContext->portIdHashArray[i]);
+        LIST_FORALL(head, link) {
+            vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
+
+            /*
+             * NOTE about portFriendlyName:
+             * If the string is NULL-terminated, the Length member does not
+             * include the terminating NULL character.
+             */
+            if (vport->portFriendlyName.Length == wstrSize &&
+                RtlEqualMemory(wsName, vport->portFriendlyName.String,
+                               vport->portFriendlyName.Length)) {
+                goto Cleanup;
+            }
+
+            vport = NULL;
+        }
+    }
+
+    /*
+     * Look in the list of ports that were added from the Hyper-V switch and
+     * deleted.
+     */
+    if (vport == NULL) {
+        for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
+            head = &(switchContext->portNoHashArray[i]);
+            LIST_FORALL(head, link) {
+                vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
+                if (vport->portFriendlyName.Length == wstrSize &&
+                    RtlEqualMemory(wsName, vport->portFriendlyName.String,
+                                   vport->portFriendlyName.Length)) {
+                    goto Cleanup;
+                }
+
+                vport = NULL;
+            }
+        }
+    }
+
+Cleanup:
+    return vport;
+}
+
+POVS_VPORT_ENTRY
+OvsFindVportByHvNameA(POVS_SWITCH_CONTEXT switchContext,
+                      PSTR name)
+{
+    POVS_VPORT_ENTRY vport = NULL;
+    /* 'portFriendlyName' is not NUL-terminated. */
+    SIZE_T length = strlen(name);
+    SIZE_T wstrSize = length * sizeof(WCHAR);
+    UINT i;
+
+    PWSTR wsName = OvsAllocateMemoryWithTag(wstrSize, OVS_VPORT_POOL_TAG);
+    if (!wsName) {
+        return NULL;
+    }
+    for (i = 0; i < length; i++) {
+        wsName[i] = name[i];
+    }
+    vport = OvsFindVportByHvNameW(switchContext, wsName, wstrSize);
+    OvsFreeMemoryWithTag(wsName, OVS_VPORT_POOL_TAG);
+    return vport;
+}
+
 POVS_VPORT_ENTRY
 OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT switchContext,
                                 NDIS_SWITCH_PORT_ID portId,
                                 NDIS_SWITCH_NIC_INDEX index)
 {
-    if (portId == switchContext->externalPortId) {
-        if (index == 0) {
-            return (POVS_VPORT_ENTRY)switchContext->externalVport;
-        } else if (index > OVS_MAX_PHYS_ADAPTERS) {
-            return NULL;
-        }
-        if (OVS_IS_VPORT_ENTRY_NULL(switchContext,
-                                    index + OVS_EXTERNAL_VPORT_START)) {
-           return NULL;
-        } else {
-           return (POVS_VPORT_ENTRY)switchContext->vportArray[
-                            index + OVS_EXTERNAL_VPORT_START];
-        }
-    } else if (switchContext->internalPortId == portId) {
+    if (switchContext->virtualExternalVport &&
+            portId == switchContext->virtualExternalPortId &&
+            index == switchContext->virtualExternalVport->nicIndex) {
+        return (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
+    } else if (switchContext->internalVport &&
+               portId == switchContext->internalPortId &&
+               index == switchContext->internalVport->nicIndex) {
         return (POVS_VPORT_ENTRY)switchContext->internalVport;
     } else {
         PLIST_ENTRY head, link;
         POVS_VPORT_ENTRY vport;
         UINT32 hash;
         hash = OvsJhashWords((UINT32 *)&portId, 1, OVS_HASH_BASIS);
-        head = &(switchContext->portHashArray[hash & OVS_VPORT_MASK]);
+        head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
         LIST_FORALL(head, link) {
-            vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portLink);
+            vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
             if (portId == vport->portId && index == vport->nicIndex) {
                 return vport;
             }
@@ -526,82 +847,24 @@ OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT switchContext,
     }
 }
 
-static UINT32
-OvsGetVportNo(POVS_SWITCH_CONTEXT switchContext,
-              UINT32 nicIndex,
-              OVS_VPORT_TYPE ovsType,
-              BOOLEAN isExternal)
+POVS_VPORT_ENTRY
+OvsAllocateVport(VOID)
 {
-    UINT32 index = 0xffffff, i = 0;
-    UINT64 gen;
-
-    if (isExternal) {
-        if (nicIndex == 0) {
-            return 0;  // not a valid portNo
-        } else if (nicIndex > OVS_MAX_PHYS_ADAPTERS) {
-            return 0;
-        } else {
-            index = nicIndex + OVS_EXTERNAL_VPORT_START;
-        }
+    POVS_VPORT_ENTRY vport;
+    vport = (POVS_VPORT_ENTRY)OvsAllocateMemoryWithTag(
+        sizeof(OVS_VPORT_ENTRY), OVS_VPORT_POOL_TAG);
+    if (vport == NULL) {
+        return NULL;
     }
+    RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY));
+    vport->ovsState = OVS_STATE_UNKNOWN;
+    vport->isAbsentOnHv = FALSE;
+    vport->portNo = OVS_DPPORT_NUMBER_INVALID;
 
-    switch (ovsType) {
-    case OVS_VPORT_TYPE_INTERNAL:
-        index = OVS_INTERNAL_VPORT_DEFAULT_INDEX;
-        break;
-    case OVS_VPORT_TYPE_NETDEV:
-        index = switchContext->lastPortIndex + 1;
-        if (index == OVS_MAX_VPORT_ARRAY_SIZE) {
-            index = OVS_VM_VPORT_START;
-        }
-        while (!OVS_IS_VPORT_ENTRY_NULL(switchContext, index) &&
-               i < (OVS_MAX_VPORT_ARRAY_SIZE - OVS_VM_VPORT_START)) {
-            index++;
-            i++;
-            if (index == OVS_MAX_VPORT_ARRAY_SIZE) {
-                index = OVS_VM_VPORT_START;
-            }
-        }
-        if (i == (OVS_MAX_VPORT_ARRAY_SIZE - OVS_VM_VPORT_START)) {
-            return 0; // not available
-        }
-        switchContext->lastPortIndex = index;
-        break;
-    case OVS_VPORT_TYPE_GRE:
-        index = OVS_GRE_VPORT_INDEX;
-        break;
-    case OVS_VPORT_TYPE_GRE64:
-        index = OVS_GRE64_VPORT_INDEX;
-        break;
-    case OVS_VPORT_TYPE_VXLAN:
-        index = OVS_VXLAN_VPORT_INDEX;
-        break;
-    default:
-        ASSERT(isExternal);
-    }
-    if (index > OVS_MAX_VPORT_ARRAY_SIZE) {
-        return 0;
-    }
-    gen = (UINT64)switchContext->vportArray[index];
-    if (gen > 0xff) {
-        return 0;
-    } else if (gen == 0) {
-        gen++;
-    }
-    return OVS_VPORT_PORT_NO(index, (UINT32)gen);
-}
+    InitializeListHead(&vport->ovsNameLink);
+    InitializeListHead(&vport->portIdLink);
+    InitializeListHead(&vport->portNoLink);
 
-
-static POVS_VPORT_ENTRY
-OvsAllocateVport(VOID)
-{
-    POVS_VPORT_ENTRY vport;
-    vport = (POVS_VPORT_ENTRY)OvsAllocateMemory(sizeof (OVS_VPORT_ENTRY));
-    if (vport == NULL) {
-        return NULL;
-    }
-    RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY));
-    vport->ovsState = OVS_STATE_UNKNOWN;
     return vport;
 }
 
@@ -609,12 +872,12 @@ static VOID
 OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
                           PNDIS_SWITCH_PORT_PARAMETERS portParam)
 {
-    vport->isValidationPort = portParam->IsValidationPort;
     vport->portType = portParam->PortType;
     vport->portState = portParam->PortState;
     vport->portId = portParam->PortId;
     vport->nicState = NdisSwitchNicStateUnknown;
     vport->isExternal = FALSE;
+    vport->isBridgeInternal = FALSE;
 
     switch (vport->portType) {
     case NdisSwitchPortTypeExternal:
@@ -629,8 +892,13 @@ OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
         vport->ovsType = OVS_VPORT_TYPE_NETDEV;
         break;
     }
-    RtlCopyMemory(&vport->portName, &portParam->PortName,
+    RtlCopyMemory(&vport->hvPortName, &portParam->PortName,
                   sizeof (NDIS_SWITCH_PORT_NAME));
+    /* For external and internal ports, 'portFriendlyName' is overwritten
+     * later. */
+    RtlCopyMemory(&vport->portFriendlyName, &portParam->PortFriendlyName,
+                  sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
+
     switch (vport->portState) {
     case NdisSwitchPortStateCreated:
         vport->ovsState = OVS_STATE_PORT_CREATED;
@@ -675,6 +943,7 @@ OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
     vport->mtu = nicParam->MTU;
     vport->nicState = nicParam->NicState;
     vport->nicIndex = nicParam->NicIndex;
+    vport->nicType = nicParam->NicType;
     vport->numaNodeId = nicParam->NumaNodeId;
 
     switch (vport->nicState) {
@@ -693,133 +962,430 @@ OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext,
     }
 }
 
+/*
+ * --------------------------------------------------------------------------
+ * Populates 'portParam' based on 'vport'.
+ * --------------------------------------------------------------------------
+ */
 static VOID
-OvsInitPhysNicVport(POVS_VPORT_ENTRY vport,
-                    POVS_VPORT_ENTRY virtVport,
-                    UINT32 nicIndex)
+OvsCopyPortParamsFromVport(POVS_VPORT_ENTRY vport,
+                           PNDIS_SWITCH_PORT_PARAMETERS portParam)
 {
-    vport->isValidationPort = virtVport->isValidationPort;
-    vport->portType = virtVport->portType;
-    vport->portState = virtVport->portState;
-    vport->portId = virtVport->portId;
-    vport->nicState = NdisSwitchNicStateUnknown;
-    vport->ovsType = OVS_VPORT_TYPE_NETDEV;
-    vport->isExternal = TRUE;
-    vport->nicIndex = (NDIS_SWITCH_NIC_INDEX)nicIndex;
-    RtlCopyMemory(&vport->portName, &virtVport->portName,
+    portParam->Flags = 0;
+    portParam->PortId = vport->portId;
+    RtlCopyMemory(&portParam->PortName, &vport->hvPortName,
                   sizeof (NDIS_SWITCH_PORT_NAME));
-    vport->ovsState = OVS_STATE_PORT_CREATED;
+    RtlCopyMemory(&portParam->PortFriendlyName,
+                  &vport->portFriendlyName,
+                  sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME));
+    portParam->PortType = vport->portType;
+    portParam->IsValidationPort = FALSE;
+    portParam->PortState = vport->portState;
 }
-static NDIS_STATUS
-OvsInitVportCommon(POVS_SWITCH_CONTEXT switchContext,
-POVS_VPORT_ENTRY vport)
+
+/*
+ * --------------------------------------------------------------------------
+ * Initializes a tunnel vport.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsInitTunnelVport(PVOID userContext,
+                   POVS_VPORT_ENTRY vport,
+                   OVS_VPORT_TYPE ovsType,
+                   UINT16 dstPort)
 {
-    UINT32 hash;
-    size_t len;
-    if (vport->portType != NdisSwitchPortTypeExternal ||
-        vport->nicIndex != 0) {
-        vport->portNo = OvsGetVportNo(switchContext, vport->nicIndex,
-            vport->ovsType, vport->portType == NdisSwitchPortTypeExternal);
-        if (vport->portNo == 0) {
-            return NDIS_STATUS_RESOURCES;
+    NTSTATUS status = STATUS_SUCCESS;
+    POVS_USER_PARAMS_CONTEXT usrParamsCtx =
+        (POVS_USER_PARAMS_CONTEXT)userContext;
+
+    vport->isBridgeInternal = FALSE;
+    vport->ovsType = ovsType;
+    vport->ovsState = OVS_STATE_PORT_CREATED;
+    switch (ovsType) {
+    case OVS_VPORT_TYPE_GRE:
+        status = OvsInitGreTunnel(vport);
+        break;
+    case OVS_VPORT_TYPE_VXLAN:
+    {
+        POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
+
+        tunnelContext = OvsAllocateMemoryWithTag(sizeof(*tunnelContext),
+                                                 OVS_VPORT_POOL_TAG);
+        if (tunnelContext == NULL) {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            break;
+        }
+        tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
+        tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
+        tunnelContext->outputLength = usrParamsCtx->outputLength;
+        tunnelContext->vport = vport;
+
+        status = OvsInitVxlanTunnel(usrParamsCtx->irp,
+                                    vport,
+                                    dstPort,
+                                    OvsTunnelVportPendingInit,
+                                    (PVOID)tunnelContext);
+        if (status != STATUS_PENDING) {
+            OvsFreeMemoryWithTag(tunnelContext, OVS_VPORT_POOL_TAG);
+            tunnelContext = NULL;
         }
-        ASSERT(OVS_IS_VPORT_ENTRY_NULL(switchContext,
-            OVS_VPORT_INDEX(vport->portNo)));
+        break;
+    }
+    case OVS_VPORT_TYPE_STT:
+        status = OvsInitSttTunnel(vport, dstPort);
+        break;
+    default:
+        ASSERT(0);
+    }
+    return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * Initializes a bridge internal vport ie. a port of type
+ * OVS_VPORT_TYPE_INTERNAL but not present on the Hyper-V switch.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport)
+{
+    vport->isBridgeInternal = TRUE;
+    vport->ovsType = OVS_VPORT_TYPE_INTERNAL;
+    /* Mark the status to be connected, since there is no other initialization
+     * for this port. */
+    vport->ovsState = OVS_STATE_CONNECTED;
+    return STATUS_SUCCESS;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * For external and internal vports 'portFriendlyName' parameter, provided by
+ * Hyper-V, is overwritten with the interface alias name.
+ * --------------------------------------------------------------------------
+ */
+static NTSTATUS
+GetNICAlias(GUID *netCfgInstanceId,
+            IF_COUNTED_STRING *portFriendlyName)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    WCHAR interfaceName[IF_MAX_STRING_SIZE] = { 0 };
+    NET_LUID interfaceLuid = { 0 };
+    size_t len = 0;
 
-        switchContext->vportArray[OVS_VPORT_INDEX(vport->portNo)] = vport;
+    status = ConvertInterfaceGuidToLuid(netCfgInstanceId,
+                                        &interfaceLuid);
+    if (status == STATUS_SUCCESS) {
+        /*
+         * Must be called from PASSIVE_LEVEL. Resulted in a
+         * STATUS_INVALID_DEVICE_REQUEST if not.
+         */
+        status = ConvertInterfaceLuidToAlias(&interfaceLuid, interfaceName,
+                                             IF_MAX_STRING_SIZE + 1);
+        if (status == STATUS_SUCCESS) {
+            RtlStringCbPrintfW(portFriendlyName->String,
+                               IF_MAX_STRING_SIZE, L"%s", interfaceName);
+            RtlStringCbLengthW(portFriendlyName->String, IF_MAX_STRING_SIZE,
+                               &len);
+            portFriendlyName->Length = (USHORT)len;
+        } else {
+            OVS_LOG_ERROR("Fail to convert interface LUID to alias, status: %x",
+                status);
+        }
+    } else {
+        OVS_LOG_ERROR("Fail to convert interface GUID to LUID, status: %x",
+                      status);
     }
+
+    return status;
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ * Functionality common to any port on the Hyper-V switch. This function is not
+ * to be called for a port that is not on the Hyper-V switch.
+ *
+ * Inserts the port into 'portIdHashArray' and caches the pointer in the
+ * 'switchContext' if needed.
+ * --------------------------------------------------------------------------
+ */
+static VOID
+UpdateSwitchCtxWithVport(POVS_SWITCH_CONTEXT switchContext,
+                         POVS_VPORT_ENTRY vport,
+                         BOOLEAN newPort)
+{
+    UINT32 hash;
+
     switch (vport->portType) {
     case NdisSwitchPortTypeExternal:
         if (vport->nicIndex == 0) {
-            switchContext->externalPortId = vport->portId;
-            switchContext->externalVport = vport;
-            RtlStringCbPrintfA(vport->ovsName, OVS_MAX_PORT_NAME_LENGTH - 1,
-                "external.virtualAdapter");
-        }
-        else {
+            switchContext->virtualExternalPortId = vport->portId;
+            switchContext->virtualExternalVport = vport;
+        } else {
             switchContext->numPhysicalNics++;
-            RtlStringCbPrintfA(vport->ovsName, OVS_MAX_PORT_NAME_LENGTH - 1,
-                "external.%lu", (UINT32)vport->nicIndex);
         }
         break;
     case NdisSwitchPortTypeInternal:
+        ASSERT(vport->isBridgeInternal == FALSE);
         switchContext->internalPortId = vport->portId;
         switchContext->internalVport = vport;
-        RtlStringCbPrintfA(vport->ovsName, OVS_MAX_PORT_NAME_LENGTH - 1,
-            "internal");
         break;
     case NdisSwitchPortTypeSynthetic:
-        RtlStringCbPrintfA(vport->ovsName, OVS_MAX_PORT_NAME_LENGTH - 1,
-            "vmNICSyn.%lx", vport->portNo);
-        break;
     case NdisSwitchPortTypeEmulated:
-        RtlStringCbPrintfA(vport->ovsName, OVS_MAX_PORT_NAME_LENGTH - 1,
-            "vmNICEmu.%lx", vport->portNo);
         break;
     }
-    StringCbLengthA(vport->ovsName, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
-    vport->ovsNameLen = (UINT32)len;
+
+    /*
+     * It is important to not insert vport corresponding to virtual external
+     * port into the 'portIdHashArray' since the port should not be exposed to
+     * OVS userspace.
+     */
     if (vport->portType == NdisSwitchPortTypeExternal &&
         vport->nicIndex == 0) {
-        return NDIS_STATUS_SUCCESS;
+        return;
     }
-    hash = OvsJhashBytes(vport->ovsName, vport->ovsNameLen, OVS_HASH_BASIS);
-    InsertHeadList(&switchContext->nameHashArray[hash & OVS_VPORT_MASK],
-        &vport->nameLink);
+
+    /*
+     * NOTE: OvsJhashWords has portId as "1" word. This should be ok, even
+     * though sizeof(NDIS_SWITCH_PORT_ID) = 4, not 2, because the
+     * hyper-v switch seems to use only 2 bytes out of 4.
+     */
     hash = OvsJhashWords(&vport->portId, 1, OVS_HASH_BASIS);
-    InsertHeadList(&switchContext->portHashArray[hash & OVS_VPORT_MASK],
-        &vport->portLink);
-    switchContext->numVports++;
-    return NDIS_STATUS_SUCCESS;
+    InsertHeadList(&switchContext->portIdHashArray[hash & OVS_VPORT_MASK],
+                   &vport->portIdLink);
+    if (newPort) {
+        switchContext->numHvVports++;
+    }
+    return;
 }
 
-
-static VOID
-OvsRemoveAndDeleteVport(POVS_SWITCH_CONTEXT switchContext,
-                        POVS_VPORT_ENTRY vport)
+/*
+ * --------------------------------------------------------------------------
+ * Functionality common to any port added from OVS userspace.
+ *
+ * Inserts the port into 'portNoHashArray', 'ovsPortNameHashArray' and in
+ * 'tunnelVportsArray' if appropriate.
+ * --------------------------------------------------------------------------
+ */
+NDIS_STATUS
+InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
+                   POVS_VPORT_ENTRY vport)
 {
-    UINT64 gen = vport->portNo >> 24;
+    UINT32 hash;
 
-    if (vport->isExternal) {
-        if (vport->nicIndex == 0) {
-            ASSERT(switchContext->numPhysicalNics == 0);
-            switchContext->externalPortId = 0;
-            switchContext->externalVport = NULL;
-            OvsFreeMemory(vport);
-            return;
-        } else {
-            ASSERT(switchContext->numPhysicalNics);
-            switchContext->numPhysicalNics--;
+    switch(vport->ovsType) {
+    case OVS_VPORT_TYPE_GRE:
+    case OVS_VPORT_TYPE_VXLAN:
+    case OVS_VPORT_TYPE_STT:
+    {
+        UINT16 dstPort = GetPortFromPriv(vport);
+        hash = OvsJhashBytes(&dstPort,
+                             sizeof(dstPort),
+                             OVS_HASH_BASIS);
+        InsertHeadList(
+            &gOvsSwitchContext->tunnelVportsArray[hash & OVS_VPORT_MASK],
+            &vport->tunnelVportLink);
+        switchContext->numNonHvVports++;
+        break;
+    }
+    case OVS_VPORT_TYPE_INTERNAL:
+        if (vport->isBridgeInternal) {
+            switchContext->numNonHvVports++;
         }
+    default:
+        break;
     }
 
+    /*
+     * 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, strlen(vport->ovsName) + 1,
+                         OVS_HASH_BASIS);
+    InsertHeadList(
+        &gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK],
+        &vport->ovsNameLink);
+
+    return STATUS_SUCCESS;
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ * Provides functionality that is partly complementatry to
+ * InitOvsVportCommon()/UpdateSwitchCtxWithVport().
+ *
+ * 'hvDelete' indicates if caller is removing the vport as a result of the
+ * port being removed on the Hyper-V switch.
+ * 'ovsDelete' indicates if caller is removing the vport as a result of the
+ * port being removed from OVS userspace.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsRemoveAndDeleteVport(PVOID usrParamsContext,
+                        POVS_SWITCH_CONTEXT switchContext,
+                        POVS_VPORT_ENTRY vport,
+                        BOOLEAN hvDelete,
+                        BOOLEAN ovsDelete)
+{
+    POVS_USER_PARAMS_CONTEXT usrParamsCtx =
+        (POVS_USER_PARAMS_CONTEXT)usrParamsContext;
+    BOOLEAN hvSwitchPort = FALSE;
+    BOOLEAN deletedOnOvs = FALSE;
+    BOOLEAN deletedOnHv = FALSE;
+
     switch (vport->ovsType) {
     case OVS_VPORT_TYPE_INTERNAL:
-        switchContext->internalPortId = 0;
-        switchContext->internalVport = NULL;
-        OvsInternalAdapterDown();
+        if (!vport->isBridgeInternal) {
+            if (hvDelete && vport->isAbsentOnHv == FALSE) {
+                switchContext->internalPortId = 0;
+                switchContext->internalVport = NULL;
+                OvsInternalAdapterDown();
+            }
+            hvSwitchPort = TRUE;
+        }
         break;
     case OVS_VPORT_TYPE_VXLAN:
-        OvsCleanupVxlanTunnel(vport);
+    {
+        NTSTATUS status;
+        status = OvsRemoveTunnelVport(usrParamsCtx, switchContext, vport,
+                                      hvDelete, ovsDelete);
+        if (status != STATUS_SUCCESS) {
+            return status;
+        }
+    }
+    case OVS_VPORT_TYPE_STT:
+        OvsCleanupSttTunnel(vport);
         break;
     case OVS_VPORT_TYPE_GRE:
-    case OVS_VPORT_TYPE_GRE64:
+        OvsCleanupGreTunnel(vport);
         break;
     case OVS_VPORT_TYPE_NETDEV:
+        if (vport->isExternal) {
+            if (vport->nicIndex == 0) {
+                /* Such a vport is not part of any of the hash tables, since it
+                 * is not exposed to userspace. See Vport.h for explanation. */
+                ASSERT(hvDelete == TRUE);
+                ASSERT(switchContext->numPhysicalNics == 0);
+                switchContext->virtualExternalPortId = 0;
+                switchContext->virtualExternalVport = NULL;
+                OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
+                return STATUS_SUCCESS;
+            }
+        }
+        hvSwitchPort = TRUE;
     default:
         break;
     }
 
-    RemoveEntryList(&vport->nameLink);
-    RemoveEntryList(&vport->portLink);
-    gen = (gen + 1) & 0xff;
-    switchContext->vportArray[OVS_VPORT_INDEX(vport->portNo)] =
-                     (PVOID)(UINT64)gen;
-    switchContext->numVports--;
-    OvsFreeMemory(vport);
+    /*
+     * 'hvDelete' == TRUE indicates that the port should be removed from the
+     * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port
+     * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'.
+     *
+     * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller.
+     */
+    if (vport->isAbsentOnHv == TRUE) {
+        deletedOnHv = TRUE;
+    }
+    if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
+        deletedOnOvs = TRUE;
+    }
+
+    if (hvDelete && !deletedOnHv) {
+        vport->isAbsentOnHv = TRUE;
+
+        if (vport->isExternal) {
+            ASSERT(vport->nicIndex != 0);
+            ASSERT(switchContext->numPhysicalNics);
+            switchContext->numPhysicalNics--;
+        }
+
+        /* Remove the port from the relevant lists. */
+        RemoveEntryList(&vport->portIdLink);
+        InitializeListHead(&vport->portIdLink);
+        deletedOnHv = TRUE;
+    }
+    if (ovsDelete && !deletedOnOvs) {
+        vport->portNo = OVS_DPPORT_NUMBER_INVALID;
+        vport->ovsName[0] = '\0';
+
+        /* Remove the port from the relevant lists. */
+        RemoveEntryList(&vport->ovsNameLink);
+        InitializeListHead(&vport->ovsNameLink);
+        RemoveEntryList(&vport->portNoLink);
+        InitializeListHead(&vport->portNoLink);
+        if (OVS_VPORT_TYPE_VXLAN == vport->ovsType ||
+            OVS_VPORT_TYPE_STT == vport->ovsType   ||
+            OVS_VPORT_TYPE_GRE == vport->ovsType) {
+            RemoveEntryList(&vport->tunnelVportLink);
+            InitializeListHead(&vport->tunnelVportLink);
+        }
+
+        deletedOnOvs = TRUE;
+    }
+
+    /*
+     * Deallocate the port if it has been deleted on the Hyper-V switch as well
+     * as OVS userspace.
+     */
+    if (deletedOnHv && deletedOnOvs) {
+        if (hvSwitchPort) {
+            switchContext->numHvVports--;
+        } else {
+            switchContext->numNonHvVports--;
+        }
+        OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
+    }
+
+    return STATUS_SUCCESS;
 }
 
+static NTSTATUS
+OvsRemoveTunnelVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                     POVS_SWITCH_CONTEXT switchContext,
+                     POVS_VPORT_ENTRY vport,
+                     BOOLEAN hvDelete,
+                     BOOLEAN ovsDelete)
+{
+    POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL;
+    PIRP irp = NULL;
+
+    tunnelContext = OvsAllocateMemoryWithTag(sizeof(*tunnelContext),
+                                             OVS_VPORT_POOL_TAG);
+    if (tunnelContext == NULL) {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    RtlZeroMemory(tunnelContext, sizeof(*tunnelContext));
+
+    tunnelContext->switchContext = switchContext;
+    tunnelContext->hvSwitchPort = FALSE;
+    tunnelContext->hvDelete = hvDelete;
+    tunnelContext->ovsDelete = ovsDelete;
+    tunnelContext->vport = vport;
+
+    if (usrParamsCtx) {
+        tunnelContext->inputBuffer = usrParamsCtx->inputBuffer;
+        tunnelContext->outputBuffer = usrParamsCtx->outputBuffer;
+        tunnelContext->outputLength = usrParamsCtx->outputLength;
+        irp = usrParamsCtx->irp;
+    }
+
+    return OvsCleanupVxlanTunnel(irp, vport, OvsTunnelVportPendingRemove,
+                                 tunnelContext);
+}
 
+/*
+ * --------------------------------------------------------------------------
+ * Enumerates the ports on the Hyper-V switch.
+ * --------------------------------------------------------------------------
+ */
 NDIS_STATUS
 OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
 {
@@ -827,7 +1393,6 @@ OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
     ULONG arrIndex;
     PNDIS_SWITCH_PORT_PARAMETERS portParam;
     PNDIS_SWITCH_PORT_ARRAY portArray = NULL;
-    POVS_VPORT_ENTRY vport;
 
     OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
 
@@ -838,31 +1403,34 @@ OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext)
 
     for (arrIndex = 0; arrIndex < portArray->NumElements; arrIndex++) {
          portParam = NDIS_SWITCH_PORT_AT_ARRAY_INDEX(portArray, arrIndex);
-         vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
-         if (vport == NULL) {
-             status = NDIS_STATUS_RESOURCES;
-             goto cleanup;
+
+         if (portParam->IsValidationPort) {
+             continue;
          }
-         OvsInitVportWithPortParam(vport, portParam);
-         status = OvsInitVportCommon(switchContext, vport);
-         if (status != NDIS_STATUS_SUCCESS) {
-             OvsFreeMemory(vport);
-             goto cleanup;
+
+         status = HvCreatePort(switchContext, portParam, 0);
+         if (status != STATUS_SUCCESS && status != STATUS_DATA_NOT_ACCEPTED) {
+             break;
          }
     }
+
 cleanup:
     if (status != NDIS_STATUS_SUCCESS) {
         OvsClearAllSwitchVports(switchContext);
     }
 
-    if (portArray != NULL) {
-        OvsFreeMemory(portArray);
-    }
+    OvsFreeSwitchPortsArray(portArray);
+
     OVS_LOG_TRACE("Exit: status: %x", status);
+
     return status;
 }
 
-
+/*
+ * --------------------------------------------------------------------------
+ * Enumerates the NICs on the Hyper-V switch.
+ * --------------------------------------------------------------------------
+ */
 NDIS_STATUS
 OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext)
 {
@@ -870,7 +1438,6 @@ OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext)
     PNDIS_SWITCH_NIC_ARRAY nicArray = NULL;
     ULONG arrIndex;
     PNDIS_SWITCH_NIC_PARAMETERS nicParam;
-    POVS_VPORT_ENTRY vport;
 
     OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
     /*
@@ -881,419 +1448,121 @@ OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext)
         goto cleanup;
     }
     for (arrIndex = 0; arrIndex < nicArray->NumElements; ++arrIndex) {
-
         nicParam = NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray, arrIndex);
 
         /*
          * XXX: Check if the port is configured with a VLAN. Disallow such a
          * configuration, since we don't support tag-in-tag.
-         */
-
-        /*
          * XXX: Check if the port is connected to a VF. Disconnect the VF in
          * such a case.
          */
 
-        if (nicParam->NicType == NdisSwitchNicTypeExternal &&
-            nicParam->NicIndex != 0) {
-            POVS_VPORT_ENTRY virtVport =
-                   (POVS_VPORT_ENTRY)switchContext->externalVport;
-            vport = OvsAllocateVport();
-            if (vport) {
-                OvsInitPhysNicVport(vport, virtVport, nicParam->NicIndex);
-                status = OvsInitVportCommon(switchContext, vport);
-                if (status != NDIS_STATUS_SUCCESS) {
-                    OvsFreeMemory(vport);
-                    vport = NULL;
-                }
-            }
-        } else {
-            vport = OvsFindVportByPortIdAndNicIndex(switchContext,
-                                                    nicParam->PortId,
-                                                    nicParam->NicIndex);
-        }
-        if (vport == NULL) {
-            OVS_LOG_ERROR("Fail to allocate vport");
-            continue;
-        }
-        OvsInitVportWithNicParam(switchContext, vport, nicParam);
-        if (nicParam->NicType == NdisSwitchNicTypeInternal) {
-            OvsInternalAdapterUp(vport->portNo, &nicParam->NetCfgInstanceId);
+        status = HvCreateNic(switchContext, nicParam);
+        if (status == NDIS_STATUS_SUCCESS) {
+            HvConnectNic(switchContext, nicParam);
         }
     }
 cleanup:
 
-    if (nicArray != NULL) {
-        OvsFreeMemory(nicArray);
-    }
+    OvsFreeSwitchNicsArray(nicArray);
+
     OVS_LOG_TRACE("Exit: status: %x", status);
     return status;
 }
 
+/*
+ * --------------------------------------------------------------------------
+ * Deletes ports added from the Hyper-V switch as well as OVS usersapce. The
+ * function deletes ports in 'portIdHashArray'. This will delete most of the
+ * ports that are in the 'portNoHashArray' as well. Any remaining ports
+ * are deleted by walking the 'portNoHashArray'.
+ * --------------------------------------------------------------------------
+ */
 VOID
 OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext)
 {
-    UINT32 i;
+    for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
+        PLIST_ENTRY head, link, next;
 
-    for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
-        if (!OVS_IS_VPORT_ENTRY_NULL(switchContext, i)) {
-            OvsRemoveAndDeleteVport(switchContext,
-                       (POVS_VPORT_ENTRY)switchContext->vportArray[i]);
+        head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]);
+        LIST_FORALL_SAFE(head, link, next) {
+            POVS_VPORT_ENTRY vport;
+            vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink);
+            OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
         }
     }
-    if (switchContext->externalVport) {
-        OvsRemoveAndDeleteVport(switchContext,
-                        (POVS_VPORT_ENTRY)switchContext->externalVport);
+
+    /*
+     * Remove 'virtualExternalVport' as well. This port is not part of the
+     * 'portIdHashArray'.
+     */
+    if (switchContext->virtualExternalVport) {
+        OvsRemoveAndDeleteVport(NULL, switchContext,
+            (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE);
+    }
+
+
+    for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
+        PLIST_ENTRY head, link, next;
+        head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
+        LIST_FORALL_SAFE(head, link, next) {
+            POVS_VPORT_ENTRY vport;
+            vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
+            ASSERT(OvsIsTunnelVportType(vport->ovsType) ||
+                   (vport->ovsType == OVS_VPORT_TYPE_INTERNAL &&
+                    vport->isBridgeInternal) || vport->isAbsentOnHv == TRUE);
+            OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
+        }
     }
+
+    ASSERT(switchContext->virtualExternalVport == NULL);
+    ASSERT(switchContext->internalVport == NULL);
 }
 
+
 NTSTATUS
-OvsDumpVportIoctl(PVOID inputBuffer,
-                  UINT32 inputLength,
-                  PVOID outputBuffer,
-                  UINT32 outputLength,
-                  UINT32 *replyLen)
+OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
+                                CHAR *str,
+                                UINT16 maxStrLen)
 {
-    UINT32 numVports, count;
-    UINT32 dpNo, i;
-    UINT32 *outPtr;
-    POVS_VPORT_ENTRY vport;
-    LOCK_STATE_EX lockState;
+    ANSI_STRING astr;
+    UNICODE_STRING ustr;
+    NTSTATUS status;
+    UINT32 size;
 
-    if (inputLength < sizeof (UINT32)) {
-        return STATUS_INVALID_PARAMETER;
-    }
-    dpNo = *(UINT32 *)inputBuffer;
+    ustr.Buffer = wStr->String;
+    ustr.Length = wStr->Length;
+    ustr.MaximumLength = IF_MAX_STRING_SIZE;
 
-    NdisAcquireSpinLock(gOvsCtrlLock);
-    if (gOvsSwitchContext == NULL ||
-        gOvsSwitchContext->dpNo != dpNo) {
-        NdisReleaseSpinLock(gOvsCtrlLock);
-        return STATUS_INVALID_PARAMETER;
+    astr.Buffer = str;
+    astr.MaximumLength = maxStrLen;
+    astr.Length = 0;
+
+    size = RtlUnicodeStringToAnsiSize(&ustr);
+    if (size > maxStrLen) {
+        return STATUS_BUFFER_OVERFLOW;
     }
-    /*
-     * We should hold SwitchContext RW lock
-     */
 
-    NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
-                          NDIS_RWL_AT_DISPATCH_LEVEL);
-    numVports = outputLength/sizeof (UINT32);
-    numVports = MIN(gOvsSwitchContext->numVports, numVports);
-    outPtr = (UINT32 *)outputBuffer;
-    for (i = 0, count = 0;
-         i < OVS_MAX_VPORT_ARRAY_SIZE && count < numVports; i++) {
-        vport = (POVS_VPORT_ENTRY)gOvsSwitchContext->vportArray[i];
-        if (OVS_IS_VPORT_ENTRY_NULL(gOvsSwitchContext, i)) {
-            continue;
-        }
-        if (vport->ovsState == OVS_STATE_CONNECTED ||
-            vport->ovsState == OVS_STATE_NIC_CREATED) {
-            *outPtr = vport->portNo;
-            outPtr++;
-            count++;
-        }
+    status = RtlUnicodeStringToAnsiString(&astr, &ustr, FALSE);
+
+    ASSERT(status == STATUS_SUCCESS);
+    if (status != STATUS_SUCCESS) {
+        return status;
     }
-    NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
-    NdisReleaseSpinLock(gOvsCtrlLock);
-    *replyLen = count * sizeof (UINT32);
+    ASSERT(astr.Length <= maxStrLen);
+    str[astr.Length] = 0;
     return STATUS_SUCCESS;
 }
 
-
+/*
+ * --------------------------------------------------------------------------
+ * Utility function that populates a 'OVS_VPORT_EXT_INFO' structure for the
+ * specified vport.
+ * --------------------------------------------------------------------------
+ */
 NTSTATUS
-OvsGetVportIoctl(PVOID inputBuffer,
-                 UINT32 inputLength,
-                 PVOID outputBuffer,
-                 UINT32 outputLength,
-                 UINT32 *replyLen)
-{
-    UINT32 dpNo;
-    POVS_VPORT_GET get;
-    POVS_VPORT_INFO info;
-    POVS_VPORT_ENTRY vport;
-    size_t len;
-    LOCK_STATE_EX lockState;
-
-    if (inputLength < sizeof (OVS_VPORT_GET) ||
-        outputLength < sizeof (OVS_VPORT_INFO)) {
-        return STATUS_INVALID_PARAMETER;
-    }
-    get = (POVS_VPORT_GET)inputBuffer;
-    dpNo = get->dpNo;
-    info = (POVS_VPORT_INFO)outputBuffer;
-    RtlZeroMemory(info, sizeof (POVS_VPORT_INFO));
-
-    NdisAcquireSpinLock(gOvsCtrlLock);
-    if (gOvsSwitchContext == NULL ||
-        gOvsSwitchContext->dpNo != dpNo) {
-        NdisReleaseSpinLock(gOvsCtrlLock);
-        return STATUS_INVALID_PARAMETER;
-    }
-
-    NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
-                          NDIS_RWL_AT_DISPATCH_LEVEL);
-    if (get->portNo == 0) {
-        StringCbLengthA(get->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
-        vport = OvsFindVportByOvsName(gOvsSwitchContext, get->name, (UINT32)len);
-    } else {
-        vport = OvsFindVportByPortNo(gOvsSwitchContext, get->portNo);
-    }
-    if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED &&
-                          vport->ovsState != OVS_STATE_NIC_CREATED)) {
-        NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
-        NdisReleaseSpinLock(gOvsCtrlLock);
-        /*
-         * XXX Change to NO DEVICE
-         */
-        return STATUS_DEVICE_DOES_NOT_EXIST;
-    }
-    info->dpNo = dpNo;
-    info->portNo = vport->portNo;
-    info->type = vport->ovsType;
-    RtlCopyMemory(info->macAddress, vport->permMacAddress,
-                  sizeof (vport->permMacAddress));
-    RtlCopyMemory(info->name, vport->ovsName, vport->ovsNameLen + 1);
-
-    info->rxPackets = vport->stats.rxPackets;
-    info->rxBytes = vport->stats.rxBytes;
-    info->txPackets = vport->stats.txPackets;
-    info->txBytes = vport->stats.txBytes;
-    info->rxErrors = vport->errStats.rxErrors;
-    info->txErrors = vport->errStats.txErrors;
-    info->rxDropped = vport->errStats.rxDropped;
-    info->txDropped = vport->errStats.txDropped;
-
-    NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
-    NdisReleaseSpinLock(gOvsCtrlLock);
-    *replyLen = sizeof (OVS_VPORT_INFO);
-    return STATUS_SUCCESS;
-}
-
-
-NTSTATUS
-OvsInitTunnelVport(POVS_VPORT_ENTRY vport,
-                   POVS_VPORT_ADD_REQUEST addReq)
-{
-    size_t len;
-    NTSTATUS status = STATUS_SUCCESS;
-
-    vport->isValidationPort = FALSE;
-    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
-OvsAddVportIoctl(PVOID inputBuffer,
-                 UINT32 inputLength,
-                 PVOID outputBuffer,
-                 UINT32 outputLength,
-                 UINT32 *replyLen)
-{
-    NTSTATUS status = STATUS_SUCCESS;
-    POVS_VPORT_INFO vportInfo;
-    POVS_VPORT_ADD_REQUEST addReq;
-    POVS_VPORT_ENTRY vport;
-    LOCK_STATE_EX lockState;
-    UINT32 index;
-    UINT32 portNo;
-
-    OVS_LOG_TRACE("Enter: inputLength: %u, outputLength: %u",
-                  inputLength, outputLength);
-    if (inputLength < sizeof (OVS_VPORT_ADD_REQUEST) ||
-        outputLength < sizeof (OVS_VPORT_INFO)) {
-        status = STATUS_INVALID_PARAMETER;
-        goto vport_add_done;
-    }
-    addReq = (POVS_VPORT_ADD_REQUEST)inputBuffer;
-    addReq->name[OVS_MAX_PORT_NAME_LENGTH - 1] = 0;
-
-    switch (addReq->type) {
-    case OVS_VPORT_TYPE_GRE:
-        index = OVS_GRE_VPORT_INDEX;
-        break;
-    case OVS_VPORT_TYPE_GRE64:
-        index = OVS_GRE64_VPORT_INDEX;
-        break;
-    case OVS_VPORT_TYPE_VXLAN:
-        index = OVS_VXLAN_VPORT_INDEX;
-        break;
-    default:
-        status = STATUS_NOT_SUPPORTED;
-        goto vport_add_done;
-    }
-
-    vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
-    if (vport == NULL) {
-        status = STATUS_INSUFFICIENT_RESOURCES;
-        goto vport_add_done;
-    }
-
-    NdisAcquireSpinLock(gOvsCtrlLock);
-    if (gOvsSwitchContext == NULL ||
-        gOvsSwitchContext->dpNo != addReq->dpNo) {
-        NdisReleaseSpinLock(gOvsCtrlLock);
-        status = STATUS_INVALID_PARAMETER;
-        OvsFreeMemory(vport);
-        goto vport_add_done;
-    }
-    NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
-                          NDIS_RWL_AT_DISPATCH_LEVEL);
-    if (!OVS_IS_VPORT_ENTRY_NULL(gOvsSwitchContext, index)) {
-        status = STATUS_DEVICE_BUSY;
-        NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
-        NdisReleaseSpinLock(gOvsCtrlLock);
-        OvsFreeMemory(vport);
-        goto vport_add_done;
-    }
-
-    status = OvsInitTunnelVport(vport, addReq);
-    if (status != STATUS_SUCCESS) {
-        NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
-        NdisReleaseSpinLock(gOvsCtrlLock);
-        OvsFreeMemory(vport);
-        goto vport_add_done;
-    }
-
-    status = OvsInitVportCommon(gOvsSwitchContext, vport);
-    ASSERT(status == NDIS_STATUS_SUCCESS);
-
-    vport->ovsState = OVS_STATE_CONNECTED;
-    vport->nicState = NdisSwitchNicStateConnected;
-
-    vportInfo = (POVS_VPORT_INFO)outputBuffer;
-
-    RtlZeroMemory(vportInfo, sizeof (POVS_VPORT_INFO));
-    vportInfo->dpNo = gOvsSwitchContext->dpNo;
-    vportInfo->portNo = vport->portNo;
-    vportInfo->type = vport->ovsType;
-    RtlCopyMemory(vportInfo->name, vport->ovsName, vport->ovsNameLen + 1);
-    portNo = vport->portNo;
-
-    NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
-    NdisReleaseSpinLock(gOvsCtrlLock);
-    OvsPostEvent(portNo, OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP);
-    *replyLen = sizeof (OVS_VPORT_INFO);
-    status = STATUS_SUCCESS;
-vport_add_done:
-    OVS_LOG_TRACE("Exit: byteReturned: %u, status: %x",
-                  *replyLen, status);
-    return status;
-}
-
-NTSTATUS
-OvsDelVportIoctl(PVOID inputBuffer,
-                 UINT32 inputLength,
-                 UINT32 *replyLen)
-{
-    NTSTATUS status = STATUS_SUCCESS;
-    POVS_VPORT_DELETE_REQUEST delReq;
-    LOCK_STATE_EX lockState;
-    POVS_VPORT_ENTRY vport;
-    size_t len;
-    UINT32 portNo = 0;
-
-    OVS_LOG_TRACE("Enter: inputLength: %u", inputLength);
-
-    if (inputLength < sizeof (OVS_VPORT_DELETE_REQUEST)) {
-        status = STATUS_INVALID_PARAMETER;
-        goto vport_del_done;
-    }
-    delReq = (POVS_VPORT_DELETE_REQUEST)inputBuffer;
-
-    NdisAcquireSpinLock(gOvsCtrlLock);
-    if (gOvsSwitchContext == NULL ||
-        gOvsSwitchContext->dpNo != delReq->dpNo) {
-        NdisReleaseSpinLock(gOvsCtrlLock);
-        status = STATUS_INVALID_PARAMETER;
-        goto vport_del_done;
-    }
-    NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
-                          NDIS_RWL_AT_DISPATCH_LEVEL);
-    if (delReq->portNo == 0) {
-        StringCbLengthA(delReq->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
-        vport = OvsFindVportByOvsName(gOvsSwitchContext, delReq->name,
-                                      (UINT32)len);
-    } else {
-        vport = OvsFindVportByPortNo(gOvsSwitchContext, delReq->portNo);
-    }
-    if (vport) {
-        OVS_LOG_INFO("delete vport: %s, portNo: %x", vport->ovsName,
-                     vport->portNo);
-        portNo = vport->portNo;
-        OvsRemoveAndDeleteVport(gOvsSwitchContext, vport);
-    } else {
-        status = STATUS_DEVICE_DOES_NOT_EXIST;
-    }
-    NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
-    NdisReleaseSpinLock(gOvsCtrlLock);
-    if (vport) {
-        OvsPostEvent(portNo, OVS_EVENT_DISCONNECT | OVS_EVENT_LINK_DOWN);
-    }
-vport_del_done:
-    OVS_LOG_TRACE("Exit: byteReturned: %u, status: %x",
-                  *replyLen, status);
-    return status;
-}
-
-NTSTATUS
-OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
-                                CHAR *str,
-                                UINT16 maxStrLen)
-{
-    ANSI_STRING astr;
-    UNICODE_STRING ustr;
-    NTSTATUS status;
-    UINT32 size;
-
-    ustr.Buffer = wStr->String;
-    ustr.Length = wStr->Length;
-    ustr.MaximumLength = IF_MAX_STRING_SIZE;
-
-    astr.Buffer = str;
-    astr.MaximumLength = maxStrLen;
-    astr.Length = 0;
-
-    size = RtlUnicodeStringToAnsiSize(&ustr);
-    if (size > maxStrLen) {
-        return STATUS_BUFFER_OVERFLOW;
-    }
-
-    status = RtlUnicodeStringToAnsiString(&astr, &ustr, FALSE);
-
-    ASSERT(status == STATUS_SUCCESS);
-    if (status != STATUS_SUCCESS) {
-        return status;
-    }
-    ASSERT(astr.Length <= maxStrLen);
-    str[astr.Length] = 0;
-    return STATUS_SUCCESS;
-}
-
-
-/*
- * 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
-NTSTATUS
-OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
-                   POVS_VPORT_EXT_INFO extInfo)
+OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
+                   POVS_VPORT_EXT_INFO extInfo)
 {
     POVS_VPORT_ENTRY vport;
     size_t len;
@@ -1302,23 +1571,21 @@ OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
     BOOLEAN doConvert = FALSE;
 
     RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO));
-    NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
-                          NDIS_RWL_AT_DISPATCH_LEVEL);
+    NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
     if (vportGet->portNo == 0) {
         StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len);
-#if USE_NEW_VPORT_ADD_WORKFLOW == 0
-        vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name,
-                                      (UINT32)len);
-#else
-        vport = OvsFindVportByHvName(gOvsSwitchContext, vportGet->name);
-#endif
+        vport = OvsFindVportByHvNameA(gOvsSwitchContext, vportGet->name);
+        if (vport == NULL) {
+            /* If the port is not a Hyper-V port and it has been added earlier,
+             * we'll find it in 'ovsPortNameHashArray'. */
+            vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name);
+        }
     } else {
         vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo);
     }
     if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED &&
                           vport->ovsState != OVS_STATE_NIC_CREATED)) {
         NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
-        NdisReleaseSpinLock(gOvsCtrlLock);
         if (vportGet->portNo) {
             OVS_LOG_WARN("vport %u does not exist any more", vportGet->portNo);
         } else {
@@ -1359,13 +1626,8 @@ OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
         extInfo->vmUUID[0] = 0;
         extInfo->vifUUID[0] = 0;
     }
-#if USE_NEW_VPORT_ADD_WORKFLOW == 0
-    RtlCopyMemory(extInfo->name, vport->ovsName, vport->ovsNameLen + 1);
-#endif
     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
-    NdisReleaseSpinLock(gOvsCtrlLock);
     if (doConvert) {
-#if USE_NEW_VPORT_ADD_WORKFLOW == 1
         status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName,
                                                  extInfo->name,
                                                  OVS_MAX_PORT_NAME_LENGTH);
@@ -1373,7 +1635,6 @@ OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet,
             OVS_LOG_INFO("Fail to convert NIC name.");
             extInfo->vmUUID[0] = 0;
         }
-#endif
 
         status = OvsConvertIfCountedStrToAnsiStr(&vport->vmName,
                                                  extInfo->vmUUID,
@@ -1415,7 +1676,6 @@ OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
     NL_ERROR nlError = NL_ERROR_SUCCESS;
     OVS_VPORT_GET vportGet;
     OVS_VPORT_EXT_INFO info;
-    LOCK_STATE_EX lockState;
 
     static const NL_POLICY ovsNetdevPolicy[] = {
         [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING,
@@ -1435,13 +1695,8 @@ OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
-        ovsNetdevPolicy, netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
-        return STATUS_INVALID_PARAMETER;
-    }
-
-    OvsAcquireCtrlLock();
-    if (!gOvsSwitchContext) {
-        OvsReleaseCtrlLock();
+        ovsNetdevPolicy, ARRAY_SIZE(ovsNetdevPolicy),
+        netdevAttrs, ARRAY_SIZE(netdevAttrs))) {
         return STATUS_INVALID_PARAMETER;
     }
 
@@ -1449,15 +1704,11 @@ OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
     RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]),
                   NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME]));
 
-    NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
     status = OvsGetExtInfoIoctl(&vportGet, &info);
     if (status == STATUS_DEVICE_DOES_NOT_EXIST) {
         nlError = NL_ERROR_NODEV;
-        NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
-        OvsReleaseCtrlLock();
         goto cleanup;
     }
-    NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
 
     status = CreateNetlinkMesgForNetdev(&info, msgIn,
                  usrParamsCtx->outputBuffer, usrParamsCtx->outputLength,
@@ -1465,14 +1716,13 @@ OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
     if (status == STATUS_SUCCESS) {
         *replyLen = msgOut->nlMsg.nlmsgLen;
     }
-    OvsReleaseCtrlLock();
 
 cleanup:
     if (nlError != NL_ERROR_SUCCESS) {
         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
             usrParamsCtx->outputBuffer;
 
-        BuildErrorMsg(msgIn, msgError, nlError);
+        NlBuildErrorMsg(msgIn, msgError, nlError);
         *replyLen = msgError->nlMsg.nlmsgLen;
     }
 
@@ -1495,46 +1745,45 @@ CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
 {
     NL_BUFFER nlBuffer;
     BOOLEAN ok;
-    OVS_MESSAGE msgOut;
     PNL_MSG_HDR nlMsg;
     UINT32 netdevFlags = 0;
 
     NlBufInit(&nlBuffer, outBuffer, outBufLen);
 
-    BuildReplyMsgFromMsgIn(msgIn, &msgOut, 0);
-    msgOut.ovsHdr.dp_ifindex = dpIfIndex;
-
-    ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
+    ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
+                      msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
+                      msgIn->genlMsg.cmd, msgIn->genlMsg.version,
+                      dpIfIndex);
     if (!ok) {
-        return STATUS_INSUFFICIENT_RESOURCES;
+        return STATUS_INVALID_BUFFER_SIZE;
     }
 
     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO,
                          info->portNo);
     if (!ok) {
-        return STATUS_INSUFFICIENT_RESOURCES;
+        return STATUS_INVALID_BUFFER_SIZE;
     }
 
     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type);
     if (!ok) {
-        return STATUS_INSUFFICIENT_RESOURCES;
+        return STATUS_INVALID_BUFFER_SIZE;
     }
 
     ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME,
                             info->name);
     if (!ok) {
-        return STATUS_INSUFFICIENT_RESOURCES;
+        return STATUS_INVALID_BUFFER_SIZE;
     }
 
     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR,
              (PCHAR)info->macAddress, sizeof (info->macAddress));
     if (!ok) {
-        return STATUS_INSUFFICIENT_RESOURCES;
+        return STATUS_INVALID_BUFFER_SIZE;
     }
 
     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu);
     if (!ok) {
-        return STATUS_INSUFFICIENT_RESOURCES;
+        return STATUS_INVALID_BUFFER_SIZE;
     }
 
     if (info->status != OVS_EVENT_CONNECT) {
@@ -1543,7 +1792,7 @@ CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info,
     ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS,
                          netdevFlags);
     if (!ok) {
-        return STATUS_INSUFFICIENT_RESOURCES;
+        return STATUS_INVALID_BUFFER_SIZE;
     }
 
     /*
@@ -1567,3 +1816,909 @@ OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec)
         NdisMSleep(sleepMicroSec);
     }
 }
+
+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;
+    PNL_MSG_HDR nlMsg;
+
+    NlBufInit(&nlBuffer, outBuffer, outBufLen);
+
+    ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI,
+                      msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid,
+                      msgIn->genlMsg.cmd, msgIn->genlMsg.version,
+                      dpIfIndex);
+    if (!ok) {
+        return STATUS_INVALID_BUFFER_SIZE;
+    }
+
+    ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
+    if (!ok) {
+        return STATUS_INVALID_BUFFER_SIZE;
+    }
+
+    ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
+    if (!ok) {
+        return STATUS_INVALID_BUFFER_SIZE;
+    }
+
+    ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
+    if (!ok) {
+        return STATUS_INVALID_BUFFER_SIZE;
+    }
+
+    /*
+     * 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_INVALID_BUFFER_SIZE;
+    }
+
+    /*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_INVALID_BUFFER_SIZE;
+    }
+
+    /*
+     * 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;
+
+    /*
+     * 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.
+    */
+    NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
+
+    if (gOvsSwitchContext->numHvVports > 0 ||
+            gOvsSwitchContext->numNonHvVports > 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->portNoHashArray[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, portNoLink);
+
+                    ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
+                    OvsCreateMsgFromVport(vport, msgIn,
+                                          usrParamsCtx->outputBuffer,
+                                          usrParamsCtx->outputLength,
+                                          gOvsSwitchContext->dpNo);
+                    ++outIndex;
+                    break;
+                }
+
+                ++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);
+
+    /* 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;
+    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 },
+        [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, ARRAY_SIZE(ovsVportPolicy),
+        vportAttrs, ARRAY_SIZE(vportAttrs))) {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Output buffer has been validated while validating transact dev op. */
+    ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
+
+    NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
+    if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
+        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) {
+        portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
+
+        vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber);
+    } 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;
+
+        NlBuildErrorMsg(msgIn, msgError, nlError);
+        *replyLen = msgError->nlMsg.nlmsgLen;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *  Command Handler for 'OVS_VPORT_CMD_GET'.
+ *
+ *  The function handles the initial call to setup the dump state, as well as
+ *  subsequent calls to continue dumping data.
+ * --------------------------------------------------------------------------
+*/
+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;
+    }
+
+}
+
+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;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *  Command Handler for 'OVS_VPORT_CMD_NEW'.
+ * --------------------------------------------------------------------------
+ */
+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;
+    BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE;
+
+    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);
+
+    /* Output buffer has been validated while validating transact dev op. */
+    ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
+
+    if (!NlAttrParse((PNL_MSG_HDR)msgIn,
+        NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
+        NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
+        ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
+        vportAttrs, ARRAY_SIZE(vportAttrs))) {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    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_NETDEV) {
+        /* External ports can also be looked up like VIF ports. */
+        vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
+    } else {
+        ASSERT(OvsIsTunnelVportType(portType) ||
+               portType == OVS_VPORT_TYPE_INTERNAL);
+
+        vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
+        if (vport == NULL) {
+            nlError = NL_ERROR_NOMEM;
+            goto Cleanup;
+        }
+        vportAllocated = TRUE;
+
+        if (OvsIsTunnelVportType(portType)) {
+            UINT16 transportPortDest = 0;
+
+            switch (portType) {
+            case OVS_VPORT_TYPE_GRE:
+                break;
+            case OVS_VPORT_TYPE_VXLAN:
+                transportPortDest = VXLAN_UDP_PORT;
+                break;
+            case OVS_VPORT_TYPE_STT:
+                transportPortDest = STT_TCP_PORT;
+                break;
+            default:
+                nlError = NL_ERROR_INVAL;
+                goto Cleanup;
+            }
+
+            if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
+                PNL_ATTR attr = NlAttrFindNested(vportAttrs[OVS_VPORT_ATTR_OPTIONS],
+                                                 OVS_TUNNEL_ATTR_DST_PORT);
+                if (attr) {
+                    transportPortDest = NlAttrGetU16(attr);
+                }
+            }
+
+            status = OvsInitTunnelVport(usrParamsCtx,
+                                        vport,
+                                        portType,
+                                        transportPortDest);
+
+            nlError = NlMapStatusToNlErr(status);
+        } else {
+            OvsInitBridgeInternalVport(vport);
+        }
+
+        vportInitialized = TRUE;
+
+        if (nlError == NL_ERROR_SUCCESS) {
+            vport->ovsState = OVS_STATE_CONNECTED;
+            vport->nicState = NdisSwitchNicStateConnected;
+
+            /*
+             * Allow the vport to be deleted, because there is no
+             * corresponding hyper-v switch part.
+             */
+            vport->isAbsentOnHv = TRUE;
+        } else {
+            goto Cleanup;
+        }
+    }
+
+    if (!vport) {
+        nlError = NL_ERROR_INVAL;
+        goto Cleanup;
+    }
+    if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
+        nlError = NL_ERROR_EXIST;
+        goto Cleanup;
+    }
+
+    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]);
+
+    status = InitOvsVportCommon(gOvsSwitchContext, vport);
+    ASSERT(status == STATUS_SUCCESS);
+
+    status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
+                                   usrParamsCtx->outputLength,
+                                   gOvsSwitchContext->dpNo);
+
+    *replyLen = msgOut->nlMsg.nlmsgLen;
+
+Cleanup:
+    NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
+
+    if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
+        POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
+            usrParamsCtx->outputBuffer;
+
+        if (vport && vportAllocated == TRUE) {
+            if (vportInitialized == TRUE) {
+                if (OvsIsTunnelVportType(portType)) {
+                    switch (vport->ovsType) {
+                    case OVS_VPORT_TYPE_VXLAN:
+                        OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
+                        break;
+                    case OVS_VPORT_TYPE_STT:
+                        OvsCleanupSttTunnel(vport);
+                        break;
+                    default:
+                        ASSERT(!"Invalid tunnel port type");
+                    }
+                }
+            }
+            OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
+        }
+
+        NlBuildErrorMsg(msgIn, msgError, nlError);
+        *replyLen = msgError->nlMsg.nlmsgLen;
+    }
+
+    return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ *  Command Handler for 'OVS_VPORT_CMD_SET'.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                      UINT32 *replyLen)
+{
+    NDIS_STATUS 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_TYPE] = { .type = NL_A_U32, .optional = TRUE },
+        [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
+                                  .optional = TRUE },
+        [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
+                                        .optional = TRUE },
+        [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC,
+                                   .minLen = sizeof(OVS_VPORT_FULL_STATS),
+                                   .maxLen = sizeof(OVS_VPORT_FULL_STATS),
+                                   .optional = TRUE },
+        [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
+    };
+    PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
+
+    ASSERT(usrParamsCtx->inputBuffer != NULL);
+
+    if (!NlAttrParse((PNL_MSG_HDR)msgIn,
+        NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
+        NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
+        ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
+        vportAttrs, ARRAY_SIZE(vportAttrs))) {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Output buffer has been validated while validating transact dev op. */
+    ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
+
+    NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
+    if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
+        PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
+#ifdef DBG
+        UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
+#endif
+        /* 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]));
+    }
+
+    if (!vport) {
+        nlError = NL_ERROR_NODEV;
+        goto Cleanup;
+    }
+
+    /*
+     * 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.
+     * Currently, we support only one pid.
+     */
+    if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) {
+        vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
+    }
+
+    if (vportAttrs[OVS_VPORT_ATTR_TYPE]) {
+        OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
+        if (type != vport->ovsType) {
+            nlError = NL_ERROR_INVAL;
+            goto Cleanup;
+        }
+    }
+
+    if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) {
+        OVS_LOG_ERROR("Vport options not supported");
+        nlError = NL_ERROR_NOTSUPP;
+        goto Cleanup;
+    }
+
+    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;
+
+        NlBuildErrorMsg(msgIn, msgError, nlError);
+        *replyLen = msgError->nlMsg.nlmsgLen;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *  Command Handler for 'OVS_VPORT_CMD_DEL'.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
+                         UINT32 *replyLen)
+{
+    NDIS_STATUS 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;
+    PSTR portName = NULL;
+    UINT32 portNameLen = 0;
+
+    static const NL_POLICY ovsVportPolicy[] = {
+        [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
+        [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
+                                  .optional = TRUE },
+    };
+    PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
+
+    ASSERT(usrParamsCtx->inputBuffer != NULL);
+
+    if (!NlAttrParse((PNL_MSG_HDR)msgIn,
+        NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
+        NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
+        ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
+        vportAttrs, ARRAY_SIZE(vportAttrs))) {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Output buffer has been validated while validating transact dev op. */
+    ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
+
+    NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0);
+    if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
+        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]));
+    }
+
+    if (!vport) {
+        nlError = NL_ERROR_NODEV;
+        goto Cleanup;
+    }
+
+    status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
+                                   usrParamsCtx->outputLength,
+                                   gOvsSwitchContext->dpNo);
+
+    *replyLen = msgOut->nlMsg.nlmsgLen;
+
+    /*
+     * Mark the port as deleted from OVS userspace. If the port does not exist
+     * on the Hyper-V switch, it gets deallocated. Otherwise, it stays.
+     */
+    status = OvsRemoveAndDeleteVport(usrParamsCtx,
+                                     gOvsSwitchContext,
+                                     vport,
+                                     FALSE,
+                                     TRUE);
+    if (status) {
+        nlError = NlMapStatusToNlErr(status);
+    }
+
+Cleanup:
+    NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
+
+    if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) {
+        POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
+            usrParamsCtx->outputBuffer;
+
+        NlBuildErrorMsg(msgIn, msgError, nlError);
+        *replyLen = msgError->nlMsg.nlmsgLen;
+    }
+
+    return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS;
+}
+
+static VOID
+OvsTunnelVportPendingRemove(PVOID context,
+                            NTSTATUS status,
+                            UINT32 *replyLen)
+{
+    POVS_TUNFLT_INIT_CONTEXT tunnelContext =
+        (POVS_TUNFLT_INIT_CONTEXT) context;
+    POVS_SWITCH_CONTEXT switchContext = tunnelContext->switchContext;
+    POVS_VPORT_ENTRY vport = tunnelContext->vport;
+    POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
+    POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
+    NL_ERROR nlError = NlMapStatusToNlErr(status);
+    LOCK_STATE_EX lockState;
+
+    NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
+
+    if (msgIn && msgOut) {
+        /* Check the received status to reply to the caller. */
+        if (STATUS_SUCCESS == status) {
+            OvsCreateMsgFromVport(vport,
+                                  msgIn,
+                                  msgOut,
+                                  tunnelContext->outputLength,
+                                  switchContext->dpNo);
+
+            *replyLen = msgOut->nlMsg.nlmsgLen;
+        } else {
+            POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)msgOut;
+
+            NlBuildErrorMsg(msgIn, msgError, nlError);
+            *replyLen = msgError->nlMsg.nlmsgLen;
+        }
+    }
+
+    ASSERT(vport->isAbsentOnHv == TRUE);
+    ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID);
+
+    /* Remove the port from the relevant lists. */
+    switchContext->numNonHvVports--;
+    RemoveEntryList(&vport->ovsNameLink);
+    RemoveEntryList(&vport->portNoLink);
+    RemoveEntryList(&vport->tunnelVportLink);
+
+    if (vport->priv) {
+        OvsFreeMemoryWithTag(vport->priv, OVS_VXLAN_POOL_TAG);
+        vport->priv = NULL;
+    }
+
+    OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG);
+
+    NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
+}
+
+static VOID
+OvsTunnelVportPendingInit(PVOID context,
+                          NTSTATUS status,
+                          UINT32 *replyLen)
+{
+    POVS_TUNFLT_INIT_CONTEXT tunnelContext =
+        (POVS_TUNFLT_INIT_CONTEXT) context;
+    POVS_VPORT_ENTRY vport = tunnelContext->vport;
+    POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer;
+    POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer;
+    PCHAR portName;
+    ULONG portNameLen = 0;
+    UINT32 portType = 0;
+    NL_ERROR nlError = NL_ERROR_SUCCESS;
+    BOOLEAN error = TRUE;
+
+    do {
+        if (!NT_SUCCESS(status)) {
+            nlError = NlMapStatusToNlErr(status);
+            break;
+        }
+
+        static const NL_POLICY ovsVportPolicy[] = {
+            [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
+            [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE },
+            [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ,
+            .optional = FALSE },
+            [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC,
+            .optional = FALSE },
+            [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE },
+        };
+
+        PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
+
+        /* input buffer has been validated while validating write dev op. */
+        ASSERT(msgIn != NULL);
+
+        /* Output buffer has been validated while validating transact dev op. */
+        ASSERT(msgOut != NULL && tunnelContext->outputLength >= sizeof *msgOut);
+
+        if (!NlAttrParse((PNL_MSG_HDR)msgIn,
+            NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
+            NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
+            ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy),
+            vportAttrs, ARRAY_SIZE(vportAttrs))) {
+            nlError = NL_ERROR_INVAL;
+            break;
+        }
+
+        portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]);
+        portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]);
+        portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]);
+
+        if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) {
+            nlError = NL_ERROR_EXIST;
+            break;
+        }
+
+        vport->ovsState = OVS_STATE_CONNECTED;
+        vport->nicState = NdisSwitchNicStateConnected;
+
+        /*
+         * Allow the vport to be deleted, because there is no
+         * corresponding hyper-v switch part.
+         */
+        vport->isAbsentOnHv = TRUE;
+
+        if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
+            /*
+             * XXX: when we implement the limit for OVS port number to be
+             * MAXUINT16, we'll need to check the port number received from the
+             * userspace.
+             */
+            vport->portNo =
+                NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]);
+        } else {
+            vport->portNo =
+                OvsComputeVportNo(gOvsSwitchContext);
+            if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) {
+                nlError = NL_ERROR_NOMEM;
+                break;
+            }
+        }
+
+        /* The ovs port name must be uninitialized. */
+        ASSERT(vport->ovsName[0] == '\0');
+        ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH);
+
+        RtlCopyMemory(vport->ovsName, portName, portNameLen);
+        /* if we don't have options, then vport->portOptions will be NULL */
+        vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS];
+
+        /*
+         * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
+         * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
+         * it means we have an array of pids, instead of a single pid.
+         * ATM we assume we have one pid only.
+         */
+        vport->upcallPid =
+            NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]);
+
+        status = InitOvsVportCommon(gOvsSwitchContext, vport);
+        ASSERT(status == STATUS_SUCCESS);
+
+        OvsCreateMsgFromVport(vport,
+                              msgIn,
+                              msgOut,
+                              tunnelContext->outputLength,
+                              gOvsSwitchContext->dpNo);
+
+        *replyLen = msgOut->nlMsg.nlmsgLen;
+
+        error = FALSE;
+    } while (error);
+
+    if (error) {
+        POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) msgOut;
+
+        OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL);
+        OvsFreeMemory(vport);
+
+        NlBuildErrorMsg(msgIn, msgError, nlError);
+        *replyLen = msgError->nlMsg.nlmsgLen;
+    }
+}