[ovs-dev] [PATCH 2/3 v8] datapath-windows: Add multiple switch internal ports

Sairam Venugopal vsairam at vmware.com
Tue Dec 20 20:13:02 UTC 2016


Acked-by: Sairam Venugopal <vsairam at vmware.com>


On 12/20/16, 11:42 AM, "Alin Serdean" <aserdean at cloudbasesolutions.com>
wrote:

>
>This patch adds multiple internal ports support to the windows datapath.
>All tunnels types have been updated to accommodate this new functionality.
>
>Signed-off-by: Alin Gabriel Serdean <aserdean at cloudbasesolutions.com>
>Co-authored-by: Sorin Vinturis <svinturis at cloudbasesolutions.com>
>Signed-off-by: Sorin Vinturis <svinturis at cloudbasesolutions.com>
>Acked-by: Paul Boca <pboca at cloudbasesolutions.com>
>---
>v8: Rebase
>v7: Rebase
>v6: Rebase
>v5: Rebase
>v4: Address comments (fix aligment, switch goto labels)
>v3: Add acked
>v2: Rebase
>---
> datpath-windows/ovsext/Actions.c  |  49 +-
> datapath-windows/ovsext/Geneve.c   |   7 +-
> datapath-windows/ovsext/Geneve.h   |   6 +-
> datapath-windows/ovsext/Gre.c      |   7 +-
> datapath-windows/ovsext/Gre.h      |   8 +-
> datapath-windows/ovsext/IpHelper.c | 919
>+++++++++++++++++++++++++++----------
> datapath-windows/ovsext/IpHelper.h |  22 +-
> datapath-windows/ovsext/Stt.c      |   7 +-
> datapath-windows/ovsext/Stt.h      |   7 +-
> datapath-windows/ovsext/Switch.h   |   9 +-
> datapath-windows/ovsext/Vport.c    | 131 +++---
> datapath-windows/ovsext/Vport.h    | 104 ++---
> datapath-windows/ovsext/Vxlan.c    |  10 +-
> datapath-windows/ovsext/Vxlan.h    |  10 +-
> 14 files changed, 868 insertions(+), 428 deletions(-)
>
>diff --git a/datapath-windows/ovsext/Actions.c
>b/datapath-windows/ovsext/Actions.c
>index c1e0121..2a24410 100644
>--- a/datapath-windows/ovsext/Actions.c
>+++ b/datapath-windows/ovsext/Actions.c
>@@ -301,7 +301,6 @@ OvsDetectTunnelPkt(OvsForwardingContext *ovsFwdCtx,
>             return TRUE;
>         }
>     } else if (OvsIsTunnelVportType(dstVport->ovsType)) {
>-        ASSERT(ovsFwdCtx->tunnelTxNic == NULL);
>         ASSERT(ovsFwdCtx->tunnelRxNic == NULL);
> 
>         /*
>@@ -322,7 +321,7 @@ OvsDetecTunnelPkt(OvsForwardingContext *ovsFwdCtx,
> 
>             if (!vport ||
>                 (vport->ovsType != OVS_VPORT_TYPE_NETDEV &&
>-                 !OvsIsBridgeInternalVport(vport) &&
>+                 vport->ovsType != OVS_VPORT_TYPE_INTERNAL &&
>                  !OvsIsTunnelVportType(vport->ovsType))) {
>                 ovsFwdCtx->tunKey.dst = 0;
>             }
>@@ -403,10 +402,6 @@ OvsAddPorts(OvsForwardingContext *ovsFwdCtx,
>     vport->stats.txBytes +=
>         
>NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl));
> 
>-    if (OvsIsBridgeInternalVport(vport)) {
>-        return NDIS_STATUS_SUCCESS;
>-    }
>-
>     if (OvsDetectTunnelPkt(ovsFwdCtx, vport, flowKey)) {
>         return NDIS_STATUS_SUCCESS;
>     }
>@@ -671,7 +666,7 @@ OvsTunnelPortTx(OvsForwardingContext *ovsFwdCtx)
>      * Setup the source port to be the internal port to as to facilitate
>the
>      * second OvsLookupFlow.
>      */
>-    if (ovsFwdCtx->switchContext->internalVport == NULL ||
>+    if (ovsFwdCtx->switchContext->countInternalVports <= 0 ||
>         ovsFwdCtx->switchContext->virtualExternalVport == NULL) {
>         OvsClearTunTxCtx(ovsFwdCtx);
>         OvsCompleteNBLForwardingCtx(ovsFwdCtx,
>@@ -679,37 +674,28 @@ OvsTunnelPortTx(OvsForwardingContext *ovsFwdCtx)
>         return NDIS_STATUS_FAILURE;
>     }
> 
>-    /*
>-     * Save the 'srcVportNo', 'srcPortId', 'srcNicIndex' so that
>-     * this can be applied to the new NBL later on.
>-     */
>-    srcVportNo =
>-        
>((POVS_VPORT_ENTRY)ovsFwdCtx->switchContext->internalVport)->portNo;
>-    srcPortId = ovsFwdCtx->switchContext->internalPortId;
>-    srcNicIndex =
>-        
>((POVS_VPORT_ENTRY)ovsFwdCtx->switchContext->internalVport)->nicIndex;
>-
>-    /* Do the encap. Encap function does not consume the NBL. */
>+    OVS_FWD_INFO switchFwdInfo = { 0 };
>+    /* Apply the encapsulation. The encapsulation will not consume the
>NBL. */
>     switch(ovsFwdCtx->tunnelTxNic->ovsType) {
>     case OVS_VPORT_TYPE_GRE:
>         status = OvsEncapGre(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
>                              &ovsFwdCtx->tunKey,
>ovsFwdCtx->switchContext,
>-                             &ovsFwdCtx->layers, &newNbl);
>+                             &ovsFwdCtx->layers, &newNbl,
>&switchFwdInfo);
>         break;
>     case OVS_VPORT_TYPE_VXLAN:
>         status = OvsEncapVxlan(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
>                                &ovsFwdCtx->tunKey,
>ovsFwdCtx->switchContext,
>-                               &ovsFwdCtx->layers, &newNbl);
>+                               &ovsFwdCtx->layers, &newNbl,
>&switchFwdInfo);
>         break;
>     case OVS_VPORT_TYPE_STT:
>         status = OvsEncapStt(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
>                              &ovsFwdCtx->tunKey,
>ovsFwdCtx->switchContext,
>-                             &ovsFwdCtx->layers, &newNbl);
>+                            &ovsFwdCtx->layers, &newNbl,
>&switchFwdInfo);
>         break;
>     case OVS_VPORT_TYPE_GENEVE:
>         status = OvsEncapGeneve(ovsFwdCtx->tunnelTxNic,
>ovsFwdCtx->curNbl,
>                                 &ovsFwdCtx->tunKey,
>ovsFwdCtx->switchContext,
>-                                &ovsFwdCtx->layers, &newNbl);
>+                                &ovsFwdCtx->layers, &newNbl,
>&switchFwdInfo);
>         break;
>     default:
>         ASSERT(! "Tx: Unhandled tunnel type");
>@@ -718,8 +704,16 @@ OvsTunnelPortTx(OvsForwardingContext *ovsFwdCtx)
>     /* Reset the tunnel context so that it doesn't get used after this
>point. */
>     OvsClearTunTxCtx(ovsFwdCtx);
> 
>-    if (status == NDIS_STATUS_SUCCESS) {
>+    if (status == NDIS_STATUS_SUCCESS && switchFwdInfo.vport != NULL) {
>         ASSERT(newNbl);
>+        /*
>+         * Save the 'srcVportNo', 'srcPortId', 'srcNicIndex' so that
>+         * this can be applied to the new NBL later on.
>+         */
>+        srcVportNo = switchFwdInfo.vport->portNo;
>+        srcPortId = switchFwdInfo.vport->portId;
>+        srcNiIndex = switchFwdInfo.vport->nicIndex;
>+
>         OvsCompleteNBLForwardingCtx(ovsFwdCtx,
>                                     L"Complete after cloning NBL for
>encapsulation");
>         status = OvsInitForwardingCtx(ovsFwdCtx,
>ovsFwdCtx->switchContext,
>@@ -971,12 +965,11 @@ dropit:
> VOID
> OvsLookupFlowOutput(POVS_SWITCH_CONTEXT switchContext,
>                     VOID *compList,
>-                    PNET_BUFFER_LIST curNbl)
>+                    PNET_BUFFER_LIST curNbl,
>+                    POVS_VPORT_ENTRY internalVport)
> {
>     NDIS_STATUS status;
>     OvsForwardingContext ovsFwdCtx;
>-    POVS_VPORT_ENTRY internalVport =
>-        (POVS_VPORT_ENTRY)switchContext->internalVport;
> 
>     /* XXX: make sure comp list was not a stack variable previously. */
>     OvsCompletionList *completionList = (OvsCompletionList *)compList;
>@@ -986,7 +979,7 @@ OvsLookupFlowOutput(POVS_SWITCH_CONTEXT switchContext,
>      * It could, but will we get this callback from IP helper in that
>case. Need
>      * to check.
>      */
>-    ASSERT(switchContet->internalVport);
>+    ASSERT(switchContext->countInternalVports > 0);
>     status = OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl,
>                                   internalVport->portNo, 0,
>                  
>NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl),
>@@ -1076,7 +1069,7 @@ OvsOutputBeforeSetAction(OvsForwardingContext
>*ovsFwdCtx)
>  * 
>--------------------------------------------------------------------------
>  * OvsPopFieldInPacketBuf --
>  *     Function to pop a specified field of length 'shiftLength' located
>at
>- *     'shiftOffset' from the ethernet header. The data on the left of
>the
>+ *     'shiftOffset' from the Ethernet header. The data on the left of
>the
>  *     'shiftOffset' is right shifted.
>  *
>  *     Returns a pointer to the new start in 'bufferData'.
>diff --git a/datapath-windows/ovsext/Geneve.c
>b/datapath-windows/ovsext/Geneve.c
>index efdf9f7..d38a656 00644
>--- a/datapath-windows/ovsext/Geneve.c
>+++ b/datapath-windows/ovsext/Geneve.c
>@@ -72,7 +72,8 @@ NDIS_STATUS OvsEncapGenee(POVS_VPORT_ENTRY vport,
>                            OvsIPv4TunnelKey *tunKey,
>                            POVS_SWITCH_CONTEXT switchContext,
>                           POVS_PACKET_HDR_INFO layers,
>-                           PNET_BUFFER_LIST *newNbl)
>+                           PNET_BUFFER_LIST *newNl,
>+                           POVS_FWD_INFO switchFwdnfo)
> {
>     NTSTATUS status;
>     OVS_FWD_INFO fwdInfo;
>@@ -90,7 +91,7 @@ NDIS_STATUS OvsEncapGeneve(POVS_VPORT_ENTRY vport,
>     ULONG mss = 0;
>     NDIS_TCP_IP_CHECKSUM_NT_BUFFER_LIST_INFO csumInfo;
> 
>-    status = OvsLookupIPFwdInfo(tunKey->dst, &fwdInfo);
>+    status = OvsLookupIPFwdInfo(tunKey->src, tunKey->dst, &fwdInfo);
>     if (status != STATUS_SUCCESS) {
>         OvsFwdIPHelperRequest(NULL, 0, tunKey, NULL, NULL, NULL);
>         // return NDIS_STATUS_PENDING;
@@ -104,6 +105,8 @@ NDIS_STATUS OvsEncapGeneve(POVS_VPORT_ENTRY vport,
>         retun NDIS_STATUS_FAILURE;
>     }
> 
>+    RtlCopyMemory(switchFwdInfo->value, fwdInfo.value, sizeof
>fwdInfo.value);
>+
>     curNb = NET_BUFFER_LIST_FIRST_NB(urNbl);
>     packetLength = NET_BUFFER_DATA_LENGTH(curNb);
> 
>diff --git /datapath-windows/ovsext/Geneve.h
>b/datapath-windows/ovsext/Geneve.h
>index 057f80a..be8a834 100644
>--- a/datapath-windows/ovsext/Geneve.h
>+++ b/datapath-windows/ovsext/Geneve.h
>@@ -19,6 +19,9 @@
> #define __GENEVE_H_ 1
> 
> #include "NetProto.h"
>+
>+typedef union _OVS_FWD_INFO *POVS_FWD_INFO;
>+
> typedef struct _OVS_GENEVE_VPORT {
>     UINT16 dstPort;
>     UINT64 filterID;
>@@ -87,7 +90,8 @@ NDIS_STATUS OvsEncapGeneve(POVS_VPORT_ENTRY vport,
>                            OvsIPv4TunnelKey *tunKey,
>                            POVS_SWITCH_CONTEXT switchContext,
>                            POVS_PACKET_HDR_INFO layers,
>-                           PNET_BUFFER_LIST *newNbl);
>+                           PNET_BUFFER_LIST *newNbl,
>+                           POVS_FWD_INFO switchFwdInfo);
> 
> NDIS_STATUS OvsDecapGeneve(POVS_SWITCH_CONTEXT switchContext,
>                            PNET_BUFFER_LIST curNbl,
>diff --git a/datapath-windows/ovsext/Gre.c b/datapath-windows/ovsext/Gre.c
>index 7d6c0a2..c5da064 100644
>--- a/datapath-windows/ovsext/Gre.c
>+++ b/datapath-windows/ovsext/Gre.c
>@@ -96,17 +96,20 @@ OvsEncapGre(POVS_VPORT_ENTRY vport,
>             OvsIPv4TunnelKey *tunKey,
>             POVS_SWITCH_CONTEXT switchContext,
>             POVS_PACKET_HDR_INFO layers,
>-            PNET_BUFFER_LIST *newNbl)
>+            PNET_BUFFER_LIST *newNbl,
>+            POVS_FWD_INFO switchFwdInfo)
> {
>     OVS_FWD_INFO fwdInfo;
>     NDIS_STATUS status;
> 
>-    status = OvsLookupIPFwdInfo(tunKey->dst, &fwdInfo);
>+    status = OvsLookupIPFwdInfo(tunKey->src, tunKey->dst, &fwdInfo);
>     if (status != STATUS_SUCCESS) {
>         OvsFwdIPHelperRequest(NULL, 0, tunKey, NULL, NULL, NULL);
>         return NDIS_STATUS_FAILURE;
>     }
> 
>+    RtlCopyMemory(switchFwdInfo->value, fwdInfo.value, sizeof
>fwdInfo.value);
>+
>     status = OvsDoEncapGre(vport, curNbl, tunKey, &fwdInfo layers,
>                            switchContext, newNbl);
>     return status;
>diff --git a/datapath-windows/ovsext/Gre.h b/datapath-windows/ovsext/Gre.h
>index 7e20ced..c45df8f 100644
>--- a/datapath-windows/ovsext/Gre.h
>+++ b/datapath-windows/ovsext/Gre.h
>@@ -17,8 +17,11 @@
> #ifndef __GRE_H_
> #define __GRE_H_ 1
> 
>-#include "NetProto.h"
> #include "Flow.h"
>+#include "IpHelper.h"
>+#include "NetProto.h"
>+
>+typedef union _OVS_FWD_INFO *POVS_FWD_INFO;
> 
> typedef struct _O;
>@@ -66,7 +69,8 @@ NDIS_STATUS OvsEncapGre(POVS_VPORT_ENTRY vport,
>                         OvsIPv4unnelKey *tunKey,
>                         POVS_SWITCH_CONTEXT switchContext,
>                         POVS_PACKET_HDR_INFO layers,
>-                        PNET_BUFFER_LIST *newNbl);
>+                        PNET_BUFFER_LIST *newNbl,
>+                        POVS_FWD_INFO switchFwdInfo);
> 
> NDIS_STATUS OvsDecapGre(POVS_SWITCH_CONTEXT switchContext,
>                         PNET_BUFFER_LIST curNbl,
>diff --git a/datapath-windows/ovsext/IpHelper.c
>b/datapath-windows/ovsext/IpHelper.c
>index 636cf95..0220572 100644
>--- a/datapath-windows/ovsext/IpHelper.c
>+++ b/datapath-windows/ovsext/IpHelper.c
>@@ -19,6 +19,8 @@
> #include "Switch.h"
> #include "Jhash.h"
> 
>+extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
>+
> #ifdef OVS_DBG_MOD
> #undef OVS_DBG_MOD
> #endif
>@@ -26,28 +28,44 @@
> #include "Debug.h"
> 
> /*
>- * Fow now, we assume only one internal adapter
>+ * IpHelper supports multiple iternal adapters.
>  */
> 
> KSTART_ROUTINE             OvsStartIpHelper;
> 
>+/* Contains the entries of internal adapter objects. */
>+static LIST_ENTRY          ovsInstanceList;
>+
>+/* Passive-level loc used to protect the internal adapter object list.
>*/
>+static ERESOURCE           ovsInstanceListLock;
> 
> /*
>+ * This structure is used to define each adapter instance.
>+ *
>+ * Note:
>  * Only when the internal IP is configured and virtual
>  * internal port is connected, the IP helper request can be
>  * queued.
>+ *
>+ * We only keep internal IP for reference, it will not be used for
>determining
>+ * SRC IP of the Tunnel.
>+ *
>+ * The lock must not raise the IRQL higher than PASSIVE_LEVEL in order
>for the
>+ * route manipulation functions, i.e. GetBestRoute, to work.
>  */
>-static BOOLEAN             ovsInternalIPConfigured;
>-static BOOLEAN             ovsInternalAdapterUp;
>-static GUID                ovsInternalNetCfgId;
>-static MIB_IF_ROW2         ovsInternalRow;
>-static MIB_IPINTERFACE_ROW ovsInternalIPRow;
>-
>-/* we only keep one internal IP for reference, it will not be used for
>- * determining SRC IP of Tunnel
>- */
>-static UINT32               ovsInternalIP;
>+typedef struct _OVS_IPHELPER_INSTANCE
>+{
>+    LIST_ENTRY          link;
>+
>+    BOOLEAN             isIpConfigured;
>+    UINT32              portNo;
>+    GUID                netCfgId;
>+    MIB_IF_ROW2         internalRow;
>+    MIB_IPINTERFACE_ROW internalIPRow;
>+    UINT32              ipAddress;
> 
>+    ERESOURCE           lock;
>+} OVS_IPHELPER_INSTANCE, *POVS_IPHELPER_INSTANCE;
> 
> /*
>  * FWD_ENTRY -------->  IPFORWARD_ENTRY
>@@ -82,9 +100,22 @@ static OVS_IP_HELPER_HREAD_CONTEXT
>ovsIpHelperThreadContext;
> static POVS_IPFORWARD_ENTRY OvsLookupIPForwardEntry(PIP_ADDRESS_PREFIX
>prefix);
> static VOID OvsRemoveIPForwardEntry(POVS_IPFORWARD_ENTRY ipf);
> static VOID OvsRemoveAllFwdEntriesWithSrc(UINT32 ipAddr);
>+static VOID OvsRemoveAllFwdEntriesWithPortNo(UINT32 portNo);
> static VOID OvsCleanupIpHelperRequestList(VOID);
> static VOID OvsCleanupFwdTable(VOID);
> static VOID OvsAddToSortedNeighList(POVS_IPNEIGH_ENTRY ipn);
>+static POVS_IPHELPER_INSTANCE OvsIpHelperAllocateInstance(
>+                                               POVS_IP_HELPER_REQUEST
>request);
>+static VOID OvsIpHelperDeleteInstance(POVS_IPHELPER_INSTANCE instance);
>+
>+
>+static VOID
>+OvsDumpMessageWithGuid(char* message, GUID guid)
>+{
>+    OVS_LOG_INFO(message, guid.Data1, guid.Data2, guid.Data3,
>+                 *(UINT16 *)guid.Data4, guid.Data4[2], guid.Data4[3],
>+                 guid.Data4[], guid.Data4[5], guid.Data4[6],
>guid.Data4[7]);
>+}
> 
> static VOID
> OvsDumpIfRow(PMIB_IF_ROW2 ifRow)
>@@ -94,17 +125,9 @@ OvsDumpIfRow(PMIB_IF_ROW2 ifRow)
>                  ifRow->InterfaceLuid.Info.IfType);
>     OVS_LOG_INFO("InterfaceIndex: %d", ifRow->InterfaceIndex);
> 
>-    OVS_LOG_INFO("Interfce GUID:
>%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
>-                 ifRow>InterfaceGuid.Data1,
>-                 ifRow->InterfaceGuid.Data2,
>-                 ifRow->InterfaceGuid.Data3,
>-                 *(UINT6 *)ifRow->InterfaceGuid.Data4,
>-                 ifRow->InterfaceGuid.Data4[2],
>-                 ifRow->InterfaceGuid.Data4[3],
>-                 ifRow->InterfaceGuid.Data4[4],
>-                 ifRow->InterfaceGuid.Data4[5],
>-                 ifRow->InterfaceGuid.Data4[6],
>-                 ifRow->InterfaceGuid.Data4[7]);
>+    OvsDumpMessageWithGuid("Interface GUID: "
>+                 
>"%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
>+                           ifRow->InterfaceGuid);
>     OVS_LOG_INFO("Perm MAC Address: %02x:%02x:%02x:%02x:%02x:%02x",
>                  ifRow->PermanentPhysicalAddress[0],
>                  ifRow->PermanentPhysicalAddress[1],
>@@ -114,7 +137,6 @@ OvsDumpIfRow(PMIB_IF_ROW2 ifRow)
>                  ifRow->PermanentPhysicalAddress[5]);
> }
> 
>-
> static VOID
> OvsDumpIfTable(PMIB_IF_TABLE2 ifTable)
> {
>@@ -325,30 +347,72 @@ OvsDumpRoute(const SOCKADDR_INET *sourceAddress,
> 
> 
> NTSTATUS
>-OvsGetRoute(NET_LUID interfaceLuid,
>-            const SOCKADDR_INET *destinationAddress,
>+OvsGetRoute(SOCKADDR_INET *destinationAddress,
>            PMIB_IPFORWARD_ROW2 route,
>-            SOCKADDR_INET *sourceAddress)
>+            SOCKADDR_INET *sourceAddress,
>+            POVS_IPHELPER_INSTANCE *instance,
>+            POVS_VPORT_ENTRY* vport,
>+            UINT32 srcIp)
> {
>-    NTSTATUS status;
>+    NTSTATU status = STATUS_NETWORK_UNREACHABLE;
>+    NTSTATUS result = STATUS_SUCCESS;
>+    PLIST_ENTRY head, link, next;
>+    ULONG minMetric = MAXLONG;
> 
>     if (destinationAddress == NULL || route == NULL) {
>         return STATUS_INVALID_PARAMETER;
>     }
> 
>-    status = GetBestRoute2(&interfaceLuid, 0,
>-                           NULL, destinationAddress,
>-                           0, route, sourceAddress);
>+    ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE);
>+    head = &(ovsInstanceList);
>+    LIST_FORALL_SAFE(head, link, next) {
>+        SOCKADDR_INET crtSrcAddr = { 0 };
>+        MIB_IPFORWARD_ROW2 crtRoute = { 0 };
>+        POVS_IPHELPER_INSTANCE crtInstance = NULL;
>+        WCHAR interfaceName[IF_MAX_STRING_SIZE] = { 0 };
> 
>-    if (status != STATUS_SUCCESS) {
>-        UINT32 ipAddr = destinationAddress->Ipv4.sin_addr.s_addr;
>-        OVS_LOG_INFO("Fail to get route to %d.%d.%d.%d, status: %x",
>-                     ipAddr & 0xff, (ipAddr >> 8) & 0xff,
>-                     (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff,
>status);
>-        return status;
>+        crtIstance = CONTAINING_RECORD(link, OVS_IPHELPER_INSTANCE,
>link);
>+
>+        ExAcquireResourceExclusiveLite(&crtInstance->lock, TRUE);
>+        result = GetBestRoute2(&crtInstance->internalRow.InterfaceLuid,
>0,
>+                               NULL, destinationAddress, 0, &crtRoute,
>+                               &crtSrcAddr);
>+
>+        if (result != STATUS_SUCCESS) {
>+            ExReleaseResourceLite(&crtInstance->lock);
>+            continue;
>+        }
>+
>+        if (minMetric > crtRoute.Metric &&
>+            (!srcIp || srcIp == crtSrcAddr.Ipv4.sin_addr.S_un.S_addr)) {
>+            status = STATUS_SUCCESS;
>+            size_t len = 0;
>+            minMetric = crtRoute.Metric;
>+            OCK_STATE_EX lockState;
>+
>+            RtlCopyMemory(sourceAddress, &crtSrcAddr,
>sizeof(*sourceAddress));
>+            RtlCopyMemory(route, &crtRoute, sizeof(*route));
>+            *instance = crtInstance;
>+
>+            
>ConvertInterfaceLuidToAlias(&crtInstance->internalRow.InterfaceLuid,
>+                                        interfaceName,
>IF_MAX_STRING_SIZE + 1);
>+            RtlStringCbLengthW(interfaceName, IF_MAX_STRING_SIZE, &len);
>+
>+            if (gOvsSwitchContext != NULL) {
>+                NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock,
>+                                      &lockState, 0);
>+                *vport = OvsFindVportByHvNameW(gOvsSwitchContext,
>+                                               interfaceName,
>+                                               len);
>+                NdisReleaseRWLock(gOvsSwitchContext->dispatchLock,
>&lockState);
>+            }
>+        }
>+       ExReleaseResourceLite(&crtInstance->lock);
>     }
>+    ExReleaseResourceLite(&ovsInstanceListLock);
> 
>     OvsDumpRoute(sourceAddress, destinationAddress, route);
>+
>     return status;> }
> 
>@@ -358,8 +422,8 @@ OvsDumpIPNeigh(PMIB_IPNET_ROW2 ipNeigh)
>     UINT32 ipAddr = ipNeigh->Address.Ipv4.sin_addr.s_addr;
> 
>     OVS_LOG_INFO("Neigh: %d.%d.%d.%d",
>-                     ipAddr & 0xff, (ipAddr >> 8) & 0xff,
>-                     (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff);
>+                 ipAddr & xff, (ipAddr >> 8) & 0xff,
>+                 (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff);
>     OVS_LOG_INFO("MAC Address: %02x:02x:%02x:%02x:%02x:%02x",
>                  ipNeigh->PhysicalAddress[0],
>                  ipNeigh->PhysicalAddress[1],
>@@ -421,7 +485,8 @@ OvsResolveIPNeighEntry(PMIB_IPNET_ROW2 ipNeigh)
> 
> 
> NTSTATUS
>-OvsGetOrResolveIPNeigh(UINT32 ipAddr,
>+OvsGetOrResolveIPNeigh(MIB_IFROW2 ipRow,
>+                       UINT32 ipAddr,
>                        PMIB_IPNET_ROW2 ipNeigh)
> {
>     NTSTATUS status;
>@@ -429,8 +494,8 @@ OvsGetOrResolveIPNeigh(UINT32 ipAddr,
>     ASSERT(ipNeigh);
> 
>     RtlZeroMemory(ipNeigh, sizeof (*ipNeigh));
>-    ipNeigh->InterfaceLuid.Value = ovsInternalRow.InterfaceLuid.Value
>-    ipNeigh->InterfaceIndex = ovsInternalRow.InterfaceIndex;
>+    ipNeigh->InterfaceLuid.Value = ipRow.InterfaceLuid.Value;
>+    ipNeigh->InterfaceIndex = ipRow.InterfaceIndex;
>     ipNeigh->Address.si_family = AF_INET;
>     ipNeigh->Address.Ipv4.sin_addr.s_addr = ipAddr;
> 
>@@ -438,8 +503,8 @@ OvsGetOrResolveIPNeigh(UINT32 ipAddr,
> 
>     if (status != STATUS_SUCCESS) {
>         RtlZeroMemory(ipNeih, sizeof (*ipNeigh));
>-        ipNeigh->InterfaceLuid.Value =
>ovsInternalRow.InterfaceLuid.Value;
>-        ipNeigh->InterfaceIndex = ovsInternalRow.InterfaceIndex;
>+        ipNeigh->InterfaceLuid.Value = ipRow.InterfaceLuid.Value;
>+        ipNeigh->InterfaceIndex = ipRow.InterfaceIndex;
>        ipNeigh->Address.si_family = AF_INET;
>         ipNeigh->Address.Ipv4.sin_addr.s_addr = ipAddr;
>         status = OvsResolveIPNeighEntry(ipNeigh);
>@@ -447,57 +512,225 @@ OvsGetOrResolveIPNeigh(UINT32 ipAddr,
>     return status;
> }
> 
>+static __inline BOOLEAN
>+OvsCheckInstanceRow(PMIB_IF_ROW2 instanceRow,
>+                    PNET_LUID netLuid,
>+                    NET_IFINDEX ifIndex)
>+{
>+    return (instanceRow->InterfaceLuid.Info.NetLuidIndex ==
>+            etLuid->Info.NetLuidIndex &&
>+            instanceRow->InterfaceLuid.Info.IfType =
>+            netLuid->Info.IfType &&
>+            instanceRow->InterfaceInex ==
>+            ifIndex);
>+}
> 
> static VOID
>-OvsChangeCallbackIpInterface(PVOID context,
>-                             PMIB_IPINTERFACE_ROW ipRow,
>-                             MIB_NOTIFICATION_TYPE notificationType)
>+OvsUpdateIpInterfaceNotification(PMIB_IPINTERFACE_ROW ipRow)
> {
>-    UNREFERENCED_PARAMETER(context);
>-    switch (notificationType) {
>-    case MibParameterNotification:
>-    case MibAddInstance:
>-        if (ipRow->InterfaceLuid.Info.NetLuidIndex ==
>-            ovsInternalRow.InterfaceLuid.Info.NetLuidIndex &&
>-            ipRow->InterfaceLuid.Info.IfType ==
>-            ovsInternalRow.InterfaceLuid.Info.IfType &&
>-            ipRow->InterfaceIndex == ovsInternalRow.InterfaceIndex) {
>+    PLIST_ENTRY head, link, next;
>+
>+    ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE);
>+    head = &(ovsInstanceList);
>+    LIST_FORALL_SAFE(head, link, next) {
>+        POVS_IPHELPER_INSTANCE instance = NULL;
>+
>+        instance = CONTAINING_RECORD(link, OVS_IPHELPER_INSTANCE, link);
>+
>+        ExAcquireResourcexclusiveLite(&instance->lock, TRUE);
>+        if (OvsCheckInstanceRow(&instance->internalRow,
>+                                &ipRow->InterfaceLuid,
>+                                ipRow->InterfaceIndex)) {
>+
>             /*
>              * Update the IP Interface Row
>              */
>-            NdisAcquireSpinLock(&ovsIpHelperLock);
>-            RtlCopyMemory(&ovsInternalIPRow, ipRow,
>-                          sizeof (PMIB_IPINTERFACE_ROW));
>-            ovsInternalIPConfigured = TRUE;
>-            NdisReleaseSpinLock(&ovsIpHelperLock);
>+            RtlCopyMemory(&instance->internalIPRow, ipRow,
>+                          sizeof(PMIB_IPINTERFACE_ROW));
>+            instance->isIpConfigured = TRUE;
>+
>+            OVS_LOG_INFO("IP Interface with NetLuidIndex: %d, type: %d
>is %s",
>+                         ipRow->InterfaceLuid.Info.NetLuidIndex,
>+                         ipRow->InterfaceLuid.Info.IfType,
>+                         "modified");
>+
>+           ExReleaseResourceLite(&instance->lock);
>+            break;
>         }
>-       OVS_LOG_INFO("IP Interface with NetLuidIndex: %d, type: %d is
>%s",
>-                    ipRow->InterfaceLuid.Info.NetLuidIndex,
>-                     ipRo->InterfaceLuid.Info.IfType,
>-                     notificationType == MibAddInsance ? "added" :
>"modified");
>-        break;
>-    case MibDeleteInstance:
>-       OVS_LOG_INFO("IP Interface with NetLuidIndex: %d, type: %d,
>deleted",
>-                     ipRow->InterfaceLuid.Info.NetLuidIndex,
>-                    ipRow->InterfaceLuid.Info.IfType);
>-        if (ipRow->InterfaceLui.Info.NetLuidIndex ==
>-            ovsInternalRow.InterfaceLuid.Info.NetLuidIndex &&
>-            ipRow->InterfaceLuid.Info.IfType ==
>-            ovsInternalRow.InterfaceLuid.Info.IfType &&
>-            ipRow->InterfaceIndex == ovsInternalRow.InterfaceIndex) {
>+        ExReleaseResourceLite(&instance->lock);
>+    }
>+    ExReleaseResourceLite(&ovsInstanceListLock);
> 
>-            NdisAcquireSpinLock(&ovsIpHelperLock);
>-            ovsInternalIPConfigured = FALSE;
>-            NdisReleaseSpinLock(&ovsIpHelperLock);
>+    return;
>+}
>+
>+static VOID
>+OvsAddIpInterfaceNotification(PMIB_IPINTERFACE_ROW ipRow)
>+{
>+    PLIST_ENTRY head, link, next;
>+    BOOLEAN found = FALSE;
>+
>+    ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE);
>+    head = &(ovsInstanceList);
>+    LIST_FORALL_SAFE(head, link, next) {
>+        POVS_IPHELPER_INSTANCE instance = NULL;
>+
>+        instance = CONTAINING_RECORD(link, OVS_IPHELPER_INSTANCE, link);
>+
>+        ExAcquireResourceExclusiveLite(&instance->lock, TRUE);
>+        if (OvsCheckInstanceRow(&instance->internalRow,
>&ipRow->InterfaceLuid,
>+                                ipRow->InterfaceIndex)) {
>+
>+            instance->isIpConfigured = FALSE;
>+            ExReleaseResourceLite(&instance->lock);
>+
>+            found = TRUE;
>+
>+            break;
>+        }
>+        ExReleaseResourceLite(&instance->lock);
>+    }
>+    ExReleaseResourceLite(&ovsInstanceListLock);
> 
>-            OvsCleanupIpHelperRequestList();
>+    if (found != TRUE) {
>+        NTSTATUS status;
>+        PVS_IPHELPER_INSTANCE instance = NULL;
>+        MIB_UNICASTIPADDRESS_ROW ipEntry;
>+        BOOLEAN error = TRUE;
>+        LOCK_STATE_EX lockState;
> 
>-            OvsCleanupFwdTable();
>+       instance = (POVS_IPHELPER_INSTANCE)OvsAllocateMemoryWithTag(
>+            sizeof(*instance), OVS_IPHELPER_POOL_TAG);
>+        if (instance == NULL) {
>+            goto error;
>+        }
>+        RtlZeroMemory(instance, sizeof(*instance));
>+
>+        InitializeListHead(&instance->link);
>+        ExInitializeResourceLite(&instance->lock);
>+        WCHAR interfaceName[IF_MAX_STRING_SIZE] = { 0 };
>+        status = ConvertInterfaceLuidToAlias(ipRow->InterfaceLuid,
>+                                             interfaceName,
>+                                            IF_MAX_STRING_SIZE + 1);
>+        if (OvsSwitchContext == NULL) {
>+            goto error;
>+        }
>+        NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock,
>&lockState, 0);
>+        POVS_VPORT_ENTRY vport = OvsFindVportByHvNameW(gOvsSwitchContext,
>+                                                       interfaceName,
>+                                                       sizeof(WCHAR) *
>+                 
>wcslen(interfaceName));
>+
>+        if (vport != NULL) {
>+            RtlCopyMemory(&instance->netCfgId,
>+                          &vport->netCfgInstanceId,
>+                          sizeof(instance->netCfgId));
>+            instance->portNo = vport->portNo;
>+        }
>+        NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
>+        RtlZeroMemory(&instance->internalRow, sizeof(MIB_IF_ROW2));
>+        RtlZeroMemory(&instance->internalIPRow,
>sizeof(MIB_IPINTERFACE_ROW));
>+        status = OvsGetIfEntry(&instance->netCfgId,
>+                               &instance->internalRow);
>+
>+        if (status != STATUS_SUCCESS) {
>+            OvsDumpMessageWithGuid("Fail to get IF entry for internal
>port with GUID"
>+                                   "
>%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
>+                                  instance->netCfgId);
>+            goto error;
>         }
> 
>+        status = 
>OvsGetIPInterfaceEntry(instance->internalRow.InterfaceLuid,
>+                                        &instance->internalIPRow);
>+
>+        if (status == STATUS_SUCCESS) {
>+            instance->isIpConfigured = TRUE;
>+        } else {
>+           goto error;
>+        }
>+
>+        status = OvsGetIPEntry(instance->internalRow.InterfaceLuid,
>&ipEntry);
>+        if (status != STATUS_SUCCESS) {
>+            OvsDumpMessageWithGuid("Failed to get IP entry for internal
>port with GUID"
>+                                   "
>%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
>+                                   instance->netCfgId);
>+        }
>+
>+        ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE);
>+        InsertHeadList(&ovsInstanceList, &instance->link);
>+        ExReleaseResourceLite(&ovsInstanceListLock);
>+
>+        error = FALSE;
>+
>+error:
>+        if (error) {
>+            OvsIpHelperDeleteInstance(instance);
>+        }
>+    }
>+
>+    return;
>+}
>+
>+static VOID
>+OvsRemoveIpInterfaceNotification(PMIB_IPINTERFACE_ROW ipRow)
>+{
>+    PLIST_ENTRY head, link, next;
>+
>+    ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE);
>+    head = &(ovsInstanceList);
>+    LIST_FORALL_SAFE(head, link, next) {
>+        POVS_IPHELPER_INSTANCE instance = NULL;
>+
>+        instance = CONTAINING_RECORD(link, OVS_IPHELPER_INSTANCE, link);
>+
>+        ExAcquireResourceExclusiveLite(&instance->lock, TRUE);
>+        if (OvsCheckInstanceRow(&instance->internaRow,
>&ipRow->InterfaceLuid,
>+                                ipRow->InterfaceIndex)) {
>+
>+            instance->isIpConfigured = FALSE;
>+            RemoveEntryList(&instance->link);
>+
>+           ExReleaseResourceLite(&instance->lock);
>+            OvsIpHelperDeleteInstance(instance);
>+
>+            OVS_LOG_INFO("IP Interface with NetLuidIndex: %d, type: %d
>is "\
>+                         "deleted",
>+                         ipRow->InterfaceLuid.Info.NetLuidIndex,
>+                         ipRow->InterfaceLuid.Info.IfType);
>+
>+            break;
>+        }
>+        ExReleaseResourceLite(&instance->lock);
>+    }
>+    ExReleaseResourceLite(&ovsInstanceListLock);
>+
>+    if (IsListEmpty(&ovsInstanceList)) {
>+        OvsCleanupIpHelperRequestList();
>+       OvsCleanupFwdTable();
>+    }
>+
>+    return;
>+}
>+
>+static VOID
>+OvsChangeCllbackIpInterface(PVOID context,
>+                             PMIB_IPINTERFACE_ROW ipRow,
>+                             MIB_NOTIFICATION_TYPE notificationType)
>+{
>+    UNREFERENCED_PARAMETER(context);
>+    switch (notificationType) {
>+    case MibParameterNotification:
>+        OvsUpdateIpInterfaceNotification(ipRow);
>+        break;
>+    case MibAddInstance:
>+        OvsAddIpInterfaceNotification(ipRow);
>+        break;
>+
>+    case MibDeleteInstance:
>+        OvsRemoveIpInterfaceNotification(ipRow);
>         break;
>     case MibInitialNotification:
>-        OVS_LOG_INFO("Get Initial notification for IP Interface
>change.");
>+        OVS_LOG_INFO("Got Initial notification for IP Interface
>change.");
>     default:
>         return;
>     }
>@@ -529,10 +762,21 @@ OvsChangeCallbackIpRoute(PVOID context,
> 
>     case MibParameterNotification:
>     case MibDeleteInstance:
>+    {
>         ASSERT(ipRoute);
>         ipAddr = ipRoute->DestinationPrefix.Prefix.Ipv4.sin_addr.s_addr;
>         nextHop = ipRoute->NextHop.Ipv4.sin_addr.s_addr;
> 
>+        POVS_IPFORWARD_ENTRY ipf;
>+        LOCK_STATE_EX lockState;
>+
>+        NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0);
>+        ipf = OvsLookupIPForwardEntry(&ipRoute->DestinationPrefix);
>+        if (ipf != NULL) {
>+            OvsRemoveIPForwardEntry(ipf);
>+        }
>+        NdisReleaseRWLock(ovsTableLock, &lockState);
>+
>         OVS_LOG_INFO("IPRoute: To %d.%d.%d.%d/%d through %d.%d.%d.%d 
>%s.",
>                      ipAddr & 0xff, (ipAddr >> 8) & 0xff,
>                      (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff,
>@@ -541,24 +785,8 @@ OvsChangeCallbackIpRoute(PVOID context,
>                      (nextHop >> 16) & 0xff, (nextHop >> 24) & 0xff,
>                      notificationType == MibDeleteInstance ? "deleted" :
>                      "modified");
>-
>-        if (ipRoute->InterfaceLuid.Info.NetLuidIndex ==
>-            ovsInternalRow.InterfaceLuid.Info.NetLuidIndex &&
>-            ipRoute->InterfaceLuid.Info.IfType ==
>-            ovsInternalRow.InterfaceLuid.Info.IfType &&
>-            ipRoute->InterfaceIndex == ovsInternalRow.InterfaceIndex) {
>-
>-            POVS_IPFORWARD_ENTRY ipf;
>-            LOCK_STATE_EX lockState;
>-
>-            NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0);
>-            ipf = OvsLookupIPForwardEntry(&ipRoute->DestinationPrefix);
>-            if (ipf != NULL) {
>-                OvsRemoveIPForwardEntry(ipf);
>-            }
>-            NdisReleaseRWLock(ovsTableLock, &lockState);
>-        }
>         break;
>+    }
> 
>     case MibInitialNotification:
>         OVS_LOG_INFO("Get Initial notification for IP Route change.");
>@@ -579,40 +807,85 @@ OvsChangeCallbackUnicastIpAddress(PVOID context,
>     switch (notificationType) {
>     case MibParameterNotification:
>     case MibAddInstance:
>+    {
>+        PLIST_ENTRY head, link, next;
>+
>         ASSERT(unicastRow);
>         ipAddr = unicastRow->Address.Ipv4.sin_addr.s_addr;
>-        if (unicastRow->InterfaceLuid.Info.NetLuidIndex ==
>-            ovsInternalRow.InterfaceLuid.Info.NetLuidIndex &&
>-            unicastRow->InterfaceLuid.Info.IfType ==
>-            ovsInternalRow.InterfaceLuid.Info.IfType &&
>-            unicastRow->InterfaceIndex == ovsInternalRow.InterfaceIndex) 
>{
>-            ovsInternalIP = ipAddr;
>+
>+        ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE);
>+        head = &(ovsInstanceList);
>+        LIST_FORALL_SAFE(head, link, next) {
>+            POVS_IPHELPER_INSTANCE instance = NULL;
>+
>+            instance = CONTAINING_RECORD(link, OVS_IPHELPER_INSTANCE, 
>link);
>+
>+            ExAcquireResourceExclusiveLite(&instance->lock, TRUE);
>+            if (instance->isIpConfigured &&
>+                OvsCheckInstanceRow(&instance->internalRow,
>+                                    &unicastRow->InterfaceLuid,
>+                                    unicastRow->InterfaceIndex)) {
>+
>+                instance->ipAddress = ipAddr;
>+
>+                OVS_LOG_INFO("IP Address: %d.%d.%d.%d is %s",
>+                             ipAddr & 0xff, (ipAddr >> 8) & 0xff,
>+                             (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 
>0xff,
>+                             notificationType == MibAddInstance ? 
>"added": "modified");
>+
>+                ExReleaseResourceLite(&instance->lock);
>+                break;
>+            }
>+            ExReleaseResourceLite(&instance->lock);
>         }
>-        OVS_LOG_INFO("IP Address: %d.%d.%d.%d is %s",
>-                     ipAddr & 0xff, (ipAddr >> 8) & 0xff,
>-                     (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff,
>-                     notificationType == MibAddInstance ? "added": 
>"modified");
>+        ExReleaseResourceLite(&ovsInstanceListLock);
>+
>         break;
>+    }
> 
>     case MibDeleteInstance:
>+    {
>+        PLIST_ENTRY head, link, next;
>+        LOCK_STATE_EX lockState;
>+        BOOLEAN found = FALSE;
>+
>         ASSERT(unicastRow);
>         ipAddr = unicastRow->Address.Ipv4.sin_addr.s_addr;
>-        OVS_LOG_INFO("IP Address removed: %d.%d.%d.%d",
>-                     ipAddr & 0xff, (ipAddr >> 8) & 0xff,
>-                     (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff);
>-        if (unicastRow->InterfaceLuid.Info.NetLuidIndex ==
>-            ovsInternalRow.InterfaceLuid.Info.NetLuidIndex &&
>-            unicastRow->InterfaceLuid.Info.IfType ==
>-            ovsInternalRow.InterfaceLuid.Info.IfType &&
>-            unicastRow->InterfaceIndex == ovsInternalRow.InterfaceIndex) 
>{
> 
>-            LOCK_STATE_EX lockState;
>+        ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE);
>+        head = &(ovsInstanceList);
>+        LIST_FORALL_SAFE(head, link, next) {
>+            POVS_IPHELPER_INSTANCE instance = NULL;
>+
>+            instance = CONTAINING_RECORD(link, OVS_IPHELPER_INSTANCE, 
>link);
>+
>+            ExAcquireResourceExclusiveLite(&instance->lock, TRUE);
>+            if (instance->isIpConfigured &&
>+                OvsCheckInstanceRow(&instance->internalRow,
>+                                    &unicastRow->InterfaceLuid,
>+                                    unicastRow->InterfaceIndex)) {
>+
>+                found = TRUE;
>+
>+                ExReleaseResourceLite(&instance->lock);
>+                break;
>+            }
>+            ExReleaseResourceLite(&instance->lock);
>+        }
>+        ExReleaseResourceLite(&ovsInstanceListLock);
>+
>+        if (found) {
>             NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0);
>             OvsRemoveAllFwdEntriesWithSrc(ipAddr);
>             NdisReleaseRWLock(ovsTableLock, &lockState);
> 
>+            OVS_LOG_INFO("IP Address removed: %d.%d.%d.%d",
>+                         ipAddr & 0xff, (ipAddr >> 8) & 0xff,
>+                         (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff);
>         }
>+
>         break;
>+    }
> 
>     case MibInitialNotification:
>         OVS_LOG_INFO("Get Initial notification for Unicast IP Address 
>change.");
>@@ -651,7 +924,7 @@ OvsRegisterChangeNotification()
>                                      &ipInterfaceNotificationHandle);
>     if (status != STATUS_SUCCESS) {
>         OVS_LOG_ERROR("Fail to register Notify IP interface change, 
>status:%x.",
>-                     status);
>+                      status);
>         return status;
>     }
> 
>@@ -659,7 +932,7 @@ OvsRegisterChangeNotification()
>                                 TRUE, &ipRouteNotificationHandle);
>     if (status != STATUS_SUCCESS) {
>         OVS_LOG_ERROR("Fail to regiter ip route change, status: %x.",
>-                     status);
>+                      status);
>         goto register_cleanup;
>     }
>     status = NotifyUnicastIpAddressChange(AF_INET,
>@@ -682,10 +955,11 @@ static POVS_IPNEIGH_ENTRY
> OvsLookupIPNeighEntry(UINT32 ipAddr)
> {
>     PLIST_ENTRY link;
>-    POVS_IPNEIGH_ENTRY entry;
>     UINT32 hash = OvsJhashWords(&ipAddr, 1, OVS_HASH_BASIS);
> 
>     LIST_FORALL(&ovsNeighHashTable[hash & OVS_NEIGH_HASH_TABLE_MASK], 
>link) {
>+        POVS_IPNEIGH_ENTRY entry;
>+
>         entry = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, link);
>         if (entry->ipAddr == ipAddr) {
>             return entry;
>@@ -709,7 +983,6 @@ OvsLookupIPForwardEntry(PIP_ADDRESS_PREFIX prefix)
> {
> 
>     PLIST_ENTRY link;
>-    POVS_IPFORWARD_ENTRY ipfEntry;
>     UINT32 hash;
>     ASSERT(prefix->Prefix.si_family == AF_INET);
> 
>@@ -720,6 +993,8 @@ OvsLookupIPForwardEntry(PIP_ADDRESS_PREFIX prefix)
> 
>     hash = OvsHashIPPrefix(prefix);
>     LIST_FORALL(&ovsRouteHashTable[hash & OVS_ROUTE_HASH_TABLE_MASK], 
>link) {
>+        POVS_IPFORWARD_ENTRY ipfEntry;
>+
>         ipfEntry = CONTAINING_RECORD(link, OVS_IPFORWARD_ENTRY, link);
>         if (ipfEntry->prefix.PrefixLength == prefix->PrefixLength &&
>             ipfEntry->prefix.Prefix.Ipv4.sin_addr.s_addr ==
>@@ -732,15 +1007,17 @@ OvsLookupIPForwardEntry(PIP_ADDRESS_PREFIX prefix)
> 
> 
> static POVS_FWD_ENTRY
>-OvsLookupIPFwdEntry(UINT32 dstIp)
>+OvsLookupIPFwdEntry(UINT32 srcIp, UINT32 dstIp)
> {
>     PLIST_ENTRY link;
>-    POVS_FWD_ENTRY entry;
>     UINT32 hash = OvsJhashWords(&dstIp, 1, OVS_HASH_BASIS);
> 
>     LIST_FORALL(&ovsFwdHashTable[hash & OVS_FWD_HASH_TABLE_MASK], link) {
>+        POVS_FWD_ENTRY entry;
>+
>         entry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, link);
>-        if (entry->info.dstIpAddr == dstIp) {
>+        if (entry->info.dstIpAddr == dstIp &&
>+            (!srcIp || entry->info.srcIpAddr == srcIp)) {
>             return entry;
>         }
>     }
>@@ -749,7 +1026,8 @@ OvsLookupIPFwdEntry(UINT32 dstIp)
> 
> 
> NTSTATUS
>-OvsLookupIPFwdInfo(UINT32 dstIp,
>+OvsLookupIPFwdInfo(UINT32 srcIp,
>+                   UINT32 dstIp,
>                    POVS_FWD_INFO info)
> {
>     POVS_FWD_ENTRY entry;
>@@ -757,11 +1035,10 @@ OvsLookupIPFwdInfo(UINT32 dstIp,
>     NTSTATUS status = STATUS_NOT_FOUND;
> 
>     NdisAcquireRWLockRead(ovsTableLock, &lockState, 0);
>-    entry = OvsLookupIPFwdEntry(dstIp);
>+    entry = OvsLookupIPFwdEntry(srcIp, dstIp);
>     if (entry) {
>-        info->value[0] = entry->info.value[0];
>-        info->value[1] = entry->info.value[1];
>-        info->value[2] = entry->info.value[2];
>+        RtlCopyMemory(info->value, entry->info.value,
>+                      sizeof entry->info.value);
>         status = STATUS_SUCCESS;
>     }
>     NdisReleaseRWLock(ovsTableLock, &lockState);
>@@ -770,7 +1047,8 @@ OvsLookupIPFwdInfo(UINT32 dstIp,
> 
> 
> static POVS_IPNEIGH_ENTRY
>-OvsCreateIPNeighEntry(PMIB_IPNET_ROW2 ipNeigh)
>+OvsCreateIPNeighEntry(PMIB_IPNET_ROW2 ipNeigh,
>+                      POVS_IPHELPER_INSTANCE instance)
> {
> 
>     POVS_IPNEIGH_ENTRY entry;
>@@ -790,6 +1068,7 @@ OvsCreateIPNeighEntry(PMIB_IPNET_ROW2 ipNeigh)
>     RtlCopyMemory(entry->macAddr, ipNeigh->PhysicalAddress,
>                   ETH_ADDR_LEN);
>     InitializeListHead(&entry->fwdList);
>+    entry->context = (PVOID)instance;
> 
>     return entry;
> }
>@@ -798,7 +1077,6 @@ OvsCreateIPNeighEntry(PMIB_IPNET_ROW2 ipNeigh)
> static POVS_IPFORWARD_ENTRY
> OvsCreateIPForwardEntry(PMIB_IPFORWARD_ROW2 ipRoute)
> {
>-
>     POVS_IPFORWARD_ENTRY entry;
> 
>     ASSERT(ipRoute);
>@@ -876,12 +1154,13 @@ OvsRemoveFwdEntry(POVS_FWD_ENTRY fwdEntry)
> static VOID
> OvsRemoveIPForwardEntry(POVS_IPFORWARD_ENTRY ipf)
> {
>-    POVS_FWD_ENTRY fwdEntry;
>     PLIST_ENTRY link, next;
> 
>     ipf->refCount++;
> 
>     LIST_FORALL_SAFE(&ipf->fwdList, link, next) {
>+        POVS_FWD_ENTRY fwdEntry;
>+
>         fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipfLink);
>         OvsRemoveFwdEntry(fwdEntry);
>     }
>@@ -896,11 +1175,12 @@ static VOID
> OvsRemoveIPNeighEntry(POVS_IPNEIGH_ENTRY ipn)
> {
>     PLIST_ENTRY link, next;
>-    POVS_FWD_ENTRY fwdEntry;
> 
>     ipn->refCount++;
> 
>     LIST_FORALL_SAFE(&ipn->fwdList, link, next) {
>+        POVS_FWD_ENTRY fwdEntry;
>+
>         fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipnLink);
>         OvsRemoveFwdEntry(fwdEntry);
>     }
>@@ -919,10 +1199,10 @@ static VOID
> OvsAddToSortedNeighList(POVS_IPNEIGH_ENTRY ipn)
> {
>     PLIST_ENTRY link;
>-    POVS_IPNEIGH_ENTRY entry;
> 
>     if (!IsListEmpty(&ovsSortedIPNeighList)) {
>         link = ovsSortedIPNeighList.Blink;
>+        POVS_IPNEIGH_ENTRY entry;
>         entry = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, slink);
>         if (entry->timeout > ipn->timeout) {
>             ipn->timeout++;
>@@ -973,11 +1253,12 @@ static VOID
> OvsRemoveAllFwdEntriesWithSrc(UINT32 ipAddr)
> {
>     UINT32 i;
>-    POVS_FWD_ENTRY fwdEntry;
>     PLIST_ENTRY link, next;
> 
>     for (i = 0; i < OVS_FWD_HASH_TABLE_SIZE; i++) {
>         LIST_FORALL_SAFE(&ovsFwdHashTable[i], link, next) {
>+            POVS_FWD_ENTRY fwdEntry;
>+
>             fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, link);
>             if (fwdEntry->info.srcIpAddr == ipAddr) {
>                 OvsRemoveFwdEntry(fwdEntry);
>@@ -988,19 +1269,38 @@ OvsRemoveAllFwdEntriesWithSrc(UINT32 ipAddr)
> 
> 
> static VOID
>+OvsRemoveAllFwdEntriesWithPortNo(UINT32 portNo)
>+{
>+    UINT32 i;
>+    PLIST_ENTRY link, next;
>+
>+    for (i = 0; i < OVS_FWD_HASH_TABLE_SIZE; i++) {
>+        LIST_FORALL_SAFE(&ovsFwdHashTable[i], link, next) {
>+            POVS_FWD_ENTRY fwdEntry;
>+
>+            fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, link);
>+            if (fwdEntry->info.srcPortNo == portNo) {
>+                OvsRemoveFwdEntry(fwdEntry);
>+            }
>+        }
>+    }
>+}
>+
>+static VOID
> OvsCleanupFwdTable(VOID)
> {
>     PLIST_ENTRY link, next;
>-    POVS_IPNEIGH_ENTRY ipn;
>     UINT32 i;
>     LOCK_STATE_EX lockState;
> 
>     NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0);
>     if (ovsNumFwdEntries) {
>-       LIST_FORALL_SAFE(&ovsSortedIPNeighList, link, next) {
>-           ipn = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, slink);
>-           OvsRemoveIPNeighEntry(ipn);
>-       }
>+        LIST_FORALL_SAFE(&ovsSortedIPNeighList, link, next) {
>+            POVS_IPNEIGH_ENTRY ipn;
>+
>+            ipn = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, slink);
>+            OvsRemoveIPNeighEntry(ipn);
>+        }
>     }
>     for (i = 0; i < OVS_FWD_HASH_TABLE_SIZE; i++) {
>         ASSERT(IsListEmpty(&ovsFwdHashTable[i]));
>@@ -1017,20 +1317,16 @@ OvsCleanupIpHelperRequestList(VOID)
> {
>     LIST_ENTRY list;
>     PLIST_ENTRY next, link;
>-    POVS_IP_HELPER_REQUEST request;
> 
>     NdisAcquireSpinLock(&ovsIpHelperLock);
>-    if (ovsNumIpHelperRequests == 0) {
>-       NdisReleaseSpinLock(&ovsIpHelperLock);
>-       return;
>-    }
>-
>     InitializeListHead(&list);
>-    OvsAppendList(&list,  &ovsIpHelperRequestList);
>+    OvsAppendList(&list, &ovsIpHelperRequestList);
>     ovsNumIpHelperRequests = 0;
>     NdisReleaseSpinLock(&ovsIpHelperLock);
> 
>     LIST_FORALL_SAFE(&list, link, next) {
>+        POVS_IP_HELPER_REQUEST request;
>+
>         request = CONTAINING_RECORD(link, OVS_IP_HELPER_REQUEST, link);
> 
>         if (request->command == OVS_IP_HELPER_FWD_REQUEST &&
>@@ -1056,27 +1352,40 @@ OvsWakeupIPHelper(VOID)
> }
> 
> VOID
>-OvsInternalAdapterDown(VOID)
>+OvsInternalAdapterDown(UINT32 portNo,
>+                       GUID netCfgInstanceId)
> {
>-    NdisAcquireSpinLock(&ovsIpHelperLock);
>-    ovsInternalAdapterUp = FALSE;
>-    ovsInternalIPConfigured = FALSE;
>-    NdisReleaseSpinLock(&ovsIpHelperLock);
>+    POVS_IP_HELPER_REQUEST request;
> 
>-    OvsCleanupIpHelperRequestList();
>+    request = (POVS_IP_HELPER_REQUEST)OvsAllocateMemoryWithTag(
>+        sizeof(OVS_IP_HELPER_REQUEST), OVS_IPHELPER_POOL_TAG);
>+    if (request == NULL) {
>+        OVS_LOG_ERROR("Fail to initialize Internal Adapter");
>+        return;
>+    }
>+    RtlZeroMemory(request, sizeof (OVS_IP_HELPER_REQUEST));
>+    RtlCopyMemory(&request->instanceReq.netCfgInstanceId,
>+                  &netCfgInstanceId,
>+                  sizeof(netCfgInstanceId));
>+    request->command = OVS_IP_HELPER_INTERNAL_ADAPTER_DOWN;
>+    request->instanceReq.portNo = portNo;
> 
>-    OvsCleanupFwdTable();
>+    NdisAcquireSpinLock(&ovsIpHelperLock);
>+    InsertHeadList(&ovsIpHelperRequestList, &request->link);
>+    ovsNumIpHelperRequests++;
>+    if (ovsNumIpHelperRequests == 1) {
>+        OvsWakeupIPHelper();
>+    }
>+    NdisReleaseSpinLock(&ovsIpHelperLock);
> }
> 
> 
> VOID
>-OvsInternalAdapterUp(GUID *netCfgInstanceId)
>+OvsInternalAdapterUp(UINT32 portNo,
>+                     GUID *netCfgInstanceId)
> {
>     POVS_IP_HELPER_REQUEST request;
> 
>-    RtlCopyMemory(&ovsInternalNetCfgId, netCfgInstanceId, sizeof (GUID));
>-    RtlZeroMemory(&ovsInternalRow, sizeof (MIB_IF_ROW2));
>-
>     request = (POVS_IP_HELPER_REQUEST)OvsAllocateMemoryWithTag(
>         sizeof(OVS_IP_HELPER_REQUEST), OVS_IPHELPER_POOL_TAG);
>     if (request == NULL) {
>@@ -1084,10 +1393,13 @@ OvsInternalAdapterUp(GUID *netCfgInstanceId)
>         return;
>     }
>     RtlZeroMemory(request, sizeof (OVS_IP_HELPER_REQUEST));
>+    RtlCopyMemory(&request->instanceReq.netCfgInstanceId,
>+                  netCfgInstanceId,
>+                  sizeof(*netCfgInstanceId));
>     request->command = OVS_IP_HELPER_INTERNAL_ADAPTER_UP;
>+    request->instanceReq.portNo = portNo;
> 
>     NdisAcquireSpinLock(&ovsIpHelperLock);
>-    ovsInternalAdapterUp = TRUE;
>     InsertHeadList(&ovsIpHelperRequestList, &request->link);
>     ovsNumIpHelperRequests++;
>     if (ovsNumIpHelperRequests == 1) {
>@@ -1099,58 +1411,116 @@ OvsInternalAdapterUp(GUID *netCfgInstanceId)
> }
> 
> 
>+static POVS_IPHELPER_INSTANCE
>+OvsIpHelperAllocateInstance(POVS_IP_HELPER_REQUEST request)
>+{
>+    POVS_IPHELPER_INSTANCE instance = NULL;
>+
>+    instance = (POVS_IPHELPER_INSTANCE)OvsAllocateMemoryWithTag(
>+        sizeof(*instance), OVS_IPHELPER_POOL_TAG);
>+    if (instance) {
>+        RtlZeroMemory(instance, sizeof(*instance));
>+
>+        RtlCopyMemory(&instance->netCfgId,
>+                      &request->instanceReq.netCfgInstanceId,
>+                      sizeof(instance->netCfgId));
>+        instance->portNo = request->instanceReq.portNo;
>+
>+        InitializeListHead(&instance->link);
>+        ExInitializeResourceLite(&instance->lock);
>+    }
>+
>+    return instance;
>+}
>+
>+
>+static VOID
>+OvsIpHelperDeleteInstance(POVS_IPHELPER_INSTANCE instance)
>+{
>+    if (instance) {
>+        ExDeleteResourceLite(&instance->lock);
>+        OvsFreeMemoryWithTag(instance, OVS_IPHELPER_POOL_TAG);
>+    }
>+}
>+
>+
>+static VOID
>+OvsIpHelperDeleteAllInstances()
>+{
>+    PLIST_ENTRY head, link, next;
>+
>+    ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE);
>+    head = &ovsInstanceList;
>+    if (!IsListEmpty(head)) {
>+        LIST_FORALL_SAFE(head, link, next) {
>+            POVS_IPHELPER_INSTANCE instance = NULL;
>+            instance = CONTAINING_RECORD(link, OVS_IPHELPER_INSTANCE, 
>link);
>+
>+            ExAcquireResourceExclusiveLite(&instance->lock, TRUE);
>+
>+            instance->isIpConfigured = FALSE;
>+            RemoveEntryList(&instance->link);
>+
>+            ExReleaseResourceLite(&instance->lock);
>+
>+            OvsIpHelperDeleteInstance(instance);
>+        }
>+    }
>+    ExReleaseResourceLite(&ovsInstanceListLock);
>+}
>+
>+
> static VOID
> OvsHandleInternalAdapterUp(POVS_IP_HELPER_REQUEST request)
> {
>     NTSTATUS status;
>+    POVS_IPHELPER_INSTANCE instance = NULL;
>     MIB_UNICASTIPADDRESS_ROW ipEntry;
>-    GUID *netCfgInstanceId = &ovsInternalNetCfgId;
>+    BOOLEAN error = TRUE;
> 
>-    OvsFreeMemoryWithTag(request, OVS_IPHELPER_POOL_TAG);
>+    do {
>+        instance = OvsIpHelperAllocateInstance(request);
>+        if (instance == NULL) {
>+            break;
>+        }
>+        RtlZeroMemory(&instance->internalRow, sizeof(MIB_IF_ROW2));
>+        RtlZeroMemory(&instance->internalIPRow, 
>sizeof(MIB_IPINTERFACE_ROW));
>+        status = OvsGetIfEntry(&instance->netCfgId,
>+                               &instance->internalRow);
> 
>-    status = OvsGetIfEntry(&ovsInternalNetCfgId, &ovsInternalRow);
>+        if (status != STATUS_SUCCESS) {
>+            OvsDumpMessageWithGuid("Fail to get IF entry for internal 
>port with GUID"
>+                                   "  
>%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
>+                                   instance->netCfgId);
>+            break;
>+        }
> 
>-    if (status != STATUS_SUCCESS) {
>-        OVS_LOG_ERROR("Fali to get IF entry for internal port with GUID"
>-                      "  %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
>-                      netCfgInstanceId->Data1,
>-                      netCfgInstanceId->Data2,
>-                      netCfgInstanceId->Data3,
>-                      *(UINT16 *)netCfgInstanceId->Data4,
>-                      netCfgInstanceId->Data4[2],
>-                      netCfgInstanceId->Data4[3],
>-                      netCfgInstanceId->Data4[4],
>-                      netCfgInstanceId->Data4[5],
>-                      netCfgInstanceId->Data4[6],
>-                      netCfgInstanceId->Data4[7]);
>-        return;
>-    }
>+        status = 
>OvsGetIPInterfaceEntry(instance->internalRow.InterfaceLuid,
>+                                        &instance->internalIPRow);
> 
>-    status = OvsGetIPInterfaceEntry(ovsInternalRow.InterfaceLuid,
>-                                    &ovsInternalIPRow);
>+        if (status == STATUS_SUCCESS) {
>+            instance->isIpConfigured = TRUE;
>+        } else {
>+            break;
>+        }
> 
>-    if (status == STATUS_SUCCESS) {
>-        NdisAcquireSpinLock(&ovsIpHelperLock);
>-        ovsInternalIPConfigured = TRUE;
>-        NdisReleaseSpinLock(&ovsIpHelperLock);
>-    } else {
>-        return;
>-    }
>+        status = OvsGetIPEntry(instance->internalRow.InterfaceLuid, 
>&ipEntry);
>+        if (status != STATUS_SUCCESS) {
>+            OvsDumpMessageWithGuid("Fail to get IP entry for internal 
>port with GUID"
>+                                   "  
>%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
>+                                   instance->netCfgId);
>+        }
> 
>-    status = OvsGetIPEntry(ovsInternalRow.InterfaceLuid, &ipEntry);
>-    if (status != STATUS_SUCCESS) {
>-        OVS_LOG_INFO("Fali to get IP entry for internal port with GUID"
>-                     "  %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
>-                     netCfgInstanceId->Data1,
>-                     netCfgInstanceId->Data2,
>-                     netCfgInstanceId->Data3,
>-                     *(UINT16 *)netCfgInstanceId->Data4,
>-                     netCfgInstanceId->Data4[2],
>-                     netCfgInstanceId->Data4[3],
>-                     netCfgInstanceId->Data4[4],
>-                     netCfgInstanceId->Data4[5],
>-                     netCfgInstanceId->Data4[6],
>-                     netCfgInstanceId->Data4[7]);
>+        ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE);
>+        InsertHeadList(&ovsInstanceList, &instance->link);
>+        ExReleaseResourceLite(&ovsInstanceListLock);
>+
>+        error = FALSE;
>+    } while (error);
>+
>+    OvsFreeMemoryWithTag(request, OVS_IPHELPER_POOL_TAG);
>+    if (error) {
>+        OvsIpHelperDeleteInstance(instance);
>     }
> }
> 
>@@ -1158,15 +1528,11 @@ OvsHandleInternalAdapterUp(POVS_IP_HELPER_REQUEST 
>request)
> static NTSTATUS
> OvsEnqueueIpHelperRequest(POVS_IP_HELPER_REQUEST request)
> {
>-
>-    NdisAcquireSpinLock(&ovsIpHelperLock);
>-
>-    if (ovsInternalAdapterUp == FALSE ||
>-        ovsInternalIPConfigured == FALSE) {
>-        NdisReleaseSpinLock(&ovsIpHelperLock);
>+    if (IsListEmpty(&ovsInstanceList)) {
>         OvsFreeMemoryWithTag(request, OVS_IPHELPER_POOL_TAG);
>         return STATUS_NDIS_ADAPTER_NOT_READY;
>     } else {
>+        NdisAcquireSpinLock(&ovsIpHelperLock);
>         InsertHeadList(&ovsIpHelperRequestList, &request->link);
>         ovsNumIpHelperRequests++;
>         if (ovsNumIpHelperRequests == 1) {
>@@ -1214,7 +1580,7 @@ OvsHandleFwdRequest(POVS_IP_HELPER_REQUEST request)
>     NTSTATUS status;
>     MIB_IPFORWARD_ROW2 ipRoute;
>     MIB_IPNET_ROW2 ipNeigh;
>-    OVS_FWD_INFO fwdInfo;
>+    OVS_FWD_INFO fwdInfo = { 0 };
>     UINT32 ipAddr;
>     UINT32 srcAddr;
>     POVS_FWD_ENTRY fwdEntry = NULL;
>@@ -1224,8 +1590,10 @@ OvsHandleFwdRequest(POVS_IP_HELPER_REQUEST request)
>     BOOLEAN  newIPF = FALSE;
>     BOOLEAN  newIPN = FALSE;
>     BOOLEAN  newFWD = FALSE;
>+    POVS_IPHELPER_INSTANCE instance = NULL;
> 
>-    status = OvsLookupIPFwdInfo(request->fwdReq.tunnelKey.dst,
>+    status = OvsLookupIPFwdInfo(request->fwdReq.tunnelKey.src,
>+                                request->fwdReq.tunnelKey.dst,
>                                 &fwdInfo);
>     if (status == STATUS_SUCCESS) {
>         goto fwd_handle_nbl;
>@@ -1238,10 +1606,23 @@ OvsHandleFwdRequest(POVS_IP_HELPER_REQUEST 
>request)
>     dst.si_family = AF_INET;
>     dst.Ipv4.sin_addr.s_addr = request->fwdReq.tunnelKey.dst;
> 
>-    status = OvsGetRoute(ovsInternalRow.InterfaceLuid, &dst, &ipRoute, 
>&src);
>-    if (status != STATUS_SUCCESS) {
>+    status = OvsGetRoute(&dst, &ipRoute, &src, &instance, 
>&fwdInfo.vport, request->fwdReq.tunnelKey.src);
>+    if (request->fwdReq.tunnelKey.src && request->fwdReq.tunnelKey.src 
>!= src.Ipv4.sin_addr.s_addr) {
>+        UINT32 ipAddr = dst.Ipv4.sin_addr.s_addr;
>+        OVS_LOG_INFO("Fail to get route to %d.%d.%d.%d, status: %x",
>+            ipAddr & 0xff, (ipAddr >> 8) & 0xff,
>+            (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, status);
>+        goto fwd_handle_nbl;
>+    }
>+    if (status != STATUS_SUCCESS || instance == NULL) {
>+        UINT32 ipAddr = dst.Ipv4.sin_addr.s_addr;
>+        OVS_LOG_INFO("Fail to get route to %d.%d.%d.%d, status: %x",
>+                     ipAddr & 0xff, (ipAddr >> 8) & 0xff,
>+                     (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, 
>status);
>         goto fwd_handle_nbl;
>     }
>+
>+    ExAcquireResourceExclusiveLite(&instance->lock, TRUE);
>     srcAddr = src.Ipv4.sin_addr.s_addr;
> 
>     /* find IPNeigh */
>@@ -1254,13 +1635,16 @@ OvsHandleFwdRequest(POVS_IP_HELPER_REQUEST 
>request)
>         }
>         NdisReleaseRWLock(ovsTableLock, &lockState);
>     }
>+
>     RtlZeroMemory(&ipNeigh, sizeof (ipNeigh));
>-    ipNeigh.InterfaceLuid.Value = ovsInternalRow.InterfaceLuid.Value;
>+    ipNeigh.InterfaceLuid.Value = 
>instance->internalRow.InterfaceLuid.Value;
>     if (ipAddr == 0) {
>         ipAddr = request->fwdReq.tunnelKey.dst;
>     }
>-    status = OvsGetOrResolveIPNeigh(ipAddr, &ipNeigh);
>+    status = OvsGetOrResolveIPNeigh(instance->internalRow,
>+                                    ipAddr, &ipNeigh);
>     if (status != STATUS_SUCCESS) {
>+        ExReleaseResourceLite(&instance->lock);
>         goto fwd_handle_nbl;
>     }
> 
>@@ -1276,6 +1660,7 @@ fwd_request_done:
>         ipf = OvsCreateIPForwardEntry(&ipRoute);
>         if (ipf == NULL) {
>             NdisReleaseRWLock(ovsTableLock, &lockState);
>+            ExReleaseResourceLite(&instance->lock);
>             status = STATUS_INSUFFICIENT_RESOURCES;
>             goto fwd_handle_nbl;
>         }
>@@ -1284,6 +1669,13 @@ fwd_request_done:
>         PLIST_ENTRY link;
>         link = ipf->fwdList.Flink;
>         fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipfLink);
>+        if (fwdEntry->info.srcIpAddr != srcAddr) {
>+            OvsRemoveFwdEntry(fwdEntry);
>+            NdisReleaseRWLock(ovsTableLock, &lockState);
>+            ExReleaseResourceLite(&instance->lock);
>+            status = STATUS_INSUFFICIENT_RESOURCES;
>+            goto fwd_handle_nbl;
>+        }
>         srcAddr = fwdEntry->info.srcIpAddr;
>     }
> 
>@@ -1293,9 +1685,10 @@ fwd_request_done:
>     if (ipn == NULL) {
>         ipn = OvsLookupIPNeighEntry(ipAddr);
>         if (ipn == NULL) {
>-            ipn = OvsCreateIPNeighEntry(&ipNeigh);
>+            ipn = OvsCreateIPNeighEntry(&ipNeigh, instance);
>             if (ipn == NULL) {
>                 NdisReleaseRWLock(ovsTableLock, &lockState);
>+                ExReleaseResourceLite(&instance->lock);
>                 status = STATUS_INSUFFICIENT_RESOURCES;
>                 goto fwd_handle_nbl;
>             }
>@@ -1309,22 +1702,26 @@ fwd_request_done:
>     fwdInfo.dstIpAddr = request->fwdReq.tunnelKey.dst;
>     fwdInfo.srcIpAddr = srcAddr;
>     RtlCopyMemory(fwdInfo.dstMacAddr, ipn->macAddr, ETH_ADDR_LEN);
>-    RtlCopyMemory(fwdInfo.srcMacAddr, ovsInternalRow.PhysicalAddress,
>+    RtlCopyMemory(fwdInfo.srcMacAddr, 
>instance->internalRow.PhysicalAddress,
>                   ETH_ADDR_LEN);
>     fwdInfo.srcPortNo = request->fwdReq.inPort;
> 
>     fwdEntry = OvsCreateFwdEntry(&fwdInfo);
>     if (fwdEntry == NULL) {
>         NdisReleaseRWLock(ovsTableLock, &lockState);
>+        ExReleaseResourceLite(&instance->lock);
>         status = STATUS_INSUFFICIENT_RESOURCES;
>         goto fwd_handle_nbl;
>     }
>     newFWD = TRUE;
>-    /*
>-     * Cache the result
>-     */
>-    OvsAddIPFwdCache(fwdEntry, ipf, ipn);
>-    NdisReleaseRWLock(ovsTableLock, &lockState);
>+    if (status == STATUS_SUCCESS) {
>+        /*
>+         * Cache the result
>+         */
>+        OvsAddIPFwdCache(fwdEntry, ipf, ipn);
>+        NdisReleaseRWLock(ovsTableLock, &lockState);
>+        ExReleaseResourceLite(&instance->lock);
>+    }
> 
> fwd_handle_nbl:
> 
>@@ -1391,7 +1788,6 @@ OvsUpdateIPNeighEntry(UINT32 ipAddr,
>                (const PVOID)ipNeigh->PhysicalAddress,
>                (size_t)ETH_ADDR_LEN)) {
>         PLIST_ENTRY link;
>-        POVS_FWD_ENTRY fwdEntry;
>         NdisReleaseRWLock(ovsTableLock, &lockState);
>         /*
>          * need update, release and acquire write lock
>@@ -1407,6 +1803,7 @@ OvsUpdateIPNeighEntry(UINT32 ipAddr,
>         }
> 
>         LIST_FORALL(&ipn->fwdList, link) {
>+            POVS_FWD_ENTRY fwdEntry;
>             fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipnLink);
>             RtlCopyMemory(fwdEntry->info.dstMacAddr,
>                           ipNeigh->PhysicalAddress, ETH_ADDR_LEN);
>@@ -1425,28 +1822,15 @@ OvsUpdateIPNeighEntry(UINT32 ipAddr,
>     NdisReleaseRWLock(ovsTableLock, &lockState);
> }
> 
>-
>-static VOID
>-OvsHandleIPNeighTimeout(UINT32 ipAddr)
>-{
>-    MIB_IPNET_ROW2 ipNeigh;
>-    NTSTATUS status;
>-
>-    status = OvsGetOrResolveIPNeigh(ipAddr, &ipNeigh);
>-
>-    OvsUpdateIPNeighEntry(ipAddr, &ipNeigh, status);
>-}
>-
>-
> /*
>  
>*-------------------------------------------------------------------------
>---
>- *  IP Helper system threash handle following request
>+ *  IP Helper system thread handles the following requests:
>  *    1. Intialize Internal port row when internal port is connected
>  *    2. Handle FWD request
>  *    3. Handle IP Neigh timeout
>  *
>  *    IP Interface, unicast address, and IP route change will be handled
>- *    by the revelant callback.
>+ *    by the revelant callbacks.
>  
>*-------------------------------------------------------------------------
>---
>  */
> VOID
>@@ -1479,6 +1863,45 @@ OvsStartIpHelper(PVOID data)
>             case OVS_IP_HELPER_INTERNAL_ADAPTER_UP:
>                 OvsHandleInternalAdapterUp(req);
>                 break;
>+            case OVS_IP_HELPER_INTERNAL_ADAPTER_DOWN:
>+            {
>+                PLIST_ENTRY head, link, next;
>+                UINT32 portNo = req->instanceReq.portNo;
>+                GUID netCfgInstanceId = 
>req->instanceReq.netCfgInstanceId;
>+
>+                ExAcquireResourceExclusiveLite(&ovsInstanceListLock, 
>TRUE);
>+                head = &ovsInstanceList;
>+                LIST_FORALL_SAFE(head, link, next) {
>+                    POVS_IPHELPER_INSTANCE instance = NULL;
>+                    LOCK_STATE_EX lockState;
>+
>+                    instance = CONTAINING_RECORD(link, 
>OVS_IPHELPER_INSTANCE, link);
>+
>+                    ExAcquireResourceExclusiveLite(&instance->lock, 
>TRUE);
>+                    if (instance->portNo == portNo &&
>+                        IsEqualGUID(&instance->netCfgId, 
>&netCfgInstanceId)) {
>+
>+                        NdisAcquireRWLockWrite(ovsTableLock, &lockState, 
>0);
>+                        
>OvsRemoveAllFwdEntriesWithPortNo(instance->portNo);
>+                        NdisReleaseRWLock(ovsTableLock, &lockState);
>+
>+                        RemoveEntryList(&instance->link);
>+
>+                        ExReleaseResourceLite(&instance->lock);
>+
>+                        OvsIpHelperDeleteInstance(instance);
>+                        break;
>+                    }
>+                    ExReleaseResourceLite(&instance->lock);
>+                }
>+
>+                if (IsListEmpty(&ovsInstanceList)) {
>+                    OvsCleanupIpHelperRequestList();
>+
>+                    OvsCleanupFwdTable();
>+                }
>+                ExReleaseResourceLite(&ovsInstanceListLock);
>+            }
>             case OVS_IP_HELPER_FWD_REQUEST:
>                 OvsHandleFwdRequest(req);
>                 break;
>@@ -1506,10 +1929,18 @@ OvsStartIpHelper(PVOID data)
>                 break;
>             }
>             ipAddr = ipn->ipAddr;
>-
>+            MIB_IPNET_ROW2 ipNeigh;
>+            NTSTATUS status;
>+            POVS_IPHELPER_INSTANCE instance = 
>(POVS_IPHELPER_INSTANCE)ipn->context;
>+            MIB_IF_ROW2 internalRow = instance->internalRow;
>             NdisReleaseSpinLock(&ovsIpHelperLock);
>+            ExAcquireResourceExclusiveLite(&ovsInstanceListLock, TRUE);
> 
>-            OvsHandleIPNeighTimeout(ipAddr);
>+            status = OvsGetOrResolveIPNeigh(internalRow,
>+                                            ipAddr, &ipNeigh);
>+            OvsUpdateIPNeighEntry(ipAddr, &ipNeigh, status);
>+
>+            ExReleaseResourceLite(&ovsInstanceListLock);
> 
>             NdisAcquireSpinLock(&ovsIpHelperLock);
>         }
>@@ -1537,7 +1968,6 @@ ip_helper_wait:
>     NdisReleaseSpinLock(&ovsIpHelperLock);
>     OvsCleanupFwdTable();
>     OvsCleanupIpHelperRequestList();
>-
>     OVS_LOG_INFO("Terminating the OVS IP Helper system thread");
> 
>     PsTerminateSystemThread(STATUS_SUCCESS);
>@@ -1547,7 +1977,7 @@ ip_helper_wait:
> NTSTATUS
> OvsInitIpHelper(NDIS_HANDLE ndisFilterHandle)
> {
>-    NTSTATUS status;
>+    NTSTATUS status = NDIS_STATUS_SUCCESS;
>     HANDLE threadHandle;
>     UINT32 i;
> 
>@@ -1560,12 +1990,6 @@ OvsInitIpHelper(NDIS_HANDLE ndisFilterHandle)
>     ovsNeighHashTable = (PLIST_ENTRY)OvsAllocateMemoryWithTag(
>         sizeof(LIST_ENTRY) * OVS_NEIGH_HASH_TABLE_SIZE, 
>OVS_IPHELPER_POOL_TAG);
> 
>-    RtlZeroMemory(&ovsInternalRow, sizeof(MIB_IF_ROW2));
>-    RtlZeroMemory(&ovsInternalIPRow, sizeof (MIB_IPINTERFACE_ROW));
>-    ovsInternalIP = 0;
>-
>-    ovsInternalAdapterUp = FALSE;
>-
>     InitializeListHead(&ovsSortedIPNeighList);
> 
>     ovsTableLock = NdisAllocateRWLock(ndisFilterHandle);
>@@ -1577,6 +2001,9 @@ OvsInitIpHelper(NDIS_HANDLE ndisFilterHandle)
>     ipRouteNotificationHandle = NULL;
>     unicastIPNotificationHandle = NULL;
> 
>+    ExInitializeResourceLite(&ovsInstanceListLock);
>+    InitializeListHead(&ovsInstanceList);
>+
>     if (ovsFwdHashTable == NULL ||
>         ovsRouteHashTable == NULL ||
>         ovsNeighHashTable == NULL ||
>@@ -1636,6 +2063,7 @@ init_cleanup:
>             NdisFreeRWLock(ovsTableLock);
>             ovsTableLock = NULL;
>         }
>+        ExDeleteResourceLite(&ovsInstanceListLock);
>         NdisFreeSpinLock(&ovsIpHelperLock);
>     }
>     return STATUS_SUCCESS;
>@@ -1662,6 +2090,9 @@ OvsCleanupIpHelper(VOID)
> 
>     NdisFreeRWLock(ovsTableLock);
>     NdisFreeSpinLock(&ovsIpHelperLock);
>+
>+    OvsIpHelperDeleteAllInstances();
>+    ExDeleteResourceLite(&ovsInstanceListLock);
> }
> 
> VOID
>diff --git a/datapath-windows/ovsext/IpHelper.h 
>b/datapath-windows/ovsext/IpHelper.h
>index 8562f86..6bda1b1 100644
>--- a/datapath-windows/ovsext/IpHelper.h
>+++ b/datapath-windows/ovsext/IpHelper.h
>@@ -19,6 +19,7 @@
> 
> #include <ntddk.h>
> #include <netioapi.h>
>+#include <Vport.h>
> 
> #define OVS_FWD_HASH_TABLE_SIZE ((UINT32)1 << 10)
> #define OVS_FWD_HASH_TABLE_MASK (OVS_FWD_HASH_TABLE_SIZE - 1)
>@@ -41,6 +42,7 @@ typedef struct _OVS_IPNEIGH_ENTRY {
>     LIST_ENTRY        link;
>     LIST_ENTRY        slink;
>     LIST_ENTRY        fwdList;
>+    PVOID             context;
> } OVS_IPNEIGH_ENTRY, *POVS_IPNEIGH_ENTRY;
> 
> typedef struct _OVS_IPFORWARD_ENTRY {
>@@ -51,15 +53,16 @@ typedef struct _OVS_IPFORWARD_ENTRY {
>     LIST_ENTRY        fwdList;
> } OVS_IPFORWARD_ENTRY, *POVS_IPFORWARD_ENTRY;
> 
>-typedef union  _OVS_FWD_INFO {
>+typedef union _OVS_FWD_INFO {
>     struct {
>         UINT32        dstIpAddr;
>         UINT32        srcIpAddr;
>         UINT8         dstMacAddr[ETH_ADDR_LEN];
>         UINT8         srcMacAddr[ETH_ADDR_LEN];
>         UINT32        srcPortNo;
>+        POVS_VPORT_ENTRY   vport;
>     };
>-    UINT64            value[3];
>+    UINT64            value[4];
> } OVS_FWD_INFO, *POVS_FWD_INFO;
> 
> typedef struct _OVS_FWD_ENTRY {
>@@ -74,6 +77,7 @@ typedef struct _OVS_FWD_ENTRY {
> 
> enum {
>     OVS_IP_HELPER_INTERNAL_ADAPTER_UP,
>+    OVS_IP_HELPER_INTERNAL_ADAPTER_DOWN,
>     OVS_IP_HELPER_FWD_REQUEST,
> };
> 
>@@ -94,13 +98,17 @@ typedef struct _OVS_FWD_REQUEST_INFO {
>     PVOID             cbData2;
> } OVS_FWD_REQUEST_INFO, *POVS_FWD_REQUEST_INFO;
> 
>+typedef struct _OVS_INSTANCE_REQUEST_INFO {
>+    GUID              netCfgInstanceId;
>+    UINT32            portNo;
>+} OVS_INSTANCE_REQUEST_INFO, *POVS_INSTANCE_REQUEST_INFO;
> 
> typedef struct _OVS_IP_HELPER_REQUEST {
>     LIST_ENTRY        link;
>     UINT32            command;
>     union {
>-        OVS_FWD_REQUEST_INFO    fwdReq;
>-        UINT32                  dummy;
>+        OVS_FWD_REQUEST_INFO        fwdReq;
>+        OVS_INSTANCE_REQUEST_INFO   instanceReq;
>     };
> } OVS_IP_HELPER_REQUEST, *POVS_IP_HELPER_REQUEST;
> 
>@@ -114,15 +122,15 @@ typedef struct _OVS_IP_HELPER_THREAD_CONTEXT {
> NTSTATUS OvsInitIpHelper(NDIS_HANDLE ndisFilterHandle);
> VOID OvsCleanupIpHelper(VOID);
> 
>-VOID OvsInternalAdapterUp(GUID *netCfgInstanceId);
>-VOID OvsInternalAdapterDown(VOID);
>+VOID OvsInternalAdapterUp(UINT32 portNo, GUID *netCfgInstanceId);
>+VOID OvsInternalAdapterDown(UINT32 portNo, GUID netCfgInstanceId);
> 
> NTSTATUS OvsFwdIPHelperRequest(PNET_BUFFER_LIST nbl, UINT32 inPort,
>                                const PVOID tunnelKey,
>                                OvsIPHelperCallback cb,
>                                PVOID cbData1,
>                                PVOID cbData2);
>-NTSTATUS OvsLookupIPFwdInfo(UINT32 dstIp, POVS_FWD_INFO info);
>+NTSTATUS OvsLookupIPFwdInfo(UINT32 srcIp, UINT32 dstIp, POVS_FWD_INFO 
>info);
> VOID OvsCancelFwdIpHelperRequest(PNET_BUFFER_LIST nbl);
> 
> #endif /* __IP_HELPER_H_ */
>diff --git a/datapath-windows/ovsext/Stt.c b/datapath-windows/ovsext/Stt.c
>index b04a77f..9da81dc 100644
>--- a/datapath-windows/ovsext/Stt.c
>+++ b/datapath-windows/ovsext/Stt.c
>@@ -107,13 +107,14 @@ OvsEncapStt(POVS_VPORT_ENTRY vport,
>             OvsIPv4TunnelKey *tunKey,
>             POVS_SWITCH_CONTEXT switchContext,
>             POVS_PACKET_HDR_INFO layers,
>-            PNET_BUFFER_LIST *newNbl)
>+            PNET_BUFFER_LIST *newNbl,
>+            POVS_FWD_INFO switchFwdInfo)
> {
>     OVS_FWD_INFO fwdInfo;
>     NDIS_STATUS status;
> 
>     UNREFERENCED_PARAMETER(switchContext);
>-    status = OvsLookupIPFwdInfo(tunKey->dst, &fwdInfo);
>+    status = OvsLookupIPFwdInfo(tunKey->src, tunKey->dst, &fwdInfo);
>     if (status != STATUS_SUCCESS) {
>         OvsFwdIPHelperRequest(NULL, 0, tunKey, NULL, NULL, NULL);
>         /*
>@@ -123,6 +124,8 @@ OvsEncapStt(POVS_VPORT_ENTRY vport,
>         return NDIS_STATUS_FAILURE;
>     }
> 
>+    RtlCopyMemory(switchFwdInfo->value, fwdInfo.value, sizeof 
>fwdInfo.value);
>+
>     status = OvsDoEncapStt(vport, curNbl, tunKey, &fwdInfo, layers,
>                            switchContext, newNbl);
>     return status;
>diff --git a/datapath-windows/ovsext/Stt.h b/datapath-windows/ovsext/Stt.h
>index 1b7e797..988a1c1 100644
>--- a/datapath-windows/ovsext/Stt.h
>+++ b/datapath-windows/ovsext/Stt.h
>@@ -17,6 +17,10 @@
> #ifndef __OVS_STT_H_
> #define __OVS_STT_H_ 1
> 
>+#include "IpHelper.h"
>+
>+typedef union _OVS_FWD_INFO *POVS_FWD_INFO;
>+
> #define STT_TCP_PORT 7471
> #define STT_TCP_PORT_NBO 0x2f1d
> 
>@@ -91,7 +95,8 @@ NDIS_STATUS OvsEncapStt(POVS_VPORT_ENTRY vport,
>                         OvsIPv4TunnelKey *tunKey,
>                         POVS_SWITCH_CONTEXT switchContext,
>                         POVS_PACKET_HDR_INFO layers,
>-                        PNET_BUFFER_LIST *newNbl);
>+                        PNET_BUFFER_LIST *newNbl,
>+                        POVS_FWD_INFO switchFwdInfo);
> 
> 
> NDIS_STATUS OvsDecapStt(POVS_SWITCH_CONTEXT switchContext,
>diff --git a/datapath-windows/ovsext/Switch.h 
>b/datapath-windows/ovsext/Switch.h
>index 001335a..7c98621 100644
>--- a/datapath-windows/ovsext/Switch.h
>+++ b/datapath-windows/ovsext/Switch.h
>@@ -103,7 +103,7 @@ typedef struct _OVS_SWITCH_CONTEXT
>      *
>      * The "real" physical external NIC has 'NicIndex' > 0. For each
>      * external interface, virtual or physical, NDIS gives an NIC level
>-     * OID callback. Note that, even though there are multile "NICs",
>+     * OID callback. Note that, even though there are multiple "NICs",
>      * there's only one underlying Hyper-V port. Thus, we get a single
>      * NDIS port-level callback, but multiple NDIS NIC-level callbacks.
>      *
>@@ -127,9 +127,10 @@ typedef struct _OVS_SWITCH_CONTEXT
>      * 'numPhysicalNics'.
>      */
>     NDIS_SWITCH_PORT_ID     virtualExternalPortId;
>-    NDIS_SWITCH_PORT_ID     internalPortId;
>-    POVS_VPORT_ENTRY        virtualExternalVport;   // the virtual 
>adapter vport
>-    POVS_VPORT_ENTRY        internalVport;
>+    POVS_VPORT_ENTRY        virtualExternalVport;   /* the virtual 
>adapter
>+                                                     * vport */
>+    INT32                   countInternalVports;    /* the number of 
>internal
>+                                                     * vports */
> 
>     /*
>      * 'portIdHashArray' ONLY contains ports that exist on the Hyper-V 
>switch,
>diff --git a/datapath-windows/ovsext/Vport.c 
>b/datapath-windows/ovsext/Vport.c
>index 428259b..e9e22aa 100644
>--- a/datapath-windows/ovsext/Vport.c
>+++ b/datapath-windows/ovsext/Vport.c
>@@ -82,8 +82,6 @@ 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,
>@@ -97,8 +95,11 @@ static VOID OvsTunnelVportPendingInit(PVOID context,
> static VOID OvsTunnelVportPendingRemove(PVOID context,
>                                         NTSTATUS status,
>                                         UINT32 *replyLen);
>-static NTSTATUS GetNICAlias(GUID *netCfgInstanceId,
>+static NTSTATUS GetNICAlias(PNDIS_SWITCH_NIC_PARAMETERS nicParam,
>                             IF_COUNTED_STRING *portFriendlyName);
>+static NTSTATUS OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr,
>+                                                CHAR *str,
>+                                                UINT16 maxStrLen);
> 
> /*
>  * 
>--------------------------------------------------------------------------
>@@ -340,7 +341,7 @@ HvCreateNic(POVS_SWITCH_CONTEXT switchContext,
> 
>     if (OvsIsInternalNIC(nicParam->NicType) ||
>         OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) {
>-        GetNICAlias(&nicParam->NetCfgInstanceId, &portFriendlyName);
>+        GetNICAlias(nicParam, &portFriendlyName);
>     }
> 
>     NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0);
>@@ -350,26 +351,46 @@ HvCreateNic(POVS_SWITCH_CONTEXT switchContext,
>      * 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);
>+        /* The VPORT can be bound to OVS datapath already. Search for it
>+         * using its friendly name and if not found allocate a new port
>+         */
>         ASSERT(OvsFindVportByPortIdAndNicIndex(switchContext,
>                                                nicParam->PortId,
>                                                nicParam->NicIndex) == 
>NULL);
>-        OvsCopyPortParamsFromVport(virtExtVport, &portParam);
>+        char convertString[256];
>+        RtlZeroMemory(convertString, 256);
>         NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
>-        status = HvCreatePort(switchContext, &portParam,
>-                              nicParam->NicIndex);
>+        status = OvsConvertIfCountedStrToAnsiStr(&portFriendlyName,
>+                                                 convertString,
>+                                                 
>OVS_MAX_PORT_NAME_LENGTH);
>         NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 
>0);
>         if (status != NDIS_STATUS_SUCCESS) {
>             goto add_nic_done;
>         }
>+        POVS_VPORT_ENTRY ovsVport = OvsFindVportByOvsName(switchContext,
>+                                                          convertString);
>+        if (ovsVport != NULL) {
>+            UpdateSwitchCtxWithVport(switchContext, ovsVport, FALSE);
>+        } else {
>+            NDIS_SWITCH_PORT_PARAMETERS portParam;
>+            POVS_VPORT_ENTRY virtExtVport =
>+                (POVS_VPORT_ENTRY)switchContext->virtualExternalVport;
>+
>+            ASSERT(virtExtVport);
>+            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",
>@@ -434,7 +455,7 @@ HvConnectNic(POVS_SWITCH_CONTEXT switchContext,
>     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
> 
>     if (nicParam->NicType == NdisSwitchNicTypeInternal) {
>-        OvsInternalAdapterUp(&nicParam->NetCfgInstanceId);
>+        OvsInternalAdapterUp(vport->portNo, &vport->netCfgInstanceId);
>     }
> 
> done:
>@@ -471,7 +492,7 @@ HvUpdateNic(POVS_SWITCH_CONTEXT switchContext,
>     /* GetNICAlias() must be called outside of a lock. */
>     if (nicParam->NicType == NdisSwitchNicTypeInternal ||
>         OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) {
>-        GetNICAlias(&nicParam->NetCfgInstanceId, &portFriendlyName);
>+        GetNICAlias(nicParam, &portFriendlyName);
>         aliasLookup = TRUE;
>     }
> 
>@@ -614,7 +635,9 @@ HvDisconnectNic(POVS_SWITCH_CONTEXT switchContext,
>     NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
> 
>     if (isInternalPort) {
>-        OvsInternalAdapterDown();
>+        OvsInternalAdapterDown(vport->portNo, vport->netCfgInstanceId);
>+        OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE);
>+        OvsPostVportEvent(&event);
>     }
> 
> done:
>@@ -870,10 +893,6 @@ OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT 
>switchContext,
>             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;
>@@ -920,7 +939,6 @@ OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport,
>     vport->portId = portParam->PortId;
>     vport->nicState = NdisSwitchNicStateUnknown;
>     vport->isExternal = FALSE;
>-    vport->isBridgeInternal = FALSE;
> 
>     switch (vport->portType) {
>     case NdisSwitchPortTypeExternal:
>@@ -962,7 +980,6 @@ OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT 
>switchContext,
>                          PNDIS_SWITCH_NIC_PARAMETERS nicParam)
> {
>     ASSERT(vport->portId == nicParam->PortId);
>-    ASSERT(vport->ovsState == OVS_STATE_PORT_CREATED);
> 
>     UNREFERENCED_PARAMETER(switchContext);
> 
>@@ -980,6 +997,8 @@ OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT 
>switchContext,
>     } else {
>         RtlCopyMemory(&vport->netCfgInstanceId, 
>&nicParam->NetCfgInstanceId,
>                       sizeof (nicParam->NetCfgInstanceId));
>+        RtlCopyMemory(&vport->nicFriendlyName, 
>&nicParam->NicFriendlyName,
>+                      sizeof (nicParam->NicFriendlyName));
>     }
>     RtlCopyMemory(&vport->nicName, &nicParam->NicName,
>                   sizeof (nicParam->NicName));
>@@ -1041,7 +1060,6 @@ OvsInitTunnelVport(PVOID userContext,
>     POVS_USER_PARAMS_CONTEXT usrParamsCtx =
>         (POVS_USER_PARAMS_CONTEXT)userContext;
> 
>-    vport->isBridgeInternal = FALSE;
>     vport->ovsType = ovsType;
>     vport->ovsState = OVS_STATE_PORT_CREATED;
>     switch (ovsType) {
>@@ -1088,37 +1106,27 @@ OvsInitTunnelVport(PVOID userContext,
> 
> /*
>  * 
>--------------------------------------------------------------------------
>- * 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.
>+  * Hyper-V, is overwritten with the interface alias name and NIC 
>friendly name
>+  * equivalent.
>  * 
>--------------------------------------------------------------------------
>  */
> static NTSTATUS
>-GetNICAlias(GUID *netCfgInstanceId,
>+GetNICAlias(PNDIS_SWITCH_NIC_PARAMETERS nicParam,
>             IF_COUNTED_STRING *portFriendlyName)
> {
>-    NTSTATUS status;
>+    NTSTATUS status = STATUS_SUCCESS;
>     WCHAR interfaceName[IF_MAX_STRING_SIZE + 1];
>     NET_LUID interfaceLuid;
>     size_t len;
> 
>-    status = ConvertInterfaceGuidToLuid(netCfgInstanceId,
>+    if (nicParam->NicType == NdisSwitchNicTypeInternal) {
>+        RtlCopyMemory(portFriendlyName, &nicParam->NicFriendlyName,
>+                      sizeof nicParam->NicFriendlyName);
>+    return status;
>+    }
>+
>+    status = ConvertInterfaceGuidToLuid(&nicParam->NetCfgInstanceId,
>                                         &interfaceLuid);
>     if (status == STATUS_SUCCESS) {
>         /*
>@@ -1167,14 +1175,12 @@ UpdateSwitchCtxWithVport(POVS_SWITCH_CONTEXT 
>switchContext,
>         if (vport->nicIndex == 0) {
>             switchContext->virtualExternalPortId = vport->portId;
>             switchContext->virtualExternalVport = vport;
>-        } else {
>+        } else if (newPort == TRUE) {
>             switchContext->numPhysicalNics++;
>         }
>         break;
>     case NdisSwitchPortTypeInternal:
>-        ASSERT(vport->isBridgeInternal == FALSE);
>-        switchContext->internalPortId = vport->portId;
>-        switchContext->internalVport = vport;
>+        switchContext->countInternalVports++;
>         break;
>     case NdisSwitchPortTypeSynthetic:
>     case NdisSwitchPortTypeEmulated:
>@@ -1235,10 +1241,6 @@ InitOvsVportCommon(POVS_SWITCH_CONTEXT 
>switchContext,
>         switchContext->numNonHvVports++;
>         break;
>     }
>-    case OVS_VPORT_TYPE_INTERNAL:
>-        if (vport->isBridgeInternal) {
>-            switchContext->numNonHvVports++;
>-        }
>     default:
>         break;
>     }
>@@ -1289,14 +1291,12 @@ OvsRemoveAndDeleteVport(PVOID usrParamsContext,
> 
>     switch (vport->ovsType) {
>     case OVS_VPORT_TYPE_INTERNAL:
>-        if (!vport->isBridgeInternal) {
>-            if (hvDelete && vport->isAbsentOnHv == FALSE) {
>-                switchContext->internalPortId = 0;
>-                switchContext->internalVport = NULL;
>-                OvsInternalAdapterDown();
>-            }
>-            hvSwitchPort = TRUE;
>+        if (hvDelete && vport->isAbsentOnHv == FALSE) {
>+            switchContext->countInternalVports--;
>+            ASSERT(switchContext->countInternalVports >= 0);
>+            OvsInternalAdapterDown(vport->portNo, 
>vport->netCfgInstanceId);
>         }
>+        hvSwitchPort = TRUE;
>         break;
>     case OVS_VPORT_TYPE_VXLAN:
>     {
>@@ -1557,14 +1557,13 @@ OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT 
>switchContext)
>             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);
>+                   vport->isAbsentOnHv == TRUE);
>             OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, 
>TRUE);
>         }
>     }
> 
>     ASSERT(switchContext->virtualExternalVport == NULL);
>-    ASSERT(switchContext->internalVport == NULL);
>+    ASSERT(switchContext->countInternalVports == 0);
> }
> 
> 
>@@ -2246,12 +2245,12 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT 
>usrParamsCtx,
>         goto Cleanup;
>     }
> 
>-    if (portType == OVS_VPORT_TYPE_NETDEV) {
>-        /* External ports can also be looked up like VIF ports. */
>+    if (portType == OVS_VPORT_TYPE_NETDEV ||
>+        portType == OVS_VPORT_TYPE_INTERNAL) {
>+        /* External and internal ports can also be looked up like VIF 
>ports. */
>         vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName);
>     } else {
>-        ASSERT(OvsIsTunnelVportType(portType) ||
>-               portType == OVS_VPORT_TYPE_INTERNAL);
>+        ASSERT(OvsIsTunnelVportType(portType));
> 
>         vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
>         if (vport == NULL) {
>@@ -2315,8 +2314,6 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT 
>usrParamsCtx,
>                                         transportPortDest);
> 
>             nlError = NlMapStatusToNlErr(status);
>-        } else {
>-            OvsInitBridgeInternalVport(vport);
>         }
> 
>         vportInitialized = TRUE;
>diff --git a/datapath-windows/ovsext/Vport.h 
>b/datapath-windows/ovsext/Vport.h
>index 1f4968e..4dc4e00 100644
>--- a/datapath-windows/ovsext/Vport.h
>+++ b/datapath-windows/ovsext/Vport.h
>@@ -26,7 +26,7 @@
> #define OVS_MAX_DPPORTS             MAXUINT16
> #define OVS_DPPORT_NUMBER_INVALID   OVS_MAX_DPPORTS
> /*
>- * The local port (0) is a reserved port, that is not allowed to be be
>+ * The local port (0) is a reserved port, that is not allowed to be
>  * created by the netlink command vport add. On linux, this port is 
>created
>  * at netlink command datapath new. However, on windows, we do not need 
>to
>  * create it, and more, we shouldn't. The userspace attempts to create 
>two
>@@ -80,58 +80,44 @@ typedef struct _OVS_VPORT_FULL_STATS {
>  * tunnel type, such as vxlan, gre
>  */
> typedef struct _OVS_VPORT_ENTRY {
>-    LIST_ENTRY             ovsNameLink;
>-    LIST_ENTRY             portIdLink;
>-    LIST_ENTRY             portNoLink;
>-    LIST_ENTRY             tunnelVportLink;
>-
>-    OVS_VPORT_STATE        ovsState;
>-    OVS_VPORT_TYPE         ovsType;
>-    OVS_VPORT_STATS        stats;
>-    OVS_VPORT_ERR_STATS    errStats;
>-    UINT32                 portNo;
>-    UINT32                 mtu;
>+    LIST_ENTRY                   ovsNameLink;
>+    LIST_ENTRY                   portIdLink;
>+    LIST_ENTRY                   portNoLink;
>+    LIST_ENTRY                   tunnelVportLink;
>+
>+    OVS_VPORT_STATE              ovsState;
>+    OVS_VPORT_TYPE               ovsType;
>+    OVS_VPORT_STATS              stats;
>+    OVS_VPORT_ERR_STATS          errStats;
>+    UINT32                       portNo;
>+    UINT32                       mtu;
>     /* ovsName is the ovs (datapath) port name - it is null terminated. 
>*/
>-    CHAR                   ovsName[OVS_MAX_PORT_NAME_LENGTH];
>-
>-    PVOID                  priv;
>-    NDIS_SWITCH_PORT_ID    portId;
>-    NDIS_SWITCH_NIC_INDEX  nicIndex;
>-    NDIS_SWITCH_NIC_TYPE   nicType;
>-    UINT16                 numaNodeId;
>-    NDIS_SWITCH_PORT_STATE portState;
>-    NDIS_SWITCH_NIC_STATE  nicState;
>-    NDIS_SWITCH_PORT_TYPE  portType;
>-
>-    UINT8                  permMacAddress[ETH_ADDR_LEN];
>-    UINT8                  currMacAddress[ETH_ADDR_LEN];
>-    UINT8                  vmMacAddress[ETH_ADDR_LEN];
>-
>-    NDIS_SWITCH_PORT_NAME  hvPortName;
>-    IF_COUNTED_STRING      portFriendlyName;
>-    NDIS_SWITCH_NIC_NAME   nicName;
>-    NDIS_VM_NAME           vmName;
>-    GUID                   netCfgInstanceId;
>-    /*
>-     * OVS userpace has a notion of bridges which basically defines an
>-     * L2-domain. Each "bridge" has an "internal" port of type
>-     * OVS_VPORT_TYPE_INTERNAL. Such a port is connected to the OVS 
>datapath in
>-     * one end, and the other end is a virtual adapter on the hypervisor 
>host.
>-     * This is akin to the Hyper-V "internal" NIC. It is intuitive to 
>map the
>-     * Hyper-V "internal" NIC to the OVS bridge's "internal" port, but 
>there's
>-     * only one Hyper-V NIC but multiple bridges. To support multiple 
>OVS bridge
>-     * "internal" ports, we use the flag 'isBridgeInternal' in each 
>vport. We
>-     * support addition of multiple bridge-internal ports. A vport with
>-     * 'isBridgeInternal' == TRUE is a dummy port and has no backing 
>currently.
>-     * If a flow actions specifies the output port to be a 
>bridge-internal port,
>-     * the port is silently ignored.
>-     */
>-    BOOLEAN                isBridgeInternal;
>-    BOOLEAN                isExternal;
>-    UINT32                 upcallPid; /* netlink upcall port id */
>-    PNL_ATTR               portOptions;
>-    BOOLEAN                isAbsentOnHv; /* Is this port present on the
>-                                             Hyper-V switch? */
>+    CHAR                         ovsName[OVS_MAX_PORT_NAME_LENGTH];
>+
>+    PVOID                        priv;
>+    NDIS_SWITCH_PORT_ID          portId;
>+    NDIS_SWITCH_NIC_INDEX        nicIndex;
>+    NDIS_SWITCH_NIC_TYPE         nicType;
>+    UINT16                       numaNodeId;
>+    NDIS_SWITCH_PORT_STATE       portState;
>+    NDIS_SWITCH_NIC_STATE        nicState;
>+    NDIS_SWITCH_PORT_TYPE        portType;
>+
>+    UINT8                        permMacAddress[ETH_ADDR_LEN];
>+    UINT8                        currMacAddress[ETH_ADDR_LEN];
>+    UINT8                        vmMacAddress[ETH_ADDR_LEN];
>+
>+    NDIS_SWITCH_PORT_NAME        hvPortName;
>+    IF_COUNTED_STRING            portFriendlyName;
>+    NDIS_SWITCH_NIC_NAME         nicName;
>+    NDIS_SWITCH_NIC_FRIENDLYNAME nicFriendlyName;
>+    NDIS_VM_NAME                 vmName;
>+    GUID                         netCfgInstanceId;
>+    BOOLEAN                      isExternal;
>+    UINT32                       upcallPid; /* netlink upcall port id */
>+    PNL_ATTR                     portOptions;
>+    BOOLEAN                      isAbsentOnHv; /* Is this port present 
>on the
>+                                                  Hyper-V switch? */
> } OVS_VPORT_ENTRY, *POVS_VPORT_ENTRY;
> 
> struct _OVS_SWITCH_CONTEXT;
>@@ -143,6 +129,8 @@ POVS_VPORT_ENTRY 
>OvsFindVportByOvsName(POVS_SWITCH_CONTEXT switchContext,
>                                        PSTR name);
> POVS_VPORT_ENTRY OvsFindVportByHvNameA(POVS_SWITCH_CONTEXT switchContext,
>                                        PSTR name);
>+POVS_VPORT_ENTRY OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext,
>+                                       PWSTR wsName, SIZE_T wstrSize);
> POVS_VPORT_ENTRY OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT 
>switchContext,
>                                                  NDIS_SWITCH_PORT_ID 
>portId,
>                                                  NDIS_SWITCH_NIC_INDEX 
>index);
>@@ -217,14 +205,6 @@ OvsIsRealExternalVport(POVS_VPORT_ENTRY vport)
> }
> 
> static __inline BOOLEAN
>-OvsIsBridgeInternalVport(POVS_VPORT_ENTRY vport)
>-{
>-    ASSERT(vport->isBridgeInternal != TRUE ||
>-           vport->ovsType == OVS_VPORT_TYPE_INTERNAL);
>-    return vport->isBridgeInternal == TRUE;
>-}
>-
>-static __inline BOOLEAN
> OvsIsInternalNIC(NDIS_SWITCH_NIC_TYPE   nicType)
> {
>     return nicType == NdisSwitchNicTypeInternal;
>@@ -261,7 +241,7 @@ GetPortFromPriv(POVS_VPORT_ENTRY vport)
>     UINT16 dstPort = 0;
>     PVOID vportPriv = GetOvsVportPriv(vport);
> 
>-    /* XXX would better to have a commom tunnel "parent" structure */
>+    /* XXX would better to have a common tunnel "parent" structure */
>     ASSERT(vportPriv);
>     switch(vport->ovsType) {
>     case OVS_VPORT_TYPE_GRE:
>@@ -273,7 +253,7 @@ GetPortFromPriv(POVS_VPORT_ENTRY vport)
>         dstPort = ((POVS_VXLAN_VPORT)vportPriv)->dstPort;
>         break;
>     case OVS_VPORT_TYPE_GENEVE:
>-        dstPort = ((POVS_GENEVE_VPORT) vportPriv)->dstPort;
>+        dstPort = ((POVS_GENEVE_VPORT)vportPriv)->dstPort;
>         break;
>     default:
>         ASSERT(! "Port is not a tunnel port");
>diff --git a/datapath-windows/ovsext/Vxlan.c 
>b/datapath-windows/ovsext/Vxlan.c
>index db45248..949e069 100644
>--- a/datapath-windows/ovsext/Vxlan.c
>+++ b/datapath-windows/ovsext/Vxlan.c
>@@ -334,7 +334,7 @@ ret_error:
>  
>*-------------------------------------------------------------------------
>---
>  * OvsEncapVxlan --
>  *     Encapsulates the packet if L2/L3 for destination resolves. 
>Otherwise,
>- *     enqueues a callback that does encapsulatation after resolution.
>+ *     enqueues a callback that does encapsulation after resolution.
>  
>*-------------------------------------------------------------------------
>---
>  */
> NDIS_STATUS
>@@ -343,15 +343,15 @@ OvsEncapVxlan(POVS_VPORT_ENTRY vport,
>               OvsIPv4TunnelKey *tunKey,
>               POVS_SWITCH_CONTEXT switchContext,
>               POVS_PACKET_HDR_INFO layers,
>-              PNET_BUFFER_LIST *newNbl)
>+              PNET_BUFFER_LIST *newNbl,
>+              POVS_FWD_INFO switchFwdInfo)
> {
>     NTSTATUS status;
>     OVS_FWD_INFO fwdInfo;
> 
>-    status = OvsLookupIPFwdInfo(tunKey->dst, &fwdInfo);
>+    status = OvsLookupIPFwdInfo(tunKey->src, tunKey->dst, &fwdInfo);
>     if (status != STATUS_SUCCESS) {
>         OvsFwdIPHelperRequest(NULL, 0, tunKey, NULL, NULL, NULL);
>-        // return NDIS_STATUS_PENDING;
>         /*
>          * XXX: Don't know if the completionList will make any sense when
>          * accessed in the callback. Make sure the caveats are known.
>@@ -362,6 +362,8 @@ OvsEncapVxlan(POVS_VPORT_ENTRY vport,
>         return NDIS_STATUS_FAILURE;
>     }
> 
>+    RtlCopyMemory(switchFwdInfo->value, fwdInfo.value, sizeof 
>fwdInfo.value);
>+
>     return OvsDoEncapVxlan(vport, curNbl, tunKey, &fwdInfo, layers,
>                            switchContext, newNbl);
> }
>diff --git a/datapath-windows/ovsext/Vxlan.h 
>b/datapath-windows/ovsext/Vxlan.h
>index b9462f0..f4a8bce 100644
>--- a/datapath-windows/ovsext/Vxlan.h
>+++ b/datapath-windows/ovsext/Vxlan.h
>@@ -17,7 +17,11 @@
> #ifndef __VXLAN_H_
> #define __VXLAN_H_ 1
> 
>+#include "IpHelper.h"
> #include "NetProto.h"
>+
>+typedef union _OVS_FWD_INFO *POVS_FWD_INFO;
>+
> typedef struct _OVS_VXLAN_VPORT {
>     UINT16 dstPort;
>     UINT64 filterID;
>@@ -31,7 +35,8 @@ typedef struct _OVS_VXLAN_VPORT {
> typedef struct VXLANHdr {
>     /* Flags. */
>     UINT32   flags1:2;
>-    /* Packet needs replication to multicast group (used for multicast 
>proxy). */
>+    /* Packet needs replication to multicast group (used for multicast 
>proxy).
>+     */
>     UINT32   locallyReplicate:1;
>     /* Instance ID flag, must be set to 1. */
>     UINT32   instanceID:1;
>@@ -64,7 +69,8 @@ NDIS_STATUS OvsEncapVxlan(POVS_VPORT_ENTRY vport,
>                           OvsIPv4TunnelKey *tunKey,
>                           POVS_SWITCH_CONTEXT switchContext,
>                           POVS_PACKET_HDR_INFO layers,
>-                          PNET_BUFFER_LIST *newNbl);
>+                          PNET_BUFFER_LIST *newNbl,
>+                          POVS_FWD_INFO switchFwdInfo);
> 
> NDIS_STATUS OvsDecapVxlan(POVS_SWITCH_CONTEXT switchContext,
>                           PNET_BUFFER_LIST curNbl,
>-- 
>2.10.2.windows.1
>
>
>_______________________________________________
>dev mailing list
>dev at openvswitch.org
>https://urldefense.proofpoint.com/v2/url?u=https-3A__mail.openvswitch.org_
>mailman_listinfo_ovs-2Ddev&d=DgICAg&c=uilaK90D4TOVoH58JNXRgQ&r=Z6vowHUOjP5
>ysP_g372c49Nqc1vEKqHKNBkR5Q5Z7uo&m=lQ_oSxjf1gYibKKEwmXTos4Ark1Aq26O_lgryNr
>6kXw&s=2wdv5o0mi8vuzA9PCULtgnJxVF8pzPMW2OhW98k0UZQ&e= 



More information about the dev mailing list