Correcting bugs with cleaning timeouts in PIT/RIT/ContentStore

Removing RIT concept. Instead, keep track of nonces for each PIT entry.

Many changes in CcnxL3Protocol regarding Interest/Data handling (NACK
are not yet handled at all)

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!! Remove when is not actual !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Code is not compiling !!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
diff --git a/apps/ccnx-consumer.cc b/apps/ccnx-consumer.cc
index 81d44b8..ac3950c 100644
--- a/apps/ccnx-consumer.cc
+++ b/apps/ccnx-consumer.cc
@@ -23,6 +23,7 @@
 #include "ns3/ccnx-local-face.h"
 #include "ns3/ccnx.h"
 #include "ns3/callback.h"
+#include "ns3/string.h"
 #include "ns3/ccnx-content-object-header.h"
 
 NS_LOG_COMPONENT_DEFINE ("CcnxConsumer");
@@ -39,15 +40,15 @@
       .SetParent<Application> ()
       .AddConstructor<CcnxConsumer> ()
       .AddAttribute ("OffTime", "Time interval between packets",
-                     TimeValue (Seconds (0.1)),
+                     StringValue ("100ms"),
                      MakeTimeAccessor (&CcnxConsumer::m_offTime),
                      MakeTimeChecker ())
       .AddAttribute ("InterestName","CcnxName of the Interest (use CcnxNameComponents)",
-                     CcnxNameComponentsValue (),
+                     StringValue ("/"),
                      MakeCcnxNameComponentsAccessor (&CcnxConsumer::m_interestName),
                      MakeCcnxNameComponentsChecker ())
       .AddAttribute ("LifeTime", "LifeTime fo interest packet",
-                     TimeValue (Seconds (0)),
+                     StringValue ("2s"),
                      MakeTimeAccessor (&CcnxConsumer::m_interestLifeTime),
                      MakeTimeChecker ())
       .AddAttribute ("MinSuffixComponents", "MinSuffixComponents",
diff --git a/examples/ccnx-grid.cc b/examples/ccnx-grid.cc
index acf4a5f..9d6454a 100644
--- a/examples/ccnx-grid.cc
+++ b/examples/ccnx-grid.cc
@@ -33,6 +33,7 @@
 NS_LOG_COMPONENT_DEFINE ("CcnxGrid");
 
 uint32_t nGrid = 3;
+Time finishTime = Seconds (20.0); 
 
 void PrintTime ()
 {
@@ -46,13 +47,14 @@
 {
   Config::SetDefault ("ns3::PointToPointNetDevice::DataRate", StringValue ("1Mbps"));
   Config::SetDefault ("ns3::PointToPointChannel::Delay", StringValue ("1ms"));
-  Config::SetDefault ("ns3::CcnxConsumer::OffTime", StringValue ("100ms"));
+  Config::SetDefault ("ns3::CcnxConsumer::OffTime", StringValue ("1ms"));
     
   Packet::EnableChecking();
   Packet::EnablePrinting();
 
   CommandLine cmd;
   cmd.AddValue ("nGrid", "Number of grid nodes", nGrid);
+  cmd.AddValue ("finish", "Finish time", finishTime);
   cmd.Parse (argc, argv);
 
   PointToPointHelper p2p;
@@ -82,8 +84,8 @@
   consumerNodes.Add (grid.GetNode (0,0));
   
   // Populate FIB based on IPv4 global routing controller
-  // ccnxHelper.InstallFakeGlobalRoutes ();
-  // ccnxHelper.InstallRouteTo (producer);
+  ccnxHelper.InstallFakeGlobalRoutes ();
+  ccnxHelper.InstallRouteTo (producer);
 
   NS_LOG_INFO ("Installing Applications");
   std::ostringstream prefix;
@@ -93,13 +95,13 @@
   ApplicationContainer consumers = consumerHelper.Install (consumerNodes);
   
   consumers.Start (Seconds (0.0));
-  consumers.Stop (Seconds (20.0));
+  consumers.Stop (finishTime);
     
   CcnxProducerHelper producerHelper (prefix.str (),120);
   ApplicationContainer producers = producerHelper.Install (producer);
   
   producers.Start(Seconds(0.0));
-  producers.Stop(Seconds(20.0));
+  producers.Stop(finishTime);
 
   NS_LOG_INFO ("Outputing FIBs into [fibs.log]");
   Ptr<OutputStreamWrapper> routingStream = Create<OutputStreamWrapper> ("fibs.log", std::ios::out);
@@ -119,7 +121,7 @@
   // NS_LOG_INFO ("FIB dump:\n" << *c.Get(0)->GetObject<CcnxFib> ());
   // NS_LOG_INFO ("FIB dump:\n" << *c.Get(1)->GetObject<CcnxFib> ());
     
-  Simulator::Stop (Seconds (100));
+  Simulator::Stop (finishTime);
     
   NS_LOG_INFO ("Run Simulation.");
   Simulator::Run ();
diff --git a/helper/ccnb-parser/ccnb-parser-common.h b/helper/ccnb-parser/ccnb-parser-common.h
index 847a6b8..528bcb0 100644
--- a/helper/ccnb-parser/ccnb-parser-common.h
+++ b/helper/ccnb-parser/ccnb-parser-common.h
@@ -168,8 +168,7 @@
   CCN_DTAG_StatusResponse = 112,
   CCN_DTAG_StatusCode = 113,
   CCN_DTAG_StatusText = 114,
-  NDN_DTAG_Nack = 115,
-  NDN_DTAG_Congested = 116,
+  CCN_DTAG_Nack = 200,
   CCN_DTAG_SequenceNumber = 256,
   CCN_DTAG_CCNProtocolDataUnit = 17702112
 };
diff --git a/helper/ccnb-parser/visitors/ccnb-parser-interest-visitor.cc b/helper/ccnb-parser/visitors/ccnb-parser-interest-visitor.cc
index 65ec4ee..1964ac7 100644
--- a/helper/ccnb-parser/visitors/ccnb-parser-interest-visitor.cc
+++ b/helper/ccnb-parser/visitors/ccnb-parser-interest-visitor.cc
@@ -168,26 +168,15 @@
       break;
     
             
-    case NDN_DTAG_Nack:
+    case CCN_DTAG_Nack:
       NS_LOG_DEBUG ("Nack");
       if (n.m_nestedTags.size()!=1) // should be exactly one UDATA inside this tag
         throw CcnbDecodingException ();
             
       interest.SetNack (
-              1 == boost::any_cast<uint32_t> (
-                      (*n.m_nestedTags.begin())->accept(nonNegativeIntegerVisitor)));
+               boost::any_cast<uint32_t> (
+                                          (*n.m_nestedTags.begin())->accept(nonNegativeIntegerVisitor)));
       break;
-            
-    case NDN_DTAG_Congested:
-      NS_LOG_DEBUG ("Congested");
-      if (n.m_nestedTags.size()!=1) // should be exactly one UDATA inside this tag
-        throw CcnbDecodingException ();
-            
-      interest.SetCongested (
-              1 == boost::any_cast<uint32_t> (
-                      (*n.m_nestedTags.begin())->accept(nonNegativeIntegerVisitor)));
-      break;
-    }
 }
 
 } // namespace CcnbParser
diff --git a/helper/ccnx-decoding-helper.cc b/helper/ccnx-decoding-helper.cc
index af945c5..408190b 100644
--- a/helper/ccnx-decoding-helper.cc
+++ b/helper/ccnx-decoding-helper.cc
@@ -43,9 +43,6 @@
   Buffer::Iterator i = start;
   Ptr<CcnbParser::Block> root = CcnbParser::Block::ParseBlock (i);
   root->accept (interestVisitor, &interest);
-
-  NS_LOG_DEBUG ("refs: " << root->GetReferenceCount ());
-  NS_LOG_DEBUG ("refs: " << DynamicCast<CcnbParser::BaseTag> (root)->m_nestedTags.front ()->GetReferenceCount ());
   
   return i.GetDistanceFrom (start);
 }
diff --git a/helper/ccnx-encoding-helper.cc b/helper/ccnx-encoding-helper.cc
index 20ea101..f486c01 100644
--- a/helper/ccnx-encoding-helper.cc
+++ b/helper/ccnx-encoding-helper.cc
@@ -78,7 +78,7 @@
   if (!interest.GetInterestLifetime().IsZero())
     {
       written += AppendBlockHeader (start, CcnbParser::CCN_DTAG_InterestLifetime, CcnbParser::CCN_DTAG);
-      written += AppendTimestampBlob (start, interest.GetInterestLifetime());
+      written += AppendTimestampBlob (start, interest.GetInterestLifetime ());
       written += AppendCloser (start);
     }
   if (interest.GetNonce()>0)
@@ -89,16 +89,10 @@
                                    sizeof(nonce));
     }
     
-  if (interest.IsNack ())
+  if (interest.GetNack ()>0)
     {
-      written += AppendBlockHeader (start, CcnbParser::NDN_DTAG_Nack, CcnbParser::CCN_DTAG);
-      written += AppendNumber (start, 1);
-      written += AppendCloser (start);
-    }
-  if (interest.IsCongested ())
-    {
-      written += AppendBlockHeader (start, CcnbParser::NDN_DTAG_Congested, CcnbParser::CCN_DTAG);
-      written += AppendNumber (start, 1);
+      written += AppendBlockHeader (start, CcnbParser::CCN_DTAG_Nack, CcnbParser::CCN_DTAG);
+      written += AppendNumber (start, interest.GetNack ());
       written += AppendCloser (start);
     }
   written += AppendCloser (start); // </Interest>
diff --git a/model/ccnx-content-store.cc b/model/ccnx-content-store.cc
index 110e7ac..9232ebc 100644
--- a/model/ccnx-content-store.cc
+++ b/model/ccnx-content-store.cc
@@ -150,7 +150,7 @@
 }
 
 
-Ptr<Packet>
+boost::tuple<Ptr<Packet>, Ptr<CcnxContentObjectHeader> >
 CcnxContentStore::Lookup (Ptr<const CcnxInterestHeader> interest)
 {
   NS_LOG_FUNCTION_NOARGS ();
@@ -162,9 +162,9 @@
                                            m_contentStore.project<i_mru> (it));
 
       // return fully formed CCNx packet
-      return it->GetFullyFormedCcnxPacket ();
+      return boost::make_tuple (it->GetFullyFormedCcnxPacket (), it->GetHeader ());
     }
-  return 0;
+  return boost::tuple<Ptr<Packet>, Ptr<CcnxContentObjectHeader> > (0, 0);
 }   
     
 void 
diff --git a/model/ccnx-content-store.h b/model/ccnx-content-store.h
index 0bd8fb9..df92e1c 100644
--- a/model/ccnx-content-store.h
+++ b/model/ccnx-content-store.h
@@ -35,6 +35,7 @@
 #include <boost/multi_index/sequenced_index.hpp>
 #include <boost/multi_index/hashed_index.hpp>
 #include <boost/multi_index/mem_fun.hpp>
+#include <boost/tuple/tuple.hpp>
 
 #include "ccnx.h"
 #include "hash-helper.h"
@@ -173,7 +174,7 @@
    * If an entry is found, it is promoted to the top of most recent
    * used entries index, \see m_contentStore
    */
