diff mbox series

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

Message ID 20220511115508.74282-1-svc.ovs-community@vmware.com
State Changes Requested
Headers show
Series [ovs-dev,v1,1/1] datapath-windows: Add IPv6 conntrack ip fragment support on windows | expand

Checks

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

Commit Message

ldejing May 11, 2022, 11:55 a.m. UTC
From: ldejing <ldejing@vmware.com>

Implementation on Windows:
IPv6 conntrack ip fragment feature use a link list to store ip
fragment. When ipv6 fragment module receives a fragment packet,
it will store length of the fragment, until to the received length
equal to the packet length before fragmented, it will reassemble
fragment packet to a complete packet and send the complete packet
to conntrack module. After conntrack processed the packet, fragment
module will divide the complete packet into small fragment and send
it to destination. Currently, ipv6 was implemented in a indenpent
module, for the reason it can reduce the risk of introduce bug to
ipv4 fragmenb module.

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.UdpV6
  a) UdpV6 fragment with multiple ipv6 extension fields.
  b) UdpV6 fragment in normal scenario.
  c) UdpV6 fragment in nat scenario.

2.IcmpV6
  a) IcmpV6 fragment in normal scenario.
  b) IcmpV6 fragment in nat scenario.

Signed-off-by: ldejing <ldejing@vmware.com>
---
 Documentation/faq/releases.rst         |   1 +
 NEWS                                   |   1 +
 datapath-windows/automake.mk           |   2 +
 datapath-windows/ovsext/Actions.c      |   8 +-
 datapath-windows/ovsext/BufferMgmt.c   | 420 ++++++++++++-
 datapath-windows/ovsext/BufferMgmt.h   |   1 +
 datapath-windows/ovsext/Conntrack.c    |  45 +-
 datapath-windows/ovsext/Ip6Fragment.c  | 808 +++++++++++++++++++++++++
 datapath-windows/ovsext/Ip6Fragment.h  | 111 ++++
 datapath-windows/ovsext/PacketParser.c |   6 +-
 datapath-windows/ovsext/Switch.c       |   9 +
 datapath-windows/ovsext/Util.h         |   1 +
 datapath-windows/ovsext/ovsext.vcxproj |   2 +
 13 files changed, 1372 insertions(+), 43 deletions(-)
 create mode 100644 datapath-windows/ovsext/Ip6Fragment.c
 create mode 100644 datapath-windows/ovsext/Ip6Fragment.h

Comments

Alin-Gabriel Serdean Aug. 9, 2022, 1:06 p.m. UTC | #1
From: ldejing <ldejing@vmware.com>

Patch looks good just a few nits below.

> Implementation on Windows:
> IPv6 conntrack ip fragment feature use a link list to store ip
> fragment. When ipv6 fragment module receives a fragment packet,
> it will store length of the fragment, until to the received length
> equal to the packet length before fragmented, it will reassemble
> fragment packet to a complete packet and send the complete packet
> to conntrack module. After conntrack processed the packet, fragment
> module will divide the complete packet into small fragment and send
> it to destination. Currently, ipv6 was implemented in a indenpent
> module, for the reason it can reduce the risk of introduce bug to
> ipv4 fragmenb module.
> 
> 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.UdpV6
>   a) UdpV6 fragment with multiple ipv6 extension fields.
>   b) UdpV6 fragment in normal scenario.
>   c) UdpV6 fragment in nat scenario.
> 
> 2.IcmpV6
>   a) IcmpV6 fragment in normal scenario.
>   b) IcmpV6 fragment in nat scenario.
> 
> Signed-off-by: ldejing <ldejing@vmware.com>
> ---
>  Documentation/faq/releases.rst         |   1 +
>  NEWS                                   |   1 +
>  datapath-windows/automake.mk           |   2 +
>  datapath-windows/ovsext/Actions.c      |   8 +-
>  datapath-windows/ovsext/BufferMgmt.c   | 420 ++++++++++++-
>  datapath-windows/ovsext/BufferMgmt.h   |   1 +
>  datapath-windows/ovsext/Conntrack.c    |  45 +-
>  datapath-windows/ovsext/Ip6Fragment.c  | 808 +++++++++++++++++++++++++
>  datapath-windows/ovsext/Ip6Fragment.h  | 111 ++++
>  datapath-windows/ovsext/PacketParser.c |   6 +-
>  datapath-windows/ovsext/Switch.c       |   9 +
>  datapath-windows/ovsext/Util.h         |   1 +
>  datapath-windows/ovsext/ovsext.vcxproj |   2 +
>  13 files changed, 1372 insertions(+), 43 deletions(-)
>  create mode 100644 datapath-windows/ovsext/Ip6Fragment.c
>  create mode 100644 datapath-windows/ovsext/Ip6Fragment.h
> 
> 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));
> +    }
> +

