Implementing content store using Boost.MultiIndex container

Adding .SetGroupName("Ccnx") call to Ccnx-related objects (probably
missed some).  This call is advisory, but it is nice to give users
information about Ccnx-related objects (this information is accessible
via --PrintGroup=Ccnx command line argument)
diff --git a/model/ccnx-content-object-header.cc b/model/ccnx-content-object-header.cc
index e567207..d1ad0cf 100644
--- a/model/ccnx-content-object-header.cc
+++ b/model/ccnx-content-object-header.cc
@@ -37,6 +37,7 @@
 CcnxContentObjectHeader::GetTypeId (void)
 {
   static TypeId tid = TypeId ("ns3::CcnxContentObjectHeader")
+    .SetGroupName ("Ccnx")
     .SetParent<Header> ()
     .AddConstructor<CcnxContentObjectHeader> ()
     ;
diff --git a/model/ccnx-content-object-header.h b/model/ccnx-content-object-header.h
index c26052b..97a8d3d 100644
--- a/model/ccnx-content-object-header.h
+++ b/model/ccnx-content-object-header.h
@@ -24,6 +24,7 @@
 
 #include "ns3/integer.h"
 #include "ns3/header.h"
+#include "ns3/trailer.h"
 
 #include <string>
 #include <vector>
@@ -101,7 +102,7 @@
  * ContentObjectTail should always be 2 bytes, representing two closing tags:
  * "</Content><ContentObject>"
  */
-class CcnxContentObjectTail : public Header
+class CcnxContentObjectTail : public Trailer
 {
 public:
   CcnxContentObjectTail ();
diff --git a/model/ccnx-content-store.cc b/model/ccnx-content-store.cc
index 31a9fb3..4633000 100644
--- a/model/ccnx-content-store.cc
+++ b/model/ccnx-content-store.cc
@@ -16,88 +16,135 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * Author: Ilya Moiseenko <iliamo@cs.ucla.edu>
+ *         Alexander Afanasyev <alexander.afanasyev@ucla.edu>
  */
 
 #include "ccnx-content-store.h"
 #include "ns3/log.h"
-
+#include "ns3/packet.h"
+#include "ns3/ccnx-interest-header.h"
+#include "ns3/ccnx-content-object-header.h"
+#include "ns3/uinteger.h"
 
 NS_LOG_COMPONENT_DEFINE ("CcnxContentStore");
 
 namespace ns3
 {
-        
-CcnxContentStore::CcnxContentStore( int maxSize )
-    : m_maxSize(maxSize) { }
+
+NS_OBJECT_ENSURE_REGISTERED (CcnxContentStore);
+
+using namespace __ccnx_private;
+
+TypeId 
+CcnxContentStore::GetTypeId (void)
+{
+  static TypeId tid = TypeId ("ns3::CcnxContentStore")
+    .SetGroupName ("Ccnx")
+    .SetParent<Object> ()
+    .AddConstructor<CcnxContentStore> ()
+    .AddAttribute ("Size",
+                   "Maximum number of packets that content storage can hold",
+                   UintegerValue (100),
+                   MakeUintegerAccessor (&CcnxContentStore::SetMaxSize,
+                                         &CcnxContentStore::GetMaxSize),
+                   MakeUintegerChecker<uint32_t> ())
+    ;
+
+  return tid;
+}
+
+CcnxContentObjectTail CcnxContentStoreEntry::m_tail;
+
+CcnxContentStoreEntry::CcnxContentStoreEntry (Ptr<CcnxContentObjectHeader> header, Ptr<const Packet> packet)
+  : m_header (header)
+{
+  m_packet = packet->Copy ();
+  m_packet->RemoveHeader (*header);
+  m_packet->RemoveTrailer (m_tail);
+}
+
+Ptr<Packet>
+CcnxContentStoreEntry::GetFullyFormedCcnxPacket () const
+{
+  Ptr<Packet> packet = m_packet->Copy ();
+  packet->AddHeader (*m_header);
+  packet->AddTrailer (m_tail);
+  return packet;
+}
+
+// /// Disabled copy constructor
+// CcnxContentStoreEntry::CcnxContentStoreEntry (const CcnxContentStoreEntry &o)
+// {
+// }
+
+// /// Disables copy operator
+// CcnxContentStoreEntry& CcnxContentStoreEntry::operator= (const CcnxContentStoreEntry &o)
+// {
+//   return *this;
+// }
+
+
+
+CcnxContentStore::CcnxContentStore( )
+  : m_maxSize(100) { } // this value shouldn't matter, NS-3 should call SetSize with default value specified in AddAttribute earlier
         
 CcnxContentStore::~CcnxContentStore( ) 
-    { }
+{ }
 
-//Find corresponding CS entry for the given content name
-CsEntry* 
-CcnxContentStore::Lookup(const string prefix )
+/// Disabled copy constructor
+CcnxContentStore::CcnxContentStore (const CcnxContentStore &o)
 {
-    CriticalSection section(m_csMutex);
-        
-    CsEntry *result = &(m_contentStore.at(prefix));
-    
-    if(result != NULL)
-        Promote (*result);
-        
-    return result;
+}
+
+/// Disables copy operator
+CcnxContentStore& CcnxContentStore::operator= (const CcnxContentStore &o)
+{
+  return *this;
+}
+
+
+Ptr<Packet>
+CcnxContentStore::Lookup (Ptr<const CcnxInterestHeader> interest)
+{
+  CcnxContentStoreContainer::type::iterator it = m_contentStore.get<hash> ().find (interest->GetName ());
+  if (it != m_contentStore.end ())
+    {
+      // promote entry to the top
+      m_contentStore.get<mru> ().relocate (m_contentStore.get<mru> ().begin (),
+                                           m_contentStore.project<mru> (it));
+
+      // return fully formed CCNx packet
+      return it->GetFullyFormedCcnxPacket ();
+    }
+  return 0;
 }   
     
-//move the given CS entry to the head of the list
 void 
-CcnxContentStore::Promote(CsEntry &ce )
+CcnxContentStore::Add (Ptr<CcnxContentObjectHeader> header, Ptr<const Packet> packet)
 {
-    // should not lock mutex. Otherwise deadlocks will be welcome
-    if( m_LRU.front() == &ce ) return;
-        
-    //assert( *(ce.lruPosition)==&ce ); // should point to the same object
-        
-    // swaping positions in _lru
-    m_LRU.erase( ce.lruPosition );
-    m_LRU.push_front( &ce );
-    ce.lruPosition = m_LRU.begin( );
-        
-    //assert( *(ce.lruPosition)==&ce ); // should point to the same object
-}
-    
-//Add entry to content store, if content store is full, use LRU replacement
-void 
-CcnxContentStore::Add( const string contentName, int contentSize )
-{
-    CriticalSection section(m_csMutex);
-        
-    m_contentStore.erase(m_contentStore.find(contentName));
-    
-    if((int)m_contentStore.size() == m_maxSize )
-    {
-        CsEntry *entry = m_LRU.back();
-        m_contentStore.erase(m_contentStore.find(entry->contentName));
-        m_LRU.pop_back( );
+  CcnxContentStoreContainer::type::iterator it = m_contentStore.get<hash> ().find (header->GetName ());
+  if (it == m_contentStore.end ())
+    { // add entry to the top
+      m_contentStore.get<mru> ().push_front (CcnxContentStoreEntry (header, packet));
     }
-        
-    CsEntry ce;
-    ce.contentName = contentName;
-    ce.contentSize = contentSize;
-    
-    m_contentStore[contentName] = ce;
-    
-    CsEntry *ce_in_hash = &(m_contentStore.at(contentName));
-    m_LRU.push_front( ce_in_hash );
-    ce_in_hash->lruPosition = m_LRU.begin( );
-}
-    
-void 
-CcnxContentStore::Dump()
-{
-    CriticalSection section(m_csMutex);
-        
-    BOOST_FOREACH(string_key_hash_t<CsEntry>::value_type i, m_contentStore) 
+  else
     {
-        NS_LOG_INFO ("Key = " << i.first << " Value = " << i.second.contentName);
+      // promote entry to the top
+      m_contentStore.get<mru> ().relocate (m_contentStore.get<mru> ().begin (),
+                                           m_contentStore.project<mru> (it));
     }
 }
-}
\ No newline at end of file
+    
+void 
+CcnxContentStore::Print() const
+{
+  for( DUMP_INDEX::type::iterator it=m_contentStore.get<DUMP_INDEX_TAG> ().begin ();
+       it != m_contentStore.get<DUMP_INDEX_TAG> ().end ();
+       it++
+       )
+    {
+      NS_LOG_INFO (it->GetName ());
+    }
+}
+
+} // namespace ns3
diff --git a/model/ccnx-content-store.h b/model/ccnx-content-store.h
index 58e060e..ee29f81 100644
--- a/model/ccnx-content-store.h
+++ b/model/ccnx-content-store.h
@@ -18,60 +18,327 @@
  * Author: Ilya Moiseenko <iliamo@cs.ucla.edu>
  */
 
-#ifndef ccnx_content_store_h
-#define	ccnx_content_store_h
+#ifndef CCNX_CONTENT_STORE_H
+#define	CCNX_CONTENT_STORE_H
 
-#include <ns3/system-mutex.h>
+#include "ns3/object.h"
+#include "ns3/ptr.h"
+
 #include <list>
 #include <string>
-#include "hash-helper.h"
+#include <iostream>
 
-using namespace std;
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/tag.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/mem_fun.hpp>
+
+#include "hash-helper.h"
+#include "ccnx-content-object-header.h"
+#include "ccnx-interest-header.h"
+#include "name-components.h"
 
 namespace  ns3
 {
-    
-class CsEntry;
-typedef list<CsEntry*>::iterator CsLruIterator;
-        
-//structure for CS entry
-struct CsEntry
-{
-    string contentName;
-    int contentSize;
-            
-    CsLruIterator lruPosition;
-};
-        
-typedef string_key_hash_t<CsEntry>::iterator CsIterator;
-typedef string_key_hash_t<CsEntry>::iterator CsRangeIterator;
-    
-class CcnxContentStore
+class Packet;
+
+/**
+ * \ingroup ccnx
+ * \brief NDN content store entry
+ *
+ * Content store entry stores separately pseudo header and content of
+ * ContentObject packet.  It is responsibility of the caller to
+ * construct a fully formed CcnxPacket by calling Copy(), AddHeader(),
+ * AddTail() on the packet received by GetPacket() method.
+ *
+ * GetFullyFormedCcnxPacket method provided as a convenience
+ */
+class CcnxContentStoreEntry
 {
 public:
-    CcnxContentStore( int max_size=NDN_CONTENT_STORE_SIZE );
-    virtual ~CcnxContentStore( );
+  /**
+   * \brief Construct content store entry
+   *
+   * \param header Parsed CcnxContentObject header
+   * \param packet Original CCNx packet
+   *
+   * The constructor will make a copy of the supplied packet and calls
+   * RemoveHeader and RemoveTail on the copy.
+   */
+  CcnxContentStoreEntry (Ptr<CcnxContentObjectHeader> header, Ptr<const Packet> packet);
+
+  /**
+   * \brief Get prefix of the stored entry
+   * \returns prefix of the stored entry
+   */
+  inline const Name::Components&
+  GetName () const;
+
+  /**
+   * \brief Get CcnxContentObjectHeader of the stored entry
+   * \returns CcnxContentObjectHeader of the stored entry
+   */
+  inline Ptr<const CcnxContentObjectHeader>
+  GetHeader () const;
+
+  /**
+   * \brief Get content of the stored entry
+   * \returns content of the stored entry
+   */
+  inline Ptr<const Packet>
+  GetPacket () const;
+
+  /**
+   * \brief Convenience method to create a fully formed CCNx packet from stored header and content
+   * \returns A read-write copy of the packet with CcnxContentObjectHeader and CcxnContentObjectTail
+   */
+  Ptr<Packet>
+  GetFullyFormedCcnxPacket () const;
+
+// Copy constructor is required by the container. Though, we're
+// storing only two pointers, so shouldn't be a problem
+// private:
+//   CcnxContentStoreEntry (const CcnxContentStoreEntry &); ///< disabled copy constructor
+//   CcnxContentStoreEntry& operator= (const CcnxContentStoreEntry&); ///< disabled copy operator
+  
+private:
+  Ptr<CcnxContentObjectHeader> m_header; ///< \brief non-modifiable CcnxContentObjectHeader
+  Ptr<Packet> m_packet; ///< \brief non-modifiable content of the ContentObject packet
+
+  static CcnxContentObjectTail m_tail; ///< \internal for optimization purposes
+};
+
+/// \brief Private namespace for CCNx implementation
+namespace __ccnx_private
+{
+class hash {}; ///< tag for Boost.Multiindex container (hashed prefix)
+class mru {}; ///< tag for Boost.Multiindex container (most recent used index)
+#ifdef _DEBUG
+class ordered {}; ///< tag for Boost.MultiIndex container (ordered by prefix)
+#endif
+};
+
+/**
+ * \ingroup ccnx
+ * \brief Typedef for content store container implemented as a Boost.MultiIndex container
+ *
+ * - First index (tag<prefix>) is a unique hash index based on NDN prefix of the stored content.
+ * - Second index (tag<mru>) is a sequential index used to maintain up to m_maxSize most recent used (MRU) entries in the content store
+ * - Third index (tag<ordered>) is just a helper to provide stored prefixes in ordered way. Should be disabled in production build
+ *
+ * \see http://www.boost.org/doc/libs/1_46_1/libs/multi_index/doc/ for more information on Boost.MultiIndex library
+ */
+struct CcnxContentStoreContainer
+{
+  typedef
+  boost::multi_index::multi_index_container<
+    CcnxContentStoreEntry,
+    boost::multi_index::indexed_by<
+      boost::multi_index::hashed_unique<
+        boost::multi_index::tag<__ccnx_private::hash>,
+        boost::multi_index::const_mem_fun<CcnxContentStoreEntry,
+                                          const Name::Components&,
+                                          &CcnxContentStoreEntry::GetName>,
+        CcnxPrefixHash>,
+      boost::multi_index::sequenced<boost::multi_index::tag<__ccnx_private::mru> >
+#ifdef _DEBUG
+      ,
+      boost::multi_index::ordered_unique<
+        boost::multi_index::tag<__ccnx_private::ordered>,
+        boost::multi_index::const_mem_fun<CcnxContentStoreEntry,
+                                          const Name::Components&,
+                                          &CcnxContentStoreEntry::GetName>
+          >
+#endif
+      >
+    > type;
+};
+
+/**
+ * \ingroup ccnx
+ * \brief Typedef for hash index of content store container
+ */
+struct CcnxContentStoreByPrefix
+{
+  typedef
+  CcnxContentStoreContainer::type::index<__ccnx_private::hash>::type
+  type;
+};
+
+/**
+ * \ingroup ccnx
+ * \brief Typedef for MRU index of content store container
+ */
+struct CcnxContentStoreByMru
+{
+  typedef
+  CcnxContentStoreContainer::type::index<__ccnx_private::mru>::type
+  type;
+};
+
+#ifdef _DEBUG
+#define DUMP_INDEX_TAG ordered
+#define DUMP_INDEX     CcnxContentStoreOrderedPrefix
+/**
+ * \ingroup ccnx
+ * \brief Typedef for ordered index of content store container
+ */
+struct CcnxContentStoreOrderedPrefix
+{
+  typedef
+  CcnxContentStoreContainer::type::index<__ccnx_private::ordered>::type
+  type;
+};
+#else
+#define DUMP_INDEX_TAG mru
+#define DUMP_INDEX     CcnxContentStoreByMru
+#endif
+  
+/**
+ * \ingroup ccnx
+ * \brief NDN content store entry
+ */
+class CcnxContentStore : public Object
+{
+public:
+  /**
+   * \brief Interface ID
+   *
+   * \return interface ID
+   */
+  static TypeId GetTypeId ();
+  
+  // typedef string_key_hash_t<CsEntry>::iterator CsIterator;
+  // typedef string_key_hash_t<CsEntry>::iterator CsRangeIterator;
+
+  /**
+   * Default constructor
+   */
+  CcnxContentStore( );
+  virtual ~CcnxContentStore( );
             
-    // Find corresponding CS entry for the given content name
-    CsEntry* Lookup( const string prefix );
-    //bool isValid( const CsIterator &it ) { return it!=_cs.end(); }
+  /**
+   * \brief Find corresponding CS entry for the given interest
+   *
+   * \param interest Interest for which matching content store entry
+   * will be searched
+   *
+   * If an entry is found, it is promoted to the top of most recent
+   * used entries index, \see m_contentStore
+   */
+  Ptr<Packet>
+  Lookup (Ptr<const CcnxInterestHeader> interest);
             
-    // Add new content to the content store. Old content will be replaced
-    void Add( const string contentName, int contentSize );
-            
-    // Dump content store entries
-    void Dump( );
+  /**
+   * \brief Add a new content to the content store.
+   *
+   * \param header Fully parsed CcnxContentObjectHeader
+   * \param packet Fully formed CCNx packet to add to content store
+   * (will be copied and stripped down of headers)
+   *
+   * If entry with the same prefix exists, the old entry will be
+   * promoted to the top of the MRU hash
+   */
+  void
+  Add (Ptr<CcnxContentObjectHeader> header, Ptr<const Packet> packet);
+
+  /**
+   * \brief Set maximum size of content store
+   *
+   * \param size size in packets
+   */
+  inline void
+  SetMaxSize (uint32_t maxSize);
+
+  /**
+   * \brief Get maximum size of content store
+   *
+   * \returns size in packets
+   */
+  inline uint32_t
+  GetMaxSize () const;
+  
+  /**
+   * \brief Print out content store entries
+   *
+   * Debug build provides dumping of content store entries in
+   * lexicographical order of corresponding prefixes
+   *
+   * Release build dumps everything in MRU order
+   */
+  void Print () const;
             
 protected:
-    //move the given CS entry to the head of the list
-    void Promote( CsEntry &entry );
-            
+  // /**
+  //  * \brief Move the given CS entry to the head of the list
+  //  *
+  //  * \param entry Content Store entry
+  //  */
+  // void Promote( CsEntry &entry );
+
+  /**
+   * \todo Alex: DoDispose and NotifyNewAggregate are seem to be very
+   * important, but I'm not yet sure what exactly they are supposed to
+   * do
+   */
+  // virtual void DoDispose ();
+  // virtual void NotifyNewAggregate ();
+
 private:
-    int                   m_maxSize; // maximum number of entries in cache
-            
-    string_key_hash_t<CsEntry>  m_contentStore;     // actual content store
-    list<CsEntry*>			  m_LRU;	// LRU index of the content store
-    SystemMutex				m_csMutex;   // just to make sure we are not
+  CcnxContentStore (const CcnxContentStore &o); ///< Disabled copy constructor
+  CcnxContentStore& operator= (const CcnxContentStore &o); ///< Disabled copy operator
+ 
+private:
+  int                         m_maxSize; ///< \brief maximum number of entries in cache \internal
+  // string_key_hash_t<CsEntry>  m_contentStore;     ///< \brief actual content store \internal
+
+  /**
+   * \brief Content store implemented as a Boost.MultiIndex container
+   * \internal
+   */
+    CcnxContentStoreContainer::type m_contentStore;
 };
+
+inline std::ostream&
+operator<< (std::ostream &os, const CcnxContentStore &cs)
+{
+  cs.Print ();
+  return os;
 }
-#endif
+
+const Name::Components&
+CcnxContentStoreEntry::GetName () const
+{
+  return m_header->GetName ();
+}
+
+Ptr<const CcnxContentObjectHeader>
+CcnxContentStoreEntry::GetHeader () const
+{
+  return m_header;
+}
+
+Ptr<const Packet>
+CcnxContentStoreEntry::GetPacket () const
+{
+  return m_packet;
+}
+
+
+inline void
+CcnxContentStore::SetMaxSize (uint32_t maxSize)
+{
+  m_maxSize = maxSize;
+}
+
+inline uint32_t
+CcnxContentStore::GetMaxSize () const
+{
+  return m_maxSize;
+}
+
+} //namespace ns3
+
+#endif // CCNX_CONTENT_STORE_H
diff --git a/model/ccnx-face.cc b/model/ccnx-face.cc
index e19dc57..3250355 100644
--- a/model/ccnx-face.cc
+++ b/model/ccnx-face.cc
@@ -34,6 +34,7 @@
 CcnxFace::GetTypeId (void)
 {
   static TypeId tid = TypeId ("ns3::CcnxFace")
+    .SetGroupName ("Ccnx")
     .SetParent<Object> ()
   ;
   return tid;
diff --git a/model/ccnx-forwarding-strategy.cc b/model/ccnx-forwarding-strategy.cc
index 6d4b045..7159109 100644
--- a/model/ccnx-forwarding-strategy.cc
+++ b/model/ccnx-forwarding-strategy.cc
@@ -28,6 +28,7 @@
 TypeId CcnxForwardingStrategy::GetTypeId (void)
 {
   static TypeId tid = TypeId ("ns3::CcnxForwardingStrategy")
+    .SetGroupName ("Ccnx")
     .SetParent<Object> ()
   ;
   return tid;
diff --git a/model/ccnx-interest-header.cc b/model/ccnx-interest-header.cc
index 02ad9af..330e2f4 100644
--- a/model/ccnx-interest-header.cc
+++ b/model/ccnx-interest-header.cc
@@ -40,6 +40,7 @@
 CcnxInterestHeader::GetTypeId (void)
 {
   static TypeId tid = TypeId ("ns3::CcnxInterestHeader")
+    .SetGroupName ("Ccnx")
     .SetParent<Header> ()
     .AddConstructor<CcnxInterestHeader> ()
     ;
diff --git a/model/ccnx-l3-protocol.cc b/model/ccnx-l3-protocol.cc
index ff3f926..8b7cccd 100644
--- a/model/ccnx-l3-protocol.cc
+++ b/model/ccnx-l3-protocol.cc
@@ -37,6 +37,7 @@
 #include "ccnx-forwarding-strategy.h"
 #include "ccnx-interest-header.h"
 #include "ccnx-content-object-header.h"
+#include "ccnx-content-store.h"
 
 #include <boost/foreach.hpp>
 
@@ -53,6 +54,7 @@
 {
   static TypeId tid = TypeId ("ns3::CcnxL3Protocol")
     .SetParent<Ccnx> ()
+    .SetGroupName ("Ccnx")
     .AddConstructor<CcnxL3Protocol> ()
     // .AddTraceSource ("Tx", "Send ccnx packet to outgoing interface.",
     //                  MakeTraceSourceAccessor (&CcnxL3Protocol::m_txTrace))
@@ -179,6 +181,12 @@
 void 
 CcnxL3Protocol::Receive (const Ptr<CcnxFace> &face, const Ptr<const Packet> &p)
 {
+  if (face->IsUp ())
+    {
+      NS_LOG_LOGIC ("Dropping received packet -- interface is down");
+      // m_dropTrace (packet, DROP_INTERFACE_DOWN, m_node->GetObject<Ccnx> (), incomingFace);
+      return;
+    }
   NS_LOG_LOGIC ("Packet from face " << &face << " received on node " <<  m_node->GetId ());
 
   Ptr<Packet> packet = p->Copy (); // give upper layers a rw copy of the packet
@@ -198,16 +206,11 @@
                                         const Ptr<CcnxInterestHeader> &header,
                                         const Ptr<Packet> &packet)
 {
-  if (incomingFace->IsUp ())
-    {
-      NS_LOG_LOGIC ("Dropping received packet -- interface is down");
-      // m_dropTrace (packet, DROP_INTERFACE_DOWN, m_node->GetObject<Ccnx> (), incomingFace);
-      return;
-    }
-
-  NS_LOG_LOGIC ("Receiving interest from " << incomingFace);
+  NS_LOG_LOGIC ("Receiving interest from " << &incomingFace);
   // m_rxTrace (packet, m_node->GetObject<Ccnx> (), incomingFace);
 
+  /// \todo Processing of Interest packets
+  
   // NS_ASSERT_MSG (m_forwardingStrategy != 0, "Need a forwarding protocol object to process packets");
   // if (!m_forwardingStrategy->RouteInput (packet, incomingFace,
   //                                     MakeCallback (&CcnxL3Protocol::Send, this),
@@ -224,14 +227,9 @@
                                         const Ptr<CcnxContentObjectHeader> &header,
                                         const Ptr<Packet> &packet)
 {
-  if (incomingFace->IsUp ())
-    {
-      NS_LOG_LOGIC ("Dropping received packet -- interface is down");
-      // m_dropTrace (packet, DROP_INTERFACE_DOWN, m_node->GetObject<Ccnx> (), incomingFace);
-      return;
-    }
+  NS_LOG_LOGIC ("Receiving contentObject from " << &incomingFace);
 
-  NS_LOG_LOGIC ("Receiving contentObject from " << incomingFace);
+  /// \todo Processing of ContentObject packets
 }
 
 // fake method
@@ -245,27 +243,21 @@
 void
 CcnxL3Protocol::Send (const Ptr<CcnxFace> &face, const Ptr<Packet> &packet)
 {
-  // NS_LOG_FUNCTION (this << "packet: " << packet << ", route: "<< route);
-  
-  // if (route == 0)
-  //   {
-  //     NS_LOG_WARN ("No route to host.  Drop.");
-  //     // m_dropTrace (packet, DROP_NO_ROUTE, m_node->GetObject<Ccnx> (), 0);
-  //     return;
-  //   }
-  // Ptr<CcnxFace> outFace = route->GetOutputFace ();
+  NS_LOG_FUNCTION (this << "packet: " << &packet << ", face: "<< &face); //
 
-  // if (outFace->IsUp ())
-  //   {
-  //     NS_LOG_LOGIC ("Sending via face " << *outFace);
-  //     // m_txTrace (packet, m_node->GetObject<Ccnx> (), outFace);
-  //     outFace->Send (packet);
-  //   }
-  // else
-  //   {
-  //     NS_LOG_LOGIC ("Dropping -- outgoing interface is down: " << *outFace);
-  //     // m_dropTrace (packet, DROP_INTERFACE_DOWN, m_node->GetObject<Ccnx> (), outFace);
-  //   }
+  NS_ASSERT_MSG (face != 0, "Face should never be NULL");
+
+  if (face->IsUp ())
+    {
+      NS_LOG_LOGIC ("Sending via face " << &face); //
+      m_txTrace (packet, m_node->GetObject<Ccnx> (), face);
+      face->Send (packet);
+    }
+  else
+    {
+      NS_LOG_LOGIC ("Dropping -- outgoing interface is down: " << &face);
+      m_dropTrace (packet, DROP_INTERFACE_DOWN, m_node->GetObject<Ccnx> (), face);
+    }
 }
 
 
diff --git a/model/ccnx-l3-protocol.h b/model/ccnx-l3-protocol.h
index 145051f..6167fd0 100644
--- a/model/ccnx-l3-protocol.h
+++ b/model/ccnx-l3-protocol.h
@@ -41,7 +41,8 @@
 class Header;
 class CcnxInterestHeader;
 class CcnxContentObjectHeader;
-  
+class CcnxContentStore;
+
 /**
  * \ingroup ccnx
  * \brief Actual implementation of the Ccnx network layer
@@ -160,15 +161,25 @@
   Ptr<Node> m_node; ///< \brief node on which ccnx stack is installed
   Ptr<CcnxForwardingStrategy> m_forwardingStrategy; ///< \brief smart pointer to the selected forwarding strategy
 
-  // TracedCallback<Ptr<const Packet>, Ptr<const CcnxFace> > m_sendOutgoingTrace;
-  // TracedCallback<Ptr<const Packet>, Ptr<const CcnxFace> > m_unicastForwardTrace;
-  // TracedCallback<Ptr<const Packet>, Ptr<const CcnxFace> > m_localDeliverTrace;
+  Ptr<CcnxContentStore> m_contentStore;
+  
+  /**
+   * \brief Trace of transmitted packets, including all headers
+   * \internal
+   */
+  TracedCallback<Ptr<const Packet>, Ptr<Ccnx>, Ptr<const CcnxFace> > m_txTrace;
 
-  // // The following two traces pass a packet with an IP header
-  // TracedCallback<Ptr<const Packet>, Ptr<Ccnx>, Ptr<const CcnxFace> > m_txTrace;
-  // TracedCallback<Ptr<const Packet>, Ptr<Ccnx>, Ptr<const CcnxFace> > m_rxTrace;
-  // // <ip-header, payload, reason, ifindex> (ifindex not valid if reason is DROP_NO_ROUTE)
-  // TracedCallback<Ptr<const Packet>, DropReason, Ptr<const Ccnx>, Ptr<const CcnxFace> > m_dropTrace;
+  /**
+   * \brief Trace of received packets, including all headers
+   * \internal
+   */
+  TracedCallback<Ptr<const Packet>, Ptr<Ccnx>, Ptr<const CcnxFace> > m_rxTrace;
+
+  /**
+   * \brief Trace of dropped packets, including reason and all headers
+   * \internal
+   */
+  TracedCallback<Ptr<const Packet>, DropReason, Ptr<const Ccnx>, Ptr<const CcnxFace> > m_dropTrace;
 };
   
 } // Namespace ns3
diff --git a/model/ccnx-local-face.cc b/model/ccnx-local-face.cc
index ea2a84b..80b0b52 100644
--- a/model/ccnx-local-face.cc
+++ b/model/ccnx-local-face.cc
@@ -37,6 +37,7 @@
 CcnxLocalFace::GetTypeId (void)
 {
   static TypeId tid = TypeId ("ns3::CcnxLocalFace")
+    .SetGroupName ("Ccnx")
     .SetParent<CcnxFace> ()
   ;
   return tid;
diff --git a/model/ccnx-net-device-face.cc b/model/ccnx-net-device-face.cc
index b5816a4..9bdcd43 100644
--- a/model/ccnx-net-device-face.cc
+++ b/model/ccnx-net-device-face.cc
@@ -38,6 +38,7 @@
 CcnxNetDeviceFace::GetTypeId ()
 {
   static TypeId tid = TypeId ("ns3::CcnxNetDeviceFace")
+    .SetGroupName ("Ccnx")
     .SetParent<CcnxFace> ()
   ;
   return tid;
diff --git a/model/ccnx.cc b/model/ccnx.cc
index a940c36..0fe4b31 100644
--- a/model/ccnx.cc
+++ b/model/ccnx.cc
@@ -32,6 +32,7 @@
 Ccnx::GetTypeId (void)
 {
   static TypeId tid = TypeId ("ns3::Ccnx")
+    .SetGroupName ("Ccnx")
     .SetParent<Object> ()
   ;
   return tid;
diff --git a/model/hash-helper.h b/model/hash-helper.h
index 880a217..36a9ee8 100644
--- a/model/hash-helper.h
+++ b/model/hash-helper.h
@@ -18,48 +18,50 @@
  * Author: Ilya Moiseenko <iliamo@cs.ucla.edu>
  */
 
-#ifndef ccnx_hash_helper_h
-#define ccnx_hash_helper_h
+#ifndef CCNX_HASH_HELPER_H
+#define CCNX_HASH_HELPER_H
 
 #include <string>
-#include <boost/unordered_map.hpp>
-#include <boost/functional/hash.hpp>
 #include <boost/foreach.hpp>
+#include "name-components.h"
 
-//size of content store
-#define NDN_CONTENT_STORE_SIZE 100
-//maximum length of content name
-#define NDN_MAX_NAME_LENGTH 30
-
-//using namespace std;
-
-#define KEY(x)		x->first
-#define VALUE(x)	x->second
-
-
-/*template<typename T> 
-struct hash : public std::unary_function<T, std::size_t> {
-    std::size_t operator()(T const&) const;
-};*/
-
-struct string_hash : public std::unary_function<std::string, std::size_t>
+namespace ns3
 {
-	inline std::size_t operator( )( std::string str ) const
-	{
-        std::size_t hash = str.size() + 23;
-		for( std::string::const_iterator it = str.begin( ); it!=str.end(); it++ )
-		{
-			hash = ((hash << 6) ^ (hash >> 27)) + static_cast<std::size_t>( *it );
-		}
-		
-		return boost::hash_value(hash); //hash;
-	}
-};
 
-// A collision-chaining hash table mapping strings to ints.
-template<typename Value>
-class string_key_hash_t : public boost::unordered_map<std::string,Value, string_hash, std::equal_to<std::string>,std::allocator<std::string> >
+/**
+ * \ingroup ccnx-helpers
+ * \brief Helper providing hash value for the name prefix
+ *
+ * The whole prefix is considered as a long string with '/' delimiters
+ *
+ * \todo Testing is required to determine if this hash function
+ * actually provides good hash results
+ */
+struct CcnxPrefixHash : public std::unary_function<Name::Components, std::size_t>
 {
+  std::size_t
+  operator() (const Name::Components &prefix) const
+  {
+    std::size_t hash = 23;
+    BOOST_FOREACH (const std::string &str, prefix.GetComponents ())
+      {
+        hash += str.size ();
+        hash = ((hash << 6) ^ (hash >> 27)) + '/';
+        BOOST_FOREACH (char c, str)
+          {
+            hash = ((hash << 6) ^ (hash >> 27)) + c;
+          }
+      }
+    return hash;
+  }
 };
+  
+// // A collision-chaining hash table mapping strings to ints.
+// template<typename Value>
+// class string_key_hash_t : public boost::unordered_map<std::string,Value, string_hash, std::equal_to<std::string>,std::allocator<std::string> >
+// {
+// };
 
-#endif
\ No newline at end of file
+} // namespace ns3
+
+#endif // CCNX_HASH_HELPER_H
diff --git a/model/name-components.h b/model/name-components.h
index 0ea4821..95d2347 100644
--- a/model/name-components.h
+++ b/model/name-components.h
@@ -24,6 +24,7 @@
 #include "ns3/simple-ref-count.h"
 
 #include <string>
+#include <algorithm>
 #include <list>
 
 namespace ns3 {
@@ -58,6 +59,12 @@
 
   inline size_t
   size () const;
+
+  inline bool
+  operator== (const Components &prefix) const;
+
+  inline bool
+  operator< (const Components &prefix) const;
   
 private:
   std::list<std::string> m_prefix;
@@ -79,8 +86,20 @@
 {
   (*this) (s);
 }
-  
-  
+
+bool
+Components::operator== (const Components &prefix) const
+{
+  return std::equal (m_prefix.begin (), m_prefix.end (), prefix.m_prefix.begin ());
+}
+
+bool
+Components::operator< (const Components &prefix) const
+{
+  return std::lexicographical_compare (m_prefix.begin (), m_prefix.end (),
+                                       prefix.m_prefix.begin (), prefix.m_prefix.end ());
+}
+
 } // Namespace Name
 } // namespace ns3