diff options
-rw-r--r-- | Documentation/faq/releases.rst | 1 | ||||
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | datapath-windows/automake.mk | 2 | ||||
-rw-r--r-- | datapath-windows/ovsext/Actions.c | 8 | ||||
-rw-r--r-- | datapath-windows/ovsext/BufferMgmt.c | 464 | ||||
-rw-r--r-- | datapath-windows/ovsext/BufferMgmt.h | 4 | ||||
-rw-r--r-- | datapath-windows/ovsext/Conntrack.c | 45 | ||||
-rw-r--r-- | datapath-windows/ovsext/Ip6Fragment.c | 808 | ||||
-rw-r--r-- | datapath-windows/ovsext/Ip6Fragment.h | 111 | ||||
-rw-r--r-- | datapath-windows/ovsext/PacketParser.c | 6 | ||||
-rw-r--r-- | datapath-windows/ovsext/Switch.c | 9 | ||||
-rw-r--r-- | datapath-windows/ovsext/Util.h | 1 | ||||
-rw-r--r-- | datapath-windows/ovsext/ovsext.vcxproj | 2 |
13 files changed, 1405 insertions, 58 deletions
diff --git a/Documentation/faq/releases.rst b/Documentation/faq/releases.rst index 1bc22a6ba..607a23499 100644 --- a/Documentation/faq/releases.rst +++ b/Documentation/faq/releases.rst @@ -136,6 +136,7 @@ Q: Are all features available with all datapaths? Connection tracking 4.3 2.5 2.6 YES Connection tracking-IPv6 YES YES YES 3.0 Conntrack Fragment Reass. 4.3 2.6 2.12 YES + Conntrack IPv6 Fragment 4.3 2.6 2.12 3.1 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 @@ -3,6 +3,8 @@ Post-v3.0.0 - ovs-appctl: * "ovs-appctl ofproto/trace" command can now display port names with the "--names" option. + - Windows: + * Conntrack IPv6 fragment support. v3.0.0 - 15 Aug 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 3100532e1..884fce4e3 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; } } @@ -2410,8 +2410,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, @@ -2422,6 +2423,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..5c52757a0 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" @@ -90,8 +91,6 @@ #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_BUFMGMT - - /* * -------------------------------------------------------------------------- * OvsInitBufferPool -- @@ -1109,19 +1108,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; } @@ -1151,17 +1157,72 @@ GetSegmentHeaderInfo(PNET_BUFFER_LIST nbl, return NDIS_STATUS_SUCCESS; } +static VOID +FixFragmentHeader4(UINT16 fragmentSize, UINT16 offset, const EthHdr *dstEth, + BOOLEAN lastPacket) +{ + IPHdr *dstIP = NULL; + + dstIP = (IPHdr *)((PCHAR)dstEth + sizeof(*dstEth)); + dstIP->tot_len = htons(fragmentSize + dstIP->ihl * 4); + if (lastPacket) { + dstIP->frag_off = htons(offset & IP_OFFSET); + } else { + dstIP->frag_off = htons((offset & IP_OFFSET) | IP_MF); + } + + dstIP->check = 0; + dstIP->check = IPChecksum((UINT8 *)dstIP, dstIP->ihl * 4, 0); +} + +static VOID +FixFragmentHeader6(UINT16 offset, const EthHdr *dstEth, + UINT32 fragmentIdent, + BOOLEAN lastPacket) +{ + IPv6Hdr *dstIP = NULL; + UINT8 nextHdr; + IPv6ExtHdr *extHdr; + + 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."); + } + } +} + /* * -------------------------------------------------------------------------- * FixFragmentHeader * * Fix IP length, Offset, IP checksum. - * XXX - Support IpV6 Fragments * -------------------------------------------------------------------------- */ 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; @@ -1180,25 +1241,20 @@ FixFragmentHeader(PNET_BUFFER nb, UINT16 fragmentSize, { IPHdr *dstIP = NULL; ASSERT((INT)MmGetMdlByteCount(mdl) - NET_BUFFER_CURRENT_MDL_OFFSET(nb) - >= sizeof(EthHdr) + sizeof(IPHdr)); - + >= sizeof(EthHdr) + sizeof(IPHdr)); dstIP = (IPHdr *)((PCHAR)dstEth + sizeof(*dstEth)); ASSERT((INT)MmGetMdlByteCount(mdl) - NET_BUFFER_CURRENT_MDL_OFFSET(nb) - >= sizeof(EthHdr) + dstIP->ihl * 4); - dstIP->tot_len = htons(fragmentSize + dstIP->ihl * 4); - if (lastPacket) { - dstIP->frag_off = htons(offset & IP_OFFSET); - } else { - dstIP->frag_off = htons((offset & IP_OFFSET) | IP_MF); - } - - dstIP->check = 0; - dstIP->check = IPChecksum((UINT8 *)dstIP, dstIP->ihl * 4, 0); + >= sizeof(EthHdr) + dstIP->ihl * 4); + FixFragmentHeader4(fragmentSize, offset, dstEth, lastPacket); break; } case ETH_TYPE_IPV6_NBO: { - return NDIS_STATUS_NOT_SUPPORTED; + ASSERT((INT)MmGetMdlByteCount(mdl) - NET_BUFFER_CURRENT_MDL_OFFSET(nb) + >= sizeof(EthHdr) + sizeof(IPv6Hdr)); + FixFragmentHeader6(offset, dstEth, fragmentIdent, + lastPacket); + break; } default: OVS_LOG_ERROR("Invalid eth type: %d\n", dstEth->Type); @@ -1314,6 +1370,7 @@ FixSegmentHeader(PNET_BUFFER nb, UINT16 segmentSize, UINT32 seqNumber, return STATUS_SUCCESS; } + /* * -------------------------------------------------------------------------- * OvsTcpSegmentNBL -- @@ -1331,6 +1388,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 +GenFragIdent6(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 * OVS_FRAG_MAGIC_NUMBER + 1; + + srcHash = OvsJhashBytes((UINT32 *)(&(ipv6Hdr->saddr)), 4, randNumber); + dstHash = OvsJhashBytes((UINT32 *)(&(ipv6Hdr->daddr)), 4, srcHash); + return dstHash; +} /* * -------------------------------------------------------------------------- @@ -1366,8 +1604,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 +1620,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 +1645,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 = GenFragIdent6(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 +1686,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 +1815,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 +2147,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..90d2360f7 100644 --- a/datapath-windows/ovsext/BufferMgmt.h +++ b/datapath-windows/ovsext/BufferMgmt.h @@ -25,6 +25,10 @@ #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) +#define OVS_FRAG_MAGIC_NUMBER 0x8088405 + + /* 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 1f3929894..fe9fb74ca 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 *)¤tTime); + 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 *)¤tTime); + 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 *)¤tTime); + 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 *)¤tTime); + 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" /> |