networkprotocols/tcpipv4v6prt/src/ip6_frag.cpp
changeset 0 af10295192d8
equal deleted inserted replaced
-1:000000000000 0:af10295192d8
       
     1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 // ip6_frag.cpp - hook for IPv6 fragment header
       
    15 //
       
    16 
       
    17 #include "frag.h"
       
    18 #include <icmp6_hdr.h>	// for ICMP Type symbols
       
    19 #include <ext_hdr.h>	// for IPv6 fragment header
       
    20 #include "ip6_frag.h"
       
    21 #include <in_pkt.h>
       
    22 #include <in_chk.h>
       
    23 #include <timeout.h>
       
    24 #include "tcpip_ini.h"
       
    25 #include "inet6log.h"
       
    26 
       
    27 //	TIp6FragmentHeader
       
    28 //	******************
       
    29 //	This header is at the beginning of each fragment in the
       
    30 //	fragment queue. This attempts to be the same size as
       
    31 //	the real IPv6 fragment header (8 octets), but the code
       
    32 //	should work with any size of this [use of this specific
       
    33 //	size avoids some TrimStart() calls in some cases.]
       
    34 //
       
    35 class TIp6FragmentHeader
       
    36 	{
       
    37 public:
       
    38 	TUint iOffset;		// Offset of this fragment (bytes)
       
    39 	TUint iLength;		// Lenght of this fragment (bytes)
       
    40 	};
       
    41 
       
    42 //
       
    43 //	RIp6Fragment
       
    44 //	************
       
    45 //	The IP6 fragment data structure is an RMBufChain containing
       
    46 //	- TIp6FragmentHeader
       
    47 //	- followed by fragment content
       
    48 //	This is the interface to the RMBufFragQ class, a collection
       
    49 //	of required methods for accessing the fragment information and
       
    50 //	a join operation.
       
    51 //
       
    52 class RIp6Fragment : public RMBufFrag
       
    53 	{
       
    54 public:
       
    55 	// Internal utility to the other methods. Assumes that the fragment
       
    56 	// start is properly aligned to be cast into class pointer.
       
    57 	inline TIp6FragmentHeader *Header() const { return (TIp6FragmentHeader *)(First()->Ptr()); }
       
    58 	// Return offset of the fragment (bytes)
       
    59 	TUint Offset() const {return Header()->iOffset;}
       
    60 	// Return length of the fragment (bytes)
       
    61 	// (Does not include the fragment header, using
       
    62 	// this->Length() will give larger value that
       
    63 	// includes the header)
       
    64 	TUint FragmentLength() const {return Header()->iLength;}
       
    65 	// Join another fragment to this one
       
    66 	void Join(RIp6Fragment& aFrag)
       
    67 		{
       
    68 		TInt overlap = Offset() + FragmentLength() - aFrag.Offset();
       
    69 		if (overlap < 0)
       
    70 			{
       
    71 			aFrag.Free();	// Should panic? This should not happen!
       
    72 			User::Panic(_L("DEBUG"), 0);
       
    73 			}
       
    74 		else
       
    75 			{
       
    76 			Header()->iLength += aFrag.FragmentLength() - overlap;
       
    77 			aFrag.TrimStart(sizeof(TIp6FragmentHeader) + overlap);
       
    78 			Append(aFrag);
       
    79 			}
       
    80 		}
       
    81 	};
       
    82 
       
    83 
       
    84 //
       
    85 //	CFragmentHandler
       
    86 //	****************
       
    87 //
       
    88 class CAssembly;
       
    89 class CFragmentHandler : public CFragmentHeaderHook
       
    90 	{
       
    91 public:
       
    92 	CFragmentHandler(MNetworkService *aNetwork) : CFragmentHeaderHook(aNetwork) {}
       
    93 	~CFragmentHandler();
       
    94 	inline MNetworkService *Network() { return iNetwork; }
       
    95 	CAssembly *LookupL(const RMBufRecvInfo &aInfo, TUint32 aId, TUint aVersion);
       
    96 
       
    97 	void ConstructL();
       
    98 	void Cancel(CAssembly *aAssembly);
       
    99 	void CancelAll();
       
   100 	TInt ApplyL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo);
       
   101 	void Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ);
       
   102 
       
   103 	MTimeoutManager *iTimeoutManager;
       
   104 	CAssembly *iCache;
       
   105 	//
       
   106 	TInt iCount;	// Current incomplete assemblies
       
   107 	TInt iMaxCount;	// Maximum number of assemblies
       
   108 	TInt iTotal;	// Total amount of RMBuf space allocated to assemblies (bytes)
       
   109 	TInt iMaxTotal;	// Maximum allowed amount of RMBuf space
       
   110 private:
       
   111 	TInt Ip6ApplyL(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, const TInet6HeaderFragment &aHdr);
       
   112 	TInt Ip4ApplyL(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, const TInet6HeaderIP4 &aHdr);
       
   113 	void Ip6Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ, TInet6HeaderIP &aHdr);
       
   114 	void Ip4Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ, TInet6HeaderIP4 &aHdr);
       
   115 
       
   116 	//
       
   117 	// iFagmentId is only used for IPv6. For IPv4, the Id is already in
       
   118 	// in each IP packet.
       
   119 	//
       
   120 	TInt iFragmentId;
       
   121 	};
       
   122 
       
   123 //
       
   124 //	CAssembly
       
   125 //	*********
       
   126 //	The data structure for a partially assembled packet
       
   127 //
       
   128 class CAssembly : public CBase
       
   129 	{
       
   130 	friend class CFragmentHandler;
       
   131 	friend class CFragmentLinkage;
       
   132 
       
   133 	CAssembly(CFragmentHandler &aHandler, const TIp6Addr &aSrc, const TIp6Addr &aDst, TInt aId, TInt aVersion);
       
   134 	~CAssembly();
       
   135 
       
   136 	TInt AddFragmentL(RMBufRecvPacket &aPacket, TInt aTrim, TInt aLength, TInt aOffset, TUint32 aFh0);
       
   137 	TInt Add(RMBufRecvPacket &aPacket, TInt aTrim, TInt aLength, TInt aOffset, TUint32 aFh0 = 0);
       
   138 	TInt CompletePacket(TInt aRestoreFH = 0);
       
   139 	void Timeout();
       
   140 public:
       
   141 	//
       
   142 	// Linkage and management
       
   143 	//
       
   144 	CFragmentHandler &iHandler;
       
   145 	CAssembly *iNext;		// Linkage of the assemblies in the cache
       
   146 
       
   147 	RTimeout iTimeout;		// Timer hook
       
   148 #ifndef _LOG
       
   149 protected:
       
   150 #endif
       
   151 	const TUint8 iVersion;	// Need to have, because TAHI wants FH in IPv6 TimeExceeded ICMP
       
   152 	TUint iIsDead:1;		// =1, if this assembly is "dead" (matching fragments are dropped)
       
   153 	const TUint32 iId;		// Fragment identification
       
   154 	const TIp6Addr iSrcAddr;
       
   155 	const TIp6Addr iDstAddr;
       
   156 
       
   157 	TUint32 iFH0;			// Saved first 32 bits of the first FH (for TAHI)
       
   158 
       
   159 	TInt32 iLength;			// Total length of the fragmentable part, when
       
   160 							// known (e.g. last fragment received).
       
   161 							// [As 32 bit int is used, there shouuld be no
       
   162 							// overflow or wrap around problems because
       
   163 							// offset field is only 16 bits (in bytes)]
       
   164 	TInt iTotal;			// The amount of "iTotal" from this assembly.
       
   165 	//
       
   166 	// The unfragmentable part of the first fragment (if received)
       
   167 	//
       
   168 	RMBufRecvPacket iHead;
       
   169 	RMBufFragQ<RIp6Fragment> iQueue;
       
   170 	};
       
   171 
       
   172 //
       
   173 //	CFragmentLinkage
       
   174 //	****************
       
   175 //	Glue to bind timeout callback from the timeout manager into Timeout() call
       
   176 //	on the CAssembly.
       
   177 //
       
   178 //	*NOTE*
       
   179 //		This kludgery is all static and compile time, and only used in the constructor
       
   180 //		of CFragmentAssembly following this.
       
   181 //
       
   182 
       
   183 // This ungainly manoevure is forced on us because the offset is not evaluated early enough by GCC3.4 to be
       
   184 // passed as a template parameter
       
   185 #if defined(__X86GCC__) || defined(__GCCE__)
       
   186 #define KAssemblyTimeoutOffset 12
       
   187 __ASSERT_COMPILE(KAssemblyTimeoutOffset == _FOFF(CAssembly, iTimeout));
       
   188 #else
       
   189 #define KAssemblyTimeoutOffset _FOFF(CAssembly, iTimeout)
       
   190 #endif
       
   191 
       
   192 class CFragmentLinkage : public TimeoutLinkage<CAssembly, KAssemblyTimeoutOffset>
       
   193 	{
       
   194 public:
       
   195 	static void Timeout(RTimeout &aLink, const TTime & /*aNow*/, TAny * /*aPtr*/)
       
   196 		{
       
   197 		Object(aLink)->Timeout();
       
   198 		}
       
   199 	};
       
   200 
       
   201 #ifdef _LOG
       
   202 //
       
   203 //	LogPrintId
       
   204 //	**********
       
   205 //	Purely for LOG output, should not be compiled in the final release
       
   206 //
       
   207 static void LogPrintId(const TDesC &aText, const CAssembly &a)
       
   208 	{
       
   209 	TInetAddr src, dst;
       
   210 	TBuf<70> sbuf;
       
   211 	TBuf<70> dbuf;
       
   212 
       
   213 	src.SetAddress(a.iSrcAddr);
       
   214 	dst.SetAddress(a.iDstAddr);
       
   215 
       
   216 	src.OutputWithScope(sbuf);
       
   217 	dst.OutputWithScope(dbuf);
       
   218 	Log::Printf(_L("%S [src=%S dst=%S id=%d] (%d)"), &aText, &sbuf, &dbuf, a.iId, a.iHandler.iCount);
       
   219 	}
       
   220 #endif
       
   221 
       
   222 //
       
   223 //	CFragmentHandler::Cancel
       
   224 //	************************
       
   225 //	Cancel a specific assembly (delete it)
       
   226 //
       
   227 void CFragmentHandler::Cancel(CAssembly *aAssembly)
       
   228 	{
       
   229 	for (CAssembly **h = &iCache; *h != NULL; h = &(*h)->iNext)
       
   230 		if (aAssembly == *h)
       
   231 			{
       
   232 			*h = aAssembly->iNext;
       
   233 			delete aAssembly;
       
   234 			break;
       
   235 			}
       
   236 	// Should panic, if a was not found from the list! -- msa
       
   237 	}
       
   238 
       
   239 //
       
   240 //	CFragmentHandler::CancelAll
       
   241 //	***************************
       
   242 //	Cancel all packets waiting for assembly (delete all)
       
   243 //
       
   244 void CFragmentHandler::CancelAll()
       
   245 	{
       
   246 	CAssembly *a;
       
   247 	while ((a = iCache) != NULL)
       
   248 		{
       
   249 		iCache = a->iNext;
       
   250 		delete a;
       
   251 		}
       
   252 	}
       
   253 
       
   254 //
       
   255 //	ApplyL
       
   256 //	******
       
   257 //	Called when an IP packet contain IPv6 fragment header.
       
   258 //	(does not care whether outer IP header is v4 or v6, both work!)
       
   259 //
       
   260 TInt CFragmentHandler::ApplyL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo)
       
   261 	{
       
   262 	for (;;)	// *NOT REAL LOOP, JUST A CONSTRUCT TO ENABLE USE OF 'break!
       
   263 		{
       
   264 		if (aInfo.iProtocol != STATIC_CAST(TInt, KProtocolInet6Fragment))
       
   265 			break;	// Incorrect call, should only get IPv6 fragments here!
       
   266 		TInet6Packet<TInet6HeaderFragment> fh(aPacket, aInfo.iOffset);
       
   267 		if (fh.iHdr == NULL)
       
   268 			break;	// Drop! (packet too short)
       
   269 
       
   270 		if (aInfo.iIcmp == 0)
       
   271 			return Ip6ApplyL(aPacket, aInfo, *fh.iHdr);
       
   272 
       
   273 		if (aInfo.iIcmp != KProtocolInet6Icmp)
       
   274 			break;	// Only IPv6 complaints should get this far, drop!
       
   275 
       
   276 		const TInt offset = aInfo.iOffset - aInfo.iOffsetIp;	// Relative offset within problem packet
       
   277 		if (aInfo.iType == KInet6ICMP_ParameterProblem &&	// A parameter problem...
       
   278 			offset <= (TInt)aInfo.iParameter &&				// after start of this header?
       
   279 			STATIC_CAST(TUint, offset + sizeof(TInet6HeaderFragment)) > STATIC_CAST(TUint, aInfo.iParameter))		// and before end of this header?
       
   280 				break;		// Drop! (someone doesn't like my fragments!)
       
   281 		//
       
   282 		// Error is not Fragment Header specific, pass it on in the chain
       
   283 		// Skip over header, pass error processing to the next header
       
   284 		aInfo.iPrevNextHdr = (TUint16)aInfo.iOffset;	// Fragment next header is at +0
       
   285 		aInfo.iProtocol = fh.iHdr->NextHeader();
       
   286 		aInfo.iOffset += sizeof(TInet6HeaderFragment);
       
   287 		return KIp6Hook_DONE;
       
   288 		// WAS NOT LOOP, BE SURE TO EXIT IT ALWAYS!
       
   289 		}
       
   290 	aPacket.Free();
       
   291 	return -1;
       
   292 	}
       
   293 
       
   294 //
       
   295 //	Fragment
       
   296 //	********
       
   297 //	Called when an outgoing IP packet needs to be fragmented
       
   298 //
       
   299 void CFragmentHandler::Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ)
       
   300 	{
       
   301 	//
       
   302 	// Decide on which fragmenting from the IP header.
       
   303 	//
       
   304 	TIpHeader *ip = ((RMBufPacketPeek &)aPacket).GetIpHeader();
       
   305 	if (ip)
       
   306 		if (ip->ip4.Version() == 4)
       
   307 			Ip4Fragment(aPacket, aMtu, aFragQ, ip->ip4);
       
   308 		else if (ip->ip4.Version() == 6)
       
   309 			Ip6Fragment(aPacket, aMtu, aFragQ, ip->ip6);
       
   310 	}
       
   311 
       
   312 
       
   313 //	CAssembly
       
   314 //	*********
       
   315 //
       
   316 CAssembly::CAssembly(CFragmentHandler &aHandler, const TIp6Addr &aSrc, const TIp6Addr &aDst, TInt aId, TInt aVersion)
       
   317  : iHandler(aHandler), iTimeout(CFragmentLinkage::Timeout), iVersion((TUint8)aVersion), iId(aId),
       
   318 	iSrcAddr(aSrc), iDstAddr(aDst), iLength(65536)
       
   319 	{
       
   320 	}
       
   321 
       
   322 //	CAssembly::~CAssebly()
       
   323 //	**********************
       
   324 //
       
   325 CAssembly::~CAssembly()
       
   326 	{
       
   327 	iHandler.iTotal -= iTotal;
       
   328 	--iHandler.iCount;
       
   329 
       
   330 	LOG(LogPrintId(_L("CAssebly::~CAssebly():"), *this));
       
   331 
       
   332 	iTimeout.Cancel();
       
   333 	iQueue.Free();
       
   334 	iHead.Free();
       
   335 	}
       
   336 
       
   337 
       
   338 LOCAL_C TInt CompareStructures()
       
   339 	{
       
   340 	return sizeof(TIp6FragmentHeader) - sizeof(TInet6HeaderFragment);
       
   341 	}
       
   342 //
       
   343 //	CAssembly::CompletePacket
       
   344 //	*************************
       
   345 //	Merge the iHead and first fragment into a complete packet
       
   346 //	(including the IPv4/IPv6 header stuff!)
       
   347 //
       
   348 //	Returns,
       
   349 //	== KErrNone, if packet in iHead built
       
   350 //	!= KErrNone, if no packet available
       
   351 //
       
   352 //	*NOTE*
       
   353 //		this is also used when reassembly timeout hits, thus the
       
   354 //		"complete" can also be an incomplete packet (including
       
   355 //		only the first fragment).
       
   356 //
       
   357 TInt CAssembly::CompletePacket(TInt aRestoreFH)
       
   358 	{
       
   359 	if (iHead.IsEmpty())
       
   360 		return -1;
       
   361 
       
   362 	RMBufRecvInfo *const info = iHead.Info();
       
   363 
       
   364 	RIp6Fragment first;
       
   365 	iQueue.Remove(first);
       
   366 
       
   367 	TInt length = first.Header()->iLength;
       
   368 	ASSERT(first.Header()->iOffset == 0);
       
   369 
       
   370 	if (aRestoreFH)
       
   371 		{
       
   372 		// *********************************************************
       
   373 		// TAHI tester wants the returned ICMP to include the FH on
       
   374 		// time exceeded message. This is for that...
       
   375 		// *********************************************************
       
   376 		const TInt adjust = CompareStructures();
       
   377 		// As the value of adjust is known by compile time, compiler should eliminate
       
   378 		// unnecessary code below (although, it may give a warnings about constants
       
   379 		// being compared--this is indication that code elimination should work!)
       
   380 		if (adjust > 0)
       
   381 			first.TrimStart(adjust);
       
   382 		else if (adjust < 0)
       
   383 			{
       
   384 			TInt err = first.Prepend(-adjust);
       
   385 			if (err!=KErrNone)
       
   386 				{
       
   387 				first.Free();
       
   388 				return -1;
       
   389 				}
       
   390 			}
       
   391 		TInet6HeaderFragment fake_fh;
       
   392 		*(TUint32 *)&fake_fh = iFH0;	// Saved 1st word of original FH
       
   393 		fake_fh.SetId(iId);				// ..and this SetId cover all of FH
       
   394 										// (there is no need to zero anything else)
       
   395 		first.CopyIn(TPtrC8((TUint8 *)&fake_fh, sizeof(TInet6HeaderFragment)), 0);
       
   396 		// .. ugh, KProtocolInet6Fragment is TInt, casting to TUint8* might
       
   397 		// cause "endian problems", thus this copy to true TUint8 variable... -- msa
       
   398 		const TUint8 proto = KProtocolInet6Fragment;
       
   399 		iHead.CopyIn(TPtrC8((TUint8 *)&proto, 1), info->iPrevNextHdr);
       
   400 		length += sizeof(TInet6HeaderFragment);
       
   401 		iLength = length;
       
   402 		}
       
   403 	else
       
   404 		first.TrimStart(sizeof(TIp6FragmentHeader));
       
   405 	if (length > iLength)
       
   406 		{
       
   407 		//
       
   408 		// Just to deal with some overlapping fragment, which extends
       
   409 		// beyond the last fragment... (iLength is initialized to
       
   410 		// 65536 and is only less, when last fragment has arrived!)
       
   411 		length = iLength;
       
   412 		first.TrimEnd(length);
       
   413 		}
       
   414 
       
   415 	iHead.Append(first);
       
   416 	//
       
   417 	// Fix info and IP header
       
   418 	//
       
   419 	//
       
   420 	// Only the iLength needs to be updated, all other fields in the info
       
   421 	// must already have the correct values!
       
   422 	//
       
   423 	info->iLength = info->iOffset + length;
       
   424 
       
   425 	TInet6Packet<TIpHeader> ip(iHead, info->iOffsetIp);
       
   426 	if (ip.iHdr)
       
   427 		{
       
   428 		TInt ver = ip.iHdr->ip4.Version();
       
   429 		if (ver == 4)
       
   430 			{
       
   431 			if (ip.iLength >= ip.iHdr->ip4.HeaderLength() &&
       
   432 				info->iLength < 65536)
       
   433 				{
       
   434 				// ..just to be tidy, make sure M=0 (nobody really cares) -- msa
       
   435 				ip.iHdr->ip4.SetFlags((TUint8)(ip.iHdr->ip4.Flags() & ~KInet4IP_MF));
       
   436 				ip.iHdr->ip4.SetTotalLength(info->iLength);
       
   437 				return KErrNone;
       
   438 				}
       
   439 			}
       
   440 		else if (ver == 6)
       
   441 			{
       
   442 			if (ip.iLength >= TInet6HeaderIP::MinHeaderLength() &&
       
   443 				  STATIC_CAST(TUint, info->iLength) < STATIC_CAST(TUint, 65536 + sizeof(TInet6HeaderIP)))
       
   444 				{
       
   445 				ip.iHdr->ip6.SetPayloadLength(info->iLength - sizeof(TInet6HeaderIP));
       
   446 				return KErrNone;
       
   447 				}
       
   448 			}
       
   449 		}
       
   450 	return -1;
       
   451 	}
       
   452 
       
   453 //
       
   454 //	CAssembly::Add
       
   455 //	**********************
       
   456 //
       
   457 TInt CAssembly::Add(RMBufRecvPacket &aPacket, TInt aTrim, TInt aOffset, TInt aLength, TUint32 aFH0)
       
   458 	{
       
   459 		if (iIsDead == 0)
       
   460 			{
       
   461 			TInt ret = 0; // [ = 0, only to silence compiler]
       
   462 			TRAPD(err, ret = AddFragmentL(aPacket, aTrim, aOffset, aLength, aFH0));
       
   463 			if (err == KErrNone)
       
   464 				return ret;
       
   465 			//
       
   466 			// AddFragmentL left, change the Assembly into "dead" state
       
   467 			// (and make sure all unnecessary resources are released)
       
   468 			//
       
   469 			iIsDead = 1;
       
   470 			iHead.Free();
       
   471 			iQueue.Free();
       
   472 			iHandler.iTotal -= iTotal;
       
   473 			iTotal = 0;
       
   474 			}
       
   475 		ASSERT(iTotal == 0);
       
   476 		//
       
   477 		// The hook return code = -1 (=> packet handled and released)
       
   478 		//
       
   479 		aPacket.Free();
       
   480 		return -1;
       
   481 	}
       
   482 
       
   483 
       
   484 //	CAssembly::AddFragmentL
       
   485 //	***********************
       
   486 //  This should only be called from Add, which must trap the leaves and 
       
   487 //
       
   488 //	returns
       
   489 //		standard inbound hook return codes
       
   490 //	leaves
       
   491 //		when assembly should be declared "dead" (seed "Add")
       
   492 //
       
   493 TInt CAssembly::AddFragmentL(RMBufRecvPacket &aPacket, TInt aTrim, TInt aOffset, TInt aLength, TUint32 aFH0)
       
   494 	{
       
   495 	RMBufRecvInfo *info = aPacket.Info();
       
   496 	if (aOffset == 0)
       
   497 		{
       
   498 		//
       
   499 		// Processing the first fragment (offset == 0!)
       
   500 		//
       
   501 
       
   502 		if (!iHead.IsEmpty())
       
   503 			aTrim += info->iOffset;
       
   504 		else
       
   505 			{
       
   506 			const TInt unfrag = info->iOffset;	// = Unfragmentable length (bytes)
       
   507 			iTotal += unfrag;
       
   508 			iHandler.iTotal += unfrag;
       
   509 
       
   510 			iHead.Assign(aPacket);
       
   511 			iHead.SplitL(unfrag, aPacket);
       
   512 			iHead.SetInfo(info);
       
   513 			aPacket.SetInfo(NULL);
       
   514 			//
       
   515 			// Patch in the next header field
       
   516 			//
       
   517 			iHead.CopyIn(TPtrC8((TUint8 *)&info->iProtocol, 1), info->iPrevNextHdr);
       
   518 			//
       
   519 			// This fragment is assigned as the first fragment. Save the FH0 for it.
       
   520 			// (if there are multiple "first fragments", the first received is used)
       
   521 			// (Only needed for IPv6 and to generate TAHI-compatible ICMP Time Exceeded)
       
   522 			// *NOTE* The full first word is saved because, the reserved bits must be
       
   523 			// kept also, if someone is using them -- msa
       
   524 			iFH0 = aFH0;
       
   525 			}
       
   526 		}
       
   527 	else
       
   528 		aTrim += info->iOffset;
       
   529 	//
       
   530 	// Convert the header in the packet (if present) into
       
   531 	// internal fragment header
       
   532 	//
       
   533 	aTrim -= sizeof(TIp6FragmentHeader);
       
   534 	if (aTrim > 0)
       
   535 		// Cast needed to avoid TrimStart() looking for info block!!!
       
   536 		((RMBufChain &)aPacket).TrimStart(aTrim);
       
   537 	else if (aTrim < 0)
       
   538 		aPacket.PrependL(-aTrim);
       
   539 	// Make sure the fragment header is properly aligned in the First() buffer.
       
   540 	aPacket.Align(sizeof(TIp6FragmentHeader));
       
   541 	TIp6FragmentHeader *frag = ((RIp6Fragment &)aPacket).Header();
       
   542 	frag->iLength = aLength;
       
   543 	frag->iOffset = aOffset;
       
   544 	//
       
   545 	// Add fragment to the queue. This assumes that the RMBufChain
       
   546 	// is removed from the aPacket (IsEmpty() becomes True).
       
   547 	//
       
   548 	iQueue.Add((RIp6Fragment &)aPacket);
       
   549 	ASSERT(aPacket.IsEmpty());
       
   550 
       
   551 	if (iQueue.First().FragmentLength() >= (TUint)iLength)
       
   552 		{
       
   553 #ifdef _LOG
       
   554 		TBuf<80> buf;
       
   555 		buf.Format(_L("CAssembly::AddFragmentL(): [%d..%d (%d)] COMPLETED"), aOffset, aOffset+aLength-1, aLength);
       
   556 		LogPrintId(buf, *this);
       
   557 #endif
       
   558 		// Packet has been fully assembled, restore the
       
   559 		// complete packet into aPacket
       
   560 		//
       
   561 		if (CompletePacket() == KErrNone)
       
   562 			{
       
   563 			aPacket.Assign(iHead);
       
   564 			if (info != iHead.Info())
       
   565 				{
       
   566 				//
       
   567 				// Need to copy the Information from the iHead
       
   568 				// (can't just do "*info = *iHead.Info()", because
       
   569 				// that would copy the RMBuf base class parts too!!
       
   570 				//
       
   571 				// ...this looks a bit scary, does this always work? Should
       
   572 				// look for a cleaner way to do this? -- msa
       
   573 				const TInt off = sizeof(RMBufCell);
       
   574 				const TInt len = sizeof(*info) - off;
       
   575 				TPtr8((TUint8 *)info + off, len).Copy(TPtrC8((TUint8 *)iHead.Info() + off, len));
       
   576 				}
       
   577 			else
       
   578 				{
       
   579 				//
       
   580 				// This only happens when the only fragment is full packet!
       
   581 				//
       
   582 				iHead.SetInfo(NULL);
       
   583 				aPacket.SetInfo(info);
       
   584 				}
       
   585 			iHandler.Cancel(this);		// Deletes this!
       
   586 			return KIp6Hook_DONE;
       
   587 			}
       
   588 		aPacket.Free();					// (mainly for info block!)
       
   589 		iHandler.Cancel(this);			// Deletes this!
       
   590 		return -1;
       
   591 		}
       
   592 	else
       
   593 		{
       
   594 		aPacket.FreeInfo();
       
   595 #ifdef _LOG
       
   596 		TBuf<80> buf;
       
   597 		buf.Format(_L("CAssembly::AddFragmentL(): [%d..%d (%d)]"), aOffset, aOffset+aLength-1, aLength);
       
   598 		LogPrintId(buf, *this);
       
   599 #endif
       
   600 		// Maintain total number of allocated bytes (roughly, this "overestimates"
       
   601 		// if fragments overlap!).
       
   602 		//
       
   603 		iTotal += aLength;
       
   604 		iHandler.iTotal += aLength;
       
   605 		if (iHandler.iTotal > iHandler.iMaxTotal)
       
   606 			{
       
   607 #ifdef _LOG
       
   608 			// ...borrow the 'buf' from above LOG block!
       
   609 			buf.Format(_L("CAssembly::AddFragmentL(): %d went over max (assembly total=%d)"), iHandler.iTotal, iTotal);
       
   610 			LogPrintId(buf, *this);
       
   611 #endif
       
   612 			User::Leave(KErrOverflow); // make assebly "dead" by leaving
       
   613 			}
       
   614 		return -1;
       
   615 		}
       
   616 	}
       
   617 
       
   618 
       
   619 void CAssembly::Timeout()
       
   620 	{
       
   621 	//
       
   622 	// Reconstruct start of IPv6 packet from the unfrag part
       
   623 	//
       
   624 	if (iVersion == 4)
       
   625 		{
       
   626 		if (CompletePacket() == KErrNone)
       
   627 			iHandler.Network()->Icmp4Send(iHead, KInet4ICMP_TimeExceeded, 1, 0);
       
   628 		}
       
   629 	else
       
   630 		{
       
   631 		if (CompletePacket(1) == KErrNone)	// Request restore of FH (for TAHI)
       
   632 			iHandler.Network()->Icmp6Send(iHead, KInet6ICMP_TimeExceeded, 1, 0);
       
   633 		}
       
   634 	LOG(LogPrintId(_L("CAssembly::Timeout()"), *this));
       
   635 	iHandler.Cancel(this);	// <-- Deletes this!!!
       
   636 	}
       
   637 
       
   638 CAssembly *CFragmentHandler::LookupL(const RMBufRecvInfo &aInfo, TUint32 aId, TUint aVersion)
       
   639 	{
       
   640 	CAssembly *a = NULL;
       
   641 	CAssembly *d = NULL;
       
   642 	const TIp6Addr &src = TInetAddr::Cast(aInfo.iSrcAddr).Ip6Address();
       
   643 	const TIp6Addr &dst = TInetAddr::Cast(aInfo.iDstAddr).Ip6Address();
       
   644 	for (a = iCache; ; a = a->iNext)
       
   645 		{
       
   646 		if (a == NULL)
       
   647 			{
       
   648 			// If the quota for incomplete assemblies is all used
       
   649 			if (iCount >= iMaxCount)
       
   650 				{
       
   651 				LOG(Log::Printf(_L("CFragmentHandler::LookupL() MaxCount %d reached"), iCount));
       
   652 				if (d == NULL)
       
   653 					User::Leave(KErrOverflow); // ... should assign some specific reason code?
       
   654 				Cancel(d);
       
   655 				LOG(Log::Printf(_L("CFragmentHandler::LookupL() deleted dead assembly")));
       
   656 				}
       
   657 			a = new (ELeave) CAssembly(*this, src, dst, aId, aVersion);
       
   658 			a->iNext = iCache;
       
   659 			iCache = a;
       
   660 			a->iTimeout.Set(iTimeoutManager, 60);	// 60 seconds or bust!
       
   661 			iCount++;
       
   662 			LOG(LogPrintId(_L("CFragmentHandler::LookupL(): NEW"), *a));
       
   663 			break;
       
   664 			}
       
   665 		else if (a->iId == aId && a->iSrcAddr.IsEqual(src) && a->iDstAddr.IsEqual(dst))
       
   666 			break;
       
   667 		else if (a->iIsDead)
       
   668 			d = a;	// remember "oldest dead" assembly
       
   669 		}
       
   670 	return a;
       
   671 	}
       
   672 
       
   673 //
       
   674 //	CFragmentHeaderHook::NewL
       
   675 //
       
   676 CFragmentHeaderHook *CFragmentHeaderHook::NewL(MNetworkService *aNetwork)
       
   677 	{
       
   678 	return new (ELeave) CFragmentHandler(aNetwork);
       
   679 	}
       
   680 
       
   681 void CFragmentHandler::ConstructL()
       
   682 	{
       
   683 	iTimeoutManager = TimeoutFactory::NewL();
       
   684 	iNetwork->BindL((CProtocolBase *)this, BindHookFor(KProtocolInet6Fragment));
       
   685 
       
   686 
       
   687 	//
       
   688 	// Setup DOS protections
       
   689 	// - maximum number of incomplete assemblies (iMaxCount)
       
   690 	// - maximum amount of RMBUF space (iMaxTotal)
       
   691 	//
       
   692 	LOG(_LIT(KFormat, "\t[%S] %S = %d"));
       
   693 	TInt value;
       
   694 	if (iNetwork->Interfacer()->FindVar(TCPIP_INI_IP, TCPIP_INI_FRAG_COUNT, value))
       
   695 		iMaxCount = value;
       
   696 	else
       
   697 		iMaxCount = KTcpipIni_FragCount;
       
   698 	LOG(Log::Printf(KFormat, &TCPIP_INI_IP, &TCPIP_INI_FRAG_COUNT, iMaxCount));
       
   699 
       
   700 	if (iNetwork->Interfacer()->FindVar(TCPIP_INI_IP, TCPIP_INI_FRAG_TOTAL, value))
       
   701 		iMaxTotal = value;
       
   702 	else
       
   703 		iMaxTotal = KTcpipIni_FragTotal;
       
   704 	LOG(Log::Printf(KFormat, &TCPIP_INI_IP, &TCPIP_INI_FRAG_TOTAL, iMaxTotal));
       
   705 	}
       
   706 
       
   707 //
       
   708 //	CFragmentHandler::~CFragmentHandler()
       
   709 //	*************************************
       
   710 //
       
   711 CFragmentHandler::~CFragmentHandler()
       
   712 	{
       
   713 	CancelAll();
       
   714 	delete iTimeoutManager;
       
   715 	}
       
   716 
       
   717 
       
   718 //	******************
       
   719 //	Reassembly section
       
   720 //	******************
       
   721 //	This a normal extension header receiver hook which is called when the
       
   722 //	processing of headers reaches the Fragmentation Header.
       
   723 //
       
   724 TInt CFragmentHandler::Ip6ApplyL(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, const TInet6HeaderFragment &aHdr)
       
   725 	{
       
   726 	//
       
   727 	// Extract fragment information off from the RMBufs
       
   728 	// (after this, the fragment header can be disposed as needed)
       
   729 	//
       
   730 	aInfo.iProtocol = aHdr.NextHeader();
       
   731 	const TUint32 fh0 = *(TUint32 *)&aHdr;		// needed for ICMP Time Exceeded.
       
   732 	const TInt length = aInfo.iLength - aInfo.iOffset - sizeof(TInet6HeaderFragment);
       
   733 	const TInt offset = aHdr.FragmentOffset();
       
   734 	const TInt last = (aHdr.MFlag() == 0);
       
   735 
       
   736 	CAssembly *a = LookupL(aInfo, aHdr.Id(), aInfo.iVersion);
       
   737 
       
   738 	if (length <= 0 || ((length & 0x7) && !last))
       
   739 		{
       
   740 		// Zero length fragment, or the length of non-last fragment payload is not multiple of 8.
       
   741 		// (always reply with ICMPv6, because IPv6 fragment header was used. If the packet was
       
   742 		// actually an IPv4 packet, just adjust use different the parameter offset).
       
   743 		//
       
   744 		LOG(Log::Printf(_L("CFragmentHandler::IP6ApplyL(...): Payload Length=%d"), length));
       
   745 		Network()->Icmp6Send(aPacket, KInet6ICMP_ParameterProblem, 0,
       
   746 			aInfo.iVersion == 4 ? TInet6HeaderIP4::O_TotalLength : TInet6HeaderIP::O_PayloadLength);
       
   747 		Cancel(a);
       
   748 		// Note: Icmp6Send takes the packet -- no packet Free required!
       
   749 		return -1;
       
   750 		}
       
   751 	if (offset + length > 65535)
       
   752 		{
       
   753 		//
       
   754 		// This fragment would result too long payload for the final IPv6 packet
       
   755 		//
       
   756 		// *** above test is not correct! It should also take into account the
       
   757 		//	   length of the unfragmentable part (if that includes extension
       
   758 		//	   headers in addition to IPv6 header). However, this information
       
   759 		//	   is not available until the first fragment is received. Thus, it
       
   760 		//	   is possible that illegal fragments get entered into the fragment
       
   761 		//	   queue! Needs some checking!! -- msa
       
   762 		//
       
   763 		LOG(Log::Printf(_L("CFragmentHandler::IP6ApplyL(...): offset(%d) + length(%d) > 65535"), offset, length));
       
   764 		Network()->Icmp6Send(aPacket, KInet6ICMP_ParameterProblem, 0,
       
   765 							aInfo.iOffset + TInet6HeaderFragment::O_FragmentOffset);
       
   766 		Cancel(a);
       
   767 		// Note: Icmp6Send takes the packet -- no packet Free required!
       
   768 		return -1;
       
   769 		}
       
   770 	if (last)
       
   771 		{
       
   772 		//
       
   773 		// If this is the last fragment, the total length is now known
       
   774 		//
       
   775 		a->iLength = offset + length;
       
   776 		}
       
   777 
       
   778 	return a->Add(aPacket, sizeof(TInet6HeaderFragment), offset, length, fh0);
       
   779 	}
       
   780 
       
   781 //	*******************
       
   782 //	Fragmenting section
       
   783 //	*******************
       
   784 //	When entered, aPacket contains full IPv6 packet, and
       
   785 //	it has been already tested that this does not fit the
       
   786 //	Path MTU!
       
   787 //	If called with a packet that fits the MTU, this generates a
       
   788 //	single fragment (just inserts the IPv6 fragment header into
       
   789 //	the packet with offset==0 and M=1)
       
   790 //
       
   791 void CFragmentHandler::Ip6Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ, TInet6HeaderIP &aHdr)
       
   792 	{
       
   793 	//
       
   794 	// Check extension headers that need to be included into
       
   795 	// unfragmentable part
       
   796 	// It is somewhat fuzzy what should be included in addition
       
   797 	// of Hop-by-hop and Routing header. Currently including
       
   798 	// destination options, if they are before the routing header
       
   799 	// -- msa
       
   800 
       
   801 	// unfrag_next
       
   802 	//	point always to the last next header field of the
       
   803 	//	unfragmentable part.
       
   804 	TUint8 *unfrag_next = ((TUint8 *)&aHdr) + TInet6HeaderIP::O_NextHeader;
       
   805 	//
       
   806 	// unfrag_size
       
   807 	//	size of the unfragmentable part.
       
   808 	TInt unfrag_size = sizeof(TInet6HeaderIP);
       
   809 
       
   810 	TInt offset = sizeof(TInet6HeaderIP);
       
   811 	for (TInt header = (TInt)*unfrag_next;;)
       
   812 		{
       
   813 		TInet6Packet<TInet6HeaderExtension> ext(aPacket, offset);
       
   814 		if (ext.iHdr == NULL)
       
   815 			// If the next header (8 octets) cannot be mapped from
       
   816 			// the packet it cannot be a valid extension header anyways.
       
   817 			// This search can be terminated with what we have now.
       
   818 			break;
       
   819 		offset += ext.iHdr->HeaderLength();
       
   820 
       
   821 		if (header == STATIC_CAST(TInt, KProtocolInet6HopOptions))
       
   822 			{
       
   823 			// The mapped header is Hop-by-hop options. This is
       
   824 			// included into unfragmentable part, but must look
       
   825 			// forward if there is a routing header. Just remember
       
   826 			// this part
       
   827 			unfrag_size = offset;
       
   828 			unfrag_next = (TUint8 *)ext.iHdr;	// point to Next Hdr field.
       
   829 			}
       
   830 		else if (header != STATIC_CAST(TInt, KProtocolInet6DestinationOptions))
       
   831 			{
       
   832 			// Terminate search (we got either unknown extension
       
   833 			// or routing header). If the latter, then include it
       
   834 			// into unfragmentable part.
       
   835 			if (header == STATIC_CAST(TInt, KProtocolInet6RoutingHeader))
       
   836 				{
       
   837 				unfrag_size = offset;
       
   838 				unfrag_next = (TUint8 *)ext.iHdr;	// point to Next Hdr field.
       
   839 				}
       
   840 			break;
       
   841 			}
       
   842 		// Look for more
       
   843 		header = ext.iHdr->NextHeader();
       
   844 		}
       
   845 	//
       
   846 	// The NextHeader of the last header (either IPv6 or an extension
       
   847 	// need to be changed into KProtocolInet6Fragment. And, the old
       
   848 	// value is placed into next header of every generated fragment
       
   849 	// header.
       
   850 	TInt next_header = (TInt)*unfrag_next;
       
   851 	*unfrag_next = KProtocolInet6Fragment;
       
   852 	//
       
   853 	// Remove the fragmentable part off the packet, leaving
       
   854 	// only the unfragmentable part into the original packet
       
   855 	// (and info block!)
       
   856 	//
       
   857 	RMBufChain fragmentable;	// Fragmentable part
       
   858 	RMBufSendPacket fragment;
       
   859 	RMBufChain tailpart;
       
   860 	RMBufPktInfo *info;
       
   861 	TInet6Packet<TInet6HeaderFragment> fh;
       
   862 	TInt remainder, chunk;
       
   863 
       
   864 	TInt err = aPacket.Split(unfrag_size, fragmentable);
       
   865 	if (err == KErrNone)
       
   866 		err = aPacket.Append(sizeof(TInet6HeaderFragment));
       
   867 	if (err != KErrNone)
       
   868 		goto drop_all;		// -- probably out of memory (RMBUF's)
       
   869 
       
   870 	remainder = aPacket.Info()->iLength - unfrag_size;
       
   871 	//
       
   872 	// Prepare access for the fragment header
       
   873 	//
       
   874 	fh.Set(aPacket, unfrag_size, sizeof(TInet6HeaderFragment));
       
   875 	unfrag_size += sizeof(TInet6HeaderFragment);
       
   876 	if (err != KErrNone ||		// Memory allocation error, or...
       
   877 		fh.iHdr == NULL ||		// Cannot access fragment header, or..
       
   878 		aMtu < (unfrag_size+8))
       
   879 		goto drop_all;			// Mission impossible! Path MTU is less than
       
   880 								// required unfragmentable size + minimal payload!
       
   881 	fh.iHdr->ZeroAll();
       
   882 	fh.iHdr->SetNextHeader(next_header);
       
   883 	fh.iHdr->SetId(++iFragmentId);
       
   884 	fh.iHdr->SetMFlag(1);
       
   885 	//
       
   886 	// Fragment payload size is multiple of 8
       
   887 	//
       
   888 	chunk = (aMtu - unfrag_size) & ~0x7;
       
   889 	offset = 0;
       
   890 	//
       
   891 	// Patch in fragment payload size into IPv6 header, this will
       
   892 	// be the same for all but last fragment, so it can be set outside
       
   893 	// the loop...
       
   894 	// (making a big assumption that the aHdr pointer is still valid!!
       
   895 	// the current operations done to the aPacket are safe, but beware
       
   896 	// when changing this code...) -- msa
       
   897 	//
       
   898 	aHdr.SetPayloadLength(unfrag_size + chunk - sizeof(TInet6HeaderIP));
       
   899 	while (remainder > chunk)
       
   900 		{
       
   901 		//
       
   902 		// Setup unfragmentable part
       
   903 		//
       
   904 		TRAP(err,
       
   905 			aPacket.CopyL(fragment);
       
   906 			aPacket.CopyInfoL(fragment);
       
   907 			);
       
   908 		if (err != KErrNone)
       
   909 			goto drop_all;
       
   910 		info = fragment.Info();
       
   911 		info->iLength = chunk + unfrag_size;
       
   912 		//
       
   913 		// Extract next fragment payload
       
   914 		//
       
   915 		err = fragmentable.Split(chunk, tailpart);
       
   916 		if (err != KErrNone)
       
   917 			goto drop_all;
       
   918 		fragment.Append(fragmentable);
       
   919 		fragment.Pack();
       
   920 		aFragQ.Append(fragment);
       
   921 		fragmentable.Assign(tailpart);
       
   922 		offset += chunk;
       
   923 		remainder -= chunk;
       
   924 		fh.iHdr->SetFragmentOffset(offset);
       
   925 		}
       
   926 	//
       
   927 	// Make aPacket as the last fragment
       
   928 	//
       
   929 	fh.iHdr->SetMFlag(0);
       
   930 	aHdr.SetPayloadLength(unfrag_size + remainder - sizeof(TInet6HeaderIP));
       
   931 	aPacket.Append(fragmentable);
       
   932 	info = aPacket.Info();
       
   933 	info->iLength = unfrag_size + remainder;
       
   934 	aPacket.Pack();
       
   935 	aFragQ.Append(aPacket);
       
   936 	return;
       
   937 
       
   938 drop_all:
       
   939 	aFragQ.Free();
       
   940 	fragmentable.Free();
       
   941 	fragment.Free();
       
   942 	tailpart.Free();
       
   943 	aPacket.Free();
       
   944 	LOG(Log::Printf(_L("CFragmentHandler::Ip6Fragment(...) FAILED")));
       
   945 	}
       
   946 
       
   947 
       
   948 //	**********************
       
   949 //	IPv4 Fragment handling
       
   950 //	**********************
       
   951 
       
   952 //	******************
       
   953 //	Reassembly section
       
   954 //	******************
       
   955 //	This a called when an IPv4 fragment is received. On entry
       
   956 //	aHead.ip4 already contains a full copy of the IPv4 header
       
   957 //	and aHead.iOffset == ip4.HeaderLength()
       
   958 //
       
   959 //	*NOTE*
       
   960 //		On return, the IPv4 header checksum is not recomputed
       
   961 //		(should it? maybe for possible ICMP error message? -- msa)
       
   962 //
       
   963 TInt CFragmentHandler::Ip4ApplyL(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, const TInet6HeaderIP4 &aHdr)
       
   964 	{
       
   965 	//
       
   966 	// Extract fragment information off from the RMBufs
       
   967 	// (after this, the fragment header can be disposed as needed)
       
   968 	//
       
   969 	aInfo.iProtocol = aHdr.Protocol();				// Put the protocol into info
       
   970 	const TInt length = aInfo.iLength - aInfo.iOffset;	// Actual payload length
       
   971 	const TInt offset = (aHdr.FragmentOffset()) << 3;
       
   972 	const TInt last = (aHdr.MF() == 0);
       
   973 	//
       
   974 	// Locate or create an assemble matching this fragment
       
   975 	//
       
   976 	CAssembly *a = LookupL(aInfo, aHdr.Identification() | (aInfo.iProtocol << 16), 4);
       
   977 
       
   978 	if (length <= 0 || ((length & 0x7) && !last))
       
   979 		{
       
   980 		// Zero length fragment or,
       
   981 		// the length of non-last fragment payload is not multiple of 8.
       
   982 		//
       
   983 		Network()->Icmp4Send(aPacket, KInet4ICMP_ParameterProblem, 0,
       
   984 			TInet6HeaderIP4::O_TotalLength);
       
   985 		Cancel(a);
       
   986 		// Note: Icmp4Send takes the packet -- no packet Free required!
       
   987 		return -1;
       
   988 		}
       
   989 	if (offset + length > 65535 - TInet6HeaderIP4::MinHeaderLength())
       
   990 		{
       
   991 		//
       
   992 		// This fragment would result too long payload for the final IPv4 packet
       
   993 		// (NOTE: above is not full proof, if the first fragment has options!
       
   994 		//	The intent is just to drop obviously wrong fragments before doing
       
   995 		//  anything more with them... -- msa)
       
   996 		//
       
   997 		Network()->Icmp4Send(aPacket, KInet4ICMP_ParameterProblem, 0,
       
   998 			TInet6HeaderIP4::O_FragmentOffset);
       
   999 		Cancel(a);
       
  1000 		// Note: Icmp4Send takes the packet -- no packet Free required!
       
  1001 		return -1;
       
  1002 		}
       
  1003 
       
  1004 	if (last)
       
  1005 		a->iLength = offset + length;	// Last fragment, real payload length is now known
       
  1006 
       
  1007 	return a->Add(aPacket, 0, offset, length);
       
  1008 	}
       
  1009 
       
  1010 //
       
  1011 //	CrunchOptions
       
  1012 //	-------------
       
  1013 //		Only options marked as "copy" are copied into every fragment header.
       
  1014 //
       
  1015 //		This function takes options from the original packet and removes
       
  1016 //		all except the copiable options (process in place).
       
  1017 //
       
  1018 //		Returns the length of the "crunched" IPv4 header
       
  1019 //
       
  1020 static TInt CrunchOptions(TInet6HeaderIP4 &aHdr)
       
  1021 	{
       
  1022 	TInt length = TInet6HeaderIP4::MinHeaderLength();
       
  1023 	TUint8 *opt = ((TUint8 *)&aHdr) + length;
       
  1024 	TUint8 *dst = opt;
       
  1025 	TInt count = aHdr.HeaderLength() - length;
       
  1026 	while (count > 0)
       
  1027 		{
       
  1028 		if (*opt < 2)
       
  1029 			{
       
  1030 			// Q: Is it legal to have "Copy" on PAD? If so, this
       
  1031 			// needs some fixing yet! -- msa
       
  1032 			//
       
  1033 			++opt;	// END/PAD bytes, skip
       
  1034 			count -= 1;
       
  1035 			}
       
  1036 		else
       
  1037 			{
       
  1038 			int optlen = opt[1];
       
  1039 			if (optlen > count || optlen < 2)
       
  1040 				// An illegal option length, just bail out.
       
  1041 				// (The packet is broken anyway)
       
  1042 				break;
       
  1043 			count -= optlen;
       
  1044 			if (*opt & 0x80)
       
  1045 				{
       
  1046 				//
       
  1047 				// Copy the option
       
  1048 				//
       
  1049 				length += optlen;
       
  1050 				while (--optlen >= 0)
       
  1051 					*dst++ = *opt++;
       
  1052 				}
       
  1053 			else
       
  1054 				//
       
  1055 				// Crunch this
       
  1056 				//
       
  1057 				opt += optlen;
       
  1058 			}
       
  1059 		}
       
  1060 	//
       
  1061 	// The IPv4 header length is multiple of 4, and thus options length
       
  1062 	// must be padded, if not already aligned.. (Pad with END marker)
       
  1063 	//
       
  1064 	while (length & 0x3)
       
  1065 		{
       
  1066 		*dst++ = 0;	// EMD
       
  1067 		length++;
       
  1068 		}
       
  1069 	aHdr.SetHeaderLength(length);
       
  1070 	return length;
       
  1071 	}
       
  1072 
       
  1073 //	*******************
       
  1074 //	Fragmenting section
       
  1075 //	*******************
       
  1076 //	When entered, aPacket contains full IPv4 packet, and
       
  1077 //	it has been already tested that this does not fit the
       
  1078 //	Path MTU!
       
  1079 void CFragmentHandler::Ip4Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ, TInet6HeaderIP4 &aHdr)
       
  1080 	{
       
  1081 	RMBufPacketBase fragmentable;	// Fragmentable part
       
  1082 	RMBufChain tailpart;			// Just work space
       
  1083 	TInt hlen, chunk, remainder, offset;
       
  1084 	TInet6HeaderIP4 ip4;
       
  1085 	TPtr8 ip4ptr((TUint8 *)&ip4, sizeof(ip4), sizeof(ip4));
       
  1086 	TInt err = KErrNone;
       
  1087 	
       
  1088 	offset = aHdr.FragmentOffset() << 3;
       
  1089 	hlen = aHdr.HeaderLength();
       
  1090 
       
  1091 	chunk = (aMtu - hlen) & ~7;
       
  1092 	if (chunk <= 0 || aHdr.DF())
       
  1093 		goto drop_all;	// Unreasonable Mtu... or fragmenting not allowed
       
  1094 	//
       
  1095 	// Split off the first fragment...
       
  1096 	//
       
  1097 	err = aPacket.Split(hlen + chunk, fragmentable);
       
  1098 	if (err != KErrNone)
       
  1099 		goto drop_all;
       
  1100 	aHdr.SetTotalLength(hlen + chunk);
       
  1101 	offset += chunk;
       
  1102 	remainder = aPacket.Info()->iLength - hlen - chunk;
       
  1103 	aPacket.Info()->iLength = hlen + chunk;
       
  1104 	//
       
  1105 	// Setup a fragment header including the options that
       
  1106 	// need to be copied for all the remaining fragments
       
  1107 	//
       
  1108 	ip4ptr = TPtrC8((TUint8 *)&aHdr, hlen);
       
  1109 	hlen = CrunchOptions(ip4);
       
  1110 	ip4ptr.SetLength(hlen);
       
  1111 	//
       
  1112 	// Generate "middle fragments". All of these will
       
  1113 	// have the more (MF) as 1.
       
  1114 	//
       
  1115 	chunk = (aMtu - hlen) & ~7;
       
  1116 	ip4.SetTotalLength(hlen + chunk);
       
  1117 	ip4.SetFlags(KInet4IP_MF);
       
  1118 	while (remainder > chunk)
       
  1119 		{
       
  1120 		//
       
  1121 		// Extract next fragment payload
       
  1122 		//
       
  1123 		err = fragmentable.Split(chunk, tailpart);
       
  1124 		if (err == KErrNone)
       
  1125 			err = fragmentable.Prepend(hlen);
       
  1126 		if (err != KErrNone)
       
  1127 			goto drop_all;
       
  1128 		ip4.SetFragmentOffset((TUint16)(offset >> 3));
       
  1129 		ip4.SetChecksum(0);
       
  1130 		ip4.SetChecksum(TChecksum::ComplementedFold(TChecksum::Calculate((TUint16*)&ip4, hlen)));
       
  1131 		fragmentable.CopyIn(ip4ptr);
       
  1132 		TRAP(err, aPacket.CopyInfoL(fragmentable));
       
  1133 		if (err != KErrNone)
       
  1134 			goto drop_all;
       
  1135 		fragmentable.Info()->iLength = chunk + hlen;
       
  1136 		fragmentable.Pack();
       
  1137 		aFragQ.Append(fragmentable);
       
  1138 		fragmentable.Assign(tailpart);
       
  1139 		offset += chunk;
       
  1140 		remainder -= chunk;
       
  1141 		}
       
  1142 	//
       
  1143 	// Generate the last fragment
       
  1144 	//
       
  1145 	err = fragmentable.Prepend(hlen);
       
  1146 	if (err != KErrNone)
       
  1147 		goto drop_all;
       
  1148 	ip4.SetFragmentOffset((TUint16)(offset >> 3));
       
  1149 	ip4.SetTotalLength(hlen + remainder);
       
  1150 	ip4.SetFlags((TUint8)(aHdr.Flags()));	// need to copy More flag from the original packet!
       
  1151 	ip4.SetChecksum(0);
       
  1152 	ip4.SetChecksum(TChecksum::ComplementedFold(TChecksum::Calculate((TUint16*)&ip4, hlen)));
       
  1153 	fragmentable.CopyIn(ip4ptr);
       
  1154 	TRAP(err, aPacket.CopyInfoL(fragmentable));
       
  1155 	if (err != KErrNone)
       
  1156 		goto drop_all;
       
  1157 	fragmentable.Info()->iLength = hlen + remainder;
       
  1158 	fragmentable.Pack();
       
  1159 	aFragQ.Append(fragmentable);
       
  1160 
       
  1161 	//
       
  1162 	// Complete the header of the first fragment
       
  1163 	//
       
  1164 	aHdr.SetFlags(KInet4IP_MF);
       
  1165 
       
  1166 	aHdr.SetChecksum(0);
       
  1167 	aHdr.SetChecksum(TChecksum::ComplementedFold(TChecksum::Calculate((TUint16*)&aHdr, aHdr.HeaderLength())));
       
  1168 	aPacket.Pack();
       
  1169 	aFragQ.Prepend(aPacket);
       
  1170 	//
       
  1171 	// All fragments successfully built.
       
  1172 	//
       
  1173 	return;
       
  1174 	//
       
  1175 	// Cleanup everything
       
  1176 	//
       
  1177 drop_all:
       
  1178 	aFragQ.Free();
       
  1179 	fragmentable.Free();
       
  1180 	tailpart.Free();
       
  1181 	aPacket.Free();
       
  1182 	LOG(Log::Printf(_L("CFragmentHandler::Ip4Fragment(...) FAILED")));
       
  1183 	}