-  Ptr<Packet>
+  boost::tuple<Ptr<Packet>, Ptr<CcnxContentObjectHeader> >
   Lookup (Ptr<const CcnxInterestHeader> interest);
             
   /**
diff --git a/model/ccnx-face.cc b/model/ccnx-face.cc
index 7821055..19eb21d 100644
--- a/model/ccnx-face.cc
+++ b/model/ccnx-face.cc
@@ -29,18 +29,6 @@
 
 namespace ns3 {
 
-// NS_OBJECT_ENSURE_REGISTERED (CcnxFace);
-
-// TypeId 
-// CcnxFace::GetTypeId (void)
-// {
-//   static TypeId tid = TypeId ("ns3::CcnxFace")
-//     .SetGroupName ("Ccnx")
-//     .SetParent<Object> ()
-//   ;
-//   return tid;
-// }
-
 /** 
  * By default, Ccnx face are created in the "down" state
  *  with no IP addresses.  Before becoming useable, the user must 
@@ -69,15 +57,6 @@
   return *this;
 }
 
-  
-// void
-// CcnxFace::DoDispose (void)
-// {
-//   NS_LOG_FUNCTION_NOARGS ();
-//   m_node = 0;
-//   Object::DoDispose ();
-// }
-
 void 
 CcnxFace::SetNode (Ptr<Node> node)
 {
@@ -132,15 +111,10 @@
 }
 
 bool
-CcnxFace::IsLocal() const
-{
-    return m_isLocal;
-}
-
-bool
 CcnxFace::operator== (const CcnxFace &face) const
 {
-  NS_ASSERT_MSG (m_node->GetId () == face.m_node->GetId (), "Faces of different nodes should not be compared to each other");
+  NS_ASSERT_MSG (m_node->GetId () == face.m_node->GetId (),
+                 "Faces of different nodes should not be compared to each other");
 
   return (m_id == face.m_id);
 }
@@ -148,7 +122,8 @@
 bool
 CcnxFace::operator< (const CcnxFace &face) const
 {
-  NS_ASSERT_MSG (m_node->GetId () == face.m_node->GetId (), "Faces of different nodes should not be compared to each other");
+  NS_ASSERT_MSG (m_node->GetId () == face.m_node->GetId (),
+                 "Faces of different nodes should not be compared to each other");
 
   return (m_id < face.m_id);
 }
diff --git a/model/ccnx-face.h b/model/ccnx-face.h
index 481a55d..81700df 100644
--- a/model/ccnx-face.h
+++ b/model/ccnx-face.h
@@ -57,13 +57,6 @@
    * \param packet Received packet
    */
   typedef Callback<void,const Ptr<CcnxFace>&,const Ptr<const Packet>& > ProtocolHandler;
-  
-  // /**
-  //  * \brief Interface ID
-  //  *
-  //  * \return interface ID
-  //  */
-  // static TypeId GetTypeId (void);
 
   /**
    * \brief Default constructor
@@ -135,11 +128,6 @@
    * \brief Returns true if this face is disabled, false otherwise.
    */
   virtual bool IsDown () const;
-
-  /**
-   * \brief Return true for LocalFace, otherwise false
-   */
-  virtual bool IsLocal () const;
   
   virtual std::ostream&
   Print (std::ostream &os) const;
@@ -179,9 +167,6 @@
    */
   bool
   operator< (const CcnxFace &face) const;
-  
-// protected:
-//   virtual void DoDispose (void);
 
 private:
   CcnxFace (const CcnxFace &); ///< \brief Disabled copy constructor
@@ -191,11 +176,10 @@
   // uint16_t m_metric; ///< \brief Routing/forwarding metric
   Ptr<Node> m_node; ///< \brief Smart pointer to Node
   ProtocolHandler m_protocolHandler; ///< Callback via which packets are getting send to CCNx stack
-  bool m_isLocal;
+
 private:
   bool m_ifup; ///< \brief flag indicating that the interface is UP 
-  uint32_t m_id; ///< \brief id of the interface in CCNx stack (per-node uniqueness)
-  
+  uint32_t m_id; ///< \brief id of the interface in CCNx stack (per-node uniqueness)  
 };
 
 std::ostream& operator<< (std::ostream& os, const CcnxFace &face);
diff --git a/model/ccnx-fib.cc b/model/ccnx-fib.cc
index 8daa034..9f23a83 100644
--- a/model/ccnx-fib.cc
+++ b/model/ccnx-fib.cc
@@ -36,6 +36,10 @@
 //#define NDN_DEBUG_OSPF	0
 //#define NDN_DEBUG_OSPF_NODES 0
 
+#include <boost/lambda/lambda.hpp>
+
+using namespace boost::lambda;
+
 //#define NDN_DUMP_FIB		0
 namespace ns3 {
 
@@ -51,48 +55,6 @@
   type;
 };
 
-struct ChangeStatus
-{
-  ChangeStatus (CcnxFibFaceMetric::Status status) : m_status (status) { }
-  void operator() (CcnxFibFaceMetric &entry)
-  {
-    entry.m_status = m_status;
-  }
-private:
-  CcnxFibFaceMetric::Status m_status;
-};
-
-struct ChangeMetric
-{
-  ChangeMetric (int32_t metric) : m_metric (metric) { }
-  void operator() (CcnxFibFaceMetric &entry)
-  {
-    entry.m_routingCost = m_metric;
-  }
-private:
-  int32_t m_metric;
-};
-
-// struct SearchByFace {
-//   /**
-//    * \brief To perform effective searches by CcnxFace
-//    */
-//   bool
-//   operator() (const CcnxFibFaceMetric &m, const Ptr<CcnxFace> &face) const
-//   {
-//     return *(m.m_face) < *face;
-//   } 
-
-//   /**
-//    * \brief To perform effective searches by CcnxFace
-//    */
-//   bool
-//   operator() (const Ptr<CcnxFace> &face, const CcnxFibFaceMetric &m) const
-//   {
-//     return *face < *(m.m_face);
-//   } 
-// };
-
 }
 //////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////
@@ -112,69 +74,63 @@
   return tid;
 }
 
+/////////////////////////////////////////////////////////////////////
+
 void
-CcnxFibFaceMetric::UpdateRtt::operator() (CcnxFibFaceMetric &entry)
+CcnxFibFaceMetric::UpdateRtt (const Time &rttSample)
 {
   // const Time & this->m_rttSample
   
   //update srtt and rttvar (RFC 2988)
-  if (entry.m_sRtt.IsZero ())
+  if (m_sRtt.IsZero ())
     {
       //first RTT measurement
-      NS_ASSERT_MSG (entry.m_rttVar.IsZero (), "SRTT is zero, but variation is not");
+      NS_ASSERT_MSG (m_rttVar.IsZero (), "SRTT is zero, but variation is not");
       
-      entry.m_sRtt = m_rttSample;
-      entry.m_rttVar = Time (entry.m_sRtt / 2.0);
+      m_sRtt = rttSample;
+      m_rttVar = Time (m_sRtt / 2.0);
     }
   else
     {
-      entry.m_rttVar = Time ((1 - NDN_RTO_BETA) * entry.m_rttVar + NDN_RTO_BETA * Abs(entry.m_sRtt - m_rttSample));
-      entry.m_sRtt = Time ((1 - NDN_RTO_ALPHA) * entry.m_sRtt + NDN_RTO_ALPHA * m_rttSample);
+      m_rttVar = Time ((1 - NDN_RTO_BETA) * m_rttVar + NDN_RTO_BETA * Abs(m_sRtt - rttSample));
+      m_sRtt = Time ((1 - NDN_RTO_ALPHA) * m_sRtt + NDN_RTO_ALPHA * rttSample);
     }
 }
 
+/////////////////////////////////////////////////////////////////////
+
 void
