networkprotocols/tcpipv4v6prt/src/ip6_frag.cpp
changeset 0 af10295192d8
--- /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")));
+	}