[AS] I'm wondering why do we need this. Can we drop the function and use hdrInfo->l4Offset directly?
If we cannot, we can drop ipv6Hdr, it is not used, so no point defining it.

>      return NDIS_STATUS_SUCCESS;
>  }
>  
> @@ -1160,8 +1169,8 @@ GetSegmentHeaderInfo(PNET_BUFFER_LIST nbl,
[AS] There is a comment "*    XXX - Support IpV6 Fragments" just one
line above. It can be safely dropped.
>   * --------------------------------------------------------------------------
>   */
>  static NDIS_STATUS
[AS] Let's split this function into two FixSegmentHeader4 /
FixSegmentHeader6 and have a common FixSegmentHeader when invoking it.
It would make things more readable.
> -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;
[AS] can we have a constant for that magic number (0x8088405)?
> +
> +    srcHash = OvsJhashBytes((UINT32 *)(&(ipv6Hdr->saddr)), 4, randNumber);
> +    dstHash = OvsJhashBytes((UINT32 *)(&(ipv6Hdr->daddr)), 4, srcHash);
> +    return dstHash;
> +}
>  
>  /*
>   * --------------------------------------------------------------------------
[AS] Let's split this function into two, one for v4 and another for v6.
It will make the following more digestable.
> @@ -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" />
Dejing Liu Aug. 15, 2022, 9:45 a.m. UTC | #2
Hi alin,

I have made some modification according to your suggestion and new patch will coming soon.

Regards
Dejing



From: ldejing <ldejing@vmware.com>

Patch looks good just a few nits below.

> Implementation on Windows:
> IPv6 conntrack ip fragment feature use a link list to store ip
> fragment. When ipv6 fragment module receives a fragment packet,
> it will store length of the fragment, until to the received length
> equal to the packet length before fragmented, it will reassemble
> fragment packet to a complete packet and send the complete packet
> to conntrack module. After conntrack processed the packet, fragment
> module will divide the complete packet into small fragment and send
> it to destination. Currently, ipv6 was implemented in a indenpent
> module, for the reason it can reduce the risk of introduce bug to
> ipv4 fragmenb module.
>
> 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.UdpV6
>   a) UdpV6 fragment with multiple ipv6 extension fields.
>   b) UdpV6 fragment in normal scenario.
>   c) UdpV6 fragment in nat scenario.
>
> 2.IcmpV6
>   a) IcmpV6 fragment in normal scenario.
>   b) IcmpV6 fragment in nat scenario.
>
> Signed-off-by: ldejing <ldejing@vmware.com>
> ---
>  Documentation/faq/releases.rst         |   1 +
>  NEWS                                   |   1 +
>  datapath-windows/automake.mk           |   2 +
>  datapath-windows/ovsext/Actions.c      |   8 +-
>  datapath-windows/ovsext/BufferMgmt.c   | 420 ++++++++++++-
>  datapath-windows/ovsext/BufferMgmt.h   |   1 +
>  datapath-windows/ovsext/Conntrack.c    |  45 +-
>  datapath-windows/ovsext/Ip6Fragment.c  | 808 +++++++++++++++++++++++++
>  datapath-windows/ovsext/Ip6Fragment.h  | 111 ++++
>  datapath-windows/ovsext/PacketParser.c |   6 +-
>  datapath-windows/ovsext/Switch.c       |   9 +
>  datapath-windows/ovsext/Util.h         |   1 +
>  datapath-windows/ovsext/ovsext.vcxproj |   2 +
>  13 files changed, 1372 insertions(+), 43 deletions(-)
>  create mode 100644 datapath-windows/ovsext/Ip6Fragment.c
>  create mode 100644 datapath-windows/ovsext/Ip6Fragment.h
>
> 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));
> +    }
> +

[AS] I'm wondering why do we need this. Can we drop the function and use hdrInfo->l4Offset directly?
If we cannot, we can drop ipv6Hdr, it is not used, so no point defining it.

For this point, after read l4Offset related code, I found use hdr->l4Offset is enough, I will drop the function.


>      return NDIS_STATUS_SUCCESS;
>  }
>
> @@ -1160,8 +1169,8 @@ GetSegmentHeaderInfo(PNET_BUFFER_LIST nbl,
[AS] There is a comment "*    XXX - Support IpV6 Fragments" just one
line above. It can be safely dropped.

Will fix it.


>   * --------------------------------------------------------------------------
>   */
>  static NDIS_STATUS
[AS] Let's split this function into two FixSegmentHeader4 /
FixSegmentHeader6 and have a common FixSegmentHeader when invoking it.
It would make things more readable.