-CcnxFibEntry::UpdateStatus::operator () (CcnxFibEntry &entry)
+CcnxFibEntry::UpdateStatus (const CcnxFace &face, CcnxFibFaceMetric::Status status)
 {
-  CcnxFibFaceMetricByFace::type::iterator record = entry.m_faces.get<i_face> ().find (m_face);
-  NS_ASSERT_MSG (record != entry.m_faces.get<i_face> ().end (),
+  CcnxFibFaceMetricByFace::type::iterator record = m_faces.get<i_face> ().find (face);
+  NS_ASSERT_MSG (record != m_faces.get<i_face> ().end (),
                  "Update status can be performed only on existing faces of CcxnFibEntry");
 
-  entry.m_faces.modify (record, ChangeStatus (m_status));
+  m_faces.modify (record, _1->m_status = status);
 
   // reordering random access index same way as by metric index
-  entry.m_faces.get<i_nth> ().rearrange (entry.m_faces.get<i_metric> ().begin ());
+  m_faces.get<i_nth> ().rearrange (m_faces.get<i_metric> ().begin ());
 }
 
-void
-CcnxFibEntry::AddOrUpdateRoutingMetric::operator () (CcnxFibEntry &entry)
-{
-  NS_LOG_FUNCTION(this);
-  NS_ASSERT_MSG (m_face != NULL, "Trying to Add or Update NULL face");
+// void
+// CcnxFibEntry::AddOrUpdateRoutingMetric (Ptr<CcnxFace> face, int32_t metric)
+// {
+//   NS_LOG_FUNCTION(this);
+//   NS_ASSERT_MSG (m_face != NULL, "Trying to Add or Update NULL face");
 
-  CcnxFibFaceMetricByFace::type::iterator record = entry.m_faces.get<i_face> ().find (m_face);
-  if (record == entry.m_faces.get<i_face> ().end ())
-    {
-      entry.m_faces.insert (CcnxFibFaceMetric (m_face, m_metric));
-    }
-  else
-  {
-      entry.m_faces.modify (record, ChangeMetric (m_metric));
-    }
-  // reordering random access index same way as by metric index
-  entry.m_faces.get<i_nth> ().rearrange (entry.m_faces.get<i_metric> ().begin ());
-}
+//   CcnxFibFaceMetricByFace::type::iterator record = entry.m_faces.get<i_face> ().find (m_face);
+//   if (record == entry.m_faces.get<i_face> ().end ())
+//     {
+//       entry.m_faces.insert (CcnxFibFaceMetric (m_face, m_metric));
+//     }
+//   else
+//   {
+//       entry.m_faces.modify (record, ChangeMetric (m_metric));
+//     }
+//   // reordering random access index same way as by metric index
+//   entry.m_faces.get<i_nth> ().rearrange (entry.m_faces.get<i_metric> ().begin ());
+// }
 
-void
-CcnxFibEntry::UpdateFaceRtt::operator() (CcnxFibEntry &entry)
-{
-  CcnxFibFaceMetricContainer::type::iterator metric = entry.m_faces.find (m_face);
-  NS_ASSERT_MSG (metric != entry.m_faces.end (),
-                 "Something wrong. Cannot find entry for the face in FIB");
-
-  entry.m_faces.modify (metric, CcnxFibFaceMetric::UpdateRtt (m_rttSample));
-}
-    
 Ptr<CcnxFace>
 CcnxFibEntry::FindBestCandidate (int skip/* = 0*/) const
 {
diff --git a/model/ccnx-fib.h b/model/ccnx-fib.h
index afb86a3..4410b59 100644
--- a/model/ccnx-fib.h
+++ b/model/ccnx-fib.h
@@ -81,16 +81,11 @@
   GetFace () const { return m_face; }
 
   /**
-   * \brief Unary function to recalculate smoothed RTT and RTT variation
+   * \brief Recalculate smoothed RTT and RTT variation
    * \param rttSample RTT sample
    */
-  struct UpdateRtt
-  {
-    UpdateRtt (const Time &rttSample) : m_rttSample (rttSample) {};
-    void operator() (CcnxFibFaceMetric &entry);
-  private:
-    const Time &m_rttSample;
-  };
+  void
+  UpdateRtt (const Time &rttSample);
   
 private:
   friend std::ostream& operator<< (std::ostream& os, const CcnxFibFaceMetric &metric);
@@ -98,9 +93,9 @@
   Ptr<CcnxFace> m_face; ///< Face
   
   Status m_status;		///< \brief Status of the next hop: 
-						///<		- NDN_FIB_GREEN
-						///<		- NDN_FIB_YELLOW
-						///<		- NDN_FIB_RED
+				///<		- NDN_FIB_GREEN
+				///<		- NDN_FIB_YELLOW
+				///<		- NDN_FIB_RED
   
   int32_t m_routingCost; ///< \brief routing protocol cost (interpretation of the value depends on the underlying routing protocol)
 
@@ -165,46 +160,17 @@
   { }
 	
   /**
-   * \brief Unary function to update status of FIB next hop
+   * \brief Update status of FIB next hop
+   * \param status Status to set on the FIB entry
    */
-  struct UpdateStatus
-  {
-    UpdateStatus (Ptr<CcnxFace> face, CcnxFibFaceMetric::Status status)
-      : m_face (face), m_status (status) {}
-    void operator () (CcnxFibEntry &entry);
-  private:
-    Ptr<CcnxFace> m_face;
-    CcnxFibFaceMetric::Status m_status;
-  };
+  void UpdateStatus (const CcnxFace &face, CcnxFibFaceMetric::Status status);
 
-  /**
-   * \brief Unary function to add or update routing metric of FIB next hop
-   *
-   * Initial status of the next hop is set to YELLOW
-   */
-  struct AddOrUpdateRoutingMetric
-  {
-    AddOrUpdateRoutingMetric (Ptr<CcnxFace> face, int32_t metric)
-      : m_face (face), m_metric (metric) {}
-    void operator () (CcnxFibEntry &entry);
-  private:
-    Ptr<CcnxFace> m_face;
-    int32_t m_metric;
-  };
-
-  /**
-   * \brief Unary function to recalculate smoothed RTT and RTT variation
-   * \param rttSample RTT sample
-   */
-  struct UpdateFaceRtt
-  {
-    UpdateFaceRtt (Ptr<CcnxFace> face, const Time &rttSample)
-      : m_face (face), m_rttSample (rttSample) {};
-    void operator() (CcnxFibEntry &entry);
-  private:
-    Ptr<CcnxFace> m_face;
-    const Time &m_rttSample;
-  };
+  // /**
+  //  * \brief Add or update routing metric of FIB next hop
+  //  *
+  //  * Initial status of the next hop is set to YELLOW
+  //  */
+  // void AddOrUpdateRoutingMetric (Ptr<CcnxFace> face, int32_t metric);
 
   /**
    * \brief Get prefix for the FIB entry
@@ -276,15 +242,6 @@
   CcnxFib ();
    // * \param node smart pointer to Ccnx stack associated with particular node
 
-  // // Invalidate entries in FIB
-  // // Will leave FIB records in hash, but assign metric=NETWORK_UNREACHABLE
-  // void invalidate( );
-
-  // //Find corresponding FIB entry for the given content name
-  // //Longest match is performed
-  // FibIterator lookup( const string &name );
-  // bool isValid( const FibIterator &it ) { return it!=_fib.end(); }
-
   /**
    * \brief Perform longest prefix match
    *
@@ -309,21 +266,6 @@
    */
   CcnxFibEntryContainer::type::iterator
   Add (const CcnxNameComponents &prefix, Ptr<CcnxFace> face, int32_t metric);
-  // bool update( const string &name, int interfaceIndex, int metric);
-  // bool update( NodeAddress nodeId, int interfaceIndex, int metric);
-  // Bool update( NodeAddress nodeId, int metric, NodeAddress nextHop );
-
-  // // Update Fib from OSPF routing table (through a hack in OSPF algorithm)
-  // void updateFibFromOSPFv2( int interface );
-
-  // // Update Fib from BGP routing table (using info from RibIn)
-  // void updateFibFromBGP( );
-
-  // // Update Fib from IP routing table
-  // void updateFibFromIpRouting( );
-
-  // void dump( );
-  // void dump( const FibIterator &fib );
 
   // void resetProbing();    //reset needsProbing field for every FibEntry
 
diff --git a/model/ccnx-interest-header.cc b/model/ccnx-interest-header.cc
index f6295aa..cc21412 100644
--- a/model/ccnx-interest-header.cc
+++ b/model/ccnx-interest-header.cc
@@ -56,8 +56,7 @@
   , m_scope (-1)
   , m_interestLifetime (Seconds (0))
   , m_nonce (0)
-  , m_nack (false)
-  , m_congested (false)
+  , m_nackType (NORMAL_INTEREST)
 {
 }
 
@@ -178,28 +177,16 @@
 }
 
 void
-CcnxInterestHeader::SetNack (bool isNack)
+CcnxInterestHeader::SetNack (uint32_t nackType)
 {
-  m_nack = isNack;
+  m_nackType = nackType;
 }
-    
-bool
+
+uint32_t
 CcnxInterestHeader::IsNack () const
 {
   return m_nack;
 }
- 
-void
-CcnxInterestHeader::SetCongested (bool IsCongested)
-{
-  m_congested = IsCongested;
-}
-    
-bool
-CcnxInterestHeader::IsCongested () const
-{
-  return m_congested;
-}
 
 uint32_t
 CcnxInterestHeader::GetSerializedSize (void) const
@@ -232,10 +219,23 @@
 CcnxInterestHeader::Print (std::ostream &os) const
 {
   os << "<Interest>\n  <Name>" << GetName () << "</Name>\n";
-  if (IsNack ())
-    os << "  <NACK />\n";
-  if(IsCongested())
-    os << "  <CONGESTED />\n";
+  if (GetNack ()>0)
+    {
+      os << "  <NACK>";
+      switch (GetNack ())
+        {
+        case NACK_LOOP:
+          os << "loop";
+          break;
+        case NACK_CONGESTION:
+          os << "congestion";
+          break;
+        default:
+          os << "unknown";
+          break;
+        }
+      os << "</NACK>\n";
+    }
   if (GetMinSuffixComponents () >= 0)
     os << "  <MinSuffixComponents>" << GetMinSuffixComponents () << "</MinSuffixComponents>\n";
   if (GetMaxSuffixComponents () >= 0)
diff --git a/model/ccnx-interest-header.h b/model/ccnx-interest-header.h
index 4bfebe3..d7be5fe 100644
--- a/model/ccnx-interest-header.h
+++ b/model/ccnx-interest-header.h
@@ -202,17 +202,18 @@
   uint32_t
   GetNonce () const;
     
+  enum
+    {
+      NORMAL_INTEREST = 0,
+      NACK_LOOP = 1,
+      NACK_CONGESTION,
+    };
+
   void
-  SetNack (bool isNack);
+  SetNack (uint32_t nackType);
     
-  bool
-  IsNack () const;
-    
-  void
-  SetCongested (bool IsCongested);
-    
-  bool
-  IsCongested () const;
+  uint32_t
+  GetNack () const;
 
   //////////////////////////////////////////////////////////////////
   
@@ -233,8 +234,7 @@
   int8_t m_scope;            ///< -1 not set, 0 local scope, 1 this host, 2 immediate neighborhood
   Time  m_interestLifetime;
   uint32_t m_nonce; ///< Nonce. not used if zero
-  bool m_nack; ///< is Negative ACK
-  bool m_congested; ///< NACK because of congestion 
+  uint32_t m_nackType; 
 };
 
 class CcnxInterestHeaderException {};
diff --git a/model/ccnx-l3-protocol.cc b/model/ccnx-l3-protocol.cc
index 63c9049..a479390 100644
--- a/model/ccnx-l3-protocol.cc
+++ b/model/ccnx-l3-protocol.cc
@@ -41,6 +41,10 @@
 #include "ccnx-net-device-face.h"
 
 #include <boost/foreach.hpp>
+#include <boost/lambda/lambda.hpp>
+
+using namespace boost::tuples;
+using namespace boost::lambda;
 
 NS_LOG_COMPONENT_DEFINE ("CcnxL3Protocol");
 
@@ -257,8 +261,11 @@
             // Deserialization. Exception may be thrown
             packet->RemoveHeader (*header);
             NS_ASSERT_MSG (packet->GetSize () == 0, "Payload of Interests should be zero");
-            
-            OnInterest (face, header, p/*original packet*/);  
+
+            if (header->GetNack () > 0)
+              OnNack (face, header, p/*original packet*/);
+            else
+              OnInterest (face, header, p/*original packet*/);  
             break;
           }
         case CcnxHeaderHelper::CONTENT_OBJECT:
@@ -284,376 +291,199 @@
     }
 }
 
+void
+CcnxL3Protocol::OnNack (const Ptr<CcnxFace> &face,
+                        Ptr<CcnxInterestHeader> &header,
+                        const Ptr<const Packet> &p)
+{
+  NS_LOG_FUNCTION (face << header << p);
+
+  // Huh... Ignore all this for now
+  
+  /*if( header->IsCongested () == false )
+    m_pit->LeakBucket(incomingFace,1);
+        
+        
+    m_droppedInterestsTrace (header, DROP_CONGESTION,
+    m_node->GetObject<Ccnx> (), incomingFace);
+        
+    m_pit->modify(pitEntry, CcnxPitEntry::DeleteOutgoing(incomingFace));*/
+
+  // No matter is it duplicate or not, if it is a NACK message, remove all possible incoming
+  // entries for this interface (NACK means that neighbor gave up trying and there is no
+  // point of sending data in this direction)
+  
+  // NS_LOG_INFO("Before (header->IsNack()) && (pitEntry != m_pit->end ())");
+  // if ((header->IsNack()) && (pitEntry != m_pit->end ()))
+  //   {
+  //     //m_pit->erase (pitEntry);
+  //     NS_LOG_INFO("TRUE");
+  //     m_pit->modify(pitEntry, CcnxPitEntry::DeleteIncoming(incomingFace));
+  //   }
+
+  //   m_fib->modify (m_fib->iterator_to (pitEntry->m_fibEntry),
+  // CcnxFibEntry::UpdateStatus(incomingFace, CcnxFibFaceMetric::NDN_FIB_YELLOW));
+
+  // if (!pitEntry.AreThereMoreFacesToTry ())
+  //   {
+  //     BOOST_FOREACH (const CcnxPitEntryIncomingFace face, pitEntry.m_incoming)
+  //       {
+  //         // check all entries if the name of RIT entry matches the name of interest
+  //         for (CcnxRitByNonce::type::iterator it = m_rit->begin(); it != m_rit->end(); it++)
+  //           {
+  //             if (it->m_prefix == iter->GetPrefix() )
+  //               {
+  //                 header->SetNonce(it->m_nonce);
+  //                 header->SetNack(true);
+  //                 SendInterest(face.m_face, header, packet->Copy());
+  //               }
+  //           }
+  //       }
+
+  //     m_pit->erase(pitEntry);
+
+  //     return;
+  //   }
+}
+
 // Processing Interests
