fw: properly handle consumer retransmission in AsfStrategy

Do not reject an existing PIT entry if there are no
next hops during consumer retransmission

refs: #4874

Change-Id: Iea8602897e99fdf2f6fd0226b5eb74bd7a4bced1
diff --git a/daemon/fw/algorithm.cpp b/daemon/fw/algorithm.cpp
index a0c8bc4..6ea610f 100644
--- a/daemon/fw/algorithm.cpp
+++ b/daemon/fw/algorithm.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2018,  Regents of the University of California,
+ * Copyright (c) 2014-2019,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -128,5 +128,51 @@
   return lastOutgoing->getLastRenewed();
 }
 
+fib::NextHopList::const_iterator
+findEligibleNextHopWithEarliestOutRecord(const Face& inFace, const Interest& interest,
+                                         const fib::NextHopList& nexthops,
+                                         const shared_ptr<pit::Entry>& pitEntry)
+{
+  auto found = nexthops.end();
+  auto earliestRenewed = time::steady_clock::TimePoint::max();
+
+  for (auto it = nexthops.begin(); it != nexthops.end(); ++it) {
+    if (!isNextHopEligible(inFace, interest, *it, pitEntry))
+      continue;
+
+    auto outRecord = pitEntry->getOutRecord(it->getFace(), 0);
+    BOOST_ASSERT(outRecord != pitEntry->out_end());
+    if (outRecord->getLastRenewed() < earliestRenewed) {
+      found = it;
+      earliestRenewed = outRecord->getLastRenewed();
+    }
+  }
+  return found;
+}
+
+bool
+isNextHopEligible(const Face& inFace, const Interest& interest,
+                  const fib::NextHop& nexthop,
+                  const shared_ptr<pit::Entry>& pitEntry,
+                  bool wantUnused,
+                  time::steady_clock::TimePoint now)
+{
+  const Face& outFace = nexthop.getFace();
+
+  // do not forward back to the same face, unless it is ad hoc
+  if ((outFace.getId() == inFace.getId() && outFace.getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) ||
+     (wouldViolateScope(inFace, interest, outFace)))
+    return false;
+
+  if (wantUnused) {
+    // nexthop must not have unexpired out-record
+    auto outRecord = pitEntry->getOutRecord(outFace, 0);
+    if (outRecord != pitEntry->out_end() && outRecord->getExpiry() > now) {
+      return false;
+    }
+  }
+  return true;
+}
+
 } // namespace fw
 } // namespace nfd