Will fix it.


> -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;
[AS] can we have a constant for that magic number (0x8088405)?
Will fix it.



> +
> +    srcHash = OvsJhashBytes((UINT32 *)(&(ipv6Hdr->saddr)), 4, randNumber);
> +    dstHash = OvsJhashBytes((UINT32 *)(&(ipv6Hdr->daddr)), 4, srcHash);
> +    return dstHash;
> +}
>
>  /*
>   * --------------------------------------------------------------------------
[AS] Let's split this function into two, one for v4 and another for v6.
It will make the following more digestable.

For this function, we only have ipv6 version, because ipv4 will contains a framgnet identifier in the original packet, ipv6 won’t contain a framgment identifier in the original packet, we need the function to generate a fragment identifier for ipv6 fragment, thus this function only has an ipv6 version. However, I will rename the function name from “GenFragIdent” to “GenFragIdent6” to make it more readable.


> @@ -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:
> + *
> + *     https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&amp;data=05%7C01%7Cldejing%40vmware.com%7Cad67c8487047464c0a4a08da7a0819e8%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956472509882455%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=8T22tIMorJmoMABphzScol4h5UUAkI2cdLLOS0GVASM%3D&amp;reserved=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:
> + *
> + *     https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&amp;data=05%7C01%7Cldejing%40vmware.com%7Cad67c8487047464c0a4a08da7a0819e8%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956472509882455%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=8T22tIMorJmoMABphzScol4h5UUAkI2cdLLOS0GVASM%3D&amp;reserved=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://nam04.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.rfc-editor.org%2Frfc%2Frfc8200.html&amp;data=05%7C01%7Cldejing%40vmware.com%7Cad67c8487047464c0a4a08da7a0819e8%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956472509882455%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=%2F9Bwrz0eyFmj6A2caBoutoogSXd9cTFTAOpwWkayC80%3D&amp;reserved=0 */
> +    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" />
Dejing Liu Aug. 26, 2022, 7:28 a.m. UTC | #3
Hi Alin,

How about the status of latest patch, looking forward to your reply.

Regards
Dejing


From: Dejing Liu <ldejing@vmware.com>
Date: Monday, August 15, 2022 at 5:46 PM
To: Alin-Gabriel Serdean <aserdean@ovn.org>, dev@openvswitch.org <dev@openvswitch.org>
Cc: svc.ovs-community <svc.ovs-community@vmware.com>, Wilson Peng <pweisong@vmware.com>, Frank Guo <frankg@vmware.com>, Lina Li <linali@vmware.com>
Subject: Re: [ovs-dev] [PATCH v1 1/1] datapath-windows: Add IPv6 conntrack ip fragment support on windows
Hi alin,

I have made some modification according to your suggestion and new patch will coming soon.

Regards
Dejing



