diff --git a/datapath-windows/ovsext/Actions.c b/datapath-windows/ovsext/Actions.c
index 218e7db81..4972193f0 100644
--- a/datapath-windows/ovsext/Actions.c
+++ b/datapath-windows/ovsext/Actions.c
@@ -1607,6 +1607,180 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx,
     return NDIS_STATUS_SUCCESS;
 }
 
+UINT16
+OvsCalculateICMPv6Checksum(struct in6_addr srcAddr,
+                        struct in6_addr dstAddr,
+                        uint16_t totalLength,
+                        uint16_t protocol,
+                        uint16_t *icmpStart,
+                        uint16_t length)
+{
+    uint32_t checkSum = 0;
+    uint16_t *srcAddressPtr = (uint16_t *)&srcAddr;
+    uint16_t *dstAddressPtr = (uint16_t *)&dstAddr;
+    uint16_t *value = (uint16_t *)icmpStart;
+    int index = 0;
+
+    checkSum = totalLength + protocol;
+
+    for (int i = 0; i < 8; i++) {
+        checkSum += ntohs(srcAddressPtr[i]);
+        checkSum += ntohs(dstAddressPtr[i]);
+    }
+
+    for (index = length; index > 1; index -= 2) {
+        checkSum += ntohs(*value);
+        value++;
+    }
+
+    if (index > 0) {
+        checkSum += (uint16_t)(*((uint8_t *)value));
+    }
+
+    while ((checkSum >> 16) & 0xffff) {
+        checkSum = (checkSum & 0xffff) + ((checkSum >> 16) & 0xffff);
+    }
+
+    return htons(~((uint16_t)checkSum));
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * OvsUpdateAddressAndPortForIpv6--
+ *
+ *      Update ipv6 address in ovsFwdCtx.curNbl.
+ *
+ * Results:
+ *      None
+ *
+ * Side effects:
+ *      None
+ *
+ *-----------------------------------------------------------------------------
+ */
+NDIS_STATUS
+OvsUpdateAddressAndPortForIpv6(OvsForwardingContext *ovsFwdCtx,
+                               struct in6_addr newAddr, UINT16 newPort,
+                               BOOLEAN isSource, BOOLEAN isTx)
+{
+    PUINT8 bufferStart;
+    UINT32 hdrSize;
+    OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers;
+    IPv6Hdr *ipHdr;
+    TCPHdr *tcpHdr = NULL;
+    UDPHdr *udpHdr = NULL;
+    struct in6_addr *addrField = NULL;
+    UINT16 *portField = NULL;
+    UINT16 *checkField = NULL;
+    BOOLEAN l4Offload = FALSE;
+    NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo;
+
+    ASSERT(layers->value != 0);
+
+    if (layers->isTcp || layers->isUdp) {
+        hdrSize = layers->l4Offset +
+                  layers->isTcp ? sizeof (*tcpHdr) : sizeof (*udpHdr);
+    } else {
+        hdrSize = layers->l3Offset + sizeof (*ipHdr);
+    }
+
+    csumInfo.Value = NET_BUFFER_LIST_INFO(ovsFwdCtx->curNbl,
+                                          TcpIpChecksumNetBufferListInfo);
+
+    bufferStart = OvsGetHeaderBySize(ovsFwdCtx, hdrSize);
+    if (!bufferStart) {
+        return NDIS_STATUS_RESOURCES;
+    }
+
+    ipHdr = (IPv6Hdr *)(bufferStart + layers->l3Offset);
+
+    if (layers->isTcp) {
+        tcpHdr = (TCPHdr *)(bufferStart + layers->l4Offset);
+    } else if (layers->isUdp) {
+        udpHdr = (UDPHdr *)(bufferStart + layers->l4Offset);
+    }
+
+    if (isSource) {
+        addrField = &ipHdr->saddr;
+        if (tcpHdr) {
+            portField = &tcpHdr->source;
+            checkField = &tcpHdr->check;
+            l4Offload = isTx ? (BOOLEAN)csumInfo.Transmit.TcpChecksum :
+                        ((BOOLEAN)csumInfo.Receive.TcpChecksumSucceeded ||
+                         (BOOLEAN)csumInfo.Receive.TcpChecksumFailed);
+        } else if (udpHdr) {
+            portField = &udpHdr->source;
+            checkField = &udpHdr->check;
+            l4Offload = isTx ? (BOOLEAN)csumInfo.Transmit.UdpChecksum :
+                        ((BOOLEAN)csumInfo.Receive.UdpChecksumSucceeded ||
+                         (BOOLEAN)csumInfo.Receive.UdpChecksumFailed);
+        }
+        if (isTx && l4Offload) {
+            *checkField = IPv6PseudoChecksum((UINT32 *)&newAddr, (UINT32 *)&ipHdr->daddr,
+                                             tcpHdr ? IPPROTO_TCP : IPPROTO_UDP,
+                                             ntohs(ipHdr->payload_len) -
+                                                  (ovsFwdCtx->layers.l4Offset - ovsFwdCtx->layers.l3Offset));
+        }
+    } else {
+        addrField = &ipHdr->daddr;
+        if (tcpHdr) {
+            portField = &tcpHdr->dest;
+            checkField = &tcpHdr->check;
+            l4Offload = isTx ? (BOOLEAN)csumInfo.Transmit.TcpChecksum :
+                        ((BOOLEAN)csumInfo.Receive.TcpChecksumSucceeded ||
+                         (BOOLEAN)csumInfo.Receive.TcpChecksumFailed);
+        } else if (udpHdr) {
+            portField = &udpHdr->dest;
+            checkField = &udpHdr->check;
+            l4Offload = isTx ? (BOOLEAN)csumInfo.Transmit.UdpChecksum :
+                        ((BOOLEAN)csumInfo.Receive.UdpChecksumSucceeded ||
+                         (BOOLEAN)csumInfo.Receive.UdpChecksumFailed);
+        }
+
+        if (isTx && l4Offload) {
+            *checkField = IPv6PseudoChecksum((UINT32 *)&ipHdr->saddr, (UINT32 *)&newAddr,
+                                             tcpHdr ? IPPROTO_TCP : IPPROTO_UDP,
+                                             ntohs(ipHdr->payload_len) -
+                                             (ovsFwdCtx->layers.l4Offset - ovsFwdCtx->layers.l3Offset));
+        }
+    }
+
+    if (memcmp(addrField, &newAddr, sizeof(struct in6_addr))) {
+        if ((checkField && *checkField != 0) && (!l4Offload || !isTx)) {
+            uint32_t *oldField = (uint32_t *)addrField;
+            uint32_t *newField = (uint32_t *)&newAddr;
+            *checkField = ChecksumUpdate32(*checkField, oldField[0], newField[0]);
+            *checkField = ChecksumUpdate32(*checkField, oldField[1], newField[1]);
+            *checkField = ChecksumUpdate32(*checkField, oldField[2], newField[2]);
+            *checkField = ChecksumUpdate32(*checkField, oldField[3], newField[3]);
+        }
+
+        *addrField = newAddr;
+
+        if (layers->isIcmp) {
+            ICMPHdr *icmp =(ICMPHdr *)(bufferStart + layers->l4Offset);
+            icmp->checksum = 0x00;
+            icmp->checksum = OvsCalculateICMPv6Checksum(ipHdr->saddr, ipHdr->daddr,
+                                                        ntohs(ipHdr->payload_len),
+                                                        IPPROTO_ICMPV6,
+                                                        (uint16_t *)icmp,
+                                                        ntohs(ipHdr->payload_len));
+        }
+    }
+
+    if (portField && *portField != newPort) {
+        if ((checkField) && (!l4Offload || !isTx)) {
+            /* Recompute total checksum. */
+            *checkField = ChecksumUpdate16(*checkField, *portField,
+                                           newPort);
+        }
+        *portField = newPort;
+    }
+
+    return NDIS_STATUS_SUCCESS;
+}
+
 /*
  *----------------------------------------------------------------------------
  * OvsUpdateIPv4Header --
diff --git a/datapath-windows/ovsext/Actions.h b/datapath-windows/ovsext/Actions.h
index bc12e1166..663b64876 100644
--- a/datapath-windows/ovsext/Actions.h
+++ b/datapath-windows/ovsext/Actions.h
@@ -133,4 +133,9 @@ OvsUpdateAddressAndPort(OvsForwardingContext *ovsFwdCtx,
                         UINT32 newAddr, UINT16 newPort,
                         BOOLEAN isSource, BOOLEAN isTx);
 
+NDIS_STATUS
+OvsUpdateAddressAndPortForIpv6(OvsForwardingContext *ovsFwdCtx,
+                               struct in6_addr newAddr, UINT16 newPort,
+                               BOOLEAN isSource, BOOLEAN isTx);
+
 #endif /* __ACTIONS_H_ */
diff --git a/datapath-windows/ovsext/Conntrack-ftp.c b/datapath-windows/ovsext/Conntrack-ftp.c
index ce09a6528..066723685 100644
--- a/datapath-windows/ovsext/Conntrack-ftp.c
+++ b/datapath-windows/ovsext/Conntrack-ftp.c
@@ -14,15 +14,21 @@
  * limitations under the License.
  */
 
+#include <string.h>
 #include "Conntrack.h"
 #include "PacketParser.h"
+#include "util.h"
 
 /* Eg: 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)*/
 #define FTP_PASV_RSP_PREFIX "227"
+#define FTP_EXTEND_PASV_RSP_PREFIX "229"
+#define FTP_EXTEND_ACTIVE_RSP_PREFIX "200"
 
 typedef enum FTP_TYPE {
     FTP_TYPE_PASV = 1,
-    FTP_TYPE_ACTIVE
+    FTP_TYPE_ACTIVE,
+    FTP_EXTEND_TYPE_PASV,
+    FTP_EXTEND_TYPE_ACTIVE
 } FTP_TYPE;
 
 static __inline UINT32
@@ -123,12 +129,11 @@ OvsCtHandleFtp(PNET_BUFFER_LIST curNbl,
                POVS_CT_ENTRY entry,
                BOOLEAN request)
 {
-    NDIS_STATUS status;
+    NDIS_STATUS status = NDIS_STATUS_SUCCESS;
     FTP_TYPE ftpType = 0;
     const char *buf;
     char temp[256] = { 0 };
     char ftpMsg[256] = { 0 };
-
     UINT32 len;
     TCPHdr tcpStorage;
     const TCPHdr *tcp;
@@ -156,6 +161,9 @@ OvsCtHandleFtp(PNET_BUFFER_LIST curNbl,
         if ((len >= 5) && (OvsStrncmp("PORT", ftpMsg, 4) == 0)) {
             ftpType = FTP_TYPE_ACTIVE;
             req = ftpMsg + 4;
+        } else if ((len >= 5) && (OvsStrncmp("EPRT", ftpMsg, 4) == 0)) {
+            ftpType = FTP_EXTEND_TYPE_ACTIVE;
+            req = ftpMsg + 4;
         }
     } else {
         if ((len >= 4) && (OvsStrncmp(FTP_PASV_RSP_PREFIX, ftpMsg, 3) == 0)) {
@@ -176,6 +184,25 @@ OvsCtHandleFtp(PNET_BUFFER_LIST curNbl,
                 /* PASV command without ( */
                 req = ftpMsg + 3;
             }
+        } else if ((len >= 4) && (OvsStrncmp(FTP_EXTEND_PASV_RSP_PREFIX, ftpMsg, 3) == 0)) {
+            ftpType = FTP_EXTEND_TYPE_PASV;
+            /* The ftp extended passive mode only contain port info, ip address
+             * is same with the network protocol used by control connection.
+             * 229 Entering Extended Passive Mode (|||port|)
+             * */
+            char *paren;
+            paren = strchr(ftpMsg, '|');
+            if (paren) {
+                req = paren + 3;
+            } else {
+                /* Not a valid EPSV packet. */
+                return NDIS_STATUS_INVALID_PACKET;
+            }
+
+            if (!(*req > '0' && * req < '9')) {
+                /* Not a valid port number. */
+                return NDIS_STATUS_INVALID_PACKET;
+            }
         }
     }
 
@@ -184,28 +211,102 @@ OvsCtHandleFtp(PNET_BUFFER_LIST curNbl,
         return NDIS_STATUS_SUCCESS;
     }
 
-    UINT32 arr[6] = {0};
-    status = OvsCtExtractNumbers(req, len, arr, 6, ',');
+    struct ct_addr clientIp = {0}, serverIp = {0};
+    UINT16 port = 0;
 
-    if (status != NDIS_STATUS_SUCCESS) {
-        return status;
-    }
+    if (ftpType == FTP_TYPE_ACTIVE || ftpType == FTP_TYPE_PASV) {
+        UINT32 arr[6] = {0};
+        status = OvsCtExtractNumbers(req, len, arr, 6, ',');
+
+        if (status != NDIS_STATUS_SUCCESS) {
+            return status;
+        }
+
+        UINT32 ip = ntohl((arr[0] << 24) | (arr[1] << 16) |
+                          (arr[2] << 8) | arr[3]);
+        port = ntohs(((arr[4] << 8) | arr[5]));
+
+        serverIp.ipv4 = ip;
+        clientIp.ipv4 = key->ipKey.nwDst;
+    } else {
+        if (ftpType == FTP_EXTEND_TYPE_ACTIVE) {
+            /** In ftp active mode, we need to parse string like below:
+             * " |2|20::1|50778|", 2 represent address is ipv6, 1 represent
+             * address family is ipv4, "20::1" is ipv6 address, 50779 is port
+             * client need to listen.
+             * **/
+            char *curHdr = NULL;
+            char *nextHdr = NULL;
+            int index = 0;
+            int isIpv6AddressFamily = 0;
+            char ftpStr[1024] = {0x00};
+
+            RtlCopyMemory(ftpStr, req, strlen(req));
+            for (curHdr = ftpStr; *curHdr != '|'; curHdr++);
+            curHdr = curHdr + 1;;
+            do {
+                /** index == 0 parse address family,
+                 *  index == 1 parse address,
+                 *  index == 2 parse port **/
+                for (nextHdr = curHdr; *nextHdr != '|'; nextHdr++);
+                *nextHdr = '\0';
+
+                if (*curHdr == '0' || !curHdr || index > 2) {
+                    break;
+                }
+
+                if (index == 0 && *curHdr == '1') {
+                    isIpv6AddressFamily = 0;
+                } else if (index == 0 && *curHdr == '2') {
+                    isIpv6AddressFamily = 1;
+                }
+
+                if (index == 1 && isIpv6AddressFamily) {
+                    OvsIpv6StringToAddress(curHdr, &clientIp.ipv6);
+                }
+
+                if (index == 2) {
+                    for (char *tmp = curHdr; *tmp != '\0'; tmp++) {
+                        port = port * 10 + (*tmp - '0');
+                    }
+                    port = htons(port);
+                }
+
+                curHdr = nextHdr + 1;
+                index++;
+            } while (1);
+
+            if (index < 2) { /* Not valid packet due to less than three parameter */
+                return NDIS_STATUS_SUCCESS;
+            }
+            serverIp.ipv6 = key->ipv6Key.ipv6Dst;
+        }
+
+        if (ftpType == FTP_EXTEND_TYPE_PASV) {
+            /* Here used to parse the string "229 Entering Extended Passive Mode (|||50522|),
+             * 50522 is the port we want". */
+            char *tmp = req;
+            while (*tmp != '|' && *tmp != '\0') {
+                port = port * 10 + (*tmp - '0');
+                tmp++;
+            }
 
-    UINT32 ip = ntohl((arr[0] << 24) | (arr[1] << 16) |
-                      (arr[2] << 8) | arr[3]);
-    UINT16 port = ntohs(((arr[4] << 8) | arr[5]));
+            port = htons(port);
+
+            serverIp.ipv6 = key->ipv6Key.ipv6Src;
+            clientIp.ipv6 = key->ipv6Key.ipv6Dst;
+        }
+    }
 
     switch (ftpType) {
     case FTP_TYPE_PASV:
         /* Ensure that the command states Server's IP address */
-        ASSERT(ip == key->ipKey.nwSrc);
-
         OvsCtRelatedEntryCreate(key->ipKey.nwProto,
                                 key->l2.dlType,
                                 /* Server's IP */
-                                ip,
+                                serverIp,
                                 /* Use intended client's IP */
-                                key->ipKey.nwDst,
+                                clientIp,
                                 /* Dynamic port opened on server */
                                 port,
                                 /* We don't know the client port */
@@ -217,9 +318,33 @@ OvsCtHandleFtp(PNET_BUFFER_LIST curNbl,
         OvsCtRelatedEntryCreate(key->ipKey.nwProto,
                                 key->l2.dlType,
                                 /* Server's default IP address */
-                                key->ipKey.nwDst,
+                                serverIp,
+                                /* Client's IP address */
+                                clientIp,
+                                /* FTP Data Port is 20 */
+                                ntohs(IPPORT_FTP_DATA),
+                                /* Port opened up on Client */
+                                port,
+                                currentTime,
+                                entry);
+        break;
+    case FTP_EXTEND_TYPE_PASV:
+        OvsCtRelatedEntryCreate(key->ipv6Key.nwProto,
+                                key->l2.dlType,
+                                serverIp,
+                                clientIp,
+                                port,
+                                0,
+                                currentTime,
+                                entry);
+        break;
+    case FTP_EXTEND_TYPE_ACTIVE:
+        OvsCtRelatedEntryCreate(key->ipv6Key.nwProto,
+                                key->l2.dlType,
+                                /* Server's default IP address */
+                                serverIp,
                                 /* Client's IP address */
-                                ip,
+                                clientIp,
                                 /* FTP Data Port is 20 */
                                 ntohs(IPPORT_FTP_DATA),
                                 /* Port opened up on Client */
diff --git a/datapath-windows/ovsext/Conntrack-icmp.c b/datapath-windows/ovsext/Conntrack-icmp.c
index 28fe2bff8..9221f8518 100644
--- a/datapath-windows/ovsext/Conntrack-icmp.c
+++ b/datapath-windows/ovsext/Conntrack-icmp.c
@@ -70,6 +70,17 @@ OvsConntrackValidateIcmpPacket(const ICMPHdr *icmp)
            || icmp->type == ICMP4_TIMESTAMP_REQUEST;
 }
 
+BOOLEAN
+OvsConntrackValidateIcmp6Packet(const ICMPHdr *icmp)
+{
+    if (!icmp) {
+        OVS_LOG_TRACE("Invalid ICMP packet detected, header cannot be NULL");
+        return FALSE;
+    }
+
+    return icmp->type == ICMP6_ECHO_REQUEST;
+}
+
 OVS_CT_ENTRY *
 OvsConntrackCreateIcmpEntry(UINT64 now)
 {
diff --git a/datapath-windows/ovsext/Conntrack-nat.c b/datapath-windows/ovsext/Conntrack-nat.c
index 1607d4c4f..497354ec8 100644
--- a/datapath-windows/ovsext/Conntrack-nat.c
+++ b/datapath-windows/ovsext/Conntrack-nat.c
@@ -42,12 +42,9 @@ OvsHashNatKey(const OVS_CT_KEY *key)
 static __inline BOOLEAN
 OvsNatKeyAreSame(const OVS_CT_KEY *key1, const OVS_CT_KEY *key2)
 {
-    // XXX: Compare IPv6 key as well
 #define FIELD_COMPARE(field) \
     if (key1->field != key2->field) return FALSE
 
-    FIELD_COMPARE(src.addr.ipv4_aligned);
-    FIELD_COMPARE(dst.addr.ipv4_aligned);
     FIELD_COMPARE(src.port);
     FIELD_COMPARE(dst.port);
     FIELD_COMPARE(zone);
@@ -56,6 +53,15 @@ OvsNatKeyAreSame(const OVS_CT_KEY *key1, const OVS_CT_KEY *key2)
     FIELD_COMPARE(dst.icmp_type);
     FIELD_COMPARE(src.icmp_code);
     FIELD_COMPARE(dst.icmp_code);
+
+    if (memcmp(&(key1->src.addr), &(key2->src.addr), sizeof(struct ct_addr))) {
+        return FALSE;
+    }
+
+    if (memcmp(&(key1->dst.addr), &(key2->dst.addr), sizeof(struct ct_addr))) {
+        return FALSE;
+    }
+
     return TRUE;
 #undef FIELD_COMPARE
 }
@@ -215,18 +221,37 @@ OvsNatPacket(OvsForwardingContext *ovsFwdCtx,
         } else {
             key->ipKey.nwDst = endpoint->addr.ipv4_aligned;
         }
-    } else if (ctKey->dl_type == htons(ETH_TYPE_IPV6)){
-        // XXX: IPv6 packet not supported yet.
-        return;
+    } else if (ctKey->dl_type == htons(ETH_TYPE_IPV6)) {
+        OvsUpdateAddressAndPortForIpv6(ovsFwdCtx,
+                                       endpoint->addr.ipv6,
+                                       endpoint->port, isSrcNat,
+                                       !reverse);
+        if (isSrcNat) {
+            key->ipv6Key.ipv6Src = endpoint->addr.ipv6;
+        } else {
+            key->ipv6Key.ipv6Dst = endpoint->addr.ipv6;
+        }
     }
     if (natAction & (NAT_ACTION_SRC_PORT | NAT_ACTION_DST_PORT)) {
-        if (isSrcNat) {
-            if (key->ipKey.l4.tpSrc != 0) {
-                key->ipKey.l4.tpSrc = endpoint->port;
+        if (ctKey->dl_type == htons(ETH_TYPE_IPV4)) {
+            if (isSrcNat) {
+                if (key->ipKey.l4.tpSrc != 0) {
+                    key->ipKey.l4.tpSrc = endpoint->port;
+                }
+            } else {
+                if (key->ipKey.l4.tpDst != 0) {
+                    key->ipKey.l4.tpDst = endpoint->port;
+                }
             }
-        } else {
-            if (key->ipKey.l4.tpDst != 0) {
-                key->ipKey.l4.tpDst = endpoint->port;
+        } else if (ctKey->dl_type == htons(ETH_TYPE_IPV6)) {
+            if (isSrcNat) {
+                if (key->ipv6Key.l4.tpSrc != 0) {
+                    key->ipv6Key.l4.tpSrc = endpoint->port;
+                }
+            } else {
+                if (key->ipv6Key.l4.tpDst != 0) {
+                    key->ipv6Key.l4.tpDst = endpoint->port;
+                }
             }
         }
     }
@@ -326,8 +351,8 @@ OvsNatTranslateCtEntry(OVS_CT_ENTRY *entry)
         ctAddr.ipv4_aligned = htonl(
             ntohl(entry->natInfo.minAddr.ipv4_aligned) + addrIndex);
     } else {
-        // XXX: IPv6 not supported
-        return FALSE;
+        /** Current, only support nat single address**/
+        ctAddr.ipv6_aligned = entry->natInfo.minAddr.ipv6_aligned;
     }
 
     port = firstPort;
@@ -379,8 +404,33 @@ OvsNatTranslateCtEntry(OVS_CT_ENTRY *entry)
                     ctAddr.ipv4_aligned = htonl(
                         ntohl(ctAddr.ipv4_aligned) + 1);
                 } else {
-                    // XXX: IPv6 not supported
-                    return FALSE;
+                    /** When all ports was used, return fails indicate exceed range. **/
+                    uint32_t addr[8] = {0};
+                    uint16_t *tmpAddr = (uint16_t *)&(ctAddr.ipv6_aligned);
+                    for (int m = 0; m < 8; m++) {
+                        addr[m] = tmpAddr[m];
+                    }
+
+                    uint16_t carry = 1, i = 8;
+                    while (carry && i)
+                    {
+                        addr[i-1] += carry;
+                        if (addr[i-1] > 0xffff || !addr[i-1])
+                        {
+                            carry = 1;
+                            addr[i-1] &= 0xffff;
+                        } else carry = 0;
+                        i--;
+                    }
+
+                    if (carry) {
+                        OVS_LOG_INFO("Ipv6 address incremented overflow.");
+                        return FALSE;
+                    }
+
+                    for (int m = 0; m < 8; m++) {
+                        tmpAddr[m] = (uint16_t)addr[m];
+                    }
                 }
             } else {
                 ctAddr = entry->natInfo.minAddr;
diff --git a/datapath-windows/ovsext/Conntrack-related.c b/datapath-windows/ovsext/Conntrack-related.c
index a5bba5cf8..f985c7631 100644
--- a/datapath-windows/ovsext/Conntrack-related.c
+++ b/datapath-windows/ovsext/Conntrack-related.c
@@ -38,10 +38,22 @@ static __inline BOOLEAN
 OvsCtRelatedKeyAreSame(OVS_CT_KEY incomingKey, OVS_CT_KEY entryKey)
 {
     /* FTP PASV - Client initiates the connection from unknown port */
-    if ((incomingKey.dst.addr.ipv4 == entryKey.src.addr.ipv4) &&
+    if ((incomingKey.dl_type == entryKey.dl_type) &&
+        (incomingKey.dl_type == htons(ETH_TYPE_IPV4)) &&
+        (incomingKey.dst.addr.ipv4 == entryKey.src.addr.ipv4) &&
         (incomingKey.dst.port == entryKey.src.port) &&
         (incomingKey.src.addr.ipv4 == entryKey.dst.addr.ipv4) &&
-        (incomingKey.dl_type == entryKey.dl_type) &&
+        (incomingKey.nw_proto == entryKey.nw_proto)) {
+        return TRUE;
+    }
+
+    if ((incomingKey.dl_type == entryKey.dl_type) &&
+        (incomingKey.dl_type == htons(ETH_TYPE_IPV6)) &&
+        !memcmp(&(incomingKey.dst.addr.ipv6), &(entryKey.src.addr.ipv6),
+               sizeof(incomingKey.dst.addr.ipv6)) &&
+        (incomingKey.dst.port == entryKey.src.port) &&
+        !memcmp(&(incomingKey.src.addr.ipv6), &(entryKey.dst.addr.ipv6),
+               sizeof(incomingKey.src.addr.ipv6)) &&
         (incomingKey.nw_proto == entryKey.nw_proto)) {
         return TRUE;
     }
@@ -51,10 +63,22 @@ OvsCtRelatedKeyAreSame(OVS_CT_KEY incomingKey, OVS_CT_KEY entryKey)
      * except 20. In this case, the incomingKey's src port is different with
      * entryKey's src port.
      */
-    if ((incomingKey.src.addr.ipv4 == entryKey.src.addr.ipv4) &&
+    if ((incomingKey.dl_type == entryKey.dl_type) &&
+        (incomingKey.dl_type == htons(ETH_TYPE_IPV4)) &&
+        (incomingKey.src.addr.ipv4 == entryKey.src.addr.ipv4) &&
         (incomingKey.dst.addr.ipv4 == entryKey.dst.addr.ipv4) &&
         (incomingKey.dst.port == entryKey.dst.port) &&
-        (incomingKey.dl_type == entryKey.dl_type) &&
+        (incomingKey.nw_proto == entryKey.nw_proto)) {
+        return TRUE;
+    }
+
+    if ((incomingKey.dl_type == entryKey.dl_type) &&
+        (incomingKey.dl_type == htons(ETH_TYPE_IPV6)) &&
+        !memcmp(&(incomingKey.src.addr.ipv6), &(entryKey.src.addr.ipv6),
+               sizeof(incomingKey.src.addr.ipv6)) &&
+        !memcmp(&(incomingKey.dst.addr.ipv6), &(entryKey.dst.addr.ipv6),
+               sizeof(incomingKey.src.addr.ipv6)) &&
+        (incomingKey.dst.port == entryKey.dst.port) &&
         (incomingKey.nw_proto == entryKey.nw_proto)) {
         return TRUE;
     }
@@ -110,8 +134,8 @@ OvsCtRelatedEntryDelete(POVS_CT_REL_ENTRY entry)
 NDIS_STATUS
 OvsCtRelatedEntryCreate(UINT8 ipProto,
                         UINT16 dl_type,
-                        UINT32 serverIp,
-                        UINT32 clientIp,
+                        struct ct_addr serverIp,
+                        struct ct_addr clientIp,
                         UINT16 serverPort,
                         UINT16 clientPort,
                         UINT64 currentTime,
@@ -127,13 +151,18 @@ OvsCtRelatedEntryCreate(UINT8 ipProto,
 
     RtlZeroMemory(entry, sizeof(struct OVS_CT_REL_ENTRY));
     entry->expiration = currentTime + (CT_INTERVAL_SEC * 60);
-    entry->key.src.addr.ipv4 = serverIp;
-    entry->key.dst.addr.ipv4 = clientIp;
     entry->key.nw_proto = ipProto;
     entry->key.dl_type = dl_type;
     entry->key.src.port = serverPort;
     entry->key.dst.port = clientPort;
     entry->parent = parent;
+    if (dl_type == htons(ETH_TYPE_IPV6)) {
+        entry->key.src.addr.ipv6 = serverIp.ipv6;
+        entry->key.dst.addr.ipv6 = clientIp.ipv6;
+    } else {
+        entry->key.src.addr.ipv4 = serverIp.ipv4;
+        entry->key.dst.addr.ipv4 = clientIp.ipv4;
+    }
 
     UINT32 hash = OvsExtractCtRelatedKeyHash(&entry->key);
 
diff --git a/datapath-windows/ovsext/Conntrack.c b/datapath-windows/ovsext/Conntrack.c
index 7f1d2fb41..5686a9bf6 100644
--- a/datapath-windows/ovsext/Conntrack.c
+++ b/datapath-windows/ovsext/Conntrack.c
@@ -188,6 +188,13 @@ OvsCtSetZoneLimit(int zone, ULONG value) {
     NdisReleaseSpinLock(&ovsCtZoneLock);
 }
 
+static uint32_t
+OvsCtEndpointHashAdd(uint32_t hash, const struct ct_endpoint *ep)
+{
+    BUILD_ASSERT_DECL(sizeof *ep % 4 == 0);
+    return OvsJhashBytes((UINT32 *)ep, sizeof *ep, hash);
+}
+
 /*
  *----------------------------------------------------------------------------
  * OvsCtHashKey
@@ -198,8 +205,10 @@ UINT32
 OvsCtHashKey(const OVS_CT_KEY *key)
 {
     UINT32 hsrc, hdst, hash;
-    hsrc = key->src.addr.ipv4 | ntohl(key->src.port);
-    hdst = key->dst.addr.ipv4 | ntohl(key->dst.port);
+    hsrc = ntohl(key->src.port);
+    hdst = ntohl(key->dst.port);
+    hsrc = OvsCtEndpointHashAdd(hsrc, &key->src);
+    hdst = OvsCtEndpointHashAdd(hdst, &key->dst);
     hash = hsrc ^ hdst; /* TO identify reverse traffic */
     hash = hash | (key->zone + key->nw_proto);
     hash = OvsJhashWords((uint32_t*) &hash, 1, hash);
@@ -340,6 +349,26 @@ OvsCtEntryCreate(OvsForwardingContext *fwdCtx,
         }
         break;
     }
+    case IPPROTO_ICMPV6:
+    {
+        ICMPHdr storage;
+        const ICMPHdr *icmp;
+        icmp = OvsGetIcmp(curNbl, layers->l4Offset, &storage);
+        if (!OvsConntrackValidateIcmp6Packet(icmp)) {
+            if(icmp) {
+                OVS_LOG_TRACE("Invalid ICMP packet detected, icmp->type %u",
+                              icmp->type);
+            }
+            state = OVS_CS_F_INVALID;
+            break;
+        }
+
+        if (commit) {
+            entry = OvsConntrackCreateIcmpEntry(currentTime);
+        }
+        break;
+    }
+
     case IPPROTO_ICMP:
     {
         ICMPHdr storage;
@@ -434,6 +463,15 @@ OvsCtUpdateEntry(OVS_CT_ENTRY* entry,
         NdisReleaseSpinLock(&(entry->lock));
         break;
     }
+
+    case IPPROTO_ICMPV6:
+    {
+        NdisAcquireSpinLock(&(entry->lock));
+        status = OvsConntrackUpdateIcmpEntry(entry, reply, now);
+        NdisReleaseSpinLock(&(entry->lock));
+        break;
+    }
+
     case IPPROTO_UDP:
     {
         NdisAcquireSpinLock(&(entry->lock));
@@ -527,6 +565,23 @@ OvsDetectCtPacket(OvsForwardingContext *fwdCtx,
         }
         return NDIS_STATUS_NOT_SUPPORTED;
     case ETH_TYPE_IPV6:
+        if (key->ipv6Key.nwProto == IPPROTO_ICMPV6
+            || key->ipv6Key.nwProto == IPPROTO_TCP
+            || key->ipv6Key.nwProto == IPPROTO_UDP) {
+            /** TODO fragment **/
+
+            /** Extract flow key from packet and assign it to
+             * returned parameter. **/
+            status =  OvsExtractFlow(fwdCtx->curNbl, fwdCtx->srcVportNo,
+                                     &newFlowKey, &fwdCtx->layers,
+                                     fwdCtx->tunKey.dst != 0 ? &fwdCtx->tunKey : NULL);
+            if (status != NDIS_STATUS_SUCCESS) {
+                OVS_LOG_ERROR("Extract flow for ipv6 failed Nbl %p", fwdCtx->curNbl);
+                return status;
+            }
+            *key = newFlowKey;
+            return NDIS_STATUS_SUCCESS;
+        }
         return NDIS_STATUS_NOT_SUPPORTED;
     }
 
@@ -605,6 +660,54 @@ OvsCtLookup(OvsConntrackKeyLookupCtx *ctx)
     return found;
 }
 
+const TCPHdr*
+OvsGetTcpHeader(PNET_BUFFER_LIST nbl,
+                OVS_PACKET_HDR_INFO *layers,
+                VOID *storage,
+                UINT32 *tcpPayloadLen)
+{
+    IPHdr *ipHdr;
+    IPv6Hdr *ipv6Hdr;
+    TCPHdr *tcp;
+    VOID *dest = storage;
+    uint16_t ipv6ExtLength = 0;
+
+    if (layers->isIPv6) {
+        ipv6Hdr = NdisGetDataBuffer(NET_BUFFER_LIST_FIRST_NB(nbl),
+                                    layers->l4Offset + sizeof(TCPHdr),
+                                    NULL, 1, 0);
+        if (ipv6Hdr == NULL) {
+            return NULL;
+        }
+
+        tcp = (TCPHdr *)((PCHAR)ipv6Hdr + layers->l4Offset);
+        ipv6Hdr = (IPv6Hdr *)((PCHAR)ipv6Hdr + layers->l3Offset);
+        if (tcp->doff * 4 >= sizeof *tcp) {
+            NdisMoveMemory(dest, tcp, sizeof(TCPHdr));
+            ipv6ExtLength = layers->l4Offset - layers->l3Offset - sizeof(IPv6Hdr);
+            *tcpPayloadLen = (ntohs(ipv6Hdr->payload_len) - ipv6ExtLength - TCP_HDR_LEN(tcp));
+            return storage;
+        }
+    } else {
+        ipHdr = NdisGetDataBuffer(NET_BUFFER_LIST_FIRST_NB(nbl),
+                                  layers->l4Offset + sizeof(TCPHdr),
+                                  NULL, 1 /*no align*/, 0);
+        if (ipHdr == NULL) {
+            return NULL;
+        }
+
+        ipHdr = (IPHdr *)((PCHAR)ipHdr + layers->l3Offset);
+        tcp = (TCPHdr *)((PCHAR)ipHdr + ipHdr->ihl * 4);
+
+        if (tcp->doff * 4 >= sizeof *tcp) {
+            NdisMoveMemory(dest, tcp, sizeof(TCPHdr));
+            *tcpPayloadLen = TCP_DATA_LENGTH(ipHdr, tcp);
+            return storage;
+        }
+    }
+    return NULL;
+}
+
 static UINT8
 OvsReverseIcmpType(UINT8 type)
 {
@@ -621,6 +724,10 @@ OvsReverseIcmpType(UINT8 type)
         return ICMP4_INFO_REPLY;
     case ICMP4_INFO_REPLY:
         return ICMP4_INFO_REQUEST;
+    case ICMP6_ECHO_REQUEST:
+        return ICMP6_ECHO_REPLY;
+    case ICMP6_ECHO_REPLY:
+        return ICMP6_ECHO_REQUEST;
     default:
         return 0;
     }
@@ -691,8 +798,8 @@ OvsCtSetupLookupCtx(OvsFlowKey *flowKey,
                     return NDIS_STATUS_INVALID_PACKET;
                 }
                 /* Separate ICMP connection: identified using id */
-                ctx->key.dst.icmp_id = icmp->fields.echo.id;
-                ctx->key.src.icmp_id = icmp->fields.echo.id;
+                ctx->key.dst.icmp_id = ntohs(icmp->fields.echo.id);
+                ctx->key.src.icmp_id = ntohs(icmp->fields.echo.id);
                 ctx->key.src.icmp_type = icmp->type;
                 ctx->key.dst.icmp_type = OvsReverseIcmpType(icmp->type);
                 break;
@@ -701,7 +808,6 @@ OvsCtSetupLookupCtx(OvsFlowKey *flowKey,
             case ICMP4_PARAM_PROB:
             case ICMP4_SOURCE_QUENCH:
             case ICMP4_REDIRECT: {
-                /* XXX Handle inner packet */
                 ctx->related = TRUE;
                 break;
             }
@@ -716,7 +822,53 @@ OvsCtSetupLookupCtx(OvsFlowKey *flowKey,
 
         ctx->key.src.port = flowKey->ipv6Key.l4.tpSrc;
         ctx->key.dst.port = flowKey->ipv6Key.l4.tpDst;
-        /* XXX Handle ICMPv6 errors*/
+        if (flowKey->ipv6Key.nwProto == IPPROTO_ICMPV6) {
+            ICMPHdr icmpStorage;
+            const ICMPHdr *icmp;
+            icmp = OvsGetIcmp(curNbl, l4Offset, &icmpStorage);
+            ASSERT(icmp);
+
+            switch (icmp->type) {
+                case ICMP6_ECHO_REQUEST:
+                case ICMP6_ECHO_REPLY: {
+                    ctx->key.dst.icmp_id = ntohs(icmp->fields.echo.id);
+                    ctx->key.src.icmp_id = ntohs(icmp->fields.echo.id);
+                    ctx->key.src.icmp_type = icmp->type;
+                    ctx->key.dst.icmp_type = OvsReverseIcmpType(icmp->type);
+                    break;
+                }
+                case ICMP6_DST_UNREACH:
+                case ICMP6_TIME_EXCEEDED:
+                case ICMP6_PARAM_PROB:
+                case ICMP6_PACKET_TOO_BIG: {
+                    Ipv6Key ipv6Key;
+                    OVS_PACKET_HDR_INFO layers;
+                    OvsExtractLayers(curNbl, &layers);
+                    layers.l3Offset = layers.l7Offset;
+                    NDIS_STATUS status = OvsParseIPv6(curNbl, &ipv6Key, &layers);
+                    if (status != NDIS_STATUS_SUCCESS) {
+                        return NDIS_STATUS_INVALID_PACKET;
+                    }
+                    ctx->key.src.addr.ipv6 = ipv6Key.ipv6Src;
+                    ctx->key.dst.addr.ipv6 = ipv6Key.ipv6Dst;
+                    ctx->key.nw_proto = ipv6Key.nwProto;
+                    if (ipv6Key.nwProto == SOCKET_IPPROTO_TCP) {
+                        OvsParseTcp(curNbl, &(ipv6Key.l4), &layers);
+                    } else if (ipv6Key.nwProto == SOCKET_IPPROTO_UDP) {
+                        OvsParseUdp(curNbl, &(ipv6Key.l4), &layers);
+                    } else if (ipv6Key.nwProto == SOCKET_IPPROTO_SCTP) {
+                        OvsParseSctp(curNbl, &ipv6Key.l4, &layers);
+                    }
+                    ctx->key.src.port = ipv6Key.l4.tpSrc;
+                    ctx->key.dst.port = ipv6Key.l4.tpDst;
+                    OvsCtKeyReverse(&ctx->key);
+                    ctx->related = TRUE;
+                    break;
+                }
+                default:
+                    ctx->related = FALSE;
+            }
+        }
     } else {
         return NDIS_STATUS_INVALID_PACKET;
     }
@@ -743,6 +895,13 @@ OvsDetectFtpPacket(OvsFlowKey *key) {
             ntohs(key->ipKey.l4.tpSrc) == IPPORT_FTP));
 }
 
+static __inline BOOLEAN
+OvsDetectFtp6Packet(OvsFlowKey *key) {
+    return (key->ipv6Key.nwProto == IPPROTO_TCP &&
+            (ntohs(key->ipv6Key.l4.tpDst) == IPPORT_FTP ||
+             ntohs(key->ipv6Key.l4.tpSrc) == IPPORT_FTP));
+}
+
 /*
  *----------------------------------------------------------------------------
  * OvsProcessConntrackEntry
@@ -776,11 +935,22 @@ OvsProcessConntrackEntry(OvsForwardingContext *fwdCtx,
     } else {
         CT_UPDATE_RES result;
         UINT32 bucketIdx;
-        result = OvsCtUpdateEntry(entry, curNbl, key->ipKey.nwProto, layers,
-                                  ctx->reply, currentTime);
+
+        if (layers->isIPv6) {
+            result = OvsCtUpdateEntry(entry, curNbl, key->ipv6Key.nwProto, layers,
+                                      ctx->reply, currentTime);
+        } else {
+            result = OvsCtUpdateEntry(entry, curNbl, key->ipKey.nwProto, layers,
+                                      ctx->reply, currentTime);
+        }
+
         switch (result) {
         case CT_UPDATE_VALID:
             state |= OVS_CS_F_ESTABLISHED;
+            /** when the ct state is established, at least
+             * request/reply two packets go through,
+             * so the ct_state shouldn't contain new.**/
+            state &= ~OVS_CS_F_NEW;
             if (ctx->reply) {
                 state |= OVS_CS_F_REPLY_DIR;
             }
@@ -795,9 +965,17 @@ OvsProcessConntrackEntry(OvsForwardingContext *fwdCtx,
             OvsCtEntryDelete(ctx->entry, TRUE);
             NdisReleaseRWLock(ovsCtBucketLock[bucketIdx], &lockStateTable);
             ctx->entry = NULL;
-            entry = OvsCtEntryCreate(fwdCtx, key->ipKey.nwProto, layers,
-                                     ctx, key, natInfo, commit, currentTime,
-                                     entryCreated);
+
+            if (layers->isIPv6) {
+                entry = OvsCtEntryCreate(fwdCtx, key->ipv6Key.nwProto, layers,
+                                         ctx, key, natInfo, commit, currentTime,
+                                         entryCreated);
+            } else {
+                entry = OvsCtEntryCreate(fwdCtx, key->ipKey.nwProto, layers,
+                                         ctx, key, natInfo, commit, currentTime,
+                                         entryCreated);
+            }
+
             if (!entry) {
                 return NULL;
             }
@@ -809,7 +987,8 @@ OvsProcessConntrackEntry(OvsForwardingContext *fwdCtx,
     }
     if (entry) {
         NdisAcquireSpinLock(&(entry->lock));
-        if (key->ipKey.nwProto == IPPROTO_TCP) {
+        if ((layers->isIPv6 && key->ipv6Key.nwProto == IPPROTO_TCP) ||
+            (!(layers->isIPv6) && key->ipKey.nwProto == IPPROTO_TCP)) {
             /* Update the related bit if there is a parent */
             if (entry->parent) {
                 state |= OVS_CS_F_RELATED;
@@ -937,6 +1116,29 @@ OvsCtUpdateTuple(OvsFlowKey *key, OVS_CT_KEY *ctKey)
                                     htons(ctKey->src.icmp_code);
 }
 
+/*
+ * --------------------------------------------------------------------------
+ *  OvsCtUpdateTupleV6 name --
+ *     Update origin tuple for ipv6 packet.
+ * --------------------------------------------------------------------------
+ */
+static __inline void
+OvsCtUpdateTupleV6(OvsFlowKey *key, OVS_CT_KEY *ctKey)
+{
+    RtlCopyMemory(&key->ct.tuple_ipv6.ipv6_src, &ctKey->src.addr.ipv6_aligned, sizeof(key->ct.tuple_ipv6.ipv6_src));
+    RtlCopyMemory(&key->ct.tuple_ipv6.ipv6_dst, &ctKey->dst.addr.ipv6_aligned, sizeof(key->ct.tuple_ipv6.ipv6_dst));
+    key->ct.tuple_ipv6.ipv6_proto = ctKey->nw_proto;
+
+    /* Orig tuple Port is overloaded to take in ICMP-Type & Code */
+    /* This mimics the behavior in lib/conntrack.c*/
+    key->ct.tuple_ipv6.src_port = ctKey->nw_proto != IPPROTO_ICMPV6 ?
+                                  ctKey->src.port :
+                                  htons(ctKey->src.icmp_type);
+    key->ct.tuple_ipv6.dst_port = ctKey->nw_proto != IPPROTO_ICMPV6 ?
+                                  ctKey->dst.port :
+                                  htons(ctKey->src.icmp_code);
+}
+
 static __inline NDIS_STATUS
 OvsCtExecute_(OvsForwardingContext *fwdCtx,
               OvsFlowKey *key,
@@ -953,6 +1155,8 @@ OvsCtExecute_(OvsForwardingContext *fwdCtx,
     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
     BOOLEAN triggerUpdateEvent = FALSE;
     BOOLEAN entryCreated = FALSE;
+    BOOLEAN isFtpPacket = FALSE;
+    BOOLEAN isFtpRequestDirection = FALSE;
     POVS_CT_ENTRY entry = NULL;
     POVS_CT_ENTRY parent = NULL;
     PNET_BUFFER_LIST curNbl = fwdCtx->curNbl;
@@ -1003,10 +1207,17 @@ OvsCtExecute_(OvsForwardingContext *fwdCtx,
             return NDIS_STATUS_RESOURCES;
         }
         /* If no matching entry was found, create one and add New state */
-        entry = OvsCtEntryCreate(fwdCtx, key->ipKey.nwProto,
-                                 layers, &ctx,
-                                 key, natInfo, commit, currentTime,
-                                 &entryCreated);
+        if (key->l2.dlType == htons(ETH_TYPE_IPV6)) {
+            entry = OvsCtEntryCreate(fwdCtx, key->ipv6Key.nwProto,
+                                     layers, &ctx,
+                                     key, natInfo, commit, currentTime,
+                                     &entryCreated);
+        } else {
+            entry = OvsCtEntryCreate(fwdCtx, key->ipKey.nwProto,
+                                     layers, &ctx,
+                                     key, natInfo, commit, currentTime,
+                                     &entryCreated);
+        }
     }
 
     if (entry == NULL) {
@@ -1029,10 +1240,22 @@ OvsCtExecute_(OvsForwardingContext *fwdCtx,
 
     OvsCtSetMarkLabel(key, entry, mark, labels, &triggerUpdateEvent);
 
-    if (OvsDetectFtpPacket(key)) {
+    if (layers->isIPv6) {
+        isFtpPacket = OvsDetectFtp6Packet(key);
+        if (ntohs(key->ipv6Key.l4.tpDst) == IPPORT_FTP) {
+            isFtpRequestDirection = TRUE;
+        }
+    } else {
+        isFtpPacket = OvsDetectFtpPacket(key);
+        if (ntohs(key->ipKey.l4.tpDst) == IPPORT_FTP) {
+            isFtpRequestDirection = TRUE;
+        }
+    }
+
+    if (isFtpPacket) {
         /* FTP parser will always be loaded */
         status = OvsCtHandleFtp(curNbl, key, layers, currentTime, entry,
-                                (ntohs(key->ipKey.l4.tpDst) == IPPORT_FTP));
+                                isFtpRequestDirection);
         if (status != NDIS_STATUS_SUCCESS) {
             OVS_LOG_ERROR("Error while parsing the FTP packet");
         }
@@ -1063,6 +1286,14 @@ OvsCtExecute_(OvsForwardingContext *fwdCtx,
         } else {
             OvsCtUpdateTuple(key, &entry->key);
         }
+    } else if (entry->key.dl_type == ntohs(ETH_TYPE_IPV6)) {
+        if (parent != NULL) {
+            OVS_ACQUIRE_SPIN_LOCK(&(parent->lock), irql);
+            OvsCtUpdateTupleV6(key, &parent->key);
+            OVS_RELEASE_SPIN_LOCK(&(parent->lock), irql);
+        } else {
+            OvsCtUpdateTupleV6(key, &entry->key);
+        }
     }
 
     if (entryCreated) {
@@ -1399,13 +1630,15 @@ MapNlToCtTuple(POVS_MESSAGE msgIn, PNL_ATTR ctAttr,
     PNL_ATTR ctTupleAttrs[__CTA_MAX];
     UINT32 attrOffset;
     static const NL_POLICY ctTuplePolicy[] = {
-        [CTA_TUPLE_IP] = {.type = NL_A_NESTED, .optional = FALSE },
-        [CTA_TUPLE_PROTO] = {.type = NL_A_NESTED, .optional = FALSE},
+        [CTA_TUPLE_IP] = { .type = NL_A_NESTED, .optional = FALSE },
+        [CTA_TUPLE_PROTO] = { .type = NL_A_NESTED, .optional = FALSE},
     };
 
     static const NL_POLICY ctTupleIpPolicy[] = {
         [CTA_IP_V4_SRC] = { .type = NL_A_BE32, .optional = TRUE },
         [CTA_IP_V4_DST] = { .type = NL_A_BE32, .optional = TRUE },
+        [CTA_IP_V6_SRC] = { .type = NL_A_BE32,.optional = TRUE },
+        [CTA_IP_V6_DST] = { .type = NL_A_BE32,.optional = TRUE },
     };
 
     static const NL_POLICY ctTupleProtoPolicy[] = {
@@ -1414,6 +1647,9 @@ MapNlToCtTuple(POVS_MESSAGE msgIn, PNL_ATTR ctAttr,
         [CTA_PROTO_DST_PORT] = { .type = NL_A_BE16, .optional = TRUE },
         [CTA_PROTO_ICMP_TYPE] = { .type = NL_A_U8, .optional = TRUE },
         [CTA_PROTO_ICMP_CODE] = { .type = NL_A_U8, .optional = TRUE },
+        [CTA_PROTO_ICMPV6_ID] = { .type = NL_A_BE16,.optional = TRUE },
+        [CTA_PROTO_ICMPV6_TYPE] = { .type = NL_A_U8,.optional = TRUE },
+        [CTA_PROTO_ICMPV6_CODE] = { .type = NL_A_U8,.optional = TRUE },
     };
 
     if (!ctAttr) {
@@ -1466,8 +1702,11 @@ MapNlToCtTuple(POVS_MESSAGE msgIn, PNL_ATTR ctAttr,
                         ctTupleProtoAttrs[CTA_PROTO_ICMP_CODE] ) {
                 ct_tuple->src_port = NlAttrGetU8(ctTupleProtoAttrs[CTA_PROTO_ICMP_TYPE]);
                 ct_tuple->dst_port = NlAttrGetU8(ctTupleProtoAttrs[CTA_PROTO_ICMP_CODE]);
+            } else if (ctTupleProtoAttrs[CTA_PROTO_ICMPV6_TYPE] &&
+                       ctTupleProtoAttrs[CTA_PROTO_ICMPV6_CODE] ) {
+                ct_tuple->src_port = NlAttrGetU8(ctTupleProtoAttrs[CTA_PROTO_ICMPV6_TYPE]);
+                ct_tuple->dst_port = NlAttrGetU8(ctTupleProtoAttrs[CTA_PROTO_ICMPV6_CODE]);
             }
-
         }
     }
 
@@ -1550,15 +1789,15 @@ MapProtoTupleToNl(PNL_BUFFER nlBuf, OVS_CT_KEY *key)
                 goto done;
             }
         } else if (key->nw_proto == IPPROTO_ICMPV6) {
-            if (!NlMsgPutTailU16(nlBuf, CTA_PROTO_ICMPV6_ID, 0)) {
+            if (!NlMsgPutTailU16(nlBuf, CTA_PROTO_ICMPV6_ID, htons(key->src.icmp_id))) {
                 status = NDIS_STATUS_FAILURE;
                 goto done;
             }
-            if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMPV6_TYPE, 0)) {
+            if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMPV6_TYPE, key->src.icmp_type)) {
                 status = NDIS_STATUS_FAILURE;
                 goto done;
             }
-            if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMPV6_CODE, 0)) {
+            if (!NlMsgPutTailU8(nlBuf, CTA_PROTO_ICMPV6_CODE, key->src.icmp_code)) {
                 status = NDIS_STATUS_FAILURE;
                 goto done;
             }
diff --git a/datapath-windows/ovsext/Conntrack.h b/datapath-windows/ovsext/Conntrack.h
index bbbf49c11..b68a54f30 100644
--- a/datapath-windows/ovsext/Conntrack.h
+++ b/datapath-windows/ovsext/Conntrack.h
@@ -167,33 +167,8 @@ OvsConntrackUpdateExpiration(OVS_CT_ENTRY *ctEntry,
     ctEntry->expiration = now + interval;
 }
 
-static const TCPHdr*
-OvsGetTcpHeader(PNET_BUFFER_LIST nbl,
-                OVS_PACKET_HDR_INFO *layers,
-                VOID *storage,
-                UINT32 *tcpPayloadLen)
-{
-    IPHdr *ipHdr;
-    TCPHdr *tcp;
-    VOID *dest = storage;
-
-    ipHdr = NdisGetDataBuffer(NET_BUFFER_LIST_FIRST_NB(nbl),
-                              layers->l4Offset + sizeof(TCPHdr),
-                              NULL, 1 /*no align*/, 0);
-    if (ipHdr == NULL) {
-        return NULL;
-    }
-
-    ipHdr = (IPHdr *)((PCHAR)ipHdr + layers->l3Offset);
-    tcp = (TCPHdr *)((PCHAR)ipHdr + ipHdr->ihl * 4);
-    if (tcp->doff * 4 >= sizeof *tcp) {
-        NdisMoveMemory(dest, tcp, sizeof(TCPHdr));
-        *tcpPayloadLen = TCP_DATA_LENGTH(ipHdr, tcp);
-        return storage;
-    }
-
-    return NULL;
-}
+const TCPHdr* OvsGetTcpHeader(PNET_BUFFER_LIST nbl, OVS_PACKET_HDR_INFO *layers,
+                                     VOID *storage, UINT32 *tcpPayloadLen);
 
 VOID OvsCleanupConntrack(VOID);
 NTSTATUS OvsInitConntrack(POVS_SWITCH_CONTEXT context);
@@ -203,6 +178,7 @@ NDIS_STATUS OvsExecuteConntrackAction(OvsForwardingContext *fwdCtx,
                                       const PNL_ATTR a);
 BOOLEAN OvsConntrackValidateTcpPacket(const TCPHdr *tcp);
 BOOLEAN OvsConntrackValidateIcmpPacket(const ICMPHdr *icmp);
+BOOLEAN OvsConntrackValidateIcmp6Packet(const ICMPHdr *icmp);
 OVS_CT_ENTRY * OvsConntrackCreateTcpEntry(const TCPHdr *tcp,
                                           UINT64 now,
                                           UINT32 tcpPayloadLen);
@@ -235,8 +211,8 @@ NTSTATUS OvsInitCtRelated(POVS_SWITCH_CONTEXT context);
 VOID OvsCleanupCtRelated(VOID);
 NDIS_STATUS OvsCtRelatedEntryCreate(UINT8 ipProto,
                                     UINT16 dl_type,
-                                    UINT32 serverIp,
-                                    UINT32 clientIp,
+                                    struct ct_addr serverIp,
+                                    struct ct_addr clientIp,
                                     UINT16 serverPort,
                                     UINT16 clientPort,
                                     UINT64 currentTime,
diff --git a/datapath-windows/ovsext/DpInternal.h b/datapath-windows/ovsext/DpInternal.h
index 58e7ed8e2..a2051a64d 100644
--- a/datapath-windows/ovsext/DpInternal.h
+++ b/datapath-windows/ovsext/DpInternal.h
@@ -206,6 +206,7 @@ typedef __declspec(align(8)) struct OvsFlowKey {
         UINT32 state;
         struct ovs_key_ct_labels labels;
         struct ovs_key_ct_tuple_ipv4 tuple_ipv4;
+        struct ovs_key_ct_tuple_ipv6 tuple_ipv6;
     } ct;                        /* Connection Tracking Flags */
 } OvsFlowKey;
 
@@ -216,6 +217,7 @@ typedef __declspec(align(8)) struct OvsFlowKey {
 #define OVS_ARP_KEY_SIZE (sizeof (ArpKey))
 #define OVS_ICMPV6_KEY_SIZE (sizeof (Icmp6Key))
 #define OVS_MPLS_KEY_SIZE (sizeof (MplsKey))
+#define OVS_TUPLE_IPV6 (sizeof (struct ovs_key_ct_tuple_ipv6))
 
 typedef struct OvsFlowStats {
     Ovs64AlignedU64 packetCount;
diff --git a/datapath-windows/ovsext/Flow.c b/datapath-windows/ovsext/Flow.c
index ac0582c18..32e181d32 100644
--- a/datapath-windows/ovsext/Flow.c
+++ b/datapath-windows/ovsext/Flow.c
@@ -184,6 +184,10 @@ const NL_POLICY nlFlowKeyPolicy[] = {
     [OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4] = {.type = NL_A_UNSPEC,
                                 .minLen = sizeof(struct ovs_key_ct_tuple_ipv4),
                                 .maxLen = sizeof(struct ovs_key_ct_tuple_ipv4),
+                                .optional = TRUE},
+    [OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6] = {.type = NL_A_UNSPEC,
+                                .minLen = OVS_TUPLE_IPV6,
+                                .maxLen = OVS_TUPLE_IPV6,
                                 .optional = TRUE}
 };
 const UINT32 nlFlowKeyPolicyLen = ARRAY_SIZE(nlFlowKeyPolicy);
@@ -1551,6 +1555,13 @@ _MapKeyAttrToFlowPut(PNL_ATTR *keyAttrs,
                        sizeof(struct ovs_key_ct_tuple_ipv4));
     }
 
+    if (keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]) {
+        const struct ovs_key_ct_tuple_ipv6 *tuple_ipv6;
+        tuple_ipv6 = NlAttrGet(keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]);
+        NdisMoveMemory(&destKey->ct.tuple_ipv6, tuple_ipv6,
+                       sizeof(struct ovs_key_ct_tuple_ipv6));
+    }
+
     /* ===== L2 headers ===== */
     if (keyAttrs[OVS_KEY_ATTR_IN_PORT]) {
         destKey->l2.inPort = NlAttrGetU32(keyAttrs[OVS_KEY_ATTR_IN_PORT]);
@@ -2106,6 +2117,13 @@ OvsGetFlowMetadata(OvsFlowKey *key,
                        sizeof(struct ovs_key_ct_tuple_ipv4));
     }
 
+    if (keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]) {
+        const struct ovs_key_ct_tuple_ipv6 *tuple_ipv6;
+        tuple_ipv6 = NlAttrGet(keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]);
+        NdisMoveMemory(&key->ct.tuple_ipv6, tuple_ipv6,
+                       sizeof(struct ovs_key_ct_tuple_ipv6));
+    }
+
     return status;
 }
 
@@ -2228,6 +2246,7 @@ OvsExtractLayers(const NET_BUFFER_LIST *packet,
                     if (icmp) {
                         layers->l7Offset = layers->l4Offset + sizeof *icmp;
                     }
+                    layers->isIcmp = 1;
                 }
             }
         } else {
@@ -2586,6 +2605,8 @@ FlowEqual(OvsFlow *srcFlow,
                     sizeof(struct ovs_key_ct_labels)) &&
             !memcmp(&srcFlow->key.ct.tuple_ipv4, &dstKey->ct.tuple_ipv4,
                     sizeof(struct ovs_key_ct_tuple_ipv4)) &&
+            !memcmp(&srcFlow->key.ct.tuple_ipv6, &dstKey->ct.tuple_ipv6,
+                    sizeof(struct ovs_key_ct_tuple_ipv6)) &&
             FlowMemoryEqual((UINT64 *)((UINT8 *)&srcFlow->key + offset),
                             (UINT64 *) dstStart,
                             size));
@@ -2698,13 +2719,20 @@ OvsLookupFlow(OVS_DATAPATH *datapath,
                                            0);
             *hash = OvsJhashWords((UINT32*)hash, 1, lblHash);
         }
-        if (key->ct.tuple_ipv4.ipv4_src) {
+        if (key->ct.tuple_ipv4.ipv4_proto) {
             UINT32 tupleHash = OvsJhashBytes(
                                 &key->ct.tuple_ipv4,
                                 sizeof(struct ovs_key_ct_tuple_ipv4),
                                 0);
             *hash = OvsJhashWords((UINT32*)hash, 1, tupleHash);
         }
+
+        if (key->ct.tuple_ipv6.ipv6_proto) {
+            UINT32 tupleHash = OvsJhashBytes(&key->ct.tuple_ipv6,
+                                             sizeof(struct ovs_key_ct_tuple_ipv6),
+                                             0);
+            *hash = OvsJhashWords((UINT32*)hash, 1, tupleHash);
+        }
     }
 
     head = &datapath->flowTable[HASH_BUCKET(*hash)];
@@ -2880,7 +2908,9 @@ ReportFlowInfo(OvsFlow *flow,
     NdisMoveMemory(&info->key.ct.tuple_ipv4,
                    &flow->key.ct.tuple_ipv4,
                    sizeof(struct ovs_key_ct_tuple_ipv4));
-
+    NdisMoveMemory(&info->key.ct.tuple_ipv6,
+                   &flow->key.ct.tuple_ipv6,
+                   sizeof(struct ovs_key_ct_tuple_ipv6));
     return status;
 }
 
@@ -3272,6 +3302,13 @@ OvsProbeSupportedFeature(POVS_MESSAGE msgIn,
             OVS_LOG_ERROR("Invalid ct_tuple_ipv4.");
             status = STATUS_INVALID_PARAMETER;
         }
+    } else if (keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]) {
+        const struct ovs_key_ct_tuple_ipv6 *ct_tuple_ipv6;
+        ct_tuple_ipv6 = NlAttrGet(keyAttrs[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6]);
+        if (!ct_tuple_ipv6) {
+            OVS_LOG_ERROR("Invalid ct_tuple_ipv6.");
+            status = STATUS_INVALID_PARAMETER;
+        }
     } else {
         OVS_LOG_ERROR("Feature not supported.");
         status = STATUS_INVALID_PARAMETER;
diff --git a/datapath-windows/ovsext/NetProto.h b/datapath-windows/ovsext/NetProto.h
index 9a17deee7..39726c203 100644
--- a/datapath-windows/ovsext/NetProto.h
+++ b/datapath-windows/ovsext/NetProto.h
@@ -239,7 +239,8 @@ typedef union _OVS_PACKET_HDR_INFO {
         UINT16 tcpCsumNeeded:1;
         UINT16 udpCsumNeeded:1;
         UINT16 udpCsumZero:1;
-        UINT16 pad:8;
+        UINT16 isIcmp:1;
+        UINT16 pad:7;
     } ;
     UINT64 value;
 } OVS_PACKET_HDR_INFO, *POVS_PACKET_HDR_INFO;
@@ -300,6 +301,11 @@ typedef struct IPv6FragHdr {
     UINT32 ident;
 } IPv6FragHdr;
 
+typedef struct IPv6OptHdr {
+    UINT8 nextHdr;
+    UINT8 hdrLen;
+} IPv6OptHdr;
+
 typedef struct IPv6NdOptHdr {
     UINT8 type;
     UINT8 len;
diff --git a/datapath-windows/ovsext/PacketParser.c b/datapath-windows/ovsext/PacketParser.c
index f8adcc3c9..aeead5899 100644
--- a/datapath-windows/ovsext/PacketParser.c
+++ b/datapath-windows/ovsext/PacketParser.c
@@ -318,6 +318,7 @@ OvsParseIcmpV6(const NET_BUFFER_LIST *packet,
     }
 
     layers->l7Offset = ofs;
+    layers->isIcmp = 1;
     return NDIS_STATUS_SUCCESS;
 
 invalid:
diff --git a/datapath-windows/ovsext/Util.c b/datapath-windows/ovsext/Util.c
index abd38c2fe..d703b2468 100644
--- a/datapath-windows/ovsext/Util.c
+++ b/datapath-windows/ovsext/Util.c
@@ -161,3 +161,22 @@ OvsPerCpuDataCleanup()
 {
     OvsDeferredActionsCleanup();
 }
+
+NTSTATUS
+OvsIpv6StringToAddress(const char* ip6String, struct in6_addr *ipv6Addr)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    char *terminator = NULL;
+
+    status = RtlIpv6StringToAddressA(ip6String, &terminator, ipv6Addr);
+    return status;
+}
+
+char *
+OvsIpv6AddressToString(struct in6_addr ipv6Addr, char* ip6String)
+{
+    char *returnedIpv6Str = NULL;
+
+    returnedIpv6Str = RtlIpv6AddressToStringA((&ipv6Addr), ip6String);
+    return returnedIpv6Str;
+}
\ No newline at end of file
diff --git a/datapath-windows/ovsext/Util.h b/datapath-windows/ovsext/Util.h
index a9bccf36c..f63a885a9 100644
--- a/datapath-windows/ovsext/Util.h
+++ b/datapath-windows/ovsext/Util.h
@@ -143,6 +143,27 @@ OvsPerCpuDataInit();
 VOID
 OvsPerCpuDataCleanup();
 
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsIpv6StringToAddress --
+ *     The function was used to convert ip6string to binary ipv6 address.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsIpv6StringToAddress(const char* ip6String, struct in6_addr *ipv6Addr);
+
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsIpv6AddressToString --
+ *     The function convert bindary ipv6Addr to string.
+ * --------------------------------------------------------------------------
+ */
+char *
+OvsIpv6AddressToString(struct in6_addr ipv6Addr, char* ip6String);
+
+
 static LARGE_INTEGER seed;
 
 /*
diff --git a/datapath-windows/ovsext/precomp.h b/datapath-windows/ovsext/precomp.h
index 14f6843d3..e9f62f6ab 100644
--- a/datapath-windows/ovsext/precomp.h
+++ b/datapath-windows/ovsext/precomp.h
@@ -19,6 +19,7 @@
 #include <intsafe.h>
 #include <ntintsafe.h>
 #include <ntstrsafe.h>
+#include <ip2string.h>
 
 #include "Types.h"
 
