@@ -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 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
@@ -48,6 +48,7 @@ v3.0.0 - 15 Aug 2022
- Windows:
* Conntrack support for TCPv6, UDPv6, ICMPv6, FTPv6.
* IPv6 Geneve tunnel support.
+ * Conntrack ipv6 support fragment.
- DPDK:
* OVS validated with DPDK 21.11.1. It is recommended to use this version
until further releases.
@@ -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 \
@@ -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;
}
@@ -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);
}
}
@@ -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 */
@@ -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;
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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_
@@ -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;
}
}
}
@@ -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. */
@@ -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);
@@ -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" />