fw: allow strategies to pick outgoing Interest

This commit changes outgoing Interest pipeline and Strategy API,
to give strategies an opportunity to pick an outgoing Interest that
matches the Interest table entry.
No strategy is updated in this commit.

refs #1756

Change-Id: Iffad77ef0078c437070039fca6b24a85df13bda7
diff --git a/daemon/fw/forwarder.cpp b/daemon/fw/forwarder.cpp
index b8bd076..9648e1b 100644
--- a/daemon/fw/forwarder.cpp
+++ b/daemon/fw/forwarder.cpp
@@ -26,7 +26,6 @@
 #include "forwarder.hpp"
 #include "pit-algorithm.hpp"
 #include "core/logger.hpp"
-#include "core/random.hpp"
 #include "strategy.hpp"
 #include "table/cleanup.hpp"
 #include <ndn-cxx/lp/tags.hpp>
@@ -206,8 +205,10 @@
     // chosen NextHop face exists?
     Face* nextHopFace = m_faceTable.get(*nextHopTag);
     if (nextHopFace != nullptr) {
+      NFD_LOG_DEBUG("onContentStoreMiss interest=" << interest.getName() << " nexthop-faceid=" << nextHopFace->getId());
       // go to outgoing Interest pipeline
-      this->onOutgoingInterest(pitEntry, *nextHopFace);
+      // scope control is unnecessary, because privileged app explicitly wants to forward
+      this->onOutgoingInterest(pitEntry, *nextHopFace, interest);
     }
     return;
   }
@@ -234,50 +235,16 @@
 }
 
 void
-Forwarder::onOutgoingInterest(const shared_ptr<pit::Entry>& pitEntry, Face& outFace,
-                              bool wantNewNonce)
+Forwarder::onOutgoingInterest(const shared_ptr<pit::Entry>& pitEntry, Face& outFace, const Interest& interest)
 {
-  if (outFace.getId() == face::INVALID_FACEID) {
-    NFD_LOG_WARN("onOutgoingInterest face=invalid interest=" << pitEntry->getName());
-    return;
-  }
   NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId() <<
                 " interest=" << pitEntry->getName());
 
-  // scope control
-  if (fw::violatesScope(*pitEntry, outFace)) {
-    NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId() <<
-                  " interest=" << pitEntry->getName() << " violates scope");
-    return;
-  }
-
-  // pick Interest
-  // The outgoing Interest picked is the last incoming Interest that does not come from outFace.
-  // If all in-records come from outFace, it's fine to pick that.
-  // This happens when there's only one in-record that comes from outFace.
-  // The legit use is for vehicular network; otherwise, strategy shouldn't send to the sole inFace.
-  pit::InRecordCollection::iterator pickedInRecord = std::max_element(
-    pitEntry->in_begin(), pitEntry->in_end(),
-    [&outFace] (const pit::InRecord& a, const pit::InRecord& b) {
-      bool isOutFaceA = &a.getFace() == &outFace;
-      bool isOutFaceB = &b.getFace() == &outFace;
-      return (isOutFaceA > isOutFaceB) ||
-             (isOutFaceA == isOutFaceB && a.getLastRenewed() < b.getLastRenewed());
-    });
-  BOOST_ASSERT(pickedInRecord != pitEntry->in_end());
-  auto interest = const_pointer_cast<Interest>(pickedInRecord->getInterest().shared_from_this());
-
-  if (wantNewNonce) {
-    interest = make_shared<Interest>(*interest);
-    static std::uniform_int_distribution<uint32_t> dist;
-    interest->setNonce(dist(getGlobalRng()));
-  }
-
   // insert out-record
-  pitEntry->insertOrUpdateOutRecord(outFace, *interest);
+  pitEntry->insertOrUpdateOutRecord(outFace, interest);
 
   // send Interest
-  outFace.sendInterest(*interest);
+  outFace.sendInterest(interest);
   ++m_counters.nOutInterests;
 }
 
diff --git a/daemon/fw/forwarder.hpp b/daemon/fw/forwarder.hpp
index 44e8eb1..fca81eb 100644
--- a/daemon/fw/forwarder.hpp
+++ b/daemon/fw/forwarder.hpp
@@ -187,8 +187,7 @@
   /** \brief Content Store miss pipeline
   */
   VIRTUAL_WITH_TESTS void
-  onContentStoreMiss(const Face& inFace, const shared_ptr<pit::Entry>& pitEntry,
-                     const Interest& interest);
+  onContentStoreMiss(const Face& inFace, const shared_ptr<pit::Entry>& pitEntry, const Interest& interest);
 
   /** \brief Content Store hit pipeline
   */
