fw: per upstream retx exponential suppression for multicast strategy

refs: #4066

Change-Id: Ic1047b871dc9dc040e95ac5edceaae9994cd2849
diff --git a/daemon/fw/multicast-strategy.cpp b/daemon/fw/multicast-strategy.cpp
index 7b38bc1..f51693b 100644
--- a/daemon/fw/multicast-strategy.cpp
+++ b/daemon/fw/multicast-strategy.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -66,25 +66,25 @@
 MulticastStrategy::afterReceiveInterest(const Face& inFace, const Interest& interest,
                                         const shared_ptr<pit::Entry>& pitEntry)
 {
-  // Should the Interest be suppressed?
-  RetxSuppression::Result suppressResult = m_retxSuppression.decide(inFace, interest, *pitEntry);
-  switch (suppressResult) {
-  case RetxSuppression::NEW:
-  case RetxSuppression::FORWARD:
-    break;
-  case RetxSuppression::SUPPRESS:
-    NFD_LOG_DEBUG(interest << " from=" << inFace.getId() << " suppressed");
-    return;
-  }
-
   const fib::Entry& fibEntry = this->lookupFib(*pitEntry);
   const fib::NextHopList& nexthops = fibEntry.getNextHops();
 
   int nEligibleNextHops = 0;
 
+  bool isSuppressed = false;
+
   for (const auto& nexthop : nexthops) {
     Face& outFace = nexthop.getFace();
 
+    RetxSuppressionResult suppressResult = m_retxSuppression.decidePerUpstream(*pitEntry, outFace);
+
+    if (suppressResult == RetxSuppressionResult::SUPPRESS) {
+      NFD_LOG_DEBUG(interest << " from=" << inFace.getId()
+                    << "to=" << outFace.getId() << " suppressed");
+      isSuppressed = true;
+      continue;
+    }
+
     if ((outFace.getId() == inFace.getId() && outFace.getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) ||
         wouldViolateScope(inFace, interest, outFace)) {
       continue;
@@ -93,17 +93,21 @@
     this->sendInterest(pitEntry, outFace, interest);
     NFD_LOG_DEBUG(interest << " from=" << inFace.getId()
                            << " pitEntry-to=" << outFace.getId());
+
+    if (suppressResult == RetxSuppressionResult::FORWARD) {
+      m_retxSuppression.incrementIntervalForOutRecord(*pitEntry->getOutRecord(outFace));
+    }
     ++nEligibleNextHops;
   }
 
-  if (nEligibleNextHops == 0) {
-      NFD_LOG_DEBUG(interest << " from=" << inFace.getId() << " noNextHop");
+  if (nEligibleNextHops == 0 && !isSuppressed) {
+    NFD_LOG_DEBUG(interest << " from=" << inFace.getId() << " noNextHop");
 
-      lp::NackHeader nackHeader;
-      nackHeader.setReason(lp::NackReason::NO_ROUTE);
-      this->sendNack(pitEntry, inFace, nackHeader);
+    lp::NackHeader nackHeader;
+    nackHeader.setReason(lp::NackReason::NO_ROUTE);
+    this->sendNack(pitEntry, inFace, nackHeader);
 
-      this->rejectPendingInterest(pitEntry);
+    this->rejectPendingInterest(pitEntry);
   }
 }