diff --git a/Documentation/faq/releases.rst b/Documentation/faq/releases.rst
index c12ffaf4a..f1adce2dc 100644
--- a/Documentation/faq/releases.rst
+++ b/Documentation/faq/releases.rst
@@ -134,6 +134,7 @@ Q: Are all features available with all datapaths?
     Connection tracking             4.3            2.5          2.6      YES
     Connection tracking-IPv6        YES            YES          YES      2.18
     Conntrack Fragment Reass.       4.3            2.6          2.12     YES
+    Conntrack IPv6 Fragment         4.3            2.6          2.12     2.18
     Conntrack Timeout Policies      5.2            2.12         2.14     NO
     Conntrack Zone Limit            4.18           2.10         2.13     YES
     Conntrack NAT                   4.6            2.6          2.8      YES
diff --git a/NEWS b/NEWS
index eece0d0b2..c6defacfc 100644
--- a/NEWS
+++ b/NEWS
@@ -29,6 +29,7 @@ Post-v2.17.0
    - Windows:
      * Conntrack support for TCPv6, UDPv6, ICMPv6, FTPv6.
      * IPv6 Geneve tunnel support.
+     * Conntrack ipv6 support fragment.
 
 
 v2.17.0 - 17 Feb 2022
diff --git a/datapath-windows/automake.mk b/datapath-windows/automake.mk
index 60b3d6033..a3fe909a4 100644
--- a/datapath-windows/automake.mk
+++ b/datapath-windows/automake.mk
@@ -37,6 +37,8 @@ EXTRA_DIST += \
 	datapath-windows/ovsext/Gre.c \
 	datapath-windows/ovsext/IpFragment.c \
 	datapath-windows/ovsext/IpFragment.h \
+	datapath-windows/ovsext/Ip6Fragment.c \
+	datapath-windows/ovsext/Ip6Fragment.h \
 	datapath-windows/ovsext/IpHelper.c \
 	datapath-windows/ovsext/IpHelper.h \
 	datapath-windows/ovsext/Jhash.c \
diff --git a/datapath-windows/ovsext/Actions.c b/datapath-windows/ovsext/Actions.c
index 0f7f78932..edfac3b71 100644
--- a/datapath-windows/ovsext/Actions.c
+++ b/datapath-windows/ovsext/Actions.c
@@ -917,7 +917,7 @@ OvsOutputForwardingCtx(OvsForwardingContext *ovsFwdCtx)
                                           ovsFwdCtx->completionList,
                                           &ovsFwdCtx->layers, FALSE);
             if (status != NDIS_STATUS_SUCCESS) {
-                dropReason = L"Dropped due to resouces.";
+                dropReason = L"Dropped due to resources.";
                 goto dropit;
             }
         }
@@ -2411,8 +2411,9 @@ OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext,
                 goto dropit;
             } else if (oldNbl != ovsFwdCtx.curNbl) {
                 /*
-                 * OvsIpv4Reassemble consumes the original NBL and creates a
-                 * new one and assigns it to the curNbl of ovsFwdCtx.
+                 * OvsIpv4Reassemble/OvsIpv6Reassemble consumes the
+                 * original NBL and creates a new one and assigns
+                 * it to the curNbl of ovsFwdCtx.
                  */
                 OvsInitForwardingCtx(&ovsFwdCtx,
                                      ovsFwdCtx.switchContext,
@@ -2423,6 +2424,7 @@ OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext,
                                      ovsFwdCtx.completionList,
                                      &ovsFwdCtx.layers, FALSE);
                 key->ipKey.nwFrag = OVS_FRAG_TYPE_NONE;
+                key->ipv6Key.nwFrag = OVS_FRAG_TYPE_NONE;
             }
             break;
         }
diff --git a/datapath-windows/ovsext/BufferMgmt.c b/datapath-windows/ovsext/BufferMgmt.c
index acf3c13a2..db9d37537 100644
--- a/datapath-windows/ovsext/BufferMgmt.c
+++ b/datapath-windows/ovsext/BufferMgmt.c
@@ -77,6 +77,7 @@
  */
 
 #include "precomp.h"
+#include "jhash.h"
 #include "Debug.h"
 #include "Flow.h"
 #include "Offload.h"
@@ -92,6 +93,7 @@
 #define OVS_DBG_MOD OVS_DBG_BUFMGMT
 
 
+
 /*
  * --------------------------------------------------------------------------
  * OvsInitBufferPool --
@@ -1109,19 +1111,26 @@ GetIpHeaderInfo(PNET_BUFFER_LIST curNbl,
 {
     EthHdr *eth;
     IPHdr *ipHdr;
+    IPv6Hdr *ipv6Hdr;
     PNET_BUFFER curNb;
 
     curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
     ASSERT(NET_BUFFER_NEXT_NB(curNb) == NULL);
-
     eth = (EthHdr *)NdisGetDataBuffer(curNb,
                                       hdrInfo->l4Offset,
                                       NULL, 1, 0);
     if (eth == NULL) {
         return NDIS_STATUS_INVALID_PACKET;
     }
-    ipHdr = (IPHdr *)((PCHAR)eth + hdrInfo->l3Offset);
-    *hdrSize = (UINT32)(hdrInfo->l3Offset + (ipHdr->ihl * 4));
+
+    if (hdrInfo->isIPv6) {
+        ipv6Hdr = (IPv6Hdr *)((PCHAR)eth + hdrInfo->l3Offset);
+        *hdrSize = (UINT32)(hdrInfo->l4Offset);
+    } else {
+        ipHdr = (IPHdr *)((PCHAR)eth + hdrInfo->l3Offset);
+        *hdrSize = (UINT32)(hdrInfo->l3Offset + (ipHdr->ihl * 4));
+    }
+
     return NDIS_STATUS_SUCCESS;
 }
 
@@ -1160,8 +1169,8 @@ GetSegmentHeaderInfo(PNET_BUFFER_LIST nbl,
  * --------------------------------------------------------------------------
  */
 static NDIS_STATUS
