diff mbox series

[ovs-dev,v1,1/1] datapath-windows: Add IPv6 conntrack support on Windows.

Message ID 20220327133748.88591-1-liudj_yanjiu@163.com
State Superseded
Delegated to: Alin Gabriel Serdean
Headers show
Series [ovs-dev,v1,1/1] datapath-windows: Add IPv6 conntrack support on Windows. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/intel-ovs-compilation success test: success
ovsrobot/github-robot-_Build_and_Test success github build: passed

Commit Message

ldejing March 27, 2022, 1:37 p.m. UTC
From: ldejing <ldejing@vmware.com>

Implementation on Windows:
Currently, IPv4 conntrack was supported on the windows platform.
In this patch we have implemented ipv6 conntrack functions according
to the current logic of the IPv4 conntrack. This implementation has
included TcpV6(nat and normal scenario), UdpV6(nat and normal scenario),
IcmpV6 conntrack of echo request/reply packet and
FtpV6(nat and normal scenario).

Testing Topology:
On the Windows VM runs on the ESXi host, two hyper-v ports attached
to the ovs bridge; one hyper-v port worked as client and the
other port worked as server.

Testing Case:
1. TcpV6
  a) Tcp request/reply conntrack for normal scenario.
     In this scenario, 20::1 as client, 20::2 as server, it will generate
     following conntrack entry:
     (Origin(src=20::1, src_port=1555, dst=20::2, dst_port=1556),
      reply(src=20::2,src_port=1556,dst=20::1,dst_port=1555),protocol=tcp)

  b) Tcp request/reply conntrack for nat scenario.
     In this scenario, 20::1 as client, 20::10 as floating ip, 21::3 as server,
     it will generate following conntrack entry:
     (Origin(src=20::1, src_port=1555, dst=20::10, dst_port=1556),
      reply(src=21::3, src_port=1556, dst=20::1, dst_port= 1555),protocol=tcp)

2. UdpV6
  a) Udp request/reply conntrack for normal scenario.
     (Origin(src=20::1, src_port=1555, dst=20::2, dst_port=1556),
      reply(src=20::2,src_port=1556,dst=20::1,dst_port=1555),protocol=udp)
  b) Udp request/reply conntrack for nat scenario.
     (Origin(src=20::1, src_port=1555, dst=20::10, dst_port=1556),
      reply(src=21::3, src_port=1556, dst=20::1, dst_port= 1555),protocol=udp)

3. IcmpV6:
  a) Icmpv6 request/reply conntrack for normal scenario.
     Currently Icmpv6 only support to construct conntrack for
     echo request/reply packet, take (20::1 -> 20::2)  for example,
     it will generate following conntrack entry:
     (origin(src = 20::1, dst=20::2), reply(src=20::2, dst=20::1), protocol=icmp)
  b) Icmp request/reply conntrack for dnat scenario,
     for example (20::1->20::10->21::3), 20::1 is
     client, 20::10 is floating ip, 21::3 is server ip.
     It will generate flow like below:
     (origin(src=20::1, dst=20::10), reply(src=21::3, dst=20::1), protocol=icmp)

4. FtpV6
  a) Ftp request/reply conntrack for normal scenario.
     In this scenario, take 20::1 as client, 20::2 as server, it will generate
     two conntrack entries:
     Ftp active mode
     (Origin(src=20::1, src_port=1555, dst=20::2, dst_port=21),
      reply(src=20::2, src_port=21, dst=20::1, dst_port=1555), protocol=tcp)
     (Origin(src=20::2, src_port=20, dst=20::1, dst_port=1556),
      reply(src=20::1, src_port=1556, dst=20::2, dst_port=20), protocol=tcp)

     Ftp passive mode
     (Origin(src=20::1, src_port=1555, dst=20::2, dst_port=21),
      reply(src=20::2,src_port=21,dst=20::1,dst_port=1555),protocol=tcp)
     (Origin(src=20::1, src_port=1556, dst=20::2, dst_port=1557),
      reply(src=20::2,src_port=1557, dst=20::1, dst_port=1556) protocol=tcp)

  b) Ftp request/reply conntrack for nat scenario.
     Ftp passive mode,
     In this secnario, 20::1 as client, 20::10 as floating ip, 21::3 as server
     ip. It will generate following flow:
     (Origin(src=20::1, src_port=1555, dst=20::10, dst_port=21),
     reply(src=21::3, src_port=21, dst=20::1, dst_port= 1555),protocol=tcp)
     (Origin(src=20::1, src_port=1556, dst=20::10, dst_port=1557),
     reply(src=21::3, src_port=1557, dst=20::1, dst_port= 1556),protocol=tcp)

5. Regression test for IpV4 in Antrea project (about 60 test case)

Future work:
  1) IcmpV6 redirect packet conntrack.
  2) IpV6 fragment support on Udp.
  3) Support napt for IPv6.
  4) FtpV6 active mode for nat.

Signed-off-by: ldejing <ldejing@vmware.com>
---
 datapath-windows/ovsext/Actions.c           | 174 ++++++++++++
 datapath-windows/ovsext/Actions.h           |   5 +
 datapath-windows/ovsext/Conntrack-ftp.c     | 159 +++++++++--
 datapath-windows/ovsext/Conntrack-icmp.c    |  11 +
 datapath-windows/ovsext/Conntrack-nat.c     |  82 ++++--
 datapath-windows/ovsext/Conntrack-related.c |  45 ++-
 datapath-windows/ovsext/Conntrack.c         | 287 ++++++++++++++++++--
 datapath-windows/ovsext/Conntrack.h         |  34 +--
 datapath-windows/ovsext/DpInternal.h        |   2 +
 datapath-windows/ovsext/Flow.c              |  41 ++-
 datapath-windows/ovsext/NetProto.h          |   8 +-
 datapath-windows/ovsext/PacketParser.c      |   1 +
 datapath-windows/ovsext/Util.c              |  19 ++
 datapath-windows/ovsext/Util.h              |  21 ++
 datapath-windows/ovsext/precomp.h           |   1 +
 15 files changed, 793 insertions(+), 97 deletions(-)
diff mbox series

Patch

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"