diff -r 000000000000 -r af10295192d8 networkprotocols/tcpipv4v6prt/inc/frag.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/networkprotocols/tcpipv4v6prt/inc/frag.h Tue Jan 26 15:23:49 2010 +0200 @@ -0,0 +1,296 @@ +// Copyright (c) 2004-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: +// frag.h - IPv6/IPv4 fragment queue +// + + + +/** + @internalComponent +*/ +#ifndef __FRAG_H__ +#define __FRAG_H__ + +#include +#include "in_fmly.h" // for Panic codes + +class RMBufFrag : public RMBufChain +{ + public: + // + // The following methods must be defined in the user implementation: + // + + // Return offset of this fragment. + TUint32 Offset() { Panic(EInet6Panic_NotSupported); return 0; } + + // Return the amount of data in this fragment + TUint32 FragmentLength() { Panic(EInet6Panic_NotSupported); return 0; } + + // Join another fragment to this one. The fragment given in + // parameter either directly follows this one, or overlaps + // the tail end of this fragment. + void Join(RMBufChain& /*aChain*/) { Panic(EInet6Panic_NotSupported); } +}; + +// +// Fragment queue template. The queue holds a number of fragments +// of type T sorted by offset. T must be be derived from RMBufFrag. +// Each fragment is assumed to contain enough header information +// in order to determine the offset and data length of the fragment. +// Whenever a new fragment is inserted in the queue that overlaps +// or is adjacent to an existing fragment, the T::Join() method is +// called in order to combine the two fragments into a single fragment. +// +// +// In general, defragmentation is complete when the folllowing +// two conditions are met: +// +// 1) RMBufFragQ::Count() == 1 +// 2) RMBufFragQ::First() is verifiably a complete packet +// +template +class RMBufFragQ : public RMBufPktQ +{ + public: + inline RMBufFragQ() : iCount(0) {} + inline void Init(); + inline void Free(); + inline void Assign(RMBufFragQ &aQueue); + inline TBool Remove(T &aChain); + inline void Append(T &aChain); + inline void Append(RMBufFragQ &aQueue); + inline void Prepend(T& aFrag); + inline T& First() { return (T&)RMBufPktQ::First(); } + inline T& Last() { return (T&)RMBufPktQ::Last(); } + inline TUint Count() { return iCount; } + void Add(T& aFrag, TUint32* aOff = 0, TUint32* aLen = 0); + + protected: + // Compare two fragment offsets. Works for all offsets < 2^31 + // as well as for offsets requiring mod32 arithmetic, such as TCP + // sequence numbers. + inline TInt32 Compare(TUint32 aOffset1, TUint32 aOffset2) { return (TInt32)(aOffset1 - aOffset2); } + + private: + // Just for us + inline void Insert(RMBufChain& aNew, RMBufChain& aPrev); + inline void Remove(RMBufChain& aNew, RMBufChain& aPrev); + + // Forbidden methods + void Assign(RMBufPktQ &aQueue); + TBool Remove(RMBufChain &aChain); + void Append(RMBufChain &aChain); + void Append(RMBufPktQ &aQueue); + void Prepend(RMBufChain& aChain); + + // Members + TUint iCount; +}; + +template +inline void RMBufFragQ::Init() +{ + RMBufPktQ::Init(); + iCount = 0; +} + +template +inline void RMBufFragQ::Free() +{ + RMBufPktQ::Free(); + iCount = 0; +} + +template +inline void RMBufFragQ::Assign(RMBufFragQ &aQueue) +{ + RMBufPktQ::Assign(aQueue); + iCount = aQueue.iCount; +} + +template +inline TBool RMBufFragQ::Remove(T &aFrag) +{ + if (RMBufPktQ::Remove(aFrag)) + { + iCount--; + return ETrue; + } + return EFalse; +} + +template +inline void RMBufFragQ::Append(T &aFrag) +{ + RMBufPktQ::Append(aFrag); + iCount++; +} + +template +inline void RMBufFragQ::Append(RMBufFragQ &aQueue) +{ + RMBufPktQ::Append(aQueue); + iCount += aQueue.iCount; +} + +template +inline void RMBufFragQ::Prepend(T& aFrag) +{ + RMBufPktQ::Prepend(aFrag); + iCount++; +} + +template +inline void RMBufFragQ::Insert(RMBufChain& aNew, RMBufChain& aPrev) +{ + if (aPrev.IsEmpty()) + { + RMBufPktQ::Prepend(aNew); + } + else if (aPrev.Next().IsEmpty()) + { + RMBufPktQ::Append(aNew); + } + else + { + RMBufChain tmp; + tmp.Assign(aNew); + tmp.Link(aPrev.Next()); + aPrev.Link(tmp); + } + iCount++; +} + +// +// This form of remove is broken in class RMBufPktQ (observed in ER3). +// The only user is TMBufPktQIter::Remove() and the bug only appears +// when trying to remove the last element of the queue using the +// iterator. In this case, the iLast pointer is not updated, which +// puts the queue into a corrupted state. Any subsequent Append() calls +// will cause bad things to happen. +// +template +inline void RMBufFragQ::Remove(RMBufChain& aNew, RMBufChain& aPrev) +{ + if (aPrev.IsEmpty()) + { + RMBufPktQ::Remove(aNew); + } + else + { + aNew.Assign(aPrev.Next()); + aPrev.Link(aNew.Next()); + aNew.Unlink(); + if (aPrev.Next().IsEmpty()) + iLast = aPrev; + } + iCount--; +} + +// +// Add a fragment to queue. The routine will try to combine fragments +// where possible. +// +template +void RMBufFragQ::Add(T& aFrag, TUint32 *aOff, TUint32 *aLen) +{ + __ASSERT_DEBUG(!aFrag.IsEmpty(), User::Panic(_L("RMBufFragQ::Add(): Zero length fragment.\r\n"),0)); + + TUint32 off = aFrag.Offset(); + TUint32 len = aFrag.FragmentLength(); + TUint32 curOff, curLen; + + // We can't use TMBufPktQIter because of its buggy Remove() implementation. + RMBufChain prev, current; + T tmpFrag; + + current = First(); + while (!current.IsEmpty()) + { + T& cur = (T&)current; + curOff = cur.Offset(); + curLen = cur.FragmentLength(); + + // Find correct position. + if (Compare(off, curOff + curLen) > 0) + { + prev = current; + current = prev.Next(); + continue; + } + + // No overlap? Insert here. + if (Compare(off + len, curOff) < 0) + { + Insert(aFrag, prev); + break; + } + + // New fragment fully overlaps queued fragment? Discard it. + if (Compare(off, curOff) >= 0 && Compare(off + len, curOff + curLen) <= 0) + { + aFrag.Free(); + off = curOff; + len = curLen; + break; + } + + // We have found an overlapping queued fragment. Dequeue it into tmpFrag. + if (prev.IsEmpty()) + { + Remove(tmpFrag); + current = First(); + } + else + { + Remove(tmpFrag, prev); + current = prev.Next(); + } + + // Queued fragment fully overlaps new fragment? Discard it. + if (Compare(off, curOff) <= 0 && Compare(off + len, curOff + curLen) >= 0) + { + tmpFrag.Free(); + continue; + } + + // Partial overlap. Determine correct order and call Join(), + if (Compare(off, curOff) <= 0) + { + aFrag.Join(tmpFrag); + tmpFrag.Free(); + } + else + { + tmpFrag.Join(aFrag); + aFrag.Free(); + aFrag.Assign(tmpFrag); + off = curOff; + } + len = aFrag.FragmentLength(); + } + + + // Still have a fragment? It goes last then. + if (!aFrag.IsEmpty()) + Append(aFrag); + + // Return the offset and length of the contiguous block, + // which the inserted fragment belongs to. + if (aOff) *aOff = off; + if (aLen) *aLen = len; +} + +#endif