@@ -199,8 +198,7 @@
   /** \brief outgoing Interest pipeline
    */
   VIRTUAL_WITH_TESTS void
-  onOutgoingInterest(const shared_ptr<pit::Entry>& pitEntry, Face& outFace,
-                     bool wantNewNonce = false);
+  onOutgoingInterest(const shared_ptr<pit::Entry>& pitEntry, Face& outFace, const Interest& interest);
 
   /** \brief Interest reject pipeline
    */
diff --git a/daemon/fw/strategy.cpp b/daemon/fw/strategy.cpp
index efe1fac..27d6735 100644
--- a/daemon/fw/strategy.cpp
+++ b/daemon/fw/strategy.cpp
@@ -25,7 +25,9 @@
 
 #include "strategy.hpp"
 #include "forwarder.hpp"
+#include "pit-algorithm.hpp"
 #include "core/logger.hpp"
+#include "core/random.hpp"
 
 namespace nfd {
 namespace fw {
@@ -66,6 +68,42 @@
 }
 
 void
+Strategy::sendInterest(const shared_ptr<pit::Entry>& pitEntry, Face& outFace,
+                       bool wantNewNonce)
+{
+  // scope control
+  if (fw::violatesScope(*pitEntry, outFace)) {
+    NFD_LOG_DEBUG("sendInterestLegacy face=" << outFace.getId() <<
+                  " interest=" << pitEntry->getName() << " violates scope");
+    return;
+  }
+
+  // pick Interest
+  // The outgoing Interest picked is the last incoming Interest that does not come from outFace.
+  // If all in-records come from outFace, it's fine to pick that.
+  // This happens when there's only one in-record that comes from outFace.
+  // The legit use is for vehicular network; otherwise, strategy shouldn't send to the sole inFace.
+  pit::InRecordCollection::iterator pickedInRecord = std::max_element(
+    pitEntry->in_begin(), pitEntry->in_end(),
+    [&outFace] (const pit::InRecord& a, const pit::InRecord& b) {
+      bool isOutFaceA = &a.getFace() == &outFace;
+      bool isOutFaceB = &b.getFace() == &outFace;
+      return (isOutFaceA > isOutFaceB) ||
+             (isOutFaceA == isOutFaceB && a.getLastRenewed() < b.getLastRenewed());
+    });
+  BOOST_ASSERT(pickedInRecord != pitEntry->in_end());
+  auto interest = const_pointer_cast<Interest>(pickedInRecord->getInterest().shared_from_this());
+
+  if (wantNewNonce) {
+    interest = make_shared<Interest>(*interest);
+    static std::uniform_int_distribution<uint32_t> dist;
+    interest->setNonce(dist(getGlobalRng()));
+  }
+
+  this->sendInterest(pitEntry, outFace, *interest);
+}
+
+void
 Strategy::sendNacks(const shared_ptr<pit::Entry>& pitEntry, const lp::NackHeader& header,
                     std::initializer_list<const Face*> exceptFaces)
 {
diff --git a/daemon/fw/strategy.hpp b/daemon/fw/strategy.hpp
index 5b3cb2b..bb5b6ef 100644
--- a/daemon/fw/strategy.hpp
+++ b/daemon/fw/strategy.hpp
@@ -131,16 +131,25 @@
   /** \brief send Interest to outFace
    *  \param pitEntry PIT entry
    *  \param outFace face through which to send out the Interest
-   *  \param wantNewNonce if true, a new Nonce will be generated,
-   *                      rather than reusing a Nonce from one of the PIT in-records
+   *  \param interest the Interest packet
    */
   VIRTUAL_WITH_TESTS void
   sendInterest(const shared_ptr<pit::Entry>& pitEntry, Face& outFace,
-               bool wantNewNonce = false)
+               const Interest& interest)
   {
-    m_forwarder.onOutgoingInterest(pitEntry, outFace, wantNewNonce);
+    m_forwarder.onOutgoingInterest(pitEntry, outFace, interest);
   }
 
+  /** \brief send Interest to outFace
+   *  \param pitEntry PIT entry
+   *  \param outFace face through which to send out the Interest
+   *  \param wantNewNonce if true, a new Nonce will be generated,
+   *                      rather than reusing a Nonce from one of the PIT in-records
+   */
+  void
+  sendInterest(const shared_ptr<pit::Entry>& pitEntry, Face& outFace,
+               bool wantNewNonce = false);
+
   /** \brief decide that a pending Interest cannot be forwarded
    *  \param pitEntry PIT entry
    *