networkprotocols/tcpipv4v6prt/inc/frag.h
changeset 0 af10295192d8
--- /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 <es_mbuf.h>
+#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 T>
+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<class T>
+inline void RMBufFragQ<T>::Init()
+{
+  RMBufPktQ::Init();
+  iCount = 0;
+}
+
+template<class T>
+inline void RMBufFragQ<T>::Free()
+{
+  RMBufPktQ::Free();
+  iCount = 0;
+}
+
+template<class T>
+inline void RMBufFragQ<T>::Assign(RMBufFragQ &aQueue)
+{
+  RMBufPktQ::Assign(aQueue);
+  iCount = aQueue.iCount;
+}
+
+template<class T>
+inline TBool RMBufFragQ<T>::Remove(T &aFrag)
+{
+  if (RMBufPktQ::Remove(aFrag))
+    {
+      iCount--;
+      return ETrue;
+    }
+  return EFalse;
+}
+
+template<class T>
+inline void RMBufFragQ<T>::Append(T &aFrag)
+{
+  RMBufPktQ::Append(aFrag);
+  iCount++;
+}
+
+template<class T>
+inline void RMBufFragQ<T>::Append(RMBufFragQ &aQueue)
+{
+  RMBufPktQ::Append(aQueue);
+  iCount += aQueue.iCount;
+}
+
+template<class T>
+inline void RMBufFragQ<T>::Prepend(T& aFrag)
+{
+  RMBufPktQ::Prepend(aFrag);
+  iCount++;
+}
+
+template<class T>
+inline void RMBufFragQ<T>::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<class T>
+inline void RMBufFragQ<T>::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<class T>
+void RMBufFragQ<T>::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