+//
+// !!! Key point.
+// !!! All interests should be answerred!!! Either later with data, immediately with data, or immediately with NACK
 void CcnxL3Protocol::OnInterest (const Ptr<CcnxFace> &incomingFace,
                                  Ptr<CcnxInterestHeader> &header,
                                  const Ptr<const Packet> &packet)
 {
-  NS_LOG_LOGIC ("Receiving interest from " << &incomingFace);
-  m_receivedInterestsTrace (header, m_node->GetObject<Ccnx> (), incomingFace);
+  NS_LOG_FUNCTION (incomingFace << header << packet);
+  // m_receivedInterestsTrace (header, m_node->GetObject<Ccnx> (), incomingFace);
 
-    
-    if( header->IsNack () )
-    {
-        NS_LOG_INFO("============");
-        NS_LOG_INFO("NACK");
-        NS_LOG_INFO("==========");
-        /*if( header->IsCongested () == false )
-            m_pit->LeakBucket(incomingFace,1);
-        
-        
-        m_droppedInterestsTrace (header, DROP_CONGESTION,
-                                 m_node->GetObject<Ccnx> (), incomingFace);
-        
-        m_pit->modify(pitEntry, CcnxPitEntry::DeleteOutgoing(incomingFace));*/
-    }
+  // Lookup of Pit (and associated Fib) entry for this Interest 
+  const CcnxPitEntry &pitEntry;
+  bool isNew;
+  bool isDuplicated;
+  tie (pitEntry, isNew, isDuplicated) = m_pit->Lookup (*header);
 
-    
-    
-    
-  // Lookup of Pit and Fib entries for this Interest 
-  CcnxFibEntryContainer::type::iterator fibEntry;
-  CcnxPitEntryContainer::type::iterator pitEntry = m_pit->Lookup (*header, fibEntry);  
-    
-  // No matter is it duplicate or not, if it is a NACK message, remove all possible incoming
-  // entries for this interface (NACK means that neighbor gave up trying and there is no
-  // point of sending data in this direction)
-    NS_LOG_INFO("Before (header->IsNack()) && (pitEntry != m_pit->end ())");
-  if ((header->IsNack()) && (pitEntry != m_pit->end ()))
+  if (isDuplicated) 
     {
-      //m_pit->erase (pitEntry);
-        NS_LOG_INFO("TRUE");
-        m_pit->modify(pitEntry, CcnxPitEntry::DeleteIncoming(incomingFace));
-    }
-    
-    NS_LOG_INFO("Before WasRecentlySatisfied");
-    /*if (m_rit->WasRecentlySatisfied (*header))
-    {
-        return;
-    }*/
-  if (m_rit->WasRecentlySatisfied (*header))
-    {
-        NS_LOG_INFO("------------");
-        NS_LOG_INFO("Entering WasRecentlySatisfied");
-        NS_LOG_INFO("------------");
-      // duplicate interests (same nonce) from applications are just ignored
-      if (incomingFace->IsLocal() == true) 
-          return;
-        
-      // Update metric status for the incoming interface in the corresponding FIB entry
-      /*if (fibEntry != m_fib->end())
-        m_fib->modify (m_fib->iterator_to (pitEntry->m_fibEntry),
-                       CcnxFibEntry::UpdateStatus(incomingFace, CcnxFibFaceMetric::NDN_FIB_YELLOW));
+      /**
+       * This condition will handle "routing" loops and also recently satisfied interests.
+       * Every time interest is satisfied, PIT entry (with empty incoming and outgoing faces)
+       * is kept for another small chunk of time.
+       */
       
-      //Trace duplicate interest  
-      m_droppedInterestsTrace (header, NDN_DUPLICATE_INTEREST,
-                               m_node->GetObject<Ccnx> (), incomingFace);
-        
-      bool isMine = false;
-      //TypeId tid = TypeId ("ns3::CcnxProducer");
-      for(uint32_t i=0; i<m_node->GetNApplications();i++)
-        {
-            Ptr<Application> app = m_node->GetApplication(i);
-            NS_LOG_INFO("ApplicationName = " << app->GetTypeId().GetName());
-            if(app->GetTypeId().GetName() == "ns3::CcnxProducer")
-              {
-                if((DynamicCast<CcnxProducer>(app))->GetPrefix () == header->GetName ())
-                {
-                    isMine = true;
-                    break;
-                }
-              }
-        }
-    
-      Ptr<Packet> contentObject = m_contentStore->Lookup (header);
-      if ((isMine == true) || (contentObject != NULL))
-        {
-            //never respond with NACK to NACK
-            if(header->IsNack () )
-                return;
-            
-            // always return a duplicate packet
-            header->SetNack(true);
-            //Trace duplicate interest  
-            m_droppedInterestsTrace (header, NDN_DUPLICATE_INTEREST,
-                                     m_node->GetObject<Ccnx> (), incomingFace);
+      // //Trace duplicate interest  
+      // m_droppedInterestsTrace (header, NDN_DUPLICATE_INTEREST, m_node->GetObject<Ccnx> (), incomingFace);
 
-            SendInterest(incomingFace, header, packet->Copy());
-			
-			return;
-        }  
-           
-        
-      // check PIT.  or there is no outgoing entry for this interface,
-      // silently drop the duplicate packet
-        
-      // If no entry found, silently drop
-      if( pitEntry == m_pit->end() ) 
-        return;
-        
-      // If PIT entry timed out, silently drop
-      if( pitEntry->m_timerExpired == true ) 
-        return;
-        
-      // loop?
-        
-      // Check if there is no outgoing entry for the interface or different nonce
-      // (i.e., got a duplicate packet, but we haven't sent interest to this
-      // interface)
-      //
-      // This case means that there is a loop in the network.
-      // So, prune this link, but do not remove PIT entry
-        
-      // Alex, check this condition!!
-      if(pitEntry->m_outgoing.size () == 0)
-        {
-            //never respond with NACK to NACK
-            if(header->IsNack () )
-                return;
-            
-            // always return a duplicate packet
-            header->SetNack(true);
-            //Trace duplicate interest  
-            m_droppedInterestsTrace (header, NDN_DUPLICATE_INTEREST,
-                                     m_node->GetObject<Ccnx> (), incomingFace);
+      header->SetNack (CcnxInterestHeader::NACK_LOOP);
+      Ptr<Packet> packet = Create<Packet> ();
+      packet->AddHeader (*header);
 
-            SendInterest(incomingFace, header, packet->Copy());
-            return;
-        }
+      SendInterest (m_incomingFace, header, packet);
       
-        
-        // At this point:
-		// - there is a non-expired PIT entry,
-		// - there is an outgoing interest to the interface, and
-		// - a nonce in outgoing entry is equal to a nonce in the received duplicate packet
-        
-		// Should perform:
-		// Cleaning outgoing entry
-		// If there are no outgoing interests and available interfaces left (pe->availableInterfaces),
-		// prune all incoming interests, otherwise allow forwarding of the interest
-		if( header->IsNack () )
-		{
-			if( header->IsCongested () == false )
-                m_pit->LeakBucket(incomingFace,1);
-            
-            m_pit->modify(pitEntry, CcnxPitEntry::DeleteOutgoing(incomingFace));
-        }
-		else
-		{
-			//poit->waitingInVain = true;
-		}
-
-        
-        // prune all incoming interests
-        if((pitEntry->m_outgoing.size() ==0) && (pitEntry->m_fibEntry.m_faces.size() == 0))
-        {
-            BOOST_FOREACH (const CcnxPitEntryIncomingFace face, pitEntry->m_incoming)
-            {
-                if(face.m_face->IsLocal() == false)
-                {
-                  // check all entries if the name of RIT entry matches the name of interest
-                  for (CcnxRitByNonce::type::iterator it = m_rit->begin(); it != m_rit->end(); it++)
-                  {
-                    if (it->m_prefix == pitEntry->GetPrefix() )
-                      {
-                        
-                        header->SetNonce(it->m_nonce);
-                        header->SetNack(true);
-                        SendInterest(face.m_face, header, packet->Copy());
-                        break;
-                      }
-                  }
-                }
-             }
-            
-            // Finally, remote the PIT entry
-            m_pit->erase (pitEntry);
-            
-            return; // stop processing
-        }
-        
-      if(pitEntry->m_fibEntry.m_faces.size() == 0)  
-        return;*/
-        return;
-    }
-    
-    
-    
-  // Otherwise,
-  // propagate the interest
-  //
-  // method `propagateInterest' can/should try different interface
-  // from `availableInterfaces' list
-    
-  NS_LOG_INFO("Before SetRecentlySatisfied");
-  m_rit->SetRecentlySatisfied (*header); 
-
-  NS_LOG_INFO("Cache Lookup for " << header->GetName());
-  Ptr<Packet> contentObject = m_contentStore->Lookup (header);
-  if (contentObject != NULL)
-    {
-      NS_LOG_INFO("Found in cache");
-        
-      TransmittedDataTrace (contentObject, CACHED,
-                            m_node->GetObject<Ccnx> (), incomingFace);
-      incomingFace->Send (contentObject);
+      // //Trace duplicate interest  
+      // m_droppedInterestsTrace (header, NDN_DUPLICATE_INTEREST, m_node->GetObject<Ccnx> (), incomingFace);
       return;
     }
+
+  Ptr<Packet> contentObject;
+  Ptr<CcnxContentObjectHeader> contentObjectHeader;
+  tie (contentObject, contentObjectHeader) = m_contentStore->Lookup (header);
+  if (contentObject != 0)
+    {
+      NS_ASSERT_MSG (pitEntry.m_incoming.size () == 0,
+                     "Something strange. Data is cached, but size of incoming interests is not zero...");
+      NS_ASSERT (contentObjectHeader != 0);
+      
+      NS_LOG_LOGIC("Found in cache");
+        
+      // TransmittedDataTrace (contentObject, CACHED,
+      //                       m_node->GetObject<Ccnx> (), incomingFace);
+      SendContentObject (incomingFace, contentObjectHeader, contentObject);
+
+      // Set pruning timout on PIT entry (instead of deleting the record)
+      m_pit->modify (m_pit->iterator_to (pitEntry),
+                     boost::bind (&CcnxPitEntry::SetExpireTime, _1,
+                                  Simulator::Now () + m_pit->GetPitEntryPruningTimeout ()));
+      return;
+    }
+
+  // \todo Detect retransmissions. Not yet sure how...
   
   // Data is not in cache
-  NS_LOG_INFO("Before inFace and OutFace");
-  CcnxPitEntryIncomingFaceContainer::type::iterator inFace = pitEntry->m_incoming.find (incomingFace);
-  CcnxPitEntryOutgoingFaceContainer::type::iterator outFace = pitEntry->m_outgoing.find (incomingFace);
-    
-  NS_LOG_INFO("Before (pitEntry != m_pit->end()) && (pitEntry->m_timerExpired == false)");
-  if ((pitEntry != m_pit->end()) && (pitEntry->m_timerExpired == false))
+  CcnxPitEntryIncomingFaceContainer::type::iterator inFace = pitEntry.m_incoming.find (incomingFace);
+  CcnxPitEntryOutgoingFaceContainer::type::iterator outFace = pitEntry.m_outgoing.find (incomingFace);
+
+  if (inFace != pitEntry.m_incoming.end ())
     {
-        NS_LOG_INFO("Entering (pitEntry != m_pit->end()) && (pitEntry->m_timerExpired == false)");
-        
-        if(inFace->m_face == 0)
-            NS_LOG_INFO("in face is null");
-        if(outFace->m_face == 0)
-            NS_LOG_INFO("outface is null");
-        if(outFace == pitEntry->m_outgoing.end())
-            NS_LOG_INFO("OUTFACE = END");
-        
-        // If we're expecting data from the interface we got the interest from ("producer" asks us for "his own" data)
-        // Give up this interface, but keep a small hope when the returned packet doesn't have PRUNE status
-        if(outFace != pitEntry->m_outgoing.end()) // this is correct
-        {
-            NS_LOG_INFO("Entering outFace != pitEntry->m_outgoing.end()");
-            if( header->IsCongested() == true )
-            {
-                NS_LOG_INFO("Entering header->IsCongested() == true");
-                m_pit->LeakBucket(incomingFace, 1);
-                m_pit->modify (pitEntry, CcnxPitEntry::DeleteOutgoing(outFace->m_face));
-            }
-            //else
-            //    poit->waitingInVain = true;
-            
-            // Update metric status for the incoming interface in the corresponding FIB entry
-            if(fibEntry != m_fib->end())
-                m_fib->modify(m_fib->iterator_to (pitEntry->m_fibEntry),
-                              CcnxFibEntry::UpdateStatus(incomingFace, CcnxFibFaceMetric::NDN_FIB_YELLOW));
-        }
-    }
-
-  NS_LOG_INFO("Before (pitEntry->m_outgoing.size() == 0) && (pitEntry->m_fibEntry.m_faces.size() == 0)");
-  if((pitEntry->m_outgoing.size() == 0) && (pitEntry->m_fibEntry.m_faces.size() == 0))
-      // prune all incoming interests
-    {
-        
-        for(CcnxPitEntryContainer::type::iterator iter = m_pit->begin();
-            iter != m_pit->end(); 
-            iter++)
-        {
-            /*for(CcnxPitEntryIncomingFaceContainer::type::iterator face = iter->m_incoming.begin();
-                face != iter->m_incoming.end();
-                face++)*/
-            BOOST_FOREACH (const CcnxPitEntryIncomingFace face, iter->m_incoming)
-            {
-              if(face.m_face->IsLocal() == true)
-                {
-                    //returnInterestToApp( pkt, -piit->interfaceIndex );
-                    //continue;
-                }
-                
-                // check all entries if the name of RIT entry matches the name of interest
-                for (CcnxRitByNonce::type::iterator it = m_rit->begin(); it != m_rit->end(); it++)
-              {
-                if (it->m_prefix == iter->GetPrefix() )
-                  {
-                    header->SetNonce(it->m_nonce);
-                    header->SetNack(true);
-                    SendInterest(face.m_face, header, packet->Copy());
-                  }
-              }
-            }
-
-        }
-            
-        m_pit->erase(pitEntry);
-        
-        return; // there is nothing else to do
-    }
-
-    // Suppress this interest only if we're still expecting data from some other interface
-    if( pitEntry->m_outgoing.size() > 0 ) 
-    {
-        return; //ok. Now we can suppress this interest
-    }
-    
-    
-    // Prune and delete PIT entry if there are no available interfaces to propagate interest
-    if( pitEntry->m_fibEntry.m_faces.size() == 0)
-    {
-        //if no match is found in the FIB, drop packet
-        //printf( "Node %d: cannot process Interest packet %s (no interfaces left)\n", _node->nodeId, pkt->contentName );
-		
-        if(incomingFace->IsLocal() == false)
-        {
-            header->SetNack(true);
-            m_droppedInterestsTrace (header, NDN_SUPPRESSED_INTEREST,
-                                     m_node->GetObject<Ccnx> (), incomingFace);
-            SendInterest(incomingFace, header, packet->Copy());
-        }
-        
-        m_pit->erase(pitEntry);
-        
-    }
-
-    
-    
-    // otherwise, try one of the available interfaces
-    
-  // suppress interest if 
-  /*if (pitEntry->m_incoming.size () != 0 && // not a new PIT entry and
-      inFace != pitEntry->m_incoming.end ()) // existing entry, but interest received via different face
-    {
-      m_droppedInterestsTrace (header, NDN_SUPPRESSED_INTEREST,
-                                m_node->GetObject<Ccnx> (), incomingFace);
-      return;
-    }*/
-    
-    
-    //just in case of bug
-    header->SetNack(false);
-    header->SetCongested(false);
-  
-    NS_ASSERT_MSG (m_forwardingStrategy != 0, "Need a forwarding protocol object to process packets");
-
-    m_pit->modify (pitEntry, CcnxPitEntry::AddIncoming(incomingFace));
-    
-    bool propagated = m_forwardingStrategy->
-                      PropagateInterest (pitEntry, fibEntry,incomingFace, header, packet,
-                                         MakeCallback (&CcnxL3Protocol::SendInterest, this)
-                                        );
-
-    // If interest wasn't propagated further (probably, a limit is reached),
-    // prune and delete PIT entry if there are no outstanding interests.
-    // Stop processing otherwise.
-    if( (!propagated) && (pitEntry->m_outgoing.size() == 0)) // this line works
-      {
-        BOOST_FOREACH (const CcnxPitEntryIncomingFace face, pitEntry->m_incoming)
-          {
-            header->SetNack(true);
-            header->SetCongested(true);
-            NS_LOG_INFO("Sending CONGESTION packet");
-            SendInterest (face.m_face, header, packet->Copy());
-                
-            m_droppedInterestsTrace (header, DROP_CONGESTION,
-                                         m_node->GetObject<Ccnx> (), incomingFace);
-          }
+      // CcnxPitEntryIncomingFace.m_arrivalTime keeps track arrival time of the first packet... why?
       
-          m_pit->erase (pitEntry);
-      }
-    /*}
+      // this is almost definitely a retransmission. But should we trust the user on that?
+    }
   else
     {
-      m_droppedInterestsTrace (header, NDN_PIT_TIMER_EXPIRED,
-                                 m_node->GetObject<Ccnx> (), incomingFace);
-      return;
-    }*/
+      m_pit->modify (m_pit->iterator_to (m_pitpitEntry),
+                    iface = _1->m_incoming.insert (CcnxPitEntryIncoming (incomingFace, Simulator::Now ())));
+    }
+
+  if (outFace != pitEntry.m_outgoing.end ())
+    {
+      // got a non-duplicate interest from the face we have sent interest to
+      // Probably, there is no point in waiting data from that face... Not sure yet
+
+      // If we're expecting data from the interface we got the interest from ("producer" asks us for "his own" data)
+      // Mark interface YELLOW, but keep a small hope that data will come eventually.
+
+      // ?? not sure if we need to do that ?? ...
+      
+      m_fib->modify(m_fib->iterator_to (pitEntry.m_fibEntry),
+                    boost::bind (&CcnxFibEntry::UpdateStatus,
+                                 _1, incomingFace, CcnxFibFaceMetric::NDN_FIB_YELLOW));
+
+      // suppress?
+    }
+  else if (pitEntry->m_outgoing.size() > 0) // Suppress this interest if we're still expecting data from some other face
+
+    {
+      // We are already expecting data later in future. Suppress the interest
+      // m_droppedInterestsTrace (header, NDN_SUPPRESSED_INTEREST, m_node->GetObject<Ccnx> (), incomingFace);
+      return; 
+    }
+
+  /////////////////////////////////////////////////////////////////////
+  // Propagate
+  /////////////////////////////////////////////////////////////////////
+  
+  NS_ASSERT_MSG (m_forwardingStrategy != 0, "Need a forwarding protocol object to process packets");
+  
+  bool propagated = m_forwardingStrategy->
+    PropagateInterest (pitEntry, fibEntry,incomingFace, header, packet,
+                       MakeCallback (&CcnxL3Protocol::SendInterest, this)
+                       );
+
+  // ForwardingStrategy will try its best to forward packet to at least one interface.
+  // If no interests was propagated, then there is not other option for forwarding or
+  // ForwardingStrategy failed to find it. 
+  if (!propagated)
+    {
+      Ptr<Packet> packet = Create<Packet> ();
+      header->SetNack (CcnxInterestHeader::NACK_CONGESTION);
+      packet.AddHeader (*header);
+
+      while (pitEntry.m_incoming.size () > 0)
+        {
+          SendInterest (pitEntry.m_incoming.front ().m_face, header, packet->Copy ());
+
+          pitEntry.m_incoming.pop_front ();
+
+          // m_droppedInterestsTrace (header, DROP_CONGESTION,
+          //                          m_node->GetObject<Ccnx> (), incomingFace);
+        }
+
+      // Set pruning timout on PIT entry (instead of deleting the record)
+      m_pit->modify (m_pit->iterator_to (pitEntry),
+                     boost::bind (&CcnxPitEntry::SetExpireTime, _1,
+                                  Simulator::Now () + m_pit->GetPitEntryPruningTimeout ()));
+    }
 }
 
 // Processing ContentObjects