From: ldejing <ldejing@vmware.com>

Patch looks good just a few nits below.

> Implementation on Windows:
> IPv6 conntrack ip fragment feature use a link list to store ip
> fragment. When ipv6 fragment module receives a fragment packet,
> it will store length of the fragment, until to the received length
> equal to the packet length before fragmented, it will reassemble
> fragment packet to a complete packet and send the complete packet
> to conntrack module. After conntrack processed the packet, fragment
> module will divide the complete packet into small fragment and send
> it to destination. Currently, ipv6 was implemented in a indenpent
> module, for the reason it can reduce the risk of introduce bug to
> ipv4 fragmenb module.
>
> 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.UdpV6
>   a) UdpV6 fragment with multiple ipv6 extension fields.
>   b) UdpV6 fragment in normal scenario.
>   c) UdpV6 fragment in nat scenario.
>
> 2.IcmpV6
>   a) IcmpV6 fragment in normal scenario.
>   b) IcmpV6 fragment in nat scenario.
>
> Signed-off-by: ldejing <ldejing@vmware.com>
> ---
>  Documentation/faq/releases.rst         |   1 +
>  NEWS                                   |   1 +
>  datapath-windows/automake.mk           |   2 +
>  datapath-windows/ovsext/Actions.c      |   8 +-
>  datapath-windows/ovsext/BufferMgmt.c   | 420 ++++++++++++-
>  datapath-windows/ovsext/BufferMgmt.h   |   1 +
>  datapath-windows/ovsext/Conntrack.c    |  45 +-
>  datapath-windows/ovsext/Ip6Fragment.c  | 808 +++++++++++++++++++++++++
>  datapath-windows/ovsext/Ip6Fragment.h  | 111 ++++
>  datapath-windows/ovsext/PacketParser.c |   6 +-
>  datapath-windows/ovsext/Switch.c       |   9 +
>  datapath-windows/ovsext/Util.h         |   1 +
>  datapath-windows/ovsext/ovsext.vcxproj |   2 +
>  13 files changed, 1372 insertions(+), 43 deletions(-)
>  create mode 100644 datapath-windows/ovsext/Ip6Fragment.c
>  create mode 100644 datapath-windows/ovsext/Ip6Fragment.h
>
> 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));
> +    }
> +

[AS] I'm wondering why do we need this. Can we drop the function and use hdrInfo->l4Offset directly?
If we cannot, we can drop ipv6Hdr, it is not used, so no point defining it.


For this point, after read l4Offset related code, I found use hdr->l4Offset is enough, I will drop the function.


>      return NDIS_STATUS_SUCCESS;
>  }
>
> @@ -1160,8 +1169,8 @@ GetSegmentHeaderInfo(PNET_BUFFER_LIST nbl,
[AS] There is a comment "*    XXX - Support IpV6 Fragments" just one
line above. It can be safely dropped.

Will fix it.


>   * --------------------------------------------------------------------------
>   */
>  static NDIS_STATUS
[AS] Let's split this function into two FixSegmentHeader4 /
FixSegmentHeader6 and have a common FixSegmentHeader when invoking it.
It would make things more readable.

Will fix it.


> -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;
[AS] can we have a constant for that magic number (0x8088405)?
Will fix it.



> +
> +    srcHash = OvsJhashBytes((UINT32 *)(&(ipv6Hdr->saddr)), 4, randNumber);
> +    dstHash = OvsJhashBytes((UINT32 *)(&(ipv6Hdr->daddr)), 4, srcHash);
> +    return dstHash;
> +}
>
>  /*
>   * --------------------------------------------------------------------------
[AS] Let's split this function into two, one for v4 and another for v6.
It will make the following more digestable.

For this function, we only have ipv6 version, because ipv4 will contains a framgnet identifier in the original packet, ipv6 won’t contain a framgment identifier in the original packet, we need the function to generate a fragment identifier for ipv6 fragment, thus this function only has an ipv6 version. However, I will rename the function name from “GenFragIdent” to “GenFragIdent6” to make it more readable.


> @@ -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:
> + *
> + *     https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&amp;data=05%7C01%7Cldejing%40vmware.com%7Cad67c8487047464c0a4a08da7a0819e8%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956472509882455%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=8T22tIMorJmoMABphzScol4h5UUAkI2cdLLOS0GVASM%3D&amp;reserved=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:
> + *
> + *     https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&amp;data=05%7C01%7Cldejing%40vmware.com%7Cad67c8487047464c0a4a08da7a0819e8%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956472509882455%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=8T22tIMorJmoMABphzScol4h5UUAkI2cdLLOS0GVASM%3D&amp;reserved=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://nam04.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.rfc-editor.org%2Frfc%2Frfc8200.html&amp;data=05%7C01%7Cldejing%40vmware.com%7Cad67c8487047464c0a4a08da7a0819e8%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956472509882455%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=%2F9Bwrz0eyFmj6A2caBoutoogSXd9cTFTAOpwWkayC80%3D&amp;reserved=0 */
> +    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" />
Wilson Peng Aug. 26, 2022, 7:34 a.m. UTC | #4
Alin,

