--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/networkprotocols/tcpipv4v6prt/src/ip6_frag.cpp Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,1183 @@
+// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// ip6_frag.cpp - hook for IPv6 fragment header
+//
+
+#include "frag.h"
+#include <icmp6_hdr.h> // for ICMP Type symbols
+#include <ext_hdr.h> // for IPv6 fragment header
+#include "ip6_frag.h"
+#include <in_pkt.h>
+#include <in_chk.h>
+#include <timeout.h>
+#include "tcpip_ini.h"
+#include "inet6log.h"
+
+// TIp6FragmentHeader
+// ******************
+// This header is at the beginning of each fragment in the
+// fragment queue. This attempts to be the same size as
+// the real IPv6 fragment header (8 octets), but the code
+// should work with any size of this [use of this specific
+// size avoids some TrimStart() calls in some cases.]
+//
+class TIp6FragmentHeader
+ {
+public:
+ TUint iOffset; // Offset of this fragment (bytes)
+ TUint iLength; // Lenght of this fragment (bytes)
+ };
+
+//
+// RIp6Fragment
+// ************
+// The IP6 fragment data structure is an RMBufChain containing
+// - TIp6FragmentHeader
+// - followed by fragment content
+// This is the interface to the RMBufFragQ class, a collection
+// of required methods for accessing the fragment information and
+// a join operation.
+//
+class RIp6Fragment : public RMBufFrag
+ {
+public:
+ // Internal utility to the other methods. Assumes that the fragment
+ // start is properly aligned to be cast into class pointer.
+ inline TIp6FragmentHeader *Header() const { return (TIp6FragmentHeader *)(First()->Ptr()); }
+ // Return offset of the fragment (bytes)
+ TUint Offset() const {return Header()->iOffset;}
+ // Return length of the fragment (bytes)
+ // (Does not include the fragment header, using
+ // this->Length() will give larger value that
+ // includes the header)
+ TUint FragmentLength() const {return Header()->iLength;}
+ // Join another fragment to this one
+ void Join(RIp6Fragment& aFrag)
+ {
+ TInt overlap = Offset() + FragmentLength() - aFrag.Offset();
+ if (overlap < 0)
+ {
+ aFrag.Free(); // Should panic? This should not happen!
+ User::Panic(_L("DEBUG"), 0);
+ }
+ else
+ {
+ Header()->iLength += aFrag.FragmentLength() - overlap;
+ aFrag.TrimStart(sizeof(TIp6FragmentHeader) + overlap);
+ Append(aFrag);
+ }
+ }
+ };
+
+
+//
+// CFragmentHandler
+// ****************
+//
+class CAssembly;
+class CFragmentHandler : public CFragmentHeaderHook
+ {
+public:
+ CFragmentHandler(MNetworkService *aNetwork) : CFragmentHeaderHook(aNetwork) {}
+ ~CFragmentHandler();
+ inline MNetworkService *Network() { return iNetwork; }
+ CAssembly *LookupL(const RMBufRecvInfo &aInfo, TUint32 aId, TUint aVersion);
+
+ void ConstructL();
+ void Cancel(CAssembly *aAssembly);
+ void CancelAll();
+ TInt ApplyL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo);
+ void Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ);
+
+ MTimeoutManager *iTimeoutManager;
+ CAssembly *iCache;
+ //
+ TInt iCount; // Current incomplete assemblies
+ TInt iMaxCount; // Maximum number of assemblies
+ TInt iTotal; // Total amount of RMBuf space allocated to assemblies (bytes)
+ TInt iMaxTotal; // Maximum allowed amount of RMBuf space
+private:
+ TInt Ip6ApplyL(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, const TInet6HeaderFragment &aHdr);
+ TInt Ip4ApplyL(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, const TInet6HeaderIP4 &aHdr);
+ void Ip6Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ, TInet6HeaderIP &aHdr);
+ void Ip4Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ, TInet6HeaderIP4 &aHdr);
+
+ //
+ // iFagmentId is only used for IPv6. For IPv4, the Id is already in
+ // in each IP packet.
+ //
+ TInt iFragmentId;
+ };
+
+//
+// CAssembly
+// *********
+// The data structure for a partially assembled packet
+//
+class CAssembly : public CBase
+ {
+ friend class CFragmentHandler;
+ friend class CFragmentLinkage;
+
+ CAssembly(CFragmentHandler &aHandler, const TIp6Addr &aSrc, const TIp6Addr &aDst, TInt aId, TInt aVersion);
+ ~CAssembly();
+
+ TInt AddFragmentL(RMBufRecvPacket &aPacket, TInt aTrim, TInt aLength, TInt aOffset, TUint32 aFh0);
+ TInt Add(RMBufRecvPacket &aPacket, TInt aTrim, TInt aLength, TInt aOffset, TUint32 aFh0 = 0);
+ TInt CompletePacket(TInt aRestoreFH = 0);
+ void Timeout();
+public:
+ //
+ // Linkage and management
+ //
+ CFragmentHandler &iHandler;
+ CAssembly *iNext; // Linkage of the assemblies in the cache
+
+ RTimeout iTimeout; // Timer hook
+#ifndef _LOG
+protected:
+#endif
+ const TUint8 iVersion; // Need to have, because TAHI wants FH in IPv6 TimeExceeded ICMP
+ TUint iIsDead:1; // =1, if this assembly is "dead" (matching fragments are dropped)
+ const TUint32 iId; // Fragment identification
+ const TIp6Addr iSrcAddr;
+ const TIp6Addr iDstAddr;
+
+ TUint32 iFH0; // Saved first 32 bits of the first FH (for TAHI)
+
+ TInt32 iLength; // Total length of the fragmentable part, when
+ // known (e.g. last fragment received).
+ // [As 32 bit int is used, there shouuld be no
+ // overflow or wrap around problems because
+ // offset field is only 16 bits (in bytes)]
+ TInt iTotal; // The amount of "iTotal" from this assembly.
+ //
+ // The unfragmentable part of the first fragment (if received)
+ //
+ RMBufRecvPacket iHead;
+ RMBufFragQ<RIp6Fragment> iQueue;
+ };
+
+//
+// CFragmentLinkage
+// ****************
+// Glue to bind timeout callback from the timeout manager into Timeout() call
+// on the CAssembly.
+//
+// *NOTE*
+// This kludgery is all static and compile time, and only used in the constructor
+// of CFragmentAssembly following this.
+//
+
+// This ungainly manoevure is forced on us because the offset is not evaluated early enough by GCC3.4 to be
+// passed as a template parameter
+#if defined(__X86GCC__) || defined(__GCCE__)
+#define KAssemblyTimeoutOffset 12
+__ASSERT_COMPILE(KAssemblyTimeoutOffset == _FOFF(CAssembly, iTimeout));
+#else
+#define KAssemblyTimeoutOffset _FOFF(CAssembly, iTimeout)
+#endif
+
+class CFragmentLinkage : public TimeoutLinkage<CAssembly, KAssemblyTimeoutOffset>
+ {
+public:
+ static void Timeout(RTimeout &aLink, const TTime & /*aNow*/, TAny * /*aPtr*/)
+ {
+ Object(aLink)->Timeout();
+ }
+ };
+
+#ifdef _LOG
+//
+// LogPrintId
+// **********
+// Purely for LOG output, should not be compiled in the final release
+//
+static void LogPrintId(const TDesC &aText, const CAssembly &a)
+ {
+ TInetAddr src, dst;
+ TBuf<70> sbuf;
+ TBuf<70> dbuf;
+
+ src.SetAddress(a.iSrcAddr);
+ dst.SetAddress(a.iDstAddr);
+
+ src.OutputWithScope(sbuf);
+ dst.OutputWithScope(dbuf);
+ Log::Printf(_L("%S [src=%S dst=%S id=%d] (%d)"), &aText, &sbuf, &dbuf, a.iId, a.iHandler.iCount);
+ }
+#endif
+
+//
+// CFragmentHandler::Cancel
+// ************************
+// Cancel a specific assembly (delete it)
+//
+void CFragmentHandler::Cancel(CAssembly *aAssembly)
+ {
+ for (CAssembly **h = &iCache; *h != NULL; h = &(*h)->iNext)
+ if (aAssembly == *h)
+ {
+ *h = aAssembly->iNext;
+ delete aAssembly;
+ break;
+ }
+ // Should panic, if a was not found from the list! -- msa
+ }
+
+//
+// CFragmentHandler::CancelAll
+// ***************************
+// Cancel all packets waiting for assembly (delete all)
+//
+void CFragmentHandler::CancelAll()
+ {
+ CAssembly *a;
+ while ((a = iCache) != NULL)
+ {
+ iCache = a->iNext;
+ delete a;
+ }
+ }
+
+//
+// ApplyL
+// ******
+// Called when an IP packet contain IPv6 fragment header.
+// (does not care whether outer IP header is v4 or v6, both work!)
+//
+TInt CFragmentHandler::ApplyL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo)
+ {
+ for (;;) // *NOT REAL LOOP, JUST A CONSTRUCT TO ENABLE USE OF 'break!
+ {
+ if (aInfo.iProtocol != STATIC_CAST(TInt, KProtocolInet6Fragment))
+ break; // Incorrect call, should only get IPv6 fragments here!
+ TInet6Packet<TInet6HeaderFragment> fh(aPacket, aInfo.iOffset);
+ if (fh.iHdr == NULL)
+ break; // Drop! (packet too short)
+
+ if (aInfo.iIcmp == 0)
+ return Ip6ApplyL(aPacket, aInfo, *fh.iHdr);
+
+ if (aInfo.iIcmp != KProtocolInet6Icmp)
+ break; // Only IPv6 complaints should get this far, drop!
+
+ const TInt offset = aInfo.iOffset - aInfo.iOffsetIp; // Relative offset within problem packet
+ if (aInfo.iType == KInet6ICMP_ParameterProblem && // A parameter problem...
+ offset <= (TInt)aInfo.iParameter && // after start of this header?
+ STATIC_CAST(TUint, offset + sizeof(TInet6HeaderFragment)) > STATIC_CAST(TUint, aInfo.iParameter)) // and before end of this header?
+ break; // Drop! (someone doesn't like my fragments!)
+ //
+ // Error is not Fragment Header specific, pass it on in the chain
+ // Skip over header, pass error processing to the next header
+ aInfo.iPrevNextHdr = (TUint16)aInfo.iOffset; // Fragment next header is at +0
+ aInfo.iProtocol = fh.iHdr->NextHeader();
+ aInfo.iOffset += sizeof(TInet6HeaderFragment);
+ return KIp6Hook_DONE;
+ // WAS NOT LOOP, BE SURE TO EXIT IT ALWAYS!
+ }
+ aPacket.Free();
+ return -1;
+ }
+
+//
+// Fragment
+// ********
+// Called when an outgoing IP packet needs to be fragmented
+//
+void CFragmentHandler::Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ)
+ {
+ //
+ // Decide on which fragmenting from the IP header.
+ //
+ TIpHeader *ip = ((RMBufPacketPeek &)aPacket).GetIpHeader();
+ if (ip)
+ if (ip->ip4.Version() == 4)
+ Ip4Fragment(aPacket, aMtu, aFragQ, ip->ip4);
+ else if (ip->ip4.Version() == 6)
+ Ip6Fragment(aPacket, aMtu, aFragQ, ip->ip6);
+ }
+
+
+// CAssembly
+// *********
+//
+CAssembly::CAssembly(CFragmentHandler &aHandler, const TIp6Addr &aSrc, const TIp6Addr &aDst, TInt aId, TInt aVersion)
+ : iHandler(aHandler), iTimeout(CFragmentLinkage::Timeout), iVersion((TUint8)aVersion), iId(aId),
+ iSrcAddr(aSrc), iDstAddr(aDst), iLength(65536)
+ {
+ }
+
+// CAssembly::~CAssebly()
+// **********************
+//
+CAssembly::~CAssembly()
+ {
+ iHandler.iTotal -= iTotal;
+ --iHandler.iCount;
+
+ LOG(LogPrintId(_L("CAssebly::~CAssebly():"), *this));
+
+ iTimeout.Cancel();
+ iQueue.Free();
+ iHead.Free();
+ }
+
+
+LOCAL_C TInt CompareStructures()
+ {
+ return sizeof(TIp6FragmentHeader) - sizeof(TInet6HeaderFragment);
+ }
+//
+// CAssembly::CompletePacket
+// *************************
+// Merge the iHead and first fragment into a complete packet
+// (including the IPv4/IPv6 header stuff!)
+//
+// Returns,
+// == KErrNone, if packet in iHead built
+// != KErrNone, if no packet available
+//
+// *NOTE*
+// this is also used when reassembly timeout hits, thus the
+// "complete" can also be an incomplete packet (including
+// only the first fragment).
+//
+TInt CAssembly::CompletePacket(TInt aRestoreFH)
+ {
+ if (iHead.IsEmpty())
+ return -1;
+
+ RMBufRecvInfo *const info = iHead.Info();
+
+ RIp6Fragment first;
+ iQueue.Remove(first);
+
+ TInt length = first.Header()->iLength;
+ ASSERT(first.Header()->iOffset == 0);
+
+ if (aRestoreFH)
+ {
+ // *********************************************************
+ // TAHI tester wants the returned ICMP to include the FH on
+ // time exceeded message. This is for that...
+ // *********************************************************
+ const TInt adjust = CompareStructures();
+ // As the value of adjust is known by compile time, compiler should eliminate
+ // unnecessary code below (although, it may give a warnings about constants
+ // being compared--this is indication that code elimination should work!)
+ if (adjust > 0)
+ first.TrimStart(adjust);
+ else if (adjust < 0)
+ {
+ TInt err = first.Prepend(-adjust);
+ if (err!=KErrNone)
+ {
+ first.Free();
+ return -1;
+ }
+ }
+ TInet6HeaderFragment fake_fh;
+ *(TUint32 *)&fake_fh = iFH0; // Saved 1st word of original FH
+ fake_fh.SetId(iId); // ..and this SetId cover all of FH
+ // (there is no need to zero anything else)
+ first.CopyIn(TPtrC8((TUint8 *)&fake_fh, sizeof(TInet6HeaderFragment)), 0);
+ // .. ugh, KProtocolInet6Fragment is TInt, casting to TUint8* might
+ // cause "endian problems", thus this copy to true TUint8 variable... -- msa
+ const TUint8 proto = KProtocolInet6Fragment;
+ iHead.CopyIn(TPtrC8((TUint8 *)&proto, 1), info->iPrevNextHdr);
+ length += sizeof(TInet6HeaderFragment);
+ iLength = length;
+ }
+ else
+ first.TrimStart(sizeof(TIp6FragmentHeader));
+ if (length > iLength)
+ {
+ //
+ // Just to deal with some overlapping fragment, which extends
+ // beyond the last fragment... (iLength is initialized to
+ // 65536 and is only less, when last fragment has arrived!)
+ length = iLength;
+ first.TrimEnd(length);
+ }
+
+ iHead.Append(first);
+ //
+ // Fix info and IP header
+ //
+ //
+ // Only the iLength needs to be updated, all other fields in the info
+ // must already have the correct values!
+ //
+ info->iLength = info->iOffset + length;
+
+ TInet6Packet<TIpHeader> ip(iHead, info->iOffsetIp);
+ if (ip.iHdr)
+ {
+ TInt ver = ip.iHdr->ip4.Version();
+ if (ver == 4)
+ {
+ if (ip.iLength >= ip.iHdr->ip4.HeaderLength() &&
+ info->iLength < 65536)
+ {
+ // ..just to be tidy, make sure M=0 (nobody really cares) -- msa
+ ip.iHdr->ip4.SetFlags((TUint8)(ip.iHdr->ip4.Flags() & ~KInet4IP_MF));
+ ip.iHdr->ip4.SetTotalLength(info->iLength);
+ return KErrNone;
+ }
+ }
+ else if (ver == 6)
+ {
+ if (ip.iLength >= TInet6HeaderIP::MinHeaderLength() &&
+ STATIC_CAST(TUint, info->iLength) < STATIC_CAST(TUint, 65536 + sizeof(TInet6HeaderIP)))
+ {
+ ip.iHdr->ip6.SetPayloadLength(info->iLength - sizeof(TInet6HeaderIP));
+ return KErrNone;
+ }
+ }
+ }
+ return -1;
+ }
+
+//
+// CAssembly::Add
+// **********************
+//
+TInt CAssembly::Add(RMBufRecvPacket &aPacket, TInt aTrim, TInt aOffset, TInt aLength, TUint32 aFH0)
+ {
+ if (iIsDead == 0)
+ {
+ TInt ret = 0; // [ = 0, only to silence compiler]
+ TRAPD(err, ret = AddFragmentL(aPacket, aTrim, aOffset, aLength, aFH0));
+ if (err == KErrNone)
+ return ret;
+ //
+ // AddFragmentL left, change the Assembly into "dead" state
+ // (and make sure all unnecessary resources are released)
+ //
+ iIsDead = 1;
+ iHead.Free();
+ iQueue.Free();
+ iHandler.iTotal -= iTotal;
+ iTotal = 0;
+ }
+ ASSERT(iTotal == 0);
+ //
+ // The hook return code = -1 (=> packet handled and released)
+ //
+ aPacket.Free();
+ return -1;
+ }
+
+
+// CAssembly::AddFragmentL
+// ***********************
+// This should only be called from Add, which must trap the leaves and
+//
+// returns
+// standard inbound hook return codes
+// leaves
+// when assembly should be declared "dead" (seed "Add")
+//
+TInt CAssembly::AddFragmentL(RMBufRecvPacket &aPacket, TInt aTrim, TInt aOffset, TInt aLength, TUint32 aFH0)
+ {
+ RMBufRecvInfo *info = aPacket.Info();
+ if (aOffset == 0)
+ {
+ //
+ // Processing the first fragment (offset == 0!)
+ //
+
+ if (!iHead.IsEmpty())
+ aTrim += info->iOffset;
+ else
+ {
+ const TInt unfrag = info->iOffset; // = Unfragmentable length (bytes)
+ iTotal += unfrag;
+ iHandler.iTotal += unfrag;
+
+ iHead.Assign(aPacket);
+ iHead.SplitL(unfrag, aPacket);
+ iHead.SetInfo(info);
+ aPacket.SetInfo(NULL);
+ //
+ // Patch in the next header field
+ //
+ iHead.CopyIn(TPtrC8((TUint8 *)&info->iProtocol, 1), info->iPrevNextHdr);
+ //
+ // This fragment is assigned as the first fragment. Save the FH0 for it.
+ // (if there are multiple "first fragments", the first received is used)
+ // (Only needed for IPv6 and to generate TAHI-compatible ICMP Time Exceeded)
+ // *NOTE* The full first word is saved because, the reserved bits must be
+ // kept also, if someone is using them -- msa
+ iFH0 = aFH0;
+ }
+ }
+ else
+ aTrim += info->iOffset;
+ //
+ // Convert the header in the packet (if present) into
+ // internal fragment header
+ //
+ aTrim -= sizeof(TIp6FragmentHeader);
+ if (aTrim > 0)
+ // Cast needed to avoid TrimStart() looking for info block!!!
+ ((RMBufChain &)aPacket).TrimStart(aTrim);
+ else if (aTrim < 0)
+ aPacket.PrependL(-aTrim);
+ // Make sure the fragment header is properly aligned in the First() buffer.
+ aPacket.Align(sizeof(TIp6FragmentHeader));
+ TIp6FragmentHeader *frag = ((RIp6Fragment &)aPacket).Header();
+ frag->iLength = aLength;
+ frag->iOffset = aOffset;
+ //
+ // Add fragment to the queue. This assumes that the RMBufChain
+ // is removed from the aPacket (IsEmpty() becomes True).
+ //
+ iQueue.Add((RIp6Fragment &)aPacket);
+ ASSERT(aPacket.IsEmpty());
+
+ if (iQueue.First().FragmentLength() >= (TUint)iLength)
+ {
+#ifdef _LOG
+ TBuf<80> buf;
+ buf.Format(_L("CAssembly::AddFragmentL(): [%d..%d (%d)] COMPLETED"), aOffset, aOffset+aLength-1, aLength);
+ LogPrintId(buf, *this);
+#endif
+ // Packet has been fully assembled, restore the
+ // complete packet into aPacket
+ //
+ if (CompletePacket() == KErrNone)
+ {
+ aPacket.Assign(iHead);
+ if (info != iHead.Info())
+ {
+ //
+ // Need to copy the Information from the iHead
+ // (can't just do "*info = *iHead.Info()", because
+ // that would copy the RMBuf base class parts too!!
+ //
+ // ...this looks a bit scary, does this always work? Should
+ // look for a cleaner way to do this? -- msa
+ const TInt off = sizeof(RMBufCell);
+ const TInt len = sizeof(*info) - off;
+ TPtr8((TUint8 *)info + off, len).Copy(TPtrC8((TUint8 *)iHead.Info() + off, len));
+ }
+ else
+ {
+ //
+ // This only happens when the only fragment is full packet!
+ //
+ iHead.SetInfo(NULL);
+ aPacket.SetInfo(info);
+ }
+ iHandler.Cancel(this); // Deletes this!
+ return KIp6Hook_DONE;
+ }
+ aPacket.Free(); // (mainly for info block!)
+ iHandler.Cancel(this); // Deletes this!
+ return -1;
+ }
+ else
+ {
+ aPacket.FreeInfo();
+#ifdef _LOG
+ TBuf<80> buf;
+ buf.Format(_L("CAssembly::AddFragmentL(): [%d..%d (%d)]"), aOffset, aOffset+aLength-1, aLength);
+ LogPrintId(buf, *this);
+#endif
+ // Maintain total number of allocated bytes (roughly, this "overestimates"
+ // if fragments overlap!).
+ //
+ iTotal += aLength;
+ iHandler.iTotal += aLength;
+ if (iHandler.iTotal > iHandler.iMaxTotal)
+ {
+#ifdef _LOG
+ // ...borrow the 'buf' from above LOG block!
+ buf.Format(_L("CAssembly::AddFragmentL(): %d went over max (assembly total=%d)"), iHandler.iTotal, iTotal);
+ LogPrintId(buf, *this);
+#endif
+ User::Leave(KErrOverflow); // make assebly "dead" by leaving
+ }
+ return -1;
+ }
+ }
+
+
+void CAssembly::Timeout()
+ {
+ //
+ // Reconstruct start of IPv6 packet from the unfrag part
+ //
+ if (iVersion == 4)
+ {
+ if (CompletePacket() == KErrNone)
+ iHandler.Network()->Icmp4Send(iHead, KInet4ICMP_TimeExceeded, 1, 0);
+ }
+ else
+ {
+ if (CompletePacket(1) == KErrNone) // Request restore of FH (for TAHI)
+ iHandler.Network()->Icmp6Send(iHead, KInet6ICMP_TimeExceeded, 1, 0);
+ }
+ LOG(LogPrintId(_L("CAssembly::Timeout()"), *this));
+ iHandler.Cancel(this); // <-- Deletes this!!!
+ }
+
+CAssembly *CFragmentHandler::LookupL(const RMBufRecvInfo &aInfo, TUint32 aId, TUint aVersion)
+ {
+ CAssembly *a = NULL;
+ CAssembly *d = NULL;
+ const TIp6Addr &src = TInetAddr::Cast(aInfo.iSrcAddr).Ip6Address();
+ const TIp6Addr &dst = TInetAddr::Cast(aInfo.iDstAddr).Ip6Address();
+ for (a = iCache; ; a = a->iNext)
+ {
+ if (a == NULL)
+ {
+ // If the quota for incomplete assemblies is all used
+ if (iCount >= iMaxCount)
+ {
+ LOG(Log::Printf(_L("CFragmentHandler::LookupL() MaxCount %d reached"), iCount));
+ if (d == NULL)
+ User::Leave(KErrOverflow); // ... should assign some specific reason code?
+ Cancel(d);
+ LOG(Log::Printf(_L("CFragmentHandler::LookupL() deleted dead assembly")));
+ }
+ a = new (ELeave) CAssembly(*this, src, dst, aId, aVersion);
+ a->iNext = iCache;
+ iCache = a;
+ a->iTimeout.Set(iTimeoutManager, 60); // 60 seconds or bust!
+ iCount++;
+ LOG(LogPrintId(_L("CFragmentHandler::LookupL(): NEW"), *a));
+ break;
+ }
+ else if (a->iId == aId && a->iSrcAddr.IsEqual(src) && a->iDstAddr.IsEqual(dst))
+ break;
+ else if (a->iIsDead)
+ d = a; // remember "oldest dead" assembly
+ }
+ return a;
+ }
+
+//
+// CFragmentHeaderHook::NewL
+//
+CFragmentHeaderHook *CFragmentHeaderHook::NewL(MNetworkService *aNetwork)
+ {
+ return new (ELeave) CFragmentHandler(aNetwork);
+ }
+
+void CFragmentHandler::ConstructL()
+ {
+ iTimeoutManager = TimeoutFactory::NewL();
+ iNetwork->BindL((CProtocolBase *)this, BindHookFor(KProtocolInet6Fragment));
+
+
+ //
+ // Setup DOS protections
+ // - maximum number of incomplete assemblies (iMaxCount)
+ // - maximum amount of RMBUF space (iMaxTotal)
+ //
+ LOG(_LIT(KFormat, "\t[%S] %S = %d"));
+ TInt value;
+ if (iNetwork->Interfacer()->FindVar(TCPIP_INI_IP, TCPIP_INI_FRAG_COUNT, value))
+ iMaxCount = value;
+ else
+ iMaxCount = KTcpipIni_FragCount;
+ LOG(Log::Printf(KFormat, &TCPIP_INI_IP, &TCPIP_INI_FRAG_COUNT, iMaxCount));
+
+ if (iNetwork->Interfacer()->FindVar(TCPIP_INI_IP, TCPIP_INI_FRAG_TOTAL, value))
+ iMaxTotal = value;
+ else
+ iMaxTotal = KTcpipIni_FragTotal;
+ LOG(Log::Printf(KFormat, &TCPIP_INI_IP, &TCPIP_INI_FRAG_TOTAL, iMaxTotal));
+ }
+
+//
+// CFragmentHandler::~CFragmentHandler()
+// *************************************
+//
+CFragmentHandler::~CFragmentHandler()
+ {
+ CancelAll();
+ delete iTimeoutManager;
+ }
+
+
+// ******************
+// Reassembly section
+// ******************
+// This a normal extension header receiver hook which is called when the
+// processing of headers reaches the Fragmentation Header.
+//
+TInt CFragmentHandler::Ip6ApplyL(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, const TInet6HeaderFragment &aHdr)
+ {
+ //
+ // Extract fragment information off from the RMBufs
+ // (after this, the fragment header can be disposed as needed)
+ //
+ aInfo.iProtocol = aHdr.NextHeader();
+ const TUint32 fh0 = *(TUint32 *)&aHdr; // needed for ICMP Time Exceeded.
+ const TInt length = aInfo.iLength - aInfo.iOffset - sizeof(TInet6HeaderFragment);
+ const TInt offset = aHdr.FragmentOffset();
+ const TInt last = (aHdr.MFlag() == 0);
+
+ CAssembly *a = LookupL(aInfo, aHdr.Id(), aInfo.iVersion);
+
+ if (length <= 0 || ((length & 0x7) && !last))
+ {
+ // Zero length fragment, or the length of non-last fragment payload is not multiple of 8.
+ // (always reply with ICMPv6, because IPv6 fragment header was used. If the packet was
+ // actually an IPv4 packet, just adjust use different the parameter offset).
+ //
+ LOG(Log::Printf(_L("CFragmentHandler::IP6ApplyL(...): Payload Length=%d"), length));
+ Network()->Icmp6Send(aPacket, KInet6ICMP_ParameterProblem, 0,
+ aInfo.iVersion == 4 ? TInet6HeaderIP4::O_TotalLength : TInet6HeaderIP::O_PayloadLength);
+ Cancel(a);
+ // Note: Icmp6Send takes the packet -- no packet Free required!
+ return -1;
+ }
+ if (offset + length > 65535)
+ {
+ //
+ // This fragment would result too long payload for the final IPv6 packet
+ //
+ // *** above test is not correct! It should also take into account the
+ // length of the unfragmentable part (if that includes extension
+ // headers in addition to IPv6 header). However, this information
+ // is not available until the first fragment is received. Thus, it
+ // is possible that illegal fragments get entered into the fragment
+ // queue! Needs some checking!! -- msa
+ //
+ LOG(Log::Printf(_L("CFragmentHandler::IP6ApplyL(...): offset(%d) + length(%d) > 65535"), offset, length));
+ Network()->Icmp6Send(aPacket, KInet6ICMP_ParameterProblem, 0,
+ aInfo.iOffset + TInet6HeaderFragment::O_FragmentOffset);
+ Cancel(a);
+ // Note: Icmp6Send takes the packet -- no packet Free required!
+ return -1;
+ }
+ if (last)
+ {
+ //
+ // If this is the last fragment, the total length is now known
+ //
+ a->iLength = offset + length;
+ }
+
+ return a->Add(aPacket, sizeof(TInet6HeaderFragment), offset, length, fh0);
+ }
+
+// *******************
+// Fragmenting section
+// *******************
+// When entered, aPacket contains full IPv6 packet, and
+// it has been already tested that this does not fit the
+// Path MTU!
+// If called with a packet that fits the MTU, this generates a
+// single fragment (just inserts the IPv6 fragment header into
+// the packet with offset==0 and M=1)
+//
+void CFragmentHandler::Ip6Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ, TInet6HeaderIP &aHdr)
+ {
+ //
+ // Check extension headers that need to be included into
+ // unfragmentable part
+ // It is somewhat fuzzy what should be included in addition
+ // of Hop-by-hop and Routing header. Currently including
+ // destination options, if they are before the routing header
+ // -- msa
+
+ // unfrag_next
+ // point always to the last next header field of the
+ // unfragmentable part.
+ TUint8 *unfrag_next = ((TUint8 *)&aHdr) + TInet6HeaderIP::O_NextHeader;
+ //
+ // unfrag_size
+ // size of the unfragmentable part.
+ TInt unfrag_size = sizeof(TInet6HeaderIP);
+
+ TInt offset = sizeof(TInet6HeaderIP);
+ for (TInt header = (TInt)*unfrag_next;;)
+ {
+ TInet6Packet<TInet6HeaderExtension> ext(aPacket, offset);
+ if (ext.iHdr == NULL)
+ // If the next header (8 octets) cannot be mapped from
+ // the packet it cannot be a valid extension header anyways.
+ // This search can be terminated with what we have now.
+ break;
+ offset += ext.iHdr->HeaderLength();
+
+ if (header == STATIC_CAST(TInt, KProtocolInet6HopOptions))
+ {
+ // The mapped header is Hop-by-hop options. This is
+ // included into unfragmentable part, but must look
+ // forward if there is a routing header. Just remember
+ // this part
+ unfrag_size = offset;
+ unfrag_next = (TUint8 *)ext.iHdr; // point to Next Hdr field.
+ }
+ else if (header != STATIC_CAST(TInt, KProtocolInet6DestinationOptions))
+ {
+ // Terminate search (we got either unknown extension
+ // or routing header). If the latter, then include it
+ // into unfragmentable part.
+ if (header == STATIC_CAST(TInt, KProtocolInet6RoutingHeader))
+ {
+ unfrag_size = offset;
+ unfrag_next = (TUint8 *)ext.iHdr; // point to Next Hdr field.
+ }
+ break;
+ }
+ // Look for more
+ header = ext.iHdr->NextHeader();
+ }
+ //
+ // The NextHeader of the last header (either IPv6 or an extension
+ // need to be changed into KProtocolInet6Fragment. And, the old
+ // value is placed into next header of every generated fragment
+ // header.
+ TInt next_header = (TInt)*unfrag_next;
+ *unfrag_next = KProtocolInet6Fragment;
+ //
+ // Remove the fragmentable part off the packet, leaving
+ // only the unfragmentable part into the original packet
+ // (and info block!)
+ //
+ RMBufChain fragmentable; // Fragmentable part
+ RMBufSendPacket fragment;
+ RMBufChain tailpart;
+ RMBufPktInfo *info;
+ TInet6Packet<TInet6HeaderFragment> fh;
+ TInt remainder, chunk;
+
+ TInt err = aPacket.Split(unfrag_size, fragmentable);
+ if (err == KErrNone)
+ err = aPacket.Append(sizeof(TInet6HeaderFragment));
+ if (err != KErrNone)
+ goto drop_all; // -- probably out of memory (RMBUF's)
+
+ remainder = aPacket.Info()->iLength - unfrag_size;
+ //
+ // Prepare access for the fragment header
+ //
+ fh.Set(aPacket, unfrag_size, sizeof(TInet6HeaderFragment));
+ unfrag_size += sizeof(TInet6HeaderFragment);
+ if (err != KErrNone || // Memory allocation error, or...
+ fh.iHdr == NULL || // Cannot access fragment header, or..
+ aMtu < (unfrag_size+8))
+ goto drop_all; // Mission impossible! Path MTU is less than
+ // required unfragmentable size + minimal payload!
+ fh.iHdr->ZeroAll();
+ fh.iHdr->SetNextHeader(next_header);
+ fh.iHdr->SetId(++iFragmentId);
+ fh.iHdr->SetMFlag(1);
+ //
+ // Fragment payload size is multiple of 8
+ //
+ chunk = (aMtu - unfrag_size) & ~0x7;
+ offset = 0;
+ //
+ // Patch in fragment payload size into IPv6 header, this will
+ // be the same for all but last fragment, so it can be set outside
+ // the loop...
+ // (making a big assumption that the aHdr pointer is still valid!!
+ // the current operations done to the aPacket are safe, but beware
+ // when changing this code...) -- msa
+ //
+ aHdr.SetPayloadLength(unfrag_size + chunk - sizeof(TInet6HeaderIP));
+ while (remainder > chunk)
+ {
+ //
+ // Setup unfragmentable part
+ //
+ TRAP(err,
+ aPacket.CopyL(fragment);
+ aPacket.CopyInfoL(fragment);
+ );
+ if (err != KErrNone)
+ goto drop_all;
+ info = fragment.Info();
+ info->iLength = chunk + unfrag_size;
+ //
+ // Extract next fragment payload
+ //
+ err = fragmentable.Split(chunk, tailpart);
+ if (err != KErrNone)
+ goto drop_all;
+ fragment.Append(fragmentable);
+ fragment.Pack();
+ aFragQ.Append(fragment);
+ fragmentable.Assign(tailpart);
+ offset += chunk;
+ remainder -= chunk;
+ fh.iHdr->SetFragmentOffset(offset);
+ }
+ //
+ // Make aPacket as the last fragment
+ //
+ fh.iHdr->SetMFlag(0);
+ aHdr.SetPayloadLength(unfrag_size + remainder - sizeof(TInet6HeaderIP));
+ aPacket.Append(fragmentable);
+ info = aPacket.Info();
+ info->iLength = unfrag_size + remainder;
+ aPacket.Pack();
+ aFragQ.Append(aPacket);
+ return;
+
+drop_all:
+ aFragQ.Free();
+ fragmentable.Free();
+ fragment.Free();
+ tailpart.Free();
+ aPacket.Free();
+ LOG(Log::Printf(_L("CFragmentHandler::Ip6Fragment(...) FAILED")));
+ }
+
+
+// **********************
+// IPv4 Fragment handling
+// **********************
+
+// ******************
+// Reassembly section
+// ******************
+// This a called when an IPv4 fragment is received. On entry
+// aHead.ip4 already contains a full copy of the IPv4 header
+// and aHead.iOffset == ip4.HeaderLength()
+//
+// *NOTE*
+// On return, the IPv4 header checksum is not recomputed
+// (should it? maybe for possible ICMP error message? -- msa)
+//
+TInt CFragmentHandler::Ip4ApplyL(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, const TInet6HeaderIP4 &aHdr)
+ {
+ //
+ // Extract fragment information off from the RMBufs
+ // (after this, the fragment header can be disposed as needed)
+ //
+ aInfo.iProtocol = aHdr.Protocol(); // Put the protocol into info
+ const TInt length = aInfo.iLength - aInfo.iOffset; // Actual payload length
+ const TInt offset = (aHdr.FragmentOffset()) << 3;
+ const TInt last = (aHdr.MF() == 0);
+ //
+ // Locate or create an assemble matching this fragment
+ //
+ CAssembly *a = LookupL(aInfo, aHdr.Identification() | (aInfo.iProtocol << 16), 4);
+
+ if (length <= 0 || ((length & 0x7) && !last))
+ {
+ // Zero length fragment or,
+ // the length of non-last fragment payload is not multiple of 8.
+ //
+ Network()->Icmp4Send(aPacket, KInet4ICMP_ParameterProblem, 0,
+ TInet6HeaderIP4::O_TotalLength);
+ Cancel(a);
+ // Note: Icmp4Send takes the packet -- no packet Free required!
+ return -1;
+ }
+ if (offset + length > 65535 - TInet6HeaderIP4::MinHeaderLength())
+ {
+ //
+ // This fragment would result too long payload for the final IPv4 packet
+ // (NOTE: above is not full proof, if the first fragment has options!
+ // The intent is just to drop obviously wrong fragments before doing
+ // anything more with them... -- msa)
+ //
+ Network()->Icmp4Send(aPacket, KInet4ICMP_ParameterProblem, 0,
+ TInet6HeaderIP4::O_FragmentOffset);
+ Cancel(a);
+ // Note: Icmp4Send takes the packet -- no packet Free required!
+ return -1;
+ }
+
+ if (last)
+ a->iLength = offset + length; // Last fragment, real payload length is now known
+
+ return a->Add(aPacket, 0, offset, length);
+ }
+
+//
+// CrunchOptions
+// -------------
+// Only options marked as "copy" are copied into every fragment header.
+//
+// This function takes options from the original packet and removes
+// all except the copiable options (process in place).
+//
+// Returns the length of the "crunched" IPv4 header
+//
+static TInt CrunchOptions(TInet6HeaderIP4 &aHdr)
+ {
+ TInt length = TInet6HeaderIP4::MinHeaderLength();
+ TUint8 *opt = ((TUint8 *)&aHdr) + length;
+ TUint8 *dst = opt;
+ TInt count = aHdr.HeaderLength() - length;
+ while (count > 0)
+ {
+ if (*opt < 2)
+ {
+ // Q: Is it legal to have "Copy" on PAD? If so, this
+ // needs some fixing yet! -- msa
+ //
+ ++opt; // END/PAD bytes, skip
+ count -= 1;
+ }
+ else
+ {
+ int optlen = opt[1];
+ if (optlen > count || optlen < 2)
+ // An illegal option length, just bail out.
+ // (The packet is broken anyway)
+ break;
+ count -= optlen;
+ if (*opt & 0x80)
+ {
+ //
+ // Copy the option
+ //
+ length += optlen;
+ while (--optlen >= 0)
+ *dst++ = *opt++;
+ }
+ else
+ //
+ // Crunch this
+ //
+ opt += optlen;
+ }
+ }
+ //
+ // The IPv4 header length is multiple of 4, and thus options length
+ // must be padded, if not already aligned.. (Pad with END marker)
+ //
+ while (length & 0x3)
+ {
+ *dst++ = 0; // EMD
+ length++;
+ }
+ aHdr.SetHeaderLength(length);
+ return length;
+ }
+
+// *******************
+// Fragmenting section
+// *******************
+// When entered, aPacket contains full IPv4 packet, and
+// it has been already tested that this does not fit the
+// Path MTU!
+void CFragmentHandler::Ip4Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ, TInet6HeaderIP4 &aHdr)
+ {
+ RMBufPacketBase fragmentable; // Fragmentable part
+ RMBufChain tailpart; // Just work space
+ TInt hlen, chunk, remainder, offset;
+ TInet6HeaderIP4 ip4;
+ TPtr8 ip4ptr((TUint8 *)&ip4, sizeof(ip4), sizeof(ip4));
+ TInt err = KErrNone;
+
+ offset = aHdr.FragmentOffset() << 3;
+ hlen = aHdr.HeaderLength();
+
+ chunk = (aMtu - hlen) & ~7;
+ if (chunk <= 0 || aHdr.DF())
+ goto drop_all; // Unreasonable Mtu... or fragmenting not allowed
+ //
+ // Split off the first fragment...
+ //
+ err = aPacket.Split(hlen + chunk, fragmentable);
+ if (err != KErrNone)
+ goto drop_all;
+ aHdr.SetTotalLength(hlen + chunk);
+ offset += chunk;
+ remainder = aPacket.Info()->iLength - hlen - chunk;
+ aPacket.Info()->iLength = hlen + chunk;
+ //
+ // Setup a fragment header including the options that
+ // need to be copied for all the remaining fragments
+ //
+ ip4ptr = TPtrC8((TUint8 *)&aHdr, hlen);
+ hlen = CrunchOptions(ip4);
+ ip4ptr.SetLength(hlen);
+ //
+ // Generate "middle fragments". All of these will
+ // have the more (MF) as 1.
+ //
+ chunk = (aMtu - hlen) & ~7;
+ ip4.SetTotalLength(hlen + chunk);
+ ip4.SetFlags(KInet4IP_MF);
+ while (remainder > chunk)
+ {
+ //
+ // Extract next fragment payload
+ //
+ err = fragmentable.Split(chunk, tailpart);
+ if (err == KErrNone)
+ err = fragmentable.Prepend(hlen);
+ if (err != KErrNone)
+ goto drop_all;
+ ip4.SetFragmentOffset((TUint16)(offset >> 3));
+ ip4.SetChecksum(0);
+ ip4.SetChecksum(TChecksum::ComplementedFold(TChecksum::Calculate((TUint16*)&ip4, hlen)));
+ fragmentable.CopyIn(ip4ptr);
+ TRAP(err, aPacket.CopyInfoL(fragmentable));
+ if (err != KErrNone)
+ goto drop_all;
+ fragmentable.Info()->iLength = chunk + hlen;
+ fragmentable.Pack();
+ aFragQ.Append(fragmentable);
+ fragmentable.Assign(tailpart);
+ offset += chunk;
+ remainder -= chunk;
+ }
+ //
+ // Generate the last fragment
+ //
+ err = fragmentable.Prepend(hlen);
+ if (err != KErrNone)
+ goto drop_all;
+ ip4.SetFragmentOffset((TUint16)(offset >> 3));
+ ip4.SetTotalLength(hlen + remainder);
+ ip4.SetFlags((TUint8)(aHdr.Flags())); // need to copy More flag from the original packet!
+ ip4.SetChecksum(0);
+ ip4.SetChecksum(TChecksum::ComplementedFold(TChecksum::Calculate((TUint16*)&ip4, hlen)));
+ fragmentable.CopyIn(ip4ptr);
+ TRAP(err, aPacket.CopyInfoL(fragmentable));
+ if (err != KErrNone)
+ goto drop_all;
+ fragmentable.Info()->iLength = hlen + remainder;
+ fragmentable.Pack();
+ aFragQ.Append(fragmentable);
+
+ //
+ // Complete the header of the first fragment
+ //
+ aHdr.SetFlags(KInet4IP_MF);
+
+ aHdr.SetChecksum(0);
+ aHdr.SetChecksum(TChecksum::ComplementedFold(TChecksum::Calculate((TUint16*)&aHdr, aHdr.HeaderLength())));
+ aPacket.Pack();
+ aFragQ.Prepend(aPacket);
+ //
+ // All fragments successfully built.
+ //
+ return;
+ //
+ // Cleanup everything
+ //
+drop_all:
+ aFragQ.Free();
+ fragmentable.Free();
+ tailpart.Free();
+ aPacket.Free();
+ LOG(Log::Printf(_L("CFragmentHandler::Ip4Fragment(...) FAILED")));
+ }