@@ -663,8 +493,8 @@
                              const Ptr<const Packet> &packet)
 {
     
-  NS_LOG_LOGIC ("Receiving contentObject from " << &incomingFace);
-  m_receivedDataTrace (header, payload, m_node->GetObject<Ccnx> ()/*this*/, incomingFace);
+  NS_LOG_FUNCTION (incomingFace << header, payload, packet);
+  // m_receivedDataTrace (header, payload, m_node->GetObject<Ccnx> ()/*this*/, incomingFace);
 
   // 1. Lookup PIT entry
   try
@@ -675,46 +505,48 @@
   
       // Update metric status for the incoming interface in the corresponding FIB entry
       m_fib->modify (m_fib->iterator_to (pitEntry.m_fibEntry),
-                     CcnxFibEntry::UpdateStatus (incomingFace, CcnxFibFaceMetric::NDN_FIB_GREEN));
+                     boost::bind (CcnxFibEntry::UpdateStatus, _1, incomingFace, CcnxFibFaceMetric::NDN_FIB_GREEN));
   
       // Add or update entry in the content store
-      NS_LOG_INFO("Cached " << header->GetName());
       m_contentStore->Add (header, payload);
 
-      CcnxPitEntryOutgoingFaceContainer::type::iterator
-        out = pitEntry.m_outgoing.find (incomingFace);
+      CcnxPitEntryOutgoingFaceContainer::type::iterator out = pitEntry.m_outgoing.find (incomingFace);
   
       // If we have sent interest for this data via this face, then update stats.
       if (out != pitEntry.m_outgoing.end ())
         {
-          m_pit->modify (m_pit->iterator_to (pitEntry),
-                         CcnxPitEntry::EstimateRttAndRemoveFace(out, m_fib));
-          // face will be removed in the above call
+          m_fib->modify (m_fib->iterator_to (pitEntry.m_fibEntry),
+                         boost::bind (&CcnxFibEntry::UpdateRtt,
+                                      _1,
+                                      Simulator::Now () - out->m_sendTime));
         }
       else
         {
-          NS_LOG_WARN ("Node "<< m_node->GetId() <<
-                          ". PIT entry for "<< header->GetName ()<<" is valid, "
-                          "but outgoing entry for interface "<< incomingFace <<" doesn't exist\n");
+          // Unsolicited data, but we're interested in it... should we get it?
+          // Potential hole for attacks
+          
+          NS_LOG_ERROR ("Node "<< m_node->GetId() <<
+                       ". PIT entry for "<< header->GetName ()<<" is valid, "
+                       "but outgoing entry for interface "<< incomingFace <<" doesn't exist\n");
+
+          NS_ASSERT (false); // temporary put false here
         }
 
       //satisfy all pending incoming Interests