And help check the new fix patch below,  it could be applied to
Mater and branch-3.0. It could make Geneve V6 tunnel could be
Working for checksum offload enabled case.
[ovs-dev,ovs-dev,v1,1/1] datapath-windows: Correct Geneve IPV6 header checksum parameter
https://patchwork.ozlabs.org/project/openvswitch/patch/MA0PR01MB62181E8E4B31322BCFD2900188719@MA0PR01MB6218.INDPRD01.PROD.OUTLOOK.COM/


Regards
Wilson

From: Dejing Liu <ldejing@vmware.com>
Date: Friday, August 26, 2022 at 15:28
To: Alin-Gabriel Serdean <aserdean@ovn.org>, dev@openvswitch.org <dev@openvswitch.org>
Cc: svc.ovs-community <svc.ovs-community@vmware.com>, Wilson Peng <pweisong@vmware.com>, Frank Guo <frankg@vmware.com>, Lina Li <linali@vmware.com>
Subject: Re: [ovs-dev] [PATCH v1 1/1] datapath-windows: Add IPv6 conntrack ip fragment support on windows
Hi Alin,

How about the status of latest patch, looking forward to your reply.

Regards
Dejing


From: Dejing Liu <ldejing@vmware.com>
Date: Monday, August 15, 2022 at 5:46 PM
To: Alin-Gabriel Serdean <aserdean@ovn.org>, dev@openvswitch.org <dev@openvswitch.org>
Cc: svc.ovs-community <svc.ovs-community@vmware.com>, Wilson Peng <pweisong@vmware.com>, Frank Guo <frankg@vmware.com>, Lina Li <linali@vmware.com>
Subject: Re: [ovs-dev] [PATCH v1 1/1] datapath-windows: Add IPv6 conntrack ip fragment support on windows
Hi alin,

I have made some modification according to your suggestion and new patch will coming soon.

Regards
Dejing



From: ldejing <ldejing@vmware.com>

Patch looks good just a few nits below.