-FixFragmentHeader(PNET_BUFFER nb, UINT16 fragmentSize,
-                  BOOLEAN lastPacket, UINT16 offset)
+FixFragmentHeader(PNET_BUFFER nb, UINT16 fragmentSize, BOOLEAN lastPacket,
+                  UINT16 offset, UINT32 fragmentIdent)
 {
     EthHdr *dstEth = NULL;
     PMDL mdl = NULL;
@@ -1198,7 +1207,40 @@ FixFragmentHeader(PNET_BUFFER nb, UINT16 fragmentSize,
     }
     case ETH_TYPE_IPV6_NBO:
     {
-        return NDIS_STATUS_NOT_SUPPORTED;
+        IPv6Hdr *dstIP = NULL;
+        UINT8 nextHdr;
+        IPv6ExtHdr *extHdr;
+        ASSERT((INT)MmGetMdlByteCount(mdl) - NET_BUFFER_CURRENT_MDL_OFFSET(nb)
+               >= sizeof(EthHdr) + sizeof(IPv6Hdr));
+        dstIP = (IPv6Hdr *)((PCHAR)dstEth + sizeof(*dstEth));
+        extHdr = (IPv6ExtHdr *)((PCHAR)dstIP + sizeof(IPv6Hdr));
+        nextHdr = dstIP->nexthdr;
+
+        while (nextHdr != SOCKET_IPPROTO_FRAGMENT) {
+            nextHdr = extHdr->nextHeader;
+            extHdr = (IPv6ExtHdr *)((PCHAR)extHdr + OVS_IPV6_OPT_LEN(extHdr));
+            if (!extHdr) {
+                break;
+            }
+        }
+
+        if (nextHdr == SOCKET_IPPROTO_FRAGMENT) {
+            IPv6FragHdr *fragHdr = (IPv6FragHdr  *)extHdr;
+            fragHdr->reserved = 0x00;
+            fragHdr->offlg &= htons(0x00);
+            fragHdr->ident = fragmentIdent;
+            if (lastPacket) {
+                fragHdr->offlg |= htons(offset << 3);
+            } else {
+                fragHdr->offlg |= htons(0x01);
+                fragHdr->offlg |= htons(offset << 3) ;
+            }
+        } else {
+            if (!extHdr) {
+                ASSERT(! "Invalid fragment packet.");
+            }
+        }
+        break;
     }
     default:
         OVS_LOG_ERROR("Invalid eth type: %d\n", dstEth->Type);
@@ -1314,6 +1356,7 @@ FixSegmentHeader(PNET_BUFFER nb, UINT16 segmentSize, UINT32 seqNumber,
 
     return STATUS_SUCCESS;
 }
+
  /*
   * --------------------------------------------------------------------------
  * OvsTcpSegmentNBL --
@@ -1331,6 +1374,187 @@ OvsTcpSegmentNBL(PVOID ovsContext,
     return OvsFragmentNBL(ovsContext, nbl, hdrInfo, mss, headRoom, isIpFragment);
 }
 
+NDIS_STATUS
+OvsFigureIPV6ExtHdrLayout(PNET_BUFFER_LIST nbl,
+                          POVS_PACKET_HDR_INFO hdrInfo,
+                          UINT16 *beforeFragmentExtFieldLen)
+{
+    EthHdr *eth;
+    IPv6Hdr *ipv6Hdr;
+    IPv6ExtHdr *extHdr;
+    PNET_BUFFER curNb;
+    UINT8 nextHdr = 0;
+    UINT16 offset = 0;
+    BOOLEAN foundRouterHeader = FALSE;
+    UINT16 extFieldLen = 0;
+
+    curNb = NET_BUFFER_LIST_FIRST_NB(nbl);
+    ASSERT(NET_BUFFER_NEXT_NB(curNb) == NULL);
+    eth = (EthHdr *)NdisGetDataBuffer(curNb,
+                                      hdrInfo->l4Offset,
+                                      NULL, 1, 0);
+    if (!eth) {
+        OVS_LOG_ERROR("Invalid packet.");
+        return NDIS_STATUS_INVALID_PACKET;
+    }
+
+    ipv6Hdr = (IPv6Hdr *)((PCHAR)eth + hdrInfo->l3Offset);
+    nextHdr = ipv6Hdr->nexthdr;
+    extHdr = (IPv6ExtHdr *)((PCHAR)ipv6Hdr + sizeof(*ipv6Hdr));
+    extFieldLen = hdrInfo->l4Offset - hdrInfo->l3Offset - sizeof(IPv6Hdr);
+
+    while (offset <= extFieldLen) {
+        switch (nextHdr) {
+            case SOCKET_IPPROTO_HOPOPTS:
+                *beforeFragmentExtFieldLen += OVS_IPV6_OPT_LEN(extHdr);
+                break;
+            case SOCKET_IPPROTO_ROUTING:
+                *beforeFragmentExtFieldLen += OVS_IPV6_OPT_LEN(extHdr);
+                foundRouterHeader = TRUE;
+                break;
+            case SOCKET_IPPROTO_DSTOPTS:
+                if (foundRouterHeader) {
+                    /* In the ipv6 extension field, dst option field must
+                     * appear with routeing option filed. */
+                    *beforeFragmentExtFieldLen += OVS_IPV6_OPT_LEN(extHdr);
+                    return NDIS_STATUS_SUCCESS;
+                } else {
+                    /* dst opts field not appear with routing field,
+                     * this represent this dst option field is
+                     * bebind fragment. */
+                    return NDIS_STATUS_SUCCESS;
+                }
+                break;
+            default:
+                return NDIS_STATUS_SUCCESS;
+        }
+
+        offset += OVS_IPV6_OPT_LEN(extHdr);
+        nextHdr = extHdr->nextHeader;
+        extHdr = (IPv6ExtHdr *)((PCHAR)extHdr + OVS_IPV6_OPT_LEN(extHdr));
+    }
+
+    return NDIS_STATUS_SUCCESS;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *  function name -- FixIPV6ExtHdrField
+ *     This function mainly used to fix IPv6 extension field, because we only
+ *     add fragment field in the header, not fix the next header filed,
+ *     this function is used to fix that issue.
+ * --------------------------------------------------------------------------
+ */
+NDIS_STATUS
+FixIPV6ExtHdrField(PNET_BUFFER nb, UINT16 l3Offset, UINT16 l4Offset,
+                   UINT16 fragmentSize)
+{
+    EthHdr *eth;
+    IPv6Hdr *ipv6Hdr = NULL;
+    IPv6ExtHdr *extHdr = NULL;
+    IPv6ExtHdr *lastExtHdr = NULL;
+    IPv6FragHdr *frgHdr = NULL;
+    UINT8 nextHdr = 0;
+    UINT16 offset = 0;
+    BOOLEAN exitLookup = FALSE;
+    BOOLEAN foundRouterHeader = FALSE;
+    PMDL mdl = NULL;
+    PUINT8 bufferStart = NULL;
+    UINT16 extFieldLen = 0;
+
+    mdl = NET_BUFFER_FIRST_MDL(nb);
+    bufferStart = (PUINT8)OvsGetMdlWithLowPriority(mdl);
+    if (!bufferStart) {
+        OVS_LOG_ERROR("Return, buffer start null.");
+        return STATUS_NDIS_INVALID_PACKET;
+    }
+    eth = (EthHdr *)(bufferStart + NET_BUFFER_CURRENT_MDL_OFFSET(nb));
+    ipv6Hdr = (IPv6Hdr *)((PCHAR)eth+ l3Offset);
+    nextHdr = ipv6Hdr->nexthdr;
+    lastExtHdr = NULL;
+    extHdr = (IPv6ExtHdr *)((PCHAR)ipv6Hdr + sizeof(*ipv6Hdr));
+    extFieldLen = l4Offset - l3Offset - sizeof(IPv6Hdr);
+    ipv6Hdr->payload_len = htons(extFieldLen + fragmentSize);
+
+    while (offset <= extFieldLen) {
+        switch (nextHdr) {
+            case SOCKET_IPPROTO_HOPOPTS:
+                break;
+            case SOCKET_IPPROTO_ROUTING:
+                foundRouterHeader = TRUE;
+                break;
+            case SOCKET_IPPROTO_DSTOPTS:
+                if (foundRouterHeader) {
+                    frgHdr = (IPv6FragHdr *)((PCHAR)extHdr + offset);
+                    frgHdr->nextHeader = ipv6Hdr->nexthdr;
+                    ipv6Hdr->nexthdr = SOCKET_IPPROTO_FRAGMENT;
+
+                } else {
+                    /* This dest option field was appear behind
+                     * fragment field. */
+                    if (lastExtHdr) {
+                        frgHdr = (IPv6FragHdr *)extHdr;
+                        frgHdr->nextHeader = lastExtHdr->nextHeader;
+                        lastExtHdr->nextHeader = SOCKET_IPPROTO_FRAGMENT;
+                    } else {
+                        frgHdr = (IPv6FragHdr *)((PCHAR)extHdr + offset);
+                        frgHdr->nextHeader = ipv6Hdr->nexthdr;
+                        ipv6Hdr->nexthdr = SOCKET_IPPROTO_FRAGMENT;
+                    }
+                }
+                exitLookup = TRUE;
+                break;
+            default:
+                if (!offset) {
+                    frgHdr = (IPv6FragHdr *)((PCHAR)extHdr + offset);
+                    frgHdr->nextHeader = ipv6Hdr->nexthdr;
+                    ipv6Hdr->nexthdr = SOCKET_IPPROTO_FRAGMENT;
+                } else {
+                    frgHdr = (IPv6FragHdr *)extHdr;
+                    frgHdr->nextHeader = lastExtHdr->nextHeader;
+                    lastExtHdr->nextHeader = SOCKET_IPPROTO_FRAGMENT;
+                }
+                exitLookup = TRUE;
+                break;
+        }
+
+        if (exitLookup) {
+            break;
+        }
+
+        offset += OVS_IPV6_OPT_LEN(extHdr);
+        nextHdr = extHdr->nextHeader;
+        lastExtHdr = extHdr;
+        extHdr = (IPv6ExtHdr *)((PCHAR)extHdr + OVS_IPV6_OPT_LEN(extHdr));
+    }
+
+    return NDIS_STATUS_SUCCESS;
+}
+
+UINT32
+GenFragIdent(PNET_BUFFER_LIST nbl, POVS_PACKET_HDR_INFO hdrInfo)
+{
+    EthHdr *eth;
+    IPv6Hdr *ipv6Hdr;
+    PNET_BUFFER curNb;
+    UINT32 srcHash;
+    UINT32 dstHash;
+    UINT32 randNumber;
+    LARGE_INTEGER randomSeed;
+
+    curNb = NET_BUFFER_LIST_FIRST_NB(nbl);
+    ASSERT(NET_BUFFER_NEXT_NB(curNb) == NULL);
+    eth = (EthHdr *)NdisGetDataBuffer(curNb,
+                                      hdrInfo->l4Offset,
+                                      NULL, 1, 0);
+    ipv6Hdr = (IPv6Hdr *)((PCHAR)eth + hdrInfo->l3Offset);
+    KeQuerySystemTime(&randomSeed);
+    randNumber = randomSeed.LowPart * 0x8088405 + 1;
+
+    srcHash = OvsJhashBytes((UINT32 *)(&(ipv6Hdr->saddr)), 4, randNumber);
+    dstHash = OvsJhashBytes((UINT32 *)(&(ipv6Hdr->daddr)), 4, srcHash);
+    return dstHash;
+}
 
 /*
  * --------------------------------------------------------------------------
@@ -1366,8 +1590,11 @@ OvsFragmentNBL(PVOID ovsContext,
     PNET_BUFFER nb, newNb;
     NDIS_STATUS status;
     UINT16 segmentSize;
-    ULONG copiedSize;
+    ULONG copiedSize = 0;
     UINT16 offset = 0, packetCounter = 0;
+    UINT16 beforeFragHdrLen = 0;
+    UINT32 fragmentIdent = 0;
+    UINT16 ip6StdHeaderLen = 0;
 
     srcCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl);
     if (srcCtx == NULL || srcCtx->magic != OVS_CTX_MAGIC) {
@@ -1379,6 +1606,19 @@ OvsFragmentNBL(PVOID ovsContext,
     nb = NET_BUFFER_LIST_FIRST_NB(nbl);
     ASSERT(NET_BUFFER_NEXT_NB(nb) == NULL);
 
+    if (hdrInfo->isIPv6 && isIpFragment) {
+        /* 1. We need to calculate the header length before
+         * fragment and after fragment.
+         * */
+        status = OvsFigureIPV6ExtHdrLayout(nbl, hdrInfo,
+                                           &beforeFragHdrLen);
+        if (status != NDIS_STATUS_SUCCESS) {
+            OVS_LOG_ERROR("Figure out ipv6 header layout error.");
+            return NULL;
+        }
+        ip6StdHeaderLen = hdrInfo->l3Offset + sizeof(IPv6Hdr);
+    }
+
     /* Figure out the header size */
     if (isIpFragment) {
         status = GetIpHeaderInfo(nbl, hdrInfo, &hdrSize);
@@ -1391,21 +1631,40 @@ OvsFragmentNBL(PVOID ovsContext,
     }
     /* Get the NBL size. */
     if (isIpFragment) {
-        nblSize = fragmentSize - hdrSize;
+        if (hdrInfo->isIPv6) {
+            nblSize = fragmentSize - hdrSize - sizeof(IPv6FragHdr);
+        } else {
+            nblSize = fragmentSize - hdrSize;
+        }
     } else {
         nblSize = fragmentSize;
     }
+
     size = NET_BUFFER_DATA_LENGTH(nb) - hdrSize;
+    if (hdrInfo->isIPv6) {
+        /* Because if we want to divide ipv6 info fragments,
+         * we need add a fragment header in packet, thus we will
+         * allocate more memory(contain fragment header) for the packet. */
+        UINT32 dataOffset = hdrSize + sizeof(IPv6FragHdr) + headRoom;
+        newNbl = NdisAllocateFragmentNetBufferList(nbl, NULL, NULL, hdrSize,
+                                                   nblSize,
+                                                   dataOffset,
+                                                   0, 0);
+    } else {
+        newNbl = NdisAllocateFragmentNetBufferList(nbl, NULL, NULL, hdrSize,
+                                                   nblSize, hdrSize + headRoom,
+                                                   0, 0);
+    }
 
-    /* XXX add to ovsPool counters? */
-    newNbl = NdisAllocateFragmentNetBufferList(nbl, NULL, NULL, hdrSize,
-                                               nblSize, hdrSize + headRoom ,
-                                               0, 0);
     if (newNbl == NULL) {
         return NULL;
     }
 
-    /* Now deal with TCP payload */
+    /* Generate fragment identification */
+    if (isIpFragment && hdrInfo->isIPv6) {
+        fragmentIdent = GenFragIdent(nbl, hdrInfo);
+    }
+
     for (newNb = NET_BUFFER_LIST_FIRST_NB(newNbl); newNb != NULL;
             newNb = NET_BUFFER_NEXT_NB(newNb)) {
         segmentSize = (size > nblSize ? nblSize : size) & 0xffff;
@@ -1413,17 +1672,128 @@ OvsFragmentNBL(PVOID ovsContext,
             NdisAdvanceNetBufferDataStart(newNb, headRoom, FALSE, NULL);
         }
 
-        /* Now copy the eth/IP/TCP header and fix up */
-        status = NdisCopyFromNetBufferToNetBuffer(newNb, 0, hdrSize, nb, 0,
-                                                  &copiedSize);
-        if (status != NDIS_STATUS_SUCCESS || hdrSize != copiedSize) {
-            goto nblcopy_error;
+        if (NET_BUFFER_LIST_FIRST_NB(newNbl) == newNb) {
+            if (hdrInfo->isIPv6) {
+                /* When it is first ipv6 packet, we need copy all of ip
+                 * ext header Copy headers before fragment.
+                 * */
+                status = NdisCopyFromNetBufferToNetBuffer(newNb, 0,
+                                                          ip6StdHeaderLen,
+                                                          nb, 0 , &copiedSize);
+                if (status != NDIS_STATUS_SUCCESS ||
+                    (hdrInfo->l3Offset + sizeof(IPv6Hdr)) != copiedSize) {
+                    goto nblcopy_error;
+                }
+
+                if (beforeFragHdrLen) {
+                    status = NdisCopyFromNetBufferToNetBuffer(newNb,
+                                                              ip6StdHeaderLen,
+                                                              beforeFragHdrLen,
+                                                              nb,
+                                                              ip6StdHeaderLen,
+                                                              &copiedSize);
+                    if (status != NDIS_STATUS_SUCCESS ||
+                        beforeFragHdrLen != copiedSize) {
+                        goto nblcopy_error;
+                    }
+                }
+                /* Copy fragment headers. */
+                /* Copy headers after fragments. */
+
+                UINT32 behindFragHdrLen = hdrSize - hdrInfo->l3Offset
+                                          - sizeof(IPv6Hdr)
+                                          - beforeFragHdrLen;
+                if (behindFragHdrLen > 0) {
+                    status = NdisCopyFromNetBufferToNetBuffer(newNb,
+                                                              (ip6StdHeaderLen
+                                                               + beforeFragHdrLen
+                                                               + sizeof(IPv6FragHdr)),
+                                                              behindFragHdrLen,
+                                                              nb,
+                                                              ip6StdHeaderLen
+                                                              + beforeFragHdrLen,
+                                                              &copiedSize);
+                    if (status != NDIS_STATUS_SUCCESS ||
+                        behindFragHdrLen != copiedSize) {
+                        goto nblcopy_error;
+                    }
+                }
+
+                /* Fix IPv6 opt fields. */
+                status = FixIPV6ExtHdrField(newNb,
+                                            hdrInfo->l3Offset,
+                                            hdrInfo->l4Offset + sizeof(IPv6FragHdr),
+                                            segmentSize);
+                if (status !=  NDIS_STATUS_SUCCESS) {
+                    OVS_LOG_ERROR("Invalid reassemble packet.");
+                    goto  nblcopy_error;
+                }
+            } else {
+                /* Now copy the eth/IP/TCP header and fix up */
+                status = NdisCopyFromNetBufferToNetBuffer(newNb, 0, hdrSize,
+                                                          nb, 0, &copiedSize);
+                if (status != NDIS_STATUS_SUCCESS || hdrSize != copiedSize) {
+                    goto nblcopy_error;
+                }
+            }
+        } else {
+            if (hdrInfo->isIPv6) {
+                /* Because ipv6 fragment not first packet, doesn't exist
+                 * header after fragment, thus release some data space*/
+                UINT32 behindFragHdrLen = (hdrSize - hdrInfo->l3Offset
+                                           - sizeof(IPv6Hdr) - beforeFragHdrLen);
+                if (behindFragHdrLen > 0) {
+                    NdisAdvanceNetBufferDataStart(newNb,
+                                                  behindFragHdrLen,
+                                                  FALSE, NULL);
+                }
+
+                /* When it is not first ipv6 packet, we only need copy before
+                 * ipv6 segment. */
+                status = NdisCopyFromNetBufferToNetBuffer(newNb, 0,
+                                                          ip6StdHeaderLen,
+                                                          nb, 0 , &copiedSize);
+                if (status != NDIS_STATUS_SUCCESS ||
+                    (hdrInfo->l3Offset + sizeof(IPv6Hdr)) != copiedSize) {
+                    goto nblcopy_error;
+                }
+
+                if (beforeFragHdrLen) {
+                    status = NdisCopyFromNetBufferToNetBuffer(newNb,
+                                                              ip6StdHeaderLen,
+                                                              beforeFragHdrLen,
+                                                              nb,
+                                                              ip6StdHeaderLen,
+                                                              &copiedSize);
+                    if (status != NDIS_STATUS_SUCCESS ||
+                        beforeFragHdrLen != copiedSize) {
+                        goto nblcopy_error;
+                    }
+                }
+
+                status = FixIPV6ExtHdrField(newNb, hdrInfo->l3Offset,
+                                            (ip6StdHeaderLen +
+                                             beforeFragHdrLen +
+                                             sizeof(IPv6FragHdr)),
+                                            segmentSize);
+                if (status !=  NDIS_STATUS_SUCCESS) {
+                    OVS_LOG_ERROR("Invalid reassemble packet.");
+                    goto  nblcopy_error;
+                }
+            } else {
+                status = NdisCopyFromNetBufferToNetBuffer(newNb, 0, hdrSize,
+                                                          nb, 0, &copiedSize);
+                if (status != NDIS_STATUS_SUCCESS ||
+                    hdrSize != copiedSize) {
+                    goto nblcopy_error;
+                }
+            }
         }
 
         if (isIpFragment) {
             status = FixFragmentHeader(newNb, segmentSize,
                                        NET_BUFFER_NEXT_NB(newNb) == NULL,
-                                       offset);
+                                       offset, fragmentIdent);
         } else {
             status = FixSegmentHeader(newNb, segmentSize, seqNumber,
                                       NET_BUFFER_NEXT_NB(newNb) == NULL,
@@ -1431,12 +1801,20 @@ OvsFragmentNBL(PVOID ovsContext,
         }
 
         if (status != NDIS_STATUS_SUCCESS) {
+            OVS_LOG_INFO("nbl copy error.");
             goto nblcopy_error;
         }
 
         /* Move on to the next segment */
         if (isIpFragment) {
-            offset += (segmentSize) / 8;
+            if (NET_BUFFER_LIST_FIRST_NB(newNbl) == newNb &&
+                hdrInfo->isIPv6) {
+                offset += (segmentSize) / 8;
+                offset += (UINT16)((hdrSize - ip6StdHeaderLen -
+                                    beforeFragHdrLen) / 8);
+            } else {
+                offset += (segmentSize) / 8;
+            }
         } else {
             seqNumber += segmentSize;
         }
@@ -1755,7 +2133,7 @@ OvsCompleteNBL(PVOID switch_ctx,
         if (value == 1 && pendingSend == exchange) {
             InterlockedExchange16((SHORT volatile *)&ctx->pendingSend, 0);
             OvsSendNBLIngress(context, parent, ctx->sendFlags);
-        } else if (value == 0){
+        } else if (value == 0) {
             return OvsCompleteNBL(context, parent, FALSE);
         }
     }
diff --git a/datapath-windows/ovsext/BufferMgmt.h b/datapath-windows/ovsext/BufferMgmt.h
index 2ae32723e..150fb192e 100644
--- a/datapath-windows/ovsext/BufferMgmt.h
+++ b/datapath-windows/ovsext/BufferMgmt.h
@@ -25,6 +25,7 @@
 #define OVS_DEFAULT_DATA_SIZE           256
 #define OVS_DEFAULT_HEADROOM_SIZE       128
 #define OVS_FIX_NBL_DATA_SIZE    (OVS_DEFAULT_DATA_SIZE + OVS_DEFAULT_HEADROOM_SIZE)
+#define OVS_IPV6_OPT_LEN(p)             (((p)->hdrExtLen+1) << 3)
 
 /* Default we copy 18 bytes, to make sure ethernet header and vlan is in
  * continuous buffer */
diff --git a/datapath-windows/ovsext/Conntrack.c b/datapath-windows/ovsext/Conntrack.c
index 471bf961b..c023f2a59 100644
--- a/datapath-windows/ovsext/Conntrack.c
+++ b/datapath-windows/ovsext/Conntrack.c
@@ -16,6 +16,7 @@
 
 #include "Conntrack.h"
 #include "IpFragment.h"
+#include "Ip6Fragment.h"
 #include "Jhash.h"
 #include "PacketParser.h"
 #include "Event.h"
@@ -547,14 +548,14 @@ OvsDetectCtPacket(OvsForwardingContext *fwdCtx,
             if (status == NDIS_STATUS_SUCCESS) {
                  /* After the Ipv4 Fragment is reassembled, update flow key as
                    L3 and L4 headers are not correct */
-                 status =
-                      OvsExtractFlow(fwdCtx->curNbl, fwdCtx->srcVportNo,
-                                     &newFlowKey, &fwdCtx->layers,
-                                     !OvsIphIsZero(&(fwdCtx->tunKey.dst)) ? &(fwdCtx->tunKey) : NULL);
+                 status = OvsExtractFlow(fwdCtx->curNbl, fwdCtx->srcVportNo,
+                                         &newFlowKey, &fwdCtx->layers,
+                                         !OvsIphIsZero(&(fwdCtx->tunKey.dst)) ?
+                                         &(fwdCtx->tunKey) : NULL);
                 if (status != NDIS_STATUS_SUCCESS) {
                      OVS_LOG_ERROR("Extract flow failed Nbl %p", fwdCtx->curNbl);
                      return status;
-                 }
+                }
                 *key = newFlowKey;
             }
             return status;
@@ -566,21 +567,31 @@ OvsDetectCtPacket(OvsForwardingContext *fwdCtx,
         }
         return NDIS_STATUS_NOT_SUPPORTED;
     case ETH_TYPE_IPV6:
+        if (key->ipv6Key.nwFrag != OVS_FRAG_TYPE_NONE) {
+            status = OvsProcessIpv6Fragment(fwdCtx->switchContext,
+                                            &fwdCtx->curNbl,
+                                            fwdCtx->completionList,
+                                            fwdCtx->fwdDetail->SourcePortId,
+                                            &fwdCtx->layers,
+                                            key->tunKey.tunnelId, key);
+            if (status == NDIS_STATUS_SUCCESS) {
+                status =  OvsExtractFlow(fwdCtx->curNbl, fwdCtx->srcVportNo,
+                                         &newFlowKey, &fwdCtx->layers,
+                                         !OvsIphIsZero(&(fwdCtx->tunKey.dst)) ?
+                                         &(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 status;
+        }
+
         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,
-                                     !OvsIphIsZero(&(fwdCtx->tunKey.dst)) ? &(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;
diff --git a/datapath-windows/ovsext/Ip6Fragment.c b/datapath-windows/ovsext/Ip6Fragment.c
new file mode 100644
index 000000000..0b710905d
--- /dev/null
+++ b/datapath-windows/ovsext/Ip6Fragment.c
@@ -0,0 +1,808 @@
+/*
+ * Copyright (c) 2022 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Conntrack.h"
+#include "Ip6Fragment.h"
+#include "Util.h"
+#include "Jhash.h"
+#include "NetProto.h"
+#include "PacketParser.h"
+
+static OVS_IP6FRAG_THREAD_CTX ip6FragThreadCtx;
+static PNDIS_RW_LOCK_EX ovsIp6FragmentHashLockObj;
+static UINT64 ip6TotalEntries;
+static PLIST_ENTRY OvsIp6FragTable;
+
+#define MIN_FRAGMENT_SIZE 400
+#define MAX_IPDATAGRAM_SIZE 65535
+#define MAX_FRAGMENTS MAX_IPDATAGRAM_SIZE/MIN_FRAGMENT_SIZE + 1
+
+static __inline UINT32
+OvsGetIP6FragmentHash(POVS_IP6FRAG_KEY fragKey)
+{
+    UINT32 arr[11];
+    arr[0] = (UINT32)fragKey->id;
+    arr[1] = (UINT32)(((UINT32*)(&(fragKey->sAddr)))[0]);
+    arr[2] = (UINT32)(((UINT32*)(&(fragKey->sAddr)))[1]);
+    arr[3] = (UINT32)(((UINT32*)(&(fragKey->sAddr)))[2]);
+    arr[4] = (UINT32)(((UINT32*)(&(fragKey->sAddr)))[3]);
+    arr[5] = (UINT32)(((UINT32*)(&(fragKey->dAddr)))[0]);
+    arr[6] = (UINT32)(((UINT32*)(&(fragKey->dAddr)))[1]);
+    arr[7] = (UINT32)(((UINT32*)(&(fragKey->dAddr)))[2]);
+    arr[8] = (UINT32)(((UINT32*)(&(fragKey->dAddr)))[3]);
+    arr[9] = (UINT32)((fragKey->tunnelId & 0xFFFFFFFF00000000LL) >> 32);
+    arr[10] = (UINT32)(fragKey->tunnelId & 0xFFFFFFFFLL);
+    return OvsJhashWords(arr, 11, OVS_HASH_BASIS);
+}
+
+static VOID
+OvsIp6FragmentEntryDelete(POVS_IP6FRAG_ENTRY entry, BOOLEAN checkExpiry)
+{
+    NdisAcquireSpinLock(&(entry->lockObj));
+    if (!entry->markedForDelete && checkExpiry) {
+        UINT64 currentTime;
+        NdisGetCurrentSystemTime((LARGE_INTEGER *)&currentTime);
+        if (entry->expiration > currentTime) {
+            NdisReleaseSpinLock(&(entry->lockObj));
+            return;
+        }
+    }
+
+    POVS_FRAGMENT6_LIST head = entry->head;
+    POVS_FRAGMENT6_LIST temp = NULL;
+    while (head) {
+        temp = head;
+        head = head->next;
+        OvsFreeMemoryWithTag(temp->pbuff, OVS_IP6FRAG_POOL_TAG);
+        OvsFreeMemoryWithTag(temp, OVS_IP6FRAG_POOL_TAG);
+    }
+    RemoveEntryList(&entry->link);
+    ip6TotalEntries--;
+    NdisReleaseSpinLock(&(entry->lockObj));
+    NdisFreeSpinLock(&(entry->lockObj));
+    if (entry->beforeFragHdrLen > 0) {
+        OvsFreeMemoryWithTag(entry->beforeFragHdrBuf, OVS_IP6FRAG_POOL_TAG);
+    }
+
+    if (entry->fragHdrLen > 0) {
+        OvsFreeMemoryWithTag(entry->fragHdrBuf, OVS_IP6FRAG_POOL_TAG);
+    }
+
+    if (entry->behindFragHdrLen > 0) {
+        OvsFreeMemoryWithTag(entry->behindFragHdrBuf, OVS_IP6FRAG_POOL_TAG);
+    }
+
+    OvsFreeMemoryWithTag(entry, OVS_IP6FRAG_POOL_TAG);
+}
+
+static VOID
+OvsIp6FragmentEntryCleaner(PVOID data)
+{
+    POVS_IP6FRAG_THREAD_CTX context = (POVS_IP6FRAG_THREAD_CTX)data;
+    PLIST_ENTRY link, next;
+    POVS_IP6FRAG_ENTRY entry;
+    LOCK_STATE_EX lockState;
+    BOOLEAN success = TRUE;
+
+    while (success) {
+        if (ovsIp6FragmentHashLockObj == NULL) {
+            /* Lock has been freed by 'OvsCleanupIpFragment()' */
+            break;
+        }
+        NdisAcquireRWLockWrite(ovsIp6FragmentHashLockObj, &lockState, 0);
+        if (context->exit) {
+            NdisReleaseRWLock(ovsIp6FragmentHashLockObj, &lockState);
+            break;
+        }
+
+        /* Set the timeout for the thread and cleanup. */
+        UINT64 currentTime, threadSleepTimeout;
+        NdisGetCurrentSystemTime((LARGE_INTEGER *)&currentTime);
+        threadSleepTimeout = currentTime + IP6FRAG_CLEANUP_INTERVAL;
+        for (int i = 0; i < IP6_FRAG_HASH_TABLE_SIZE && ip6TotalEntries; i++) {
+            LIST_FORALL_SAFE(&OvsIp6FragTable[i], link, next) {
+                entry = CONTAINING_RECORD(link, OVS_IP6FRAG_ENTRY, link);
+                OvsIp6FragmentEntryDelete(entry, TRUE);
+            }
+        }
+
+        NdisReleaseRWLock(ovsIp6FragmentHashLockObj, &lockState);
+        KeWaitForSingleObject(&context->event, Executive, KernelMode,
+                              FALSE, (LARGE_INTEGER *)&threadSleepTimeout);
+    }
+
+    PsTerminateSystemThread(STATUS_SUCCESS);
+}
+
+NDIS_STATUS OvsInitIp6Fragment(POVS_SWITCH_CONTEXT context)
+{
+    NDIS_STATUS status;
+    HANDLE threadHandle = NULL;
+
+    OVS_LOG_INFO("Init ipv6 fragment.");
+    ovsIp6FragmentHashLockObj = NdisAllocateRWLock(context->NdisFilterHandle);
+    if (ovsIp6FragmentHashLockObj == NULL) {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Init the Hash Buffer */
+    OvsIp6FragTable = OvsAllocateMemoryWithTag(sizeof(LIST_ENTRY)
+                                              * IP6_FRAG_HASH_TABLE_SIZE,
+                                              OVS_IP6FRAG_POOL_TAG);
+    if (OvsIp6FragTable == NULL) {
+        NdisFreeRWLock(ovsIp6FragmentHashLockObj);
+        ovsIp6FragmentHashLockObj = NULL;
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+
+    for (int i = 0; i < IP6_FRAG_HASH_TABLE_SIZE; i++) {
+        InitializeListHead(&OvsIp6FragTable[i]);
+    }
+
+    /* Init Cleaner Thread */
+    KeInitializeEvent(&ip6FragThreadCtx.event, NotificationEvent, FALSE);
+    status = PsCreateSystemThread(&threadHandle, SYNCHRONIZE, NULL, NULL,
+                                  NULL, OvsIp6FragmentEntryCleaner,
+                                  &ip6FragThreadCtx);
+
+    if (status != STATUS_SUCCESS) {
+        OvsFreeMemoryWithTag(OvsIp6FragTable, OVS_IPFRAG_POOL_TAG);
+        OvsIp6FragTable = NULL;
+        NdisFreeRWLock(ovsIp6FragmentHashLockObj);
+        ovsIp6FragmentHashLockObj = NULL;
+        return status;
+    }
+
+    ObReferenceObjectByHandle(threadHandle, SYNCHRONIZE, NULL, KernelMode,
+                              &ip6FragThreadCtx.threadObject, NULL);
+    ZwClose(threadHandle);
+    threadHandle = NULL;
+    return STATUS_SUCCESS;
+}
+
+static __inline POVS_IP6FRAG_ENTRY
+OvsLookupIP6Frag(POVS_IP6FRAG_KEY fragKey, UINT32 hash)
+{
+    POVS_IP6FRAG_ENTRY entry;
+    PLIST_ENTRY link;
+    LOCK_STATE_EX lockState;
+
+    NdisAcquireRWLockRead(ovsIp6FragmentHashLockObj, &lockState, 0);
+    LIST_FORALL(&OvsIp6FragTable[hash & IP6_FRAG_HASH_TABLE_MASK], link) {
+        entry = CONTAINING_RECORD(link, OVS_IP6FRAG_ENTRY, link);
+        NdisAcquireSpinLock(&(entry->lockObj));
+        if (RtlCompareMemory(&entry->fragKey.dAddr, &fragKey->dAddr,
+                             sizeof(fragKey->dAddr)) == sizeof(fragKey->dAddr) &&
+            RtlCompareMemory(&entry->fragKey.sAddr, &fragKey->sAddr,
+                             sizeof(fragKey->sAddr)) == sizeof(fragKey->sAddr) &&
+            entry->fragKey.id == fragKey->id &&
+            entry->fragKey.tunnelId == fragKey->tunnelId) {
+            NdisReleaseSpinLock(&(entry->lockObj));
+            NdisReleaseRWLock(ovsIp6FragmentHashLockObj, &lockState);
+            return entry;
+        }
+        NdisReleaseSpinLock(&(entry->lockObj));
+    }
+    NdisReleaseRWLock(ovsIp6FragmentHashLockObj, &lockState);
+    return NULL;
+}
+
+VOID OvsCleanupIp6Fragment(VOID)
+{
+    PLIST_ENTRY link, next;
+    POVS_IP6FRAG_ENTRY entry;
+    LOCK_STATE_EX lockState;
+
+    ip6FragThreadCtx.exit = 1;
+    KeSetEvent(&ip6FragThreadCtx.event, 0, FALSE);
+    KeWaitForSingleObject(ip6FragThreadCtx.threadObject, Executive,
+                          KernelMode, FALSE, NULL);
+    ObDereferenceObject(ip6FragThreadCtx.threadObject);
+    NdisAcquireRWLockWrite(ovsIp6FragmentHashLockObj, &lockState, 0);
+    if (OvsIp6FragTable) {
+        for (int i = 0; i < IP6_FRAG_HASH_TABLE_SIZE && ip6TotalEntries; i++) {
+            LIST_FORALL_SAFE(&OvsIp6FragTable[i], link, next) {
+                entry = CONTAINING_RECORD(link, OVS_IP6FRAG_ENTRY, link);
+                OvsIp6FragmentEntryDelete(entry, FALSE);
+            }
+        }
+        OvsFreeMemoryWithTag(OvsIp6FragTable, OVS_IP6FRAG_POOL_TAG);
+        OvsIp6FragTable = NULL;
+    }
+    NdisReleaseRWLock(ovsIp6FragmentHashLockObj, &lockState);
+    NdisFreeRWLock(ovsIp6FragmentHashLockObj);
+    ovsIp6FragmentHashLockObj = NULL;
+}
+
+PCHAR
+OvsBuildNewIpv6Hdr(EthHdr *eth, POVS_IP6FRAG_ENTRY entry,
+                   POVS_PACKET_HDR_INFO layers,
+                   UINT32 *pktLen)
+{
+    IPv6Hdr *ipHdr = NULL;
+    IPv6Hdr *newIpHdr = NULL;
+    PCHAR ipv6StdPtr = NULL;
+    PCHAR packetBuf = NULL;
+    UINT32 packetLen = 0;
+
+    ipHdr = (IPv6Hdr *)((PCHAR)eth + layers->l3Offset);
+    if (layers->l4Offset + entry->totalLen > MAX_IPDATAGRAM_SIZE) {
+        return NULL;
+    }
+
+    packetLen = (layers->l3Offset + sizeof(IPv6Hdr) +
+                 entry->beforeFragHdrLen + entry->behindFragHdrLen + entry->totalLen);
+    packetBuf = (CHAR*)OvsAllocateMemoryWithTag(packetLen, OVS_IP6FRAG_POOL_TAG);
+    if (packetBuf == NULL) {
+        return NULL;
+    }
+    *pktLen = packetLen;
+
+    NdisMoveMemory(packetBuf, eth, layers->l3Offset + sizeof(IPv6Hdr));
+    IPv6ExtHdr *extHdr = (IPv6ExtHdr *)((PCHAR)packetBuf + layers->l3Offset +
+                                        sizeof(IPv6Hdr));
+    ipv6StdPtr = (PCHAR)extHdr;
+    newIpHdr = (IPv6Hdr *)(packetBuf + layers->l3Offset);
+    newIpHdr->payload_len = htons(entry->beforeFragHdrLen +
+                                  entry->behindFragHdrLen + entry->totalLen);
+
+    /* Copy extension header to new packet buf. */
+    if (entry->beforeFragHdrLen > 0) {
+        NdisMoveMemory(ipv6StdPtr, entry->beforeFragHdrBuf,
+                       entry->beforeFragHdrLen);
+    }
+
+    if (entry->behindFragHdrLen > 0) {
+        NdisMoveMemory((ipv6StdPtr + entry->beforeFragHdrLen),
+                       entry->behindFragHdrBuf,
+                       entry->behindFragHdrLen);
+    }
+
+    /* Fix next header. */
+    if (entry->beforeFragHdrLen > 0) {
+        extHdr = (IPv6ExtHdr *)((PCHAR)extHdr + entry->priorFragEleOffset);
+        extHdr->nextHeader =  ((IPv6FragHdr *)(entry->fragHdrBuf))->nextHeader;
+    }
+
+    if (entry->beforeFragHdrLen == 0) {
+        if (entry->behindFragHdrLen == 0) {
+            newIpHdr->nexthdr = entry->fragKey.protocol;
+        } else {
+            newIpHdr->nexthdr = ((IPv6FragHdr *)(entry->fragHdrBuf))->nextHeader;
+        }
+    }
+
+    return packetBuf;
+}
+
+
+NDIS_STATUS
+OvsIpv6Reassemble(POVS_SWITCH_CONTEXT switchContext,
+                  PNET_BUFFER_LIST *curNbl,
+                  OvsCompletionList *completionList,
+                  NDIS_SWITCH_PORT_ID sourcePort,
+                  POVS_IP6FRAG_ENTRY entry,
+                  POVS_PACKET_HDR_INFO layers)
+{
+    NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+    NDIS_STRING filterReason;
+    POVS_BUFFER_CONTEXT ctx;
+    PNET_BUFFER curNb;
+    EthHdr *eth;
+    CHAR *packetBuf;
+    POVS_FRAGMENT6_LIST head = NULL;
+    PNET_BUFFER_LIST newNbl = NULL;
+    UINT16 packetHeaderLen;
+    UINT32 packetLen;
+
+    curNb = NET_BUFFER_LIST_FIRST_NB(*curNbl);
+    ASSERT(NET_BUFFER_NEXT_NB(curNb) == NULL);
+
+    OVS_LOG_INFO("Process ipv6 reassemble, entry total length is %d.",
+                 entry->totalLen);
+    eth = (EthHdr*)NdisGetDataBuffer(curNb, layers->l4Offset,
+                                     NULL, 1, 0);
+    if (!eth) {
+        return NDIS_STATUS_INVALID_PACKET;
+    }
+
+    packetBuf = OvsBuildNewIpv6Hdr(eth, entry, layers, &packetLen);
+    if (!packetBuf) {
+        return NDIS_STATUS_INVALID_PACKET;
+    }
+
+    head = entry->head;
+    packetHeaderLen = (layers->l3Offset + sizeof(IPv6Hdr) +
+                       entry->beforeFragHdrLen + entry->behindFragHdrLen);
+    while (head) {
+        if ((UINT32)(packetHeaderLen + (head->offset * 8) + head->len) > packetLen) {
+            status = NDIS_STATUS_INVALID_DATA;
+            goto cleanup;
+        }
+        NdisMoveMemory(packetBuf + packetHeaderLen + (head->offset * 8),
+                       head->pbuff, head->len);
+        head = head->next;
+    }
+    /* Create new nbl from the flat buffer */
+    newNbl = OvsAllocateNBLFromBuffer(switchContext, packetBuf, packetLen);
+    if (newNbl == NULL) {
+        OVS_LOG_ERROR("Insufficient resources, failed to allocate newNbl");
+        status = NDIS_STATUS_RESOURCES;
+        goto cleanup;
+    }
+
+    /* Complete the fragment NBL */
+    ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(*curNbl);
+    if (ctx->flags & OVS_BUFFER_NEED_COMPLETE) {
+        RtlInitUnicodeString(&filterReason, L"Complete last fragment");
+        OvsAddPktCompletionList(completionList, TRUE, sourcePort, *curNbl, 1,
+                                &filterReason);
+    } else {
+        OvsCompleteNBL(switchContext, *curNbl, TRUE);
+    }
+    /* Store mru in the ovs buffer context. */
+    ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(newNbl);
+    ctx->mru = entry->mru;
+    *curNbl = newNbl;
+cleanup:
+    OvsFreeMemoryWithTag(packetBuf, OVS_IP6FRAG_POOL_TAG);
+    entry->markedForDelete = TRUE;
+    return status;
+}
+
+NDIS_STATUS
+OvsProcessIpv6Fragment(POVS_SWITCH_CONTEXT switchContext,
+                       PNET_BUFFER_LIST *curNbl,
+                       OvsCompletionList *completionList,
+                       NDIS_SWITCH_PORT_ID sourcePort,
+                       POVS_PACKET_HDR_INFO layers, ovs_be64 tunnelId,
+                       OvsFlowKey *key)
+{
+    NDIS_STATUS status = NDIS_STATUS_PENDING;
+    PNET_BUFFER curNb;
+    UINT32 hash;
+    UINT64 currentTime;
+    EthHdr *eth;
+    IPv6Hdr *ip6Hdr = NULL;
+    OVS_IP6FRAG_KEY frag6Key;
+    POVS_IP6FRAG_ENTRY entry;
+    POVS_FRAGMENT6_LIST fragStorage;
+    LOCK_STATE_EX htLockState;
+    IP6_PktExtHeader_Meta pktMeta = {0};
+
+    curNb = NET_BUFFER_LIST_FIRST_NB(*curNbl);
+    ASSERT(NET_BUFFER_NEXT_NB(curNb) == NULL);
+
+    OVS_LOG_INFO("Process ipv6 fragment.");
+    eth = (EthHdr*)NdisGetDataBuffer(curNb, layers->l4Offset,
+                                     NULL, 1, 0);
+    if (eth == NULL) {
+        return NDIS_STATUS_INVALID_PACKET;
+    }
+
+    ip6Hdr = (IPv6Hdr *)((PCHAR)eth + layers->l3Offset);
+    status = OvsGetPacketMeta(&pktMeta, eth, key, layers);
+    if (status != NDIS_STATUS_SUCCESS) {
+        return status;
+    }
+
+    fragStorage = (POVS_FRAGMENT6_LIST)
+            OvsAllocateMemoryWithTag(sizeof(OVS_FRAGMENT6_LIST),
+                                     OVS_IP6FRAG_POOL_TAG);
+    if (fragStorage == NULL) {
+        OVS_LOG_ERROR("Insufficient resources, fail to allocate fragStorage");
+        return NDIS_STATUS_RESOURCES;
+    }
+
+    fragStorage->len = pktMeta.dataPayloadLen;
+    fragStorage->offset = pktMeta.fragOffset;
+    fragStorage->next = NULL;
+    fragStorage->pbuff = (CHAR *)OvsAllocateMemoryWithTag(fragStorage->len,
+                                                          OVS_IP6FRAG_POOL_TAG);
+    if (fragStorage->pbuff == NULL) {
+        OVS_LOG_ERROR("Insufficient resources, fail to allocate pbuff");
+        OvsFreeMemoryWithTag(fragStorage, OVS_IP6FRAG_POOL_TAG);
+        return NDIS_STATUS_RESOURCES;
+    }
+
+    if (OvsGetPacketBytes(*curNbl, pktMeta.dataPayloadLen,
+                          layers->l4Offset,
+                          fragStorage->pbuff) == NULL) {
+        status = NDIS_STATUS_RESOURCES;
+        OVS_LOG_ERROR("Get packet bytes fail, pkt len is %d, offset is %d.",
+                      pktMeta.dataPayloadLen, layers->l4Offset);
+        goto payload_copy_error;
+    }
+
+    frag6Key.sAddr = ip6Hdr->saddr;
+    frag6Key.dAddr = ip6Hdr->daddr;
+    frag6Key.tunnelId = tunnelId;
+    frag6Key.id = pktMeta.ident;
+
+    hash = OvsGetIP6FragmentHash(&frag6Key);
+    entry = OvsLookupIP6Frag(&frag6Key, hash);
+    if (entry == NULL) {
+        entry = (POVS_IP6FRAG_ENTRY)
+                OvsAllocateMemoryWithTag(sizeof(OVS_IP6FRAG_ENTRY),
+                                         OVS_IP6FRAG_POOL_TAG);
+        if (entry == NULL) {
+            status = NDIS_STATUS_RESOURCES;
+            goto payload_copy_error;
+        }
+        /* Copy the fragmeny key. */
+        NdisZeroMemory(entry, sizeof(OVS_IP6FRAG_ENTRY));
+        NdisMoveMemory(&(entry->fragKey), &frag6Key, sizeof(OVS_IP6FRAG_KEY));
+        /* Init MRU. */
+        entry->mru = pktMeta.pktMru;
+        entry->recvdLen = fragStorage->len;
+        entry->head = entry->tail = fragStorage;
+        entry->numFragments = 1;
+
+        if (!pktMeta.fragOffset) {
+            /* First packet, fragment offset is 0 */
+            OVS_LOG_INFO("before fragment extension header len:%d "
+                         "fragment extension header len:%d "
+                         "behind fragment extension header len :%d "
+                         "last element before fragment offset %d",
+                         pktMeta.beforeFragExtHdrLen,
+                         pktMeta.fragExtHdrLen,
+                         pktMeta.behindFragExtHdrLen,
+                         pktMeta.priorFragEleOffset);
+           /* We could get all ext header info from first fragment packet. */
+           status = OvsStorageIpv6ExtHeader(entry, pktMeta.beforeFragExtHdrLen,
+                                            pktMeta.fragExtHdrLen,
+                                            pktMeta.behindFragExtHdrLen,
+                                            pktMeta.priorFragEleOffset,
+                                            (PCHAR) eth, layers);
+           if (status != NDIS_STATUS_SUCCESS) {
+               OVS_LOG_INFO("StorageIpv6 header fails, parse failed.");
+               OvsFreeMemoryWithTag(entry, OVS_IP6FRAG_POOL_TAG);
+               goto payload_copy_error;
+           }
+
+           entry->fragKey.protocol = pktMeta.protocol;
+           OVS_LOG_INFO("First packet, protocol is %d.",
+                        entry->fragKey.protocol);
+        }
+
+        if (!pktMeta.flags) {
+            /* It's the last fragment, it demonstrates the packet was arrived
+             * out of order, we calculate the complte packet total length. */
+            entry->totalLen = pktMeta.fragOffset * 8 +  pktMeta.dataPayloadLen;
+        }
+
+        NdisGetCurrentSystemTime((LARGE_INTEGER *)&currentTime);
+        entry->expiration = currentTime + IP6FRAG_ENTRY_TIMEOUT;
+
+        /* Init the sync-lock. */
+        NdisAllocateSpinLock(&(entry->lockObj));
+        NdisAcquireRWLockWrite(ovsIp6FragmentHashLockObj, &htLockState, 0);
+        InsertHeadList(&OvsIp6FragTable[hash & IP6_FRAG_HASH_TABLE_MASK],
+                       &entry->link);
+
+        ip6TotalEntries++;
+        NdisReleaseRWLock(ovsIp6FragmentHashLockObj, &htLockState);
+        return NDIS_STATUS_PENDING;
+    } else {
+        /* Acquire the entry lock. */
+        NdisAcquireSpinLock(&(entry->lockObj));
+        NdisGetCurrentSystemTime((LARGE_INTEGER *)&currentTime);
+        if (currentTime > entry->expiration ||
+            (entry->numFragments == MAX_FRAGMENTS)) {
+            /* Mark the entry for delete. */
+            OVS_LOG_ERROR("Will delete the fragment numbers.");
+            entry->markedForDelete = TRUE;
+            goto fragment_error;
+        }
+
+        if (!pktMeta.fragOffset) {
+            status = OvsStorageIpv6ExtHeader(entry, pktMeta.behindFragExtHdrLen,
+                                             pktMeta.fragExtHdrLen,
+                                             pktMeta.behindFragExtHdrLen,
+                                             pktMeta.priorFragEleOffset,
+                                             (PCHAR) eth,
+                                             layers);
+            if (status != NDIS_STATUS_SUCCESS) {
+                OVS_LOG_ERROR("IPv6 Extension header not valid.");
+                goto fragment_error;
+            }
+
+            entry->fragKey.protocol = pktMeta.protocol;
+        }
+
+        if (!pktMeta.flags) {
+            entry->totalLen = pktMeta.fragOffset * 8 +  pktMeta.dataPayloadLen;
+        }
+
+        /* Find the element offset just large than fragment and insert the
+         * fragment before it. */
+        POVS_FRAGMENT6_LIST next = entry->head;
+        POVS_FRAGMENT6_LIST prev = entry->tail;
+        if (prev != NULL && prev->offset < pktMeta.fragOffset) {
+            next = NULL;
+            goto found;
+        }
+        prev = NULL;
+        for (next = entry->head; next != NULL; next = next->next) {
+            if (next->offset > fragStorage->offset) {
+                break;
+            }
+            prev = next;
+        }
+found:
+        /*Check for overlap. */
+        if (prev) {
+            /* i bytes overlap. */
+            int i = ((prev->offset * 8) + prev->len) - (fragStorage->offset * 8);
+            if (i > 0) {
+                OVS_LOG_ERROR("IPv6 fragment error, prev offset %d, pre len "
+                              "%d, frag offset %d",
+                              prev->offset, prev->len, fragStorage->offset);
+                goto fragment_error;
+            }
+        }
+        if (next) {
+            /* i bytes overlap. */
+            int i = ((fragStorage->offset * 8) + fragStorage->len) -
+                    (next->offset * 8);
+            if (i > 0) {
+                OVS_LOG_ERROR("IPv6 fragment error, frag offset %d, frag "
+                              "len %d, next offset %d.",
+                              fragStorage->offset, fragStorage->len,
+                              next->offset);
+                goto fragment_error;
+            }
+        }
+
+        if (entry->recvdLen + fragStorage->len > entry->recvdLen) {
+            entry->recvdLen += fragStorage->len;
+        } else {
+            /* Overflow, ignore the fragment.*/
+            OVS_LOG_ERROR("IPv6 fragment error, entry recv len %d, frag "
+                          "len %d.", entry->recvdLen, fragStorage->len);
+            goto fragment_error;
+        }
+
+        /*Insert. */
+        if (prev) {
+            prev->next = fragStorage;
+            fragStorage->next = next;
+        } else {
+            fragStorage->next = next;
+            entry->head = fragStorage;
+        }
+        if (!next) {
+            entry->tail = fragStorage;
+        }
+
+        /*Update Maximum Receive Unit */
+        entry->mru = entry->mru > pktMeta.pktMru ? entry->mru : pktMeta.pktMru;
+        entry->numFragments++;
+
+        OVS_LOG_INFO("Max mru is %d, entry total length %d, entry recv length %d, "
+                     "extension header length is %d", entry->mru,
+                     entry->totalLen, entry->recvdLen,
+                     entry->behindFragHdrLen);
+        if (entry->recvdLen == (entry->totalLen - entry->behindFragHdrLen)) {
+            /* when exist ipv6 extension field behind ipv6 fragment field,
+             * the ipv6 extension field will be regard as "data", the totalLen
+             * represent the "fragment data length" + "ipv6 extension length
+             * behind fragment". However, the recvdLen only represents the
+             * data length, thus when we judge is or not receive a complete
+             * packet, we should use
+             * (entry->totalLen - entry->behindFragHdrLen) == entry->recvdLen */
+            status = OvsIpv6Reassemble(switchContext, curNbl, completionList,
+                                       sourcePort, entry, layers);
+        }
+        NdisReleaseSpinLock(&(entry->lockObj));
+        return status;
+    }
+
+fragment_error:
+    status = NDIS_STATUS_INVALID_PACKET;
+    /* Release the entry lock. */
+    NdisReleaseSpinLock(&(entry->lockObj));
+
+payload_copy_error:
+    OVS_LOG_ERROR("Payload error, exits.");
+    OvsFreeMemoryWithTag(fragStorage->pbuff, OVS_IP6FRAG_POOL_TAG);
+    OvsFreeMemoryWithTag(fragStorage, OVS_IP6FRAG_POOL_TAG);
+    return status;
+}
+
+NDIS_STATUS
+OvsGetPacketMeta(PIP6_PktExtHeader_Meta pktMeta, EthHdr *eth,
+                 OvsFlowKey *key, POVS_PACKET_HDR_INFO layers)
+{
+    IPv6Hdr *ip6Hdr = NULL;
+    IPv6ExtHdr *extHdr = NULL;
+    UINT8 nextHdr;
+
+    ip6Hdr = (IPv6Hdr *)((PCHAR)eth + layers->l3Offset);
+    if (!ip6Hdr) {
+        return NDIS_STATUS_INVALID_PACKET;
+    }
+
+    nextHdr = ip6Hdr->nexthdr;
+    pktMeta->firstHdr = nextHdr;
+
+    if ((nextHdr == SOCKET_IPPROTO_HOPOPTS) ||
+        (nextHdr == SOCKET_IPPROTO_ROUTING) ||
+        (nextHdr == SOCKET_IPPROTO_DSTOPTS) ||
+        (nextHdr == SOCKET_IPPROTO_FRAGMENT)) {
+        extHdr = (IPv6ExtHdr *)((PCHAR)ip6Hdr + sizeof(IPv6Hdr));
+        pktMeta->firstHdrPtr = extHdr;
+    } else {
+        return NDIS_STATUS_INVALID_PACKET;
+    }
+
+    for (;;) {
+        if ((nextHdr != SOCKET_IPPROTO_HOPOPTS)
+            && (nextHdr != SOCKET_IPPROTO_ROUTING)
+            && (nextHdr != SOCKET_IPPROTO_DSTOPTS)
+            && (nextHdr != SOCKET_IPPROTO_AH)
+            && (nextHdr != SOCKET_IPPROTO_FRAGMENT)) {
+            /*
+             * It's either a terminal header (e.g., TCP, UDP, Icmpv6) or one we
+             * don't understand.  In either case, we're done with the
+             * packet, so use it to fill in 'nw_proto'.
+             */
+            pktMeta->protocol = nextHdr;
+            break;
+        }
+
+        if (nextHdr == SOCKET_IPPROTO_HOPOPTS ||
+            nextHdr == SOCKET_IPPROTO_ROUTING ||
+            nextHdr == SOCKET_IPPROTO_DSTOPTS ||
+            nextHdr == SOCKET_IPPROTO_AH) {
+            UINT8 len  = extHdr->hdrExtLen;
+            nextHdr = extHdr->nextHeader;
+            if (nextHdr == SOCKET_IPPROTO_FRAGMENT) {
+                pktMeta->beforeFragElePtr = (PCHAR)(extHdr);
+            }
+
+            if (nextHdr == SOCKET_IPPROTO_AH) {
+                extHdr = (IPv6ExtHdr *)((PCHAR)extHdr + (len  + 2) * 4);
+                pktMeta->extHdrTotalLen += ((len + 2) * 4);
+            } else {
+                extHdr = (IPv6ExtHdr *)((PCHAR)extHdr + (len + 1) * 8);
+                pktMeta->extHdrTotalLen += ((len + 1) * 8);
+            }
+        } else if (nextHdr == SOCKET_IPPROTO_FRAGMENT) {
+            IPv6FragHdr *fragHdr = (IPv6FragHdr *)extHdr;
+            pktMeta->ident = fragHdr->ident;
+            pktMeta->beforeFragExtHdrLen = pktMeta->extHdrTotalLen;
+            pktMeta->fragExtHdrLen = sizeof(IPv6FragHdr);
+            pktMeta->extHdrTotalLen += sizeof(IPv6FragHdr);
+            pktMeta->fragOffset = (ntohs(fragHdr->offlg)
+                    & IP6F_OFF_HOST_ORDER_MASK) >> 3;
+            pktMeta->flags = ntohs(fragHdr->offlg) & 0x01;
+            nextHdr = extHdr->nextHeader;
+            extHdr = (IPv6ExtHdr *)((PCHAR)extHdr + sizeof(IPv6FragHdr));
+            if (key->ipv6Key.nwFrag == OVS_FRAG_TYPE_LATER) {
+                pktMeta->protocol = SOCKET_IPPROTO_FRAGMENT;
+                break;
+            }
+        }
+    }
+
+    pktMeta->dataPayloadLen = (ntohs(ip6Hdr->payload_len) -
+                               pktMeta->extHdrTotalLen);
+    OVS_LOG_INFO("playload len %d, extotalLen %d, datapyaload len %d.",
+                 ntohs(ip6Hdr->payload_len),
+                 pktMeta->extHdrTotalLen,
+                 pktMeta->dataPayloadLen);
+    pktMeta->behindFragExtHdrLen = (pktMeta->extHdrTotalLen -
+                                    pktMeta->beforeFragExtHdrLen -
+                                    pktMeta->fragExtHdrLen);
+    pktMeta->pktMru = (layers->l3Offset + sizeof(IPv6Hdr) +
+                      ntohs(ip6Hdr->payload_len));
+    if (pktMeta->beforeFragElePtr) {
+        pktMeta->priorFragEleOffset =  (UINT16)((PCHAR)pktMeta->beforeFragElePtr -
+                                                (PCHAR)pktMeta->firstHdrPtr);
+    }
+
+    return NDIS_STATUS_SUCCESS;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * OvsStorageIpv6ExtHeader --
+ *      In some scenario, we need to storage the ipv6 option header, this
+ *      function is used to do it, we could divide ipv6 option field into
+ *      three parts, including "option field before fragment
+ *      field", "fragment field", "option field behind fragment field". The
+ *      reason store extension header is that it's convenient to copy the
+ *      specified to the fragment header.
+ *-----------------------------------------------------------------------------
+ */
+NDIS_STATUS
+OvsStorageIpv6ExtHeader(POVS_IP6FRAG_ENTRY entry,
+                        UINT16 beforeFragHdrLen,
+                        UINT16 fragHdrLen,
+                        UINT16 behindFragHdrLen,
+                        UINT16 priorFragEleOffset,
+                        CHAR *pktBuf,
+                        POVS_PACKET_HDR_INFO layers)
+{
+    NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+
+    if (beforeFragHdrLen) {
+        entry->beforeFragHdrBuf =
+                OvsAllocateMemoryWithTag(beforeFragHdrLen,
+                                         OVS_IP6FRAG_POOL_TAG);
+        if (entry->beforeFragHdrBuf == NULL) {
+            goto beforeFragHdrError;
+        }
+        entry->beforeFragHdrLen = beforeFragHdrLen;
+        entry->priorFragEleOffset = priorFragEleOffset;
+    }
+
+    if (fragHdrLen) {
+        entry->fragHdrBuf = OvsAllocateMemoryWithTag(fragHdrLen,
+                                                     OVS_IP6FRAG_POOL_TAG);
+        if (entry->fragHdrBuf == NULL) {
+            goto fragHdrError;
+        }
+        entry->fragHdrLen = fragHdrLen;
+    }
+
+    if (behindFragHdrLen) {
+        entry->behindFragHdrBuf =
+                OvsAllocateMemoryWithTag(behindFragHdrLen,
+                                         OVS_IP6FRAG_POOL_TAG);
+        if (entry->behindFragHdrBuf == NULL) {
+            goto behindFragHdrError;
+        }
+        entry->behindFragHdrLen = behindFragHdrLen;
+    }
+
+    if (entry->beforeFragHdrLen) {
+        NdisMoveMemory(entry->beforeFragHdrBuf,
+                       pktBuf + layers->l3Offset + sizeof(IPv6Hdr),
+                       entry->beforeFragHdrLen);
+    }
+
+    if (entry->fragHdrLen) {
+        NdisMoveMemory(entry->fragHdrBuf,
+                       (pktBuf + layers->l3Offset +
+                        sizeof(IPv6Hdr) + beforeFragHdrLen),
+                       entry->fragHdrLen);
+    }
+
+    if (entry->behindFragHdrLen) {
+        NdisMoveMemory(entry->behindFragHdrBuf,
+                       (pktBuf + layers->l3Offset + sizeof(IPv6Hdr)
+                        + beforeFragHdrLen + fragHdrLen),
+                       entry->behindFragHdrLen);
+    }
+
+    return status;
+
+behindFragHdrError:
+fragHdrError:
+    if (entry->fragHdrBuf) {
+        OvsFreeMemoryWithTag(entry->fragHdrBuf, OVS_IP6FRAG_POOL_TAG);
+    }
+beforeFragHdrError:
+    if (entry->beforeFragHdrBuf) {
+        OvsFreeMemoryWithTag(entry->beforeFragHdrBuf, OVS_IP6FRAG_POOL_TAG);
+    }
+    status = NDIS_STATUS_RESOURCES;
+    OVS_LOG_ERROR("Storage header fails due to header.");
+    return status;
+}
diff --git a/datapath-windows/ovsext/Ip6Fragment.h b/datapath-windows/ovsext/Ip6Fragment.h
new file mode 100644
index 000000000..f978bf5c3
--- /dev/null
+++ b/datapath-windows/ovsext/Ip6Fragment.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2022 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __IP6FRAGMENT_H_
+#define __IP6FRAGMENT_H_ 1
+#include "PacketIO.h"
+
+typedef struct _OVS_FRAGMENT6_LIST {
+    CHAR *pbuff;
+    UINT16 len; /* Fragment data length. */
+    UINT16 offset; /* Fragment data offset. */
+    struct _OVS_FRAGMENT6_LIST *next;
+} OVS_FRAGMENT6_LIST, *POVS_FRAGMENT6_LIST;
+
+typedef struct _OVS_IP6FRAG_KEY {
+    UINT8 protocol;
+    UINT8 pad_1[3];             /* Align the structure to address boundaries.*/
+    UINT32 id;
+    struct in6_addr sAddr;
+    struct in6_addr dAddr;
+    ovs_be64 tunnelId;
+} OVS_IP6FRAG_KEY, *POVS_IP6FRAG_KEY;
+
+typedef struct _OVS_IP6FRAG_ENTRY {
+    NDIS_SPIN_LOCK lockObj;       /* To access the entry. */
+    BOOLEAN markedForDelete;
+    UINT8 numFragments;
+    UINT16 totalLen; /* The packet data total length(not
+                      * include ipv6 header and opt header length) before
+                      * fragment */
+    UINT16 recvdLen; /* Total data length packet contains has received */
+    UINT16 mru; /* Max receive unit(it's the whole ethernet frame
+                 * packet length), it will be used in sent out before forward */
+    UINT64 expiration;
+    /* refer https://www.rfc-editor.org/rfc/rfc8200.html */
+    PCHAR beforeFragHdrBuf;/* ipv6 extension header buf before fragment field */
+    UINT16 beforeFragHdrLen;
+    UINT16 priorFragEleOffset;/* The last element before fragment field offset */
+    PCHAR fragHdrBuf;
+    UINT16 fragHdrLen;
+    PCHAR behindFragHdrBuf;/* ipv6 extension header buf behind fragment field */
+    UINT16 behindFragHdrLen;
+    OVS_IP6FRAG_KEY fragKey;
+    POVS_FRAGMENT6_LIST head;
+    POVS_FRAGMENT6_LIST tail;
+    LIST_ENTRY link;
+} OVS_IP6FRAG_ENTRY, *POVS_IP6FRAG_ENTRY;
+
+typedef struct _IP6_PktExtHeader_Meta {
+    UINT8 firstHdr;
+    UINT8 protocol;
+    UINT16 beforeFragExtHdrLen;
+    UINT16 fragExtHdrLen;
+    UINT16 behindFragExtHdrLen;
+    UINT16 extHdrTotalLen;
+    UINT16 dataPayloadLen;/* Ipv6 data length, not include extension header */
+    UINT16 fragOffset;
+    UINT16 priorFragEleOffset;
+    UINT16 flags;
+    UINT16 pktMru;
+    UINT32 ident;
+    PCHAR beforeFragElePtr;
+    IPv6ExtHdr *firstHdrPtr;
+} IP6_PktExtHeader_Meta, *PIP6_PktExtHeader_Meta;
+
+typedef struct _OVS_IP6FRAG_THREAD_CTX {
+    KEVENT event;
+    PVOID threadObject;
+    UINT32 exit;
+} OVS_IP6FRAG_THREAD_CTX, *POVS_IP6FRAG_THREAD_CTX;
+
+#define IP6_FRAG_HASH_TABLE_SIZE ((UINT32)1 << 10)
+#define IP6_FRAG_HASH_TABLE_MASK (IP6_FRAG_HASH_TABLE_SIZE - 1)
+
+#define IP6FRAG_ENTRY_TIMEOUT 300000000LL
+#define IP6FRAG_CLEANUP_INTERVAL IP6FRAG_ENTRY_TIMEOUT * 2 /*1m.*/
+
+NDIS_STATUS OvsProcessIpv6Fragment(POVS_SWITCH_CONTEXT switchContext,
+                       PNET_BUFFER_LIST *curNbl,
+                       OvsCompletionList *completionList,
+                       NDIS_SWITCH_PORT_ID sourcePort,
+                       POVS_PACKET_HDR_INFO layers,
+                       ovs_be64 tunnelId, OvsFlowKey *key);
+NDIS_STATUS OvsStorageIpv6ExtHeader(POVS_IP6FRAG_ENTRY entry,
+                                    UINT16 beforeFragHdrLen,
+                                    UINT16 fragHdrLen,
+                                    UINT16 behindFragHdrLen,
+                                    UINT16 priorFragEleOffset,
+                                    CHAR *pktBuf,
+                                    POVS_PACKET_HDR_INFO layers);
+NDIS_STATUS OvsInitIp6Fragment(POVS_SWITCH_CONTEXT context);
+VOID OvsCleanupIp6Fragment(VOID);
+NDIS_STATUS OvsGetPacketMeta(PIP6_PktExtHeader_Meta pktMeta, EthHdr *eth,
+                             OvsFlowKey *key, POVS_PACKET_HDR_INFO layers);
+PCHAR OvsBuildNewIpv6Hdr(EthHdr *eth, POVS_IP6FRAG_ENTRY entry,
+                         POVS_PACKET_HDR_INFO layers, UINT32 *pktLen);
+
+#endif //_IP6FRAGMENT_H_
diff --git a/datapath-windows/ovsext/PacketParser.c b/datapath-windows/ovsext/PacketParser.c
index aeead5899..e89ff0641 100644
--- a/datapath-windows/ovsext/PacketParser.c
+++ b/datapath-windows/ovsext/PacketParser.c
@@ -141,7 +141,7 @@ OvsParseIPv6(const NET_BUFFER_LIST *packet,
             nextHdr = extHdr->nextHeader;
             if (OvsPacketLenNBL(packet) < ofs) {
                 return NDIS_STATUS_FAILURE;
-             }
+            }
         } else if (nextHdr == SOCKET_IPPROTO_FRAGMENT) {
             IPv6FragHdr fragHdrStorage;
             const IPv6FragHdr *fragHdr;
@@ -157,13 +157,15 @@ OvsParseIPv6(const NET_BUFFER_LIST *packet,
 
             /* We only process the first fragment. */
             if (fragHdr->offlg != htons(0)) {
-                if ((fragHdr->offlg & IP6F_OFF_HOST_ORDER_MASK) == htons(0)) {
+                if ((ntohs(fragHdr->offlg) & IP6F_OFF_HOST_ORDER_MASK) == htons(0)) {
                     ipv6Key->nwFrag = OVS_FRAG_TYPE_FIRST;
                 } else {
                     ipv6Key->nwFrag = OVS_FRAG_TYPE_LATER;
                     nextHdr = SOCKET_IPPROTO_FRAGMENT;
                     break;
                 }
+            } else {
+                ipv6Key->nwFrag = OVS_FRAG_TYPE_NONE;
             }
         }
     }
diff --git a/datapath-windows/ovsext/Switch.c b/datapath-windows/ovsext/Switch.c
index 1ac4fa77c..a40624bab 100644
--- a/datapath-windows/ovsext/Switch.c
+++ b/datapath-windows/ovsext/Switch.c
@@ -28,6 +28,7 @@
 #include "IpHelper.h"
 #include "Oid.h"
 #include "IpFragment.h"
+#include "Ip6Fragment.h"
 
 #ifdef OVS_DBG_MOD
 #undef OVS_DBG_MOD
@@ -239,6 +240,13 @@ OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
         goto create_switch_done;
     }
 
+    status = OvsInitIp6Fragment(switchContext);
+    if (status != STATUS_SUCCESS) {
+        OvsUninitSwitchContext(switchContext);
+        OVS_LOG_ERROR("Exit: Failed to initialize Ip6 Fragment");
+        goto create_switch_done;
+    }
+
     *switchContextOut = switchContext;
 
 create_switch_done:
@@ -272,6 +280,7 @@ OvsExtDetach(NDIS_HANDLE filterModuleContext)
     OvsCleanupConntrack();
     OvsCleanupCtRelated();
     OvsCleanupIpFragment();
+    OvsCleanupIp6Fragment();
 
     /* This completes the cleanup, and a new attach can be handled now. */
 
diff --git a/datapath-windows/ovsext/Util.h b/datapath-windows/ovsext/Util.h
index f63a885a9..4d9ce4210 100644
--- a/datapath-windows/ovsext/Util.h
+++ b/datapath-windows/ovsext/Util.h
@@ -40,6 +40,7 @@
 #define OVS_CT_POOL_TAG                 'CTVO'
 #define OVS_GENEVE_POOL_TAG             'GNVO'
 #define OVS_IPFRAG_POOL_TAG             'FGVO'
+#define OVS_IP6FRAG_POOL_TAG            'F6VO'
 
 _IRQL_requires_max_(DISPATCH_LEVEL)
 VOID *OvsAllocateMemory(size_t size);
diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-windows/ovsext/ovsext.vcxproj
index 7a2cbd2de..691a05706 100644
--- a/datapath-windows/ovsext/ovsext.vcxproj
+++ b/datapath-windows/ovsext/ovsext.vcxproj
@@ -162,6 +162,7 @@
     <ClInclude Include="Geneve.h" />
     <ClInclude Include="Gre.h" />
     <ClInclude Include="IpFragment.h" />
+    <ClInclude Include="Ip6Fragment.h" />
     <ClInclude Include="IpHelper.h" />
     <ClInclude Include="Jhash.h" />
     <ClInclude Include="Mpls.h" />
@@ -406,6 +407,7 @@
     <ClCompile Include="Geneve.c" />
     <ClCompile Include="Gre.c" />
     <ClCompile Include="IpFragment.c" />
+    <ClCompile Include="Ip6Fragment.c" />
     <ClCompile Include="IpHelper.c" />
     <ClCompile Include="Jhash.c" />
     <ClCompile Include="Netlink/Netlink.c" />