-      BOOST_FOREACH (const CcnxPitEntryIncomingFace &interest, pitEntry.m_incoming)
+      while (pitEntry.m_incoming.size () > 0)
         {
-          if (interest.m_face == incomingFace) continue; 
+          if (pitEntry.m_incoming.front ().m_face != incomingFace)
+            SendInterest (pitEntry.m_incoming.front ().m_face, header, packet->Copy ());
 
-          // may not work either because of 'const' thing
-          interest.m_face->Send (packet->Copy ()); // unfortunately, we have to copy packet... 
-          m_transmittedDataTrace (header, payload, FORWARDED, m_node->GetObject<Ccnx> (), interest.m_face);
+          pitEntry.m_incoming.pop_front ();
+
+          // m_transmittedDataTrace (header, payload, FORWARDED, m_node->GetObject<Ccnx> (), interest.m_face);
         }
 
-      m_pit->modify (m_pit->iterator_to (pitEntry), CcnxPitEntry::ClearIncoming()); // satisfy all incoming interests
-
-      if( pitEntry.m_outgoing.size()==0 ) // remove PIT when all outgoing interests are "satisfied"
-        {
-          m_pit->erase (m_pit->iterator_to (pitEntry));
-        }
-
+      // Set pruning timout on PIT entry (instead of deleting the record)
+      m_pit->modify (m_pit->iterator_to (pitEntry),
+                     boost::bind (&CcnxPitEntry::SetExpireTime, _1,
+                                  Simulator::Now () + m_pit->GetPitEntryPruningTimeout ()));
     }
   catch (CcnxPitEntryNotFound)
     {
@@ -722,7 +554,7 @@
       //    (unsolicited data packets should not "poison" content store)
       
       //drop dulicated or not requested data packet
-      m_droppedDataTrace (header, payload, NDN_UNSOLICITED_DATA, m_node->GetObject<Ccnx> (), incomingFace);
+      // m_droppedDataTrace (header, payload, NDN_UNSOLICITED_DATA, m_node->GetObject<Ccnx> (), incomingFace);
       return; // do not process unsoliced data packets
     }
 }
@@ -738,13 +570,13 @@
   if (face->IsUp ())
     {
       NS_LOG_LOGIC ("Sending via face " << &face); //
-      m_transmittedInterestsTrace (header, m_node->GetObject<Ccnx> (), face);
+      // m_transmittedInterestsTrace (header, m_node->GetObject<Ccnx> (), face);
       face->Send (packet);
     }
   else
     {
       NS_LOG_LOGIC ("Dropping -- outgoing interface is down: " << &face);
-      m_droppedInterestsTrace (header, INTERFACE_DOWN, m_node->GetObject<Ccnx> (), face);
+      // m_droppedInterestsTrace (header, INTERFACE_DOWN, m_node->GetObject<Ccnx> (), face);
     }
 }
 
@@ -756,8 +588,6 @@
   NS_LOG_FUNCTION (this << "packet: " << &packet << ", face: "<< &face);
   NS_ASSERT_MSG (face != 0, "Face should never be NULL");
 
-  NS_ASSERT_MSG (false, "Should not be called for now");
-  
   if (face->IsUp ())
     {
       NS_LOG_LOGIC ("Sending via face " << &face); //
@@ -777,11 +607,12 @@
     return m_pit;
 }
 
-void
-CcnxL3Protocol::ScheduleLeakage()
-{
-    m_pit->LeakBuckets();
-    Time interval = MilliSeconds (NDN_INTEREST_RESET_PERIOD);
-    Simulator::Schedule (interval, &CcnxL3Protocol::ScheduleLeakage, this);
-}
+// void
+// CcnxL3Protocol::ScheduleLeakage()
+// {
+//     m_pit->LeakBuckets();
+//     Time interval = MilliSeconds (NDN_INTEREST_RESET_PERIOD);
+    
+//     Simulator::Schedule (interval, &CcnxL3Protocol::ScheduleLeakage, this);
+// }
 } //namespace ns3
diff --git a/model/ccnx-l3-protocol.h b/model/ccnx-l3-protocol.h
index 3e2c870..3605da4 100644
--- a/model/ccnx-l3-protocol.h
+++ b/model/ccnx-l3-protocol.h
@@ -152,7 +152,7 @@
   
   Ptr<CcnxPit> GetPit();
   
-  void ScheduleLeakage();
+  // void ScheduleLeakage();
 protected:
   /**
    * \brief Actual processing of incoming CCNx interests. Note, interests do not have payload
@@ -167,6 +167,18 @@
               Ptr<CcnxInterestHeader> &header,
               const Ptr<const Packet> &p);
 
+  /**
+   * \brief Processing of incoming CCNx NACKs. Note, these packets, like interests, do not have payload
+   * 
+   * Processing NACK packets
+   * @param face    incoming face
+   * @param header  deserialized Interest header
+   * @param packet  original packet
+   */
+  virtual void
+  OnNack (const Ptr<CcnxFace> &face,
+          Ptr<CcnxInterestHeader> &header,
+          const Ptr<const Packet> &p);
   
   /**
    * \brief Actual processing of incoming CCNx content objects
@@ -196,12 +208,6 @@
   CcnxL3Protocol(const CcnxL3Protocol &); ///< copy constructor is disabled
   CcnxL3Protocol &operator = (const CcnxL3Protocol &); ///< copy operator is disabled
 
-  // /**
-  //  * \brief Fake function. should never be called. Just to trick C++ to compile
-  //  */
-  // virtual void
-  // ReceiveAndProcess (const Ptr<CcnxFace> face, Ptr<Header> header, Ptr<Packet> p);
-
   /**
    * \brief A helper function
    */
@@ -218,7 +224,7 @@
   Ptr<Node> m_node; ///< \brief node on which ccnx stack is installed
   Ptr<CcnxForwardingStrategy> m_forwardingStrategy; ///< \brief smart pointer to the selected forwarding strategy
 
-  Ptr<CcnxRit> m_rit; ///< \brief RIT (recently interest table)
+  // Ptr<CcnxRit> m_rit; ///< \brief RIT (recently interest table)
   Ptr<CcnxPit> m_pit; ///< \brief PIT (pending interest table)
   Ptr<CcnxFib> m_fib; ///< \brief FIB  
   Ptr<CcnxContentStore> m_contentStore; ///< \brief Content store (for caching purposes only)
diff --git a/model/ccnx-pit-entry-incoming-face.h b/model/ccnx-pit-entry-incoming-face.h
index 80f7e09..9a3859f 100644
--- a/model/ccnx-pit-entry-incoming-face.h
+++ b/model/ccnx-pit-entry-incoming-face.h
@@ -44,7 +44,7 @@
    * \param face face of the incoming interest
    * \param lifetime lifetime of the incoming interest
    */
-  CcnxPitEntryIncomingFace (Ptr<CcnxFace> face);
+  CcnxPitEntryIncomingFace (Ptr<CcnxFace> face, Time arrivalTime);
 
   bool operator== (const CcnxPitEntryIncomingFace &dst) { return *m_face==*(dst.m_face); }
   bool operator== (Ptr<CcnxFace> face) { return *m_face==*face; }
diff --git a/model/ccnx-pit-entry.cc b/model/ccnx-pit-entry.cc
index ddc471e..5dab9ae 100644
--- a/model/ccnx-pit-entry.cc
+++ b/model/ccnx-pit-entry.cc
@@ -27,50 +27,12 @@
 namespace ns3
 {
 
-// struct SearchByFace
-// {
-//   /**
-//    * \brief To perform effective searches by CcnxFace
-//    */
-//   bool
-//   operator() (const CcnxPitIncomingInterest &m, const Ptr<CcnxFace> &face) const
-//   {
-//     return *(m.m_face) < *face;
-//   } 
-
-//   /**
-//    * \brief To perform effective searches by CcnxFace
-//    */
-//   bool
-//   operator() (const Ptr<CcnxFace> &face, const CcnxPitIncomingInterest &m) const
-//   {
-//     return *face < *(m.m_face);
-//   } 
-
-//   /**
-//    * \brief To perform effective searches by CcnxFace
-//    */
-//   bool
-//   operator() (const CcnxPitOutgoingInterest &m, const Ptr<CcnxFace> &face) const
-//   {
-//     return *(m.m_face) < *face;
-//   } 
-
-//   /**
-//    * \brief To perform effective searches by CcnxFace
-//    */
-//   bool
-//   operator() (const Ptr<CcnxFace> &face, const CcnxPitOutgoingInterest &m) const
-//   {
-//     return *face < *(m.m_face);
-//   } 
-// };
-
-
-CcnxPitEntry::CcnxPitEntry (Ptr<CcnxNameComponents> prefix, const CcnxFibEntry &fibEntry)
+CcnxPitEntry::CcnxPitEntry (Ptr<CcnxNameComponents> prefix,
+                            const Time &expireTime,
+                            const CcnxFibEntry &fibEntry)
   : m_prefix (prefix)
   , m_fibEntry (fibEntry)
-  // , m_expireTime (?)
+  , m_expireTime (expireTime)
   , m_timerExpired (false)
   , m_counterExpirations (0)
 {
@@ -82,76 +44,42 @@
   return *m_prefix;
 }
 
-// CcnxPitEntry::SetFibEntry::SetFibEntry (Ptr<CcnxFibEntry> fib)
-//   : m_fib (fib)
+// void
+// CcnxPitEntry::AddIncoming (Ptr<CcnxFace> face)
+// {
+//   m_incoming.insert (CcnxPitEntryIncomingFace (face,     )
+// }
+
+
+// CcnxPitEntry::UpdateFibStatus::UpdateFibStatus (Ptr<CcnxFace> face,
+//                                                 CcnxFibFaceMetric::Status status,
+//                                                 Ptr<CcnxFib> fib)
+//   : m_face (face)
+//   , m_status (status)
+//   , m_fib (fib)
 // {
 // }
 
 // void
-// CcnxPitEntry::SetFibEntry::operator() (CcnxPitEntry &entry)
+// CcnxPitEntry::UpdateFibStatus::operator() (CcnxPitEntry &entry)
 // {
-//   entry.m_fib = m_fib;
+//   NS_ASSERT_MSG (false, "Broken");
+//   m_fib->modify (m_fib->iterator_to (entry.m_fibEntry),
+//                  CcnxFibEntry::UpdateStatus (m_face, m_status));
 // }
 
-void
-CcnxPitEntry::AddIncoming::operator() (CcnxPitEntry &entry)
-{
-  entry.m_incoming.insert (CcnxPitEntryIncomingFace (m_face));
-}
+// void
+// CcnxPitEntry::EstimateRttAndRemoveFace::operator() (CcnxPitEntry &entry)
+// {
+//   // similar to Karn's Algorithm, we don't use RTT measurements for retx packets
+//   if (m_outFace->m_retxNum>0)
+//     return;
 
-void
-CcnxPitEntry::DeleteIncoming::operator() (CcnxPitEntry &entry)
-{
-  entry.m_incoming.erase (m_face);
-}
+//   m_fib->modify (m_fib->iterator_to (entry.m_fibEntry),
+//                 CcnxFibEntry::UpdateFaceRtt (m_outFace->m_face,
+//                                              Simulator::Now() - m_outFace->m_sendTime));
 
-void
-CcnxPitEntry::AddOutgoing::operator() (CcnxPitEntry &entry)
-{
-  entry.m_outgoing.insert (CcnxPitEntryOutgoingFace (m_face));
-}
-
-void
-CcnxPitEntry::DeleteOutgoing::operator() (CcnxPitEntry &entry)
-{
-  entry.m_outgoing.erase (m_face);
-}
-
-void
-CcnxPitEntry::ClearIncoming::operator() (CcnxPitEntry &entry)
-{
-  entry.m_incoming.clear ();
-}
-
-CcnxPitEntry::UpdateFibStatus::UpdateFibStatus (Ptr<CcnxFace> face,
-                                                CcnxFibFaceMetric::Status status,
-                                                Ptr<CcnxFib> fib)
-  : m_face (face)
-  , m_status (status)
-  , m_fib (fib)
-{
-}
-
-void
-CcnxPitEntry::UpdateFibStatus::operator() (CcnxPitEntry &entry)
-{
-  NS_ASSERT_MSG (false, "Broken");
-  m_fib->modify (m_fib->iterator_to (entry.m_fibEntry),
-                 CcnxFibEntry::UpdateStatus (m_face, m_status));
-}
-
-void
-CcnxPitEntry::EstimateRttAndRemoveFace::operator() (CcnxPitEntry &entry)
-{
-  // similar to Karn's Algorithm, we don't use RTT measurements for retx packets
-  if (m_outFace->m_retxNum>0)
-    return;
-
-  m_fib->modify (m_fib->iterator_to (entry.m_fibEntry),
-                CcnxFibEntry::UpdateFaceRtt (m_outFace->m_face,
-                                             Simulator::Now() - m_outFace->m_sendTime));
-
-  entry.m_outgoing.erase (m_outFace);
-}
+//   entry.m_outgoing.erase (m_outFace);
+// }
 
 }  