> Implementation on Windows:
> IPv6 conntrack ip fragment feature use a link list to store ip
> fragment. When ipv6 fragment module receives a fragment packet,
> it will store length of the fragment, until to the received length
> equal to the packet length before fragmented, it will reassemble
> fragment packet to a complete packet and send the complete packet
> to conntrack module. After conntrack processed the packet, fragment
> module will divide the complete packet into small fragment and send
> it to destination. Currently, ipv6 was implemented in a indenpent
> module, for the reason it can reduce the risk of introduce bug to
> ipv4 fragmenb module.
>
> 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.UdpV6
>   a) UdpV6 fragment with multiple ipv6 extension fields.
>   b) UdpV6 fragment in normal scenario.
>   c) UdpV6 fragment in nat scenario.
>
> 2.IcmpV6
>   a) IcmpV6 fragment in normal scenario.
>   b) IcmpV6 fragment in nat scenario.
>
> Signed-off-by: ldejing <ldejing@vmware.com>
> ---
>  Documentation/faq/releases.rst         |   1 +
>  NEWS                                   |   1 +
>  datapath-windows/automake.mk           |   2 +
>  datapath-windows/ovsext/Actions.c      |   8 +-
>  datapath-windows/ovsext/BufferMgmt.c   | 420 ++++++++++++-
>  datapath-windows/ovsext/BufferMgmt.h   |   1 +
>  datapath-windows/ovsext/Conntrack.c    |  45 +-
>  datapath-windows/ovsext/Ip6Fragment.c  | 808 +++++++++++++++++++++++++
>  datapath-windows/ovsext/Ip6Fragment.h  | 111 ++++
>  datapath-windows/ovsext/PacketParser.c |   6 +-
>  datapath-windows/ovsext/Switch.c       |   9 +
>  datapath-windows/ovsext/Util.h         |   1 +
>  datapath-windows/ovsext/ovsext.vcxproj |   2 +
>  13 files changed, 1372 insertions(+), 43 deletions(-)
>  create mode 100644 datapath-windows/ovsext/Ip6Fragment.c
>  create mode 100644 datapath-windows/ovsext/Ip6Fragment.h
>
> 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));
> +    }
> +

[AS] I'm wondering why do we need this. Can we drop the function and use hdrInfo->l4Offset directly?
If we cannot, we can drop ipv6Hdr, it is not used, so no point defining it.



For this point, after read l4Offset related code, I found use hdr->l4Offset is enough, I will drop the function.


>      return NDIS_STATUS_SUCCESS;
>  }
>
> @@ -1160,8 +1169,8 @@ GetSegmentHeaderInfo(PNET_BUFFER_LIST nbl,
[AS] There is a comment "*    XXX - Support IpV6 Fragments" just one
line above. It can be safely dropped.

Will fix it.


>   * --------------------------------------------------------------------------
>   */
>  static NDIS_STATUS
[AS] Let's split this function into two FixSegmentHeader4 /
FixSegmentHeader6 and have a common FixSegmentHeader when invoking it.
It would make things more readable.

Will fix it.


> -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;
[AS] can we have a constant for that magic number (0x8088405)?
Will fix it.



> +
> +    srcHash = OvsJhashBytes((UINT32 *)(&(ipv6Hdr->saddr)), 4, randNumber);
> +    dstHash = OvsJhashBytes((UINT32 *)(&(ipv6Hdr->daddr)), 4, srcHash);
> +    return dstHash;
> +}
>
>  /*
>   * --------------------------------------------------------------------------
[AS] Let's split this function into two, one for v4 and another for v6.
It will make the following more digestable.

For this function, we only have ipv6 version, because ipv4 will contains a framgnet identifier in the original packet, ipv6 won’t contain a framgment identifier in the original packet, we need the function to generate a fragment identifier for ipv6 fragment, thus this function only has an ipv6 version. However, I will rename the function name from “GenFragIdent” to “GenFragIdent6” to make it more readable.


> @@ -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:
> + *
> + *     https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&amp;data=05%7C01%7Cldejing%40vmware.com%7Cad67c8487047464c0a4a08da7a0819e8%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956472509882455%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=8T22tIMorJmoMABphzScol4h5UUAkI2cdLLOS0GVASM%3D&amp;reserved=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:
> + *
> + *     https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&amp;data=05%7C01%7Cldejing%40vmware.com%7Cad67c8487047464c0a4a08da7a0819e8%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956472509882455%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=8T22tIMorJmoMABphzScol4h5UUAkI2cdLLOS0GVASM%3D&amp;reserved=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://nam04.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.rfc-editor.org%2Frfc%2Frfc8200.html&amp;data=05%7C01%7Cldejing%40vmware.com%7Cad67c8487047464c0a4a08da7a0819e8%7Cb39138ca3cee4b4aa4d6cd83d9dd62f0%7C0%7C0%7C637956472509882455%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&amp;sdata=%2F9Bwrz0eyFmj6A2caBoutoogSXd9cTFTAOpwWkayC80%3D&amp;reserved=0 */
> +    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" />
diff mbox series

Patch

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" />