fw: Nack in pipelines and best-route strategy

* in PIT out-record, add last incoming Nack field
* create incoming Nack pipeline
* create outgoing Nack pipeline
* modify Interest loop pipeline to send Nack upon duplicate Nonce
* in strategy API, add after receive Nack trigger and send Nack action
* in best-route strategy, send Nack-NoRoute before rejecting pending Interest
* in best-route strategy, process incoming Nack

Other changes include:

* Pit::find
* StrategyTester saved arguments structs
* TopologyTester transmit at Transport level

refs #3156

Change-Id: I7868561c0838231083d471261200aeb280cc6e9d
diff --git a/daemon/fw/forwarder.cpp b/daemon/fw/forwarder.cpp
index 27bfa38..20428c5 100644
--- a/daemon/fw/forwarder.cpp
+++ b/daemon/fw/forwarder.cpp
@@ -60,7 +60,7 @@
       interest.getLink();
     }
   }
-  catch (tlv::Error&) {
+  catch (const tlv::Error&) {
     NFD_LOG_DEBUG("startProcessInterest face=" << face.getId() <<
                   " interest=" << interest.getName() << " malformed");
     // It's safe to call interest.getName() because Name has been fully parsed
@@ -80,6 +80,25 @@
 }
 
 void
+Forwarder::startProcessNack(Face& face, const lp::Nack& nack)
+{
+  // check fields used by forwarding are well-formed
+  try {
+    if (nack.getInterest().hasLink()) {
+      nack.getInterest().getLink();
+    }
+  }
+  catch (const tlv::Error&) {
+    NFD_LOG_DEBUG("startProcessNack face=" << face.getId() <<
+                  " nack=" << nack.getInterest().getName() <<
+                  "~" << nack.getReason() << " malformed");
+    return;
+  }
+
+  this->onIncomingNack(face, nack);
+}
+
+void
 Forwarder::onIncomingInterest(Face& inFace, const Interest& interest)
 {
   // receive Interest
@@ -131,10 +150,23 @@
 Forwarder::onInterestLoop(Face& inFace, const Interest& interest,
                           shared_ptr<pit::Entry> pitEntry)
 {
-  NFD_LOG_DEBUG("onInterestLoop face=" << inFace.getId() <<
-                " interest=" << interest.getName());
+  // if multi-access face, drop
+  if (inFace.isMultiAccess()) {
+    NFD_LOG_DEBUG("onInterestLoop face=" << inFace.getId() <<
+                  " interest=" << interest.getName() <<
+                  " drop");
+    return;
+  }
 
-  // (drop)
+  NFD_LOG_DEBUG("onInterestLoop face=" << inFace.getId() <<
+                " interest=" << interest.getName() <<
+                " send-Nack-duplicate");
+
+  // send Nack with reason=DUPLICATE
+  // note: Don't enter outgoing Nack pipeline because it needs an in-record.
+  lp::Nack nack(interest);
+  nack.setReason(lp::NackReason::DUPLICATE);
+  inFace.sendNack(nack);
 }
 
 void
@@ -362,7 +394,7 @@
   // CS insert
   m_cs.insert(data);
 
-  std::set<shared_ptr<Face> > pendingDownstreams;
+  std::set<Face*> pendingDownstreams;
   // foreach PitEntry
   for (const shared_ptr<pit::Entry>& pitEntry : pitMatches) {
     NFD_LOG_DEBUG("onIncomingData matching=" << pitEntry->getName());
@@ -372,10 +404,9 @@
 
     // remember pending downstreams
     const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
-    for (pit::InRecordCollection::const_iterator it = inRecords.begin();
-                                                 it != inRecords.end(); ++it) {
-      if (it->getExpiry() > time::steady_clock::now()) {
-        pendingDownstreams.insert(it->getFace());
+    for (const pit::InRecord& inRecord : inRecords) {
+      if (inRecord.getExpiry() > time::steady_clock::now()) {
+        pendingDownstreams.insert(inRecord.getFace().get());
       }
     }
 
@@ -395,10 +426,8 @@
   }
 
   // foreach pending downstream
-  for (std::set<shared_ptr<Face> >::iterator it = pendingDownstreams.begin();
-      it != pendingDownstreams.end(); ++it) {
-    shared_ptr<Face> pendingDownstream = *it;
-    if (pendingDownstream.get() == &inFace) {
+  for (Face* pendingDownstream : pendingDownstreams) {
+    if (pendingDownstream == &inFace) {
       continue;
     }
     // goto outgoing Data pipeline
@@ -447,6 +476,104 @@
   ++m_counters.getNOutDatas();
 }
 
+void
+Forwarder::onIncomingNack(Face& inFace, const lp::Nack& nack)
+{
+  // if multi-access face, drop
+  if (inFace.isMultiAccess()) {
+    NFD_LOG_DEBUG("onIncomingNack face=" << inFace.getId() <<
+                  " nack=" << nack.getInterest().getName() <<
+                  "~" << nack.getReason() << " face-is-multi-access");
+    return;
+  }
+
+  // PIT match
+  shared_ptr<pit::Entry> pitEntry = m_pit.find(nack.getInterest());
+  // if no PIT entry found, drop
+  if (pitEntry == nullptr) {
+    NFD_LOG_DEBUG("onIncomingNack face=" << inFace.getId() <<
+                  " nack=" << nack.getInterest().getName() <<
+                  "~" << nack.getReason() << " no-PIT-entry");
+    return;
+  }
+
+  // has out-record?
+  pit::OutRecordCollection::iterator outRecord = pitEntry->getOutRecord(inFace);
+  // if no out-record found, drop
+  if (outRecord == pitEntry->getOutRecords().end()) {
+    NFD_LOG_DEBUG("onIncomingNack face=" << inFace.getId() <<
+                  " nack=" << nack.getInterest().getName() <<
+                  "~" << nack.getReason() << " no-out-record");
+    return;
+  }
+
+  // if out-record has different Nonce, drop
+  if (nack.getInterest().getNonce() != outRecord->getLastNonce()) {
+    NFD_LOG_DEBUG("onIncomingNack face=" << inFace.getId() <<
+                  " nack=" << nack.getInterest().getName() <<
+                  "~" << nack.getReason() << " wrong-Nonce " <<
+                  nack.getInterest().getNonce() << "!=" << outRecord->getLastNonce());
+    return;
+  }
+
+  NFD_LOG_DEBUG("onIncomingNack face=" << inFace.getId() <<
+                " nack=" << nack.getInterest().getName() <<
+                "~" << nack.getReason() << " OK");
+
+  // record Nack on out-record
+  outRecord->setIncomingNack(nack);
+
+  // trigger strategy: after receive NACK
+  shared_ptr<fib::Entry> fibEntry = m_fib.findLongestPrefixMatch(*pitEntry);
+  this->dispatchToStrategy(pitEntry, bind(&Strategy::afterReceiveNack, _1,
+                                          cref(inFace), cref(nack), fibEntry, pitEntry));
+}
+
+void
+Forwarder::onOutgoingNack(shared_ptr<pit::Entry> pitEntry, const Face& outFace,
+                          const lp::NackHeader& nack)
+{
+  if (outFace.getId() == INVALID_FACEID) {
+    NFD_LOG_WARN("onOutgoingNack face=invalid" <<
+                  " nack=" << pitEntry->getInterest().getName() <<
+                  "~" << nack.getReason() << " no-in-record");
+    return;
+  }
+
+  // has in-record?
+  pit::InRecordCollection::const_iterator inRecord = pitEntry->getInRecord(outFace);
+
+  // if no in-record found, drop
+  if (inRecord == pitEntry->getInRecords().end()) {
+    NFD_LOG_DEBUG("onOutgoingNack face=" << outFace.getId() <<
+                  " nack=" << pitEntry->getInterest().getName() <<
+                  "~" << nack.getReason() << " no-in-record");
+    return;
+  }
+
+  // if multi-access face, drop
+  if (outFace.isMultiAccess()) {
+    NFD_LOG_DEBUG("onOutgoingNack face=" << outFace.getId() <<
+                  " nack=" << pitEntry->getInterest().getName() <<
+                  "~" << nack.getReason() << " face-is-multi-access");
+    return;
+  }
+
+  NFD_LOG_DEBUG("onOutgoingNack face=" << outFace.getId() <<
+                " nack=" << pitEntry->getInterest().getName() <<
+                "~" << nack.getReason() << " OK");
+
+  // create Nack packet with the Interest from in-record
+  lp::Nack nackPkt(inRecord->getInterest());
+  nackPkt.setHeader(nack);
+
+  // erase in-record
+  pitEntry->deleteInRecord(outFace);
+
+  // send Nack on face
+  const_cast<Face&>(outFace).sendNack(nackPkt);
+}
+
 static inline bool
 compare_InRecord_expiry(const pit::InRecord& a, const pit::InRecord& b)
 {