diff --git a/model/ccnx-pit-entry.h b/model/ccnx-pit-entry.h
index fa89b5e..59f2a3e 100644
--- a/model/ccnx-pit-entry.h
+++ b/model/ccnx-pit-entry.h
@@ -34,6 +34,7 @@
 #include <boost/multi_index/hashed_index.hpp>
 #include <boost/multi_index/member.hpp>
 #include <boost/multi_index/mem_fun.hpp>
+#include <set>
 
 #include <iostream>
 
@@ -102,127 +103,42 @@
    * \param prefix Prefix of the PIT entry
    * \param fibEntry A FIB entry associated with the PIT entry
    */
-  CcnxPitEntry (Ptr<CcnxNameComponents> prefix, const CcnxFibEntry &fibEntry);
+  CcnxPitEntry (Ptr<CcnxNameComponents> prefix, const Time &expireTime, const CcnxFibEntry &fibEntry);
   
   // // Get number of outgoing interests that we're expecting data from
   // inline size_t numberOfPromisingInterests( ) const; 
 
-  // /**
-  //  * \brief Unary function to set or update FIB entry with this PIT entry
-  //  * \param fib smart pointer to FIB entry
-  //  */
-  // struct SetFibEntry
-  // {
-  //   SetFibEntry (Ptr<CcnxFibEntry> fib);
-  //   void operator() (CcnxPitEntry &entry);
-  // private:
-  //   Ptr<CcnxFibEntry> m_fib;
-  // };
-  
-  /**
-   * \brief Unary Function to add incoming interest to the PIT entry
-   *
-   * \param incomingFace smart pointer to the face of the incoming interest
-   * \returns const iterator to a newly added or updated
-   * CcnxPitIncomingInterest entry
-   */
-  struct AddIncoming
-  {
-    AddIncoming (Ptr<CcnxFace> incomingFace) : m_face (incomingFace) {}
-    void operator() (CcnxPitEntry &entry);
-    
-  private:
-    Ptr<CcnxFace> m_face;
-    Time m_lifeTime;
-  };
-
-  /**
-   * \brief Unary function to delete incoming interest for the interface
-   * \param face face that should be removed from the list of incoming interests
-   */
-  struct DeleteIncoming
-  {
-    DeleteIncoming (Ptr<CcnxFace> face) : m_face (face) {}
-    void operator() (CcnxPitEntry &entry);
-  private:
-    Ptr<CcnxFace> m_face;
-  };
-
-  /**
-   * \brief Unary function to add outgoing interest to PIT entry
-   *
-   * \param outgoingFace smart pointer to the face of the outgoing interest
-   * \returns const iterator to a newly added or updated
-   * CcnxPitOutgoingInterest entry
-   */
-  struct AddOutgoing
-  {
-    AddOutgoing (Ptr<CcnxFace> outgoingFace) : m_face (outgoingFace) {}
-    void operator() (CcnxPitEntry &entry);
-  private:
-    Ptr<CcnxFace> m_face;
-  };
-
-  /**
-   * \brief Unary function to delete incoming interest for the interface
-   * \param face face that should be removed from the list of incoming interests
-   */
-  struct DeleteOutgoing
-  {
-    DeleteOutgoing (Ptr<CcnxFace> face) : m_face (face) {}
-    void operator() (CcnxPitEntry &entry);
-  private:
-    Ptr<CcnxFace> m_face;
-  };
-
-  /**
-   * \brief Unary function to remove all incoming interests
-   */
-  struct ClearIncoming
-  {
-    ClearIncoming () {};
-    void operator() (CcnxPitEntry &entry);
-  };
-
-  /**
-   * \brief Unary function to update FIB status
-   */
-  struct UpdateFibStatus
-  {
-    UpdateFibStatus (Ptr<CcnxFace> face, CcnxFibFaceMetric::Status status, Ptr<CcnxFib> fib);
-    void operator() (CcnxPitEntry &entry);
-  private:
-    Ptr<CcnxFace> m_face;
-    CcnxFibFaceMetric::Status m_status;
-    Ptr<CcnxFib> m_fib;
-  };
-
-  /**
-   * \brief Unary function to estimate RTT and update smoothed RTT value in FIB
-   * \param outFace iterator of the outgoing face entry
-   */
-  struct EstimateRttAndRemoveFace
-  {
-    EstimateRttAndRemoveFace (CcnxPitEntryOutgoingFaceContainer::type::iterator outFace, Ptr<CcnxFib> fib)
-      : m_outFace (outFace), m_fib (fib) { };
-    void operator() (CcnxPitEntry &entry);
-  private:
-    CcnxPitEntryOutgoingFaceContainer::type::iterator m_outFace;
-    Ptr<CcnxFib> m_fib;
-  };
-
   const CcnxNameComponents &
   GetPrefix () const;
 
   const Time &
-  GetExpireTime () const { return m_expireTime; }
+  GetExpireTime () const
+  { return m_expireTime; }
 
+  void
+  SetExpireTime (const Time &expireTime)
+  {
+    m_expireTime = expireTime;
+  }
+  
+  bool
+  IsNonceSeen (uint32_t nonce) const
+  { return m_seenNonces.find (nonce) != m_seenNonces.end (); }
+
+  void
+  AddSeenNonce (uint32_t nonce)
+  { m_seenNonces.insert (nonce); }
+
+  void
+  AddIncoming (const CcnxFace &face);
+  
 private:
   friend std::ostream& operator<< (std::ostream& os, const CcnxPitEntry &entry);
   
 public:
   Ptr<CcnxNameComponents> m_prefix; ///< \brief Prefix of the PIT entry
   const CcnxFibEntry &m_fibEntry; ///< \brief FIB entry related to this prefix
+  std::set<uint32_t> m_seenNonces; ///< \brief map of nonces that were seen for this prefix
   
   CcnxPitEntryIncomingFaceContainer::type m_incoming; ///< \brief container for incoming interests
   CcnxPitEntryOutgoingFaceContainer::type m_outgoing; ///< \brief container for outgoing interests
@@ -232,7 +148,6 @@
   int  m_counterExpirations; ///< \brief whether timer is expired (+ number of times timer expired)
 };
 
-
 } // namespace ns3
 
 #endif // _CCNX_PIT_ENTRY_H_
diff --git a/model/ccnx-pit.cc b/model/ccnx-pit.cc
index f51aa33..3017857 100644
--- a/model/ccnx-pit.cc
+++ b/model/ccnx-pit.cc
@@ -26,13 +26,15 @@
 
 NS_LOG_COMPONENT_DEFINE ("CcnxPit");
 
+using namespace boost::tuples;
+
 namespace ns3 {
 
 NS_OBJECT_ENSURE_REGISTERED (CcnxPit);
 
 using namespace __ccnx_private;
 
-TypeId 
+TypeId
 CcnxPit::GetTypeId ()
 {
   static TypeId tid = TypeId ("ns3::CcnxPit")
@@ -44,6 +46,11 @@
                    TimeValue (Seconds (1)),
                    MakeTimeAccessor (&CcnxPit::GetCleanupTimeout, &CcnxPit::SetCleanupTimeout),
                    MakeTimeChecker ())
+    .AddAttribute ("PitEntryPruningTimout",
+                   "Timeout for PIT entry to live after being satisfied. To make sure recently satisfied interest will not be satisfied again",
+                   StringValue ("100ms"),
+                   MakeTimeAccessor (&CcnxPit::m_PitEntryPruningTimout),
+                   MakeTimeChecker ())
     ;
 
   return tid;
@@ -83,7 +90,7 @@
     m_cleanupEvent.Cancel (); // cancel any scheduled cleanup events
 
   // schedule even with new timeout
-  m_cleanupEvent = Simulator::Schedule (Simulator::Now () + m_cleanupTimeout,
+  m_cleanupEvent = Simulator::Schedule (m_cleanupTimeout,
                                         &CcnxPit::CleanExpired, this); 
 }
 
@@ -95,21 +102,25 @@
 
 void CcnxPit::CleanExpired ()
 {
-  NS_LOG_LOGIC ("Cleaning PIT");
+  NS_LOG_LOGIC ("Cleaning PIT. Total: " << size ());
   Time now = Simulator::Now ();
-  
+
+  uint32_t count = 0;
   while( !empty() )
     {
       if( get<i_timestamp> ().front ().GetExpireTime () <= now ) // is the record stale?
         {
           get<i_timestamp> ().pop_front( );
+          count ++;
         }
       else
         break; // nothing else to do. All later records will not be stale
     }
-  
+
+  // NS_LOG_LOGIC ("Cleaned " << count << " records. Total: " << size ());
   // schedule next even
-  m_cleanupEvent = Simulator::Schedule (Simulator::Now () + m_cleanupTimeout,
+  
+  m_cleanupEvent = Simulator::Schedule (m_cleanupTimeout,
                                         &CcnxPit::CleanExpired, this); 
 }
 
@@ -120,48 +131,46 @@
 }
 
 /*CcnxPitEntryContainer::type::iterator
-CcnxPit::Add (const CcnxInterestHeader &header, CcnxFibEntryContainer::type::iterator fibEntry, Ptr<CcnxFace> face)
-{
-    if( m_bucketsPerFace[face->GetId()]+1.0 >= maxBucketsPerFace[face->GetId()] )
-	{
-        //		printf( "DEBUG: bucket overflow. Should not forward anything to interface %d\n", interest.interfaceIndex );
-		return end();
-	}
+  CcnxPit::Add (const CcnxInterestHeader &header, CcnxFibEntryContainer::type::iterator fibEntry, Ptr<CcnxFace> face)
+  {
+  if( m_bucketsPerFace[face->GetId()]+1.0 >= maxBucketsPerFace[face->GetId()] )
+  {
+  //		printf( "DEBUG: bucket overflow. Should not forward anything to interface %d\n", interest.interfaceIndex );
+  return end();
+  }
     
-    CcnxPitEntryContainer::type::iterator entry = insert (end (),
-                    CcnxPitEntry (Create<CcnxNameComponents> (header.GetName ()),
-                                *fibEntry));
-    return entry;
-}*/
+  CcnxPitEntryContainer::type::iterator entry = insert (end (),
+  CcnxPitEntry (Create<CcnxNameComponents> (header.GetName ()),
+  *fibEntry));
+  return entry;
+  }*/
 
-    
-    
 bool
-CcnxPit::TryAddOutgoing(CcnxPitEntryContainer::type::iterator pitEntry, Ptr<CcnxFace> face)
+CcnxPit::TryAddOutgoing (CcnxPitEntryContainer::type::iterator pitEntry, Ptr<CcnxFace> face)
 {
-    NS_LOG_INFO ("Face has " << m_bucketsPerFace[face->GetId()] << " packets with max allowance " << maxBucketsPerFace[face->GetId()]); 
+  NS_LOG_INFO ("Face has " << m_bucketsPerFace[face->GetId()] << " packets with max allowance " << maxBucketsPerFace[face->GetId()]); 
     
-    if((face->IsLocal() == false) 
-       && (m_bucketsPerFace[face->GetId()]+1.0 >= maxBucketsPerFace[face->GetId()] ))
-	{
-        NS_LOG_INFO("********LIMIT**************");
-		return false;
-	}
+  if((face->IsLocal() == false) 
+     && (m_bucketsPerFace[face->GetId()]+1.0 >= maxBucketsPerFace[face->GetId()] ))
+    {
+      NS_LOG_INFO("********LIMIT**************");
+      return false;
+    }
     
-    m_bucketsPerFace[face->GetId()] = m_bucketsPerFace[face->GetId()] + 1.0;
+  m_bucketsPerFace[face->GetId()] = m_bucketsPerFace[face->GetId()] + 1.0;
 	
-    NS_LOG_INFO(this->size());
-    NS_LOG_INFO("before modify");
-    NS_LOG_INFO(pitEntry->GetPrefix());
-    modify (pitEntry, CcnxPitEntry::AddOutgoing(face));
-    NS_LOG_INFO("after modify");
-    return true;
+  NS_LOG_INFO(this->size());
+  NS_LOG_INFO("before modify");
+  NS_LOG_INFO(pitEntry->GetPrefix());
+  modify (pitEntry, CcnxPitEntry::AddOutgoing(face));
+  NS_LOG_INFO("after modify");
+  return true;
 }
 
 const CcnxPitEntry&
 CcnxPit::Lookup (const CcnxContentObjectHeader &header) const
 {
-  NS_LOG_FUNCTION_NOARGS ();
+  // NS_LOG_FUNCTION_NOARGS ();
 
   CcnxPitEntryContainer::type::iterator entry =
     get<i_prefix> ().find (header.GetName ());
@@ -172,50 +181,65 @@
   return *entry;
 }
 
-CcnxPitEntryContainer::type::iterator
-CcnxPit::Lookup (const CcnxInterestHeader &header, CcnxFibEntryContainer::type::iterator &outFibEntry)
+std::pair<CcnxPitEntryContainer::type::iterator,std::pair<bool, bool> >
+CcnxPit::Lookup (const CcnxInterestHeader &header)
 {
   NS_LOG_FUNCTION_NOARGS ();
   NS_ASSERT_MSG (m_fib != 0, "FIB should be set");
 
+  bool isDuplicate = false;
+  bool isNew = true;
+
   CcnxPitEntryContainer::type::iterator entry =
     get<i_prefix> ().find (header.GetName ());
 
-  CcnxFibEntryContainer::type::iterator fibEntry = m_fib->LongestPrefixMatch (header);
-  if (fibEntry == m_fib->end ())
-    {
-      NS_LOG_WARN ("FIB entry wasn't found. Creating an empty record");
-      fibEntry = m_fib->insert (m_fib->end (), CcnxFibEntry (header.GetName ()));
-    }
-  
   if (entry == end ())
-  {
-      NS_LOG_INFO("entry == end");
-      NS_LOG_INFO(this->size());
-        entry = insert (end (),
-                    CcnxPitEntry (Create<CcnxNameComponents> (header.GetName ()),
-                                  *fibEntry));
-      NS_LOG_INFO(this->size());
-  }
-  outFibEntry = fibEntry;
-  return entry;
+    {
+      CcnxFibEntryContainer::type::iterator fibEntry = m_fib->LongestPrefixMatch (header);
+      NS_ASSERT_MSG (fibEntry != m_fib->end (),
+                     "There should be at least default route set");
+
+      entry = insert (end (),
+                      CcnxPitEntry (Create<CcnxNameComponents> (header.GetName ()),
+                                    Simulator::Now () +
+                                    (header.GetInterestLifetime ().IsZero ()?DEFAULT_INTEREST_LIFETIME:
+                                                                             header.GetInterestLifetime ())
+                                    *fibEntry));
+
+      // isDuplicate = false; // redundant
+      // isNew = true; // also redundant
+    }
+  else
+    {
+      isNew = false;
+      isDuplicate = entry->IsNonceSeen (header->GetNonce ());
+    }
+
+  if (!isDuplicate)
+    {
+      modify (entry, boost::bind(&CcnxPitEntry::AddSeenNonce, boost::lambda::_1, header->GetNonce ()));
+    }
+
+  return make_tuple (cref(*entry), isNew, isDuplicate);
 }
 
+///////////////////////////////////////////////////////////////////////////////////////////
+
 void 
-CcnxPit::LeakBuckets( )
+CcnxPit::LeakBuckets ()
 {
-    for( PitBucketIterator it=m_bucketsPerFace.begin(); 
-        it != m_bucketsPerFace.end();
-        it++ )
+  for (PitBucketIterator it = m_bucketsPerFace.begin(); 
+       it != m_bucketsPerFace.end();
+       it++)
     {
-        it->second = std::max( 0.0, it->second - leakSize[it->first] );
+      it->second = std::max (0.0, it->second - leakSize[it->first]);
     }
 }
     
 void 
-CcnxPit::LeakBucket(Ptr<CcnxFace> face, int amount )
+CcnxPit::LeakBucket (Ptr<CcnxFace> face, int amount)
 {
-    m_bucketsPerFace[face->GetId()] = std::max( 0.0, m_bucketsPerFace[face->GetId()] - amount );
+  m_bucketsPerFace[face->GetId()] = std::max (0.0, m_bucketsPerFace[face->GetId()] - amount);
 }
 
 
diff --git a/model/ccnx-pit.h b/model/ccnx-pit.h
index 16a3283..538e127 100644
--- a/model/ccnx-pit.h
+++ b/model/ccnx-pit.h
@@ -39,6 +39,7 @@
 #include <map>
 #include <iostream>
 #include <algorithm>
+#include <boost/tuple/tuple.hpp>
 
 namespace ns3 {
 
@@ -126,7 +127,8 @@
   Add (const CcnxInterestHeader &header, CcnxFibEntryContainer::type::iterator fibEntry, Ptr<CcnxFace> face);*/
   
   bool
-    TryAddOutgoing(CcnxPitEntryContainer::type::iterator pitEntry, Ptr<CcnxFace> face);
+  TryAddOutgoing(CcnxPitEntryContainer::type::iterator pitEntry, Ptr<CcnxFace> face);
+  
   /**
    * \brief Find corresponding PIT entry for the given content name
    * \param prefix Prefix for which to lookup the entry
@@ -139,10 +141,14 @@
   /**
    * \brief Find corresponding PIT entry for the given content name
    * \param prefix Prefix for which to lookup the entry
-   * \returns const reference to Pit entry. If record does not exist, it will be created
+   * \returns a tuple:
+   * get<0>: `const CcnxPitEntry&`: a valid PIT entry (if record does not exist, it will be created)
+   * get<1>: `bool`: true if a new entry was created
+   * get<2>: `bool`: true if a PIT entry exists and Nonce that present in header has been already seen
+   * 
    */
-  CcnxPitEntryContainer::type::iterator
-  Lookup (const CcnxInterestHeader &header,CcnxFibEntryContainer::type::iterator &outFibEntry);
+  boost::tuple<const CcnxPitEntry&, bool, bool>
+  Lookup (const CcnxInterestHeader &header);
   
   // remove a PIT entry
   //void erase (const string &contentName);
@@ -175,6 +181,11 @@
    */
   Time GetCleanupTimeout () const;
 
+  Time GetPitEntryPruningTimeout () const
+  {
+    return m_PitEntryPruningTimout;
+  }
+  
   /**
    * \brief Set FIB table
    */
@@ -199,6 +210,7 @@
 private:
   Time    m_cleanupTimeout; ///< \brief Configurable timeout of how often cleanup events are working
   EventId m_cleanupEvent;   ///< \brief Cleanup event
+  Time    m_PitEntryPruningTimout;
 
   Ptr<CcnxFib> m_fib; ///< \brief Link to FIB table
   PitBucket    m_bucketsPerFace; ///< \brief pending interface counter per face
diff --git a/model/ccnx-rit.cc b/model/ccnx-rit.cc
index 32c9ddb..2a1b7f0 100644
--- a/model/ccnx-rit.cc
+++ b/model/ccnx-rit.cc
@@ -119,7 +119,7 @@
     m_cleanupEvent.Cancel (); // cancel any scheduled cleanup events
 
   // schedule even with new timeout
-  m_cleanupEvent = Simulator::Schedule (Simulator::Now () + m_cleanupTimeout,
+  m_cleanupEvent = Simulator::Schedule (m_cleanupTimeout,
                                         &CcnxRit::CleanExpired, this); 
 }
 
@@ -132,7 +132,7 @@
 bool
 CcnxRit::WasRecentlySatisfied (const CcnxInterestHeader &header)
 {
-  NS_LOG_FUNCTION_NOARGS ();
+  // NS_LOG_FUNCTION_NOARGS ();
   std::pair<CcnxRitByNonce::type::iterator,CcnxRitByNonce::type::iterator>
     entries = get<nonce> ().equal_range (header.GetNonce ());
   
@@ -153,7 +153,7 @@
 void
 CcnxRit::SetRecentlySatisfied (const CcnxInterestHeader &header)
 {
-  NS_LOG_FUNCTION_NOARGS ();
+  // NS_LOG_FUNCTION_NOARGS ();
   NS_ASSERT_MSG (!WasRecentlySatisfied (header), "Duplicate recent interest should not be added to RIT");
   
   get<timestamp> ().push_back (
@@ -166,30 +166,21 @@
 
 void CcnxRit::CleanExpired ()
 {
-  // NS_LOG_LOGIC ("Cleaning RIT");
+  NS_LOG_LOGIC ("Cleaning RIT, total: " << size ());
   Time now = Simulator::Now ();
-// #ifdef _DEBUG
-//   uint32_t count = 0;
-// #endif
   
   while( !empty() )
     {
       if( get<timestamp> ().front ().m_expireTime <= now ) // is the record stale?
         {
          get<timestamp> ().pop_front( );
-// #ifdef _DEBUG
-//          count++;
-// #endif
         }
       else
         break; // nothing else to do. All later records will not be stale
     }
-// #ifdef _DEBUG
-//   NS_LOG_DEBUG (count << " records cleaned");
-// #endif
   
   // schedule next even
-  m_cleanupEvent = Simulator::Schedule (Simulator::Now () + m_cleanupTimeout,
+  m_cleanupEvent = Simulator::Schedule (m_cleanupTimeout,
                                         &CcnxRit::CleanExpired, this); 
 }