fw: Add ASF strategy parameters n-silent-timeouts & probing-interval

refs: #4193

Change-Id: I9572425a2fdcbf67b9886c2a5b6b50a10a0856e2
diff --git a/daemon/fw/asf-measurements.cpp b/daemon/fw/asf-measurements.cpp
index e8bc8fd..e85ec3f 100644
--- a/daemon/fw/asf-measurements.cpp
+++ b/daemon/fw/asf-measurements.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+/*
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -66,6 +66,7 @@
 
 FaceInfo::FaceInfo()
   : m_isTimeoutScheduled(false)
+  , m_nSilentTimeouts(0)
 {
 }
 
@@ -147,9 +148,9 @@
 }
 
 FaceInfo*
-NamespaceInfo::getFaceInfo(const fib::Entry& fibEntry, const Face& face)
+NamespaceInfo::getFaceInfo(const fib::Entry& fibEntry, FaceId faceId)
 {
-  FaceInfoTable::iterator it = m_fit.find(face.getId());
+  FaceInfoTable::iterator it = m_fit.find(faceId);
 
   if (it != m_fit.end()) {
     return &it->second;
@@ -160,17 +161,17 @@
 }
 
 FaceInfo&
-NamespaceInfo::getOrCreateFaceInfo(const fib::Entry& fibEntry, const Face& face)
+NamespaceInfo::getOrCreateFaceInfo(const fib::Entry& fibEntry, FaceId faceId)
 {
-  FaceInfoTable::iterator it = m_fit.find(face.getId());
+  FaceInfoTable::iterator it = m_fit.find(faceId);
 
   FaceInfo* info = nullptr;
 
   if (it == m_fit.end()) {
-    const auto& pair = m_fit.insert(std::make_pair(face.getId(), FaceInfo()));
+    const auto& pair = m_fit.insert(std::make_pair(faceId, FaceInfo()));
     info = &pair.first->second;
 
-    extendFaceInfoLifetime(*info, face);
+    extendFaceInfoLifetime(*info, faceId);
   }
   else {
     info = &it->second;
@@ -180,20 +181,20 @@
 }
 
 void
-NamespaceInfo::expireFaceInfo(nfd::face::FaceId faceId)
+NamespaceInfo::expireFaceInfo(FaceId faceId)
 {
   m_fit.erase(faceId);
 }
 
 void
-NamespaceInfo::extendFaceInfoLifetime(FaceInfo& info, const Face& face)
+NamespaceInfo::extendFaceInfoLifetime(FaceInfo& info, FaceId faceId)
 {
   // Cancel previous expiration
   scheduler::cancel(info.getMeasurementExpirationEventId());
 
   // Refresh measurement
   scheduler::EventId id = scheduler::schedule(AsfMeasurements::MEASUREMENTS_LIFETIME,
-    bind(&NamespaceInfo::expireFaceInfo, this, face.getId()));
+    bind(&NamespaceInfo::expireFaceInfo, this, faceId));
 
   info.setMeasurementExpirationEventId(id);
 }
@@ -209,17 +210,18 @@
 }
 
 FaceInfo*
-AsfMeasurements::getFaceInfo(const fib::Entry& fibEntry, const Interest& interest, const Face& face)
+AsfMeasurements::getFaceInfo(const fib::Entry& fibEntry, const Interest& interest, FaceId faceId)
 {
   NamespaceInfo& info = getOrCreateNamespaceInfo(fibEntry, interest);
-  return info.getFaceInfo(fibEntry, face);
+  return info.getFaceInfo(fibEntry, faceId);
 }
 
 FaceInfo&
-AsfMeasurements::getOrCreateFaceInfo(const fib::Entry& fibEntry, const Interest& interest, const Face& face)
+AsfMeasurements::getOrCreateFaceInfo(const fib::Entry& fibEntry, const Interest& interest,
+                                     FaceId faceId)
 {
   NamespaceInfo& info = getOrCreateNamespaceInfo(fibEntry, interest);
-  return info.getOrCreateFaceInfo(fibEntry, face);
+  return info.getOrCreateFaceInfo(fibEntry, faceId);
 }
 
 NamespaceInfo*
diff --git a/daemon/fw/asf-measurements.hpp b/daemon/fw/asf-measurements.hpp
index ba858df..ee5885d 100644
--- a/daemon/fw/asf-measurements.hpp
+++ b/daemon/fw/asf-measurements.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2017,  Regents of the University of California,
+/*
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -166,6 +166,18 @@
     return getSrtt() != RttStats::RTT_NO_MEASUREMENT;
   }
 
+  size_t
+  getNSilentTimeouts() const
+  {
+    return m_nSilentTimeouts;
+  }
+
+  void
+  setNSilentTimeouts(size_t nSilentTimeouts)
+  {
+    m_nSilentTimeouts = nSilentTimeouts;
+  }
+
 private:
   void
   cancelTimeoutEvent();
@@ -183,9 +195,10 @@
   // RTO associated with Interest
   scheduler::EventId m_timeoutEventId;
   bool m_isTimeoutScheduled;
+  size_t m_nSilentTimeouts;
 };
 
-typedef std::unordered_map<face::FaceId, FaceInfo> FaceInfoTable;
+typedef std::unordered_map<FaceId, FaceInfo> FaceInfoTable;
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
@@ -204,19 +217,19 @@
   }
 
   FaceInfo&
-  getOrCreateFaceInfo(const fib::Entry& fibEntry, const Face& face);
+  getOrCreateFaceInfo(const fib::Entry& fibEntry, FaceId faceId);
 
   FaceInfo*
-  getFaceInfo(const fib::Entry& fibEntry, const Face& face);
+  getFaceInfo(const fib::Entry& fibEntry, FaceId faceId);
 
   void
-  expireFaceInfo(nfd::face::FaceId faceId);
+  expireFaceInfo(FaceId faceId);
 
   void
-  extendFaceInfoLifetime(FaceInfo& info, const Face& face);
+  extendFaceInfoLifetime(FaceInfo& info, FaceId faceId);
 
   FaceInfo*
-  get(nfd::face::FaceId faceId)
+  get(FaceId faceId)
   {
     if (m_fit.find(faceId) != m_fit.end()) {
       return &m_fit.at(faceId);
@@ -227,7 +240,7 @@
   }
 
   FaceInfoTable::iterator
-  find(nfd::face::FaceId faceId)
+  find(FaceId faceId)
   {
     return m_fit.find(faceId);
   }
@@ -239,7 +252,7 @@
   }
 
   const FaceInfoTable::iterator
-  insert(nfd::face::FaceId faceId)
+  insert(FaceId faceId)
   {
     const auto& pair = m_fit.insert(std::make_pair(faceId, FaceInfo()));
     return pair.first;
@@ -288,10 +301,10 @@
   AsfMeasurements(MeasurementsAccessor& measurements);
 
   FaceInfo*
-  getFaceInfo(const fib::Entry& fibEntry, const Interest& interest, const Face& face);
+  getFaceInfo(const fib::Entry& fibEntry, const Interest& interest, FaceId faceId);
 
   FaceInfo&
-  getOrCreateFaceInfo(const fib::Entry& fibEntry, const Interest& interest, const Face& face);
+  getOrCreateFaceInfo(const fib::Entry& fibEntry, const Interest& interest, FaceId faceId);
 
   NamespaceInfo*
   getNamespaceInfo(const Name& prefix);
diff --git a/daemon/fw/asf-probing-module.cpp b/daemon/fw/asf-probing-module.cpp
index f29d0e7..24b4577 100644
--- a/daemon/fw/asf-probing-module.cpp
+++ b/daemon/fw/asf-probing-module.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+/*
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -31,7 +31,8 @@
 namespace fw {
 namespace asf {
 
-constexpr time::seconds ProbingModule::DEFAULT_PROBING_INTERVAL;
+constexpr time::milliseconds ProbingModule::DEFAULT_PROBING_INTERVAL;
+constexpr time::milliseconds ProbingModule::MIN_PROBING_INTERVAL;
 
 static_assert(ProbingModule::DEFAULT_PROBING_INTERVAL < AsfMeasurements::MEASUREMENTS_LIFETIME,
               "ProbingModule::DEFAULT_PROBING_INTERVAL must be less than AsfMeasurements::MEASUREMENTS_LIFETIME");
@@ -91,7 +92,7 @@
       continue;
     }
 
-    FaceInfo* info = m_measurements.getFaceInfo(fibEntry, interest, hopFace);
+    FaceInfo* info = m_measurements.getFaceInfo(fibEntry, interest, hopFace.getId());
 
     // If no RTT has been recorded, probe this face
     if (info == nullptr || !info->hasSrttMeasurement()) {
@@ -189,6 +190,19 @@
   return dist(getGlobalRng());
 }
 
+void
+ProbingModule::setProbingInterval(size_t probingInterval)
+{
+  if (time::milliseconds(probingInterval) >= MIN_PROBING_INTERVAL) {
+    m_probingInterval = time::milliseconds(probingInterval);
+  }
+  else {
+    BOOST_THROW_EXCEPTION(std::invalid_argument("Probing interval should be >= "
+                                                + to_string(MIN_PROBING_INTERVAL.count())
+                                                + " milliseconds"));
+  }
+}
+
 } // namespace asf
 } // namespace fw
 } // namespace nfd
diff --git a/daemon/fw/asf-probing-module.hpp b/daemon/fw/asf-probing-module.hpp
index 055adb0..785abd8 100644
--- a/daemon/fw/asf-probing-module.hpp
+++ b/daemon/fw/asf-probing-module.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+/*
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -55,6 +55,15 @@
   void
   afterForwardingProbe(const fib::Entry& fibEntry, const Interest& interest);
 
+  void
+  setProbingInterval(size_t probingInterval);
+
+  time::milliseconds
+  getProbingInterval() const
+  {
+    return m_probingInterval;
+  }
+
 private:
   // Used to associate FaceInfo with the face in a NextHop
   typedef std::pair<FaceInfo*, Face*> FaceInfoFacePair;
@@ -71,10 +80,11 @@
   getRandomNumber(double start, double end);
 
 public:
-  static constexpr time::seconds DEFAULT_PROBING_INTERVAL = time::seconds(60);
+  static constexpr time::milliseconds DEFAULT_PROBING_INTERVAL = time::milliseconds(60000);
+  static constexpr time::milliseconds MIN_PROBING_INTERVAL = time::milliseconds(1000);
 
 private:
-  time::seconds m_probingInterval;
+  time::milliseconds m_probingInterval;
   AsfMeasurements& m_measurements;
 };
 
diff --git a/daemon/fw/asf-strategy.cpp b/daemon/fw/asf-strategy.cpp
index 5e14e37..ab629f9 100644
--- a/daemon/fw/asf-strategy.cpp
+++ b/daemon/fw/asf-strategy.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2017,  Regents of the University of California,
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -42,29 +42,72 @@
   : Strategy(forwarder)
   , m_measurements(getMeasurements())
   , m_probing(m_measurements)
+  , m_maxSilentTimeouts(0)
   , m_retxSuppression(RETX_SUPPRESSION_INITIAL,
                       RetxSuppressionExponential::DEFAULT_MULTIPLIER,
                       RETX_SUPPRESSION_MAX)
 {
   ParsedInstanceName parsed = parseInstanceName(name);
   if (!parsed.parameters.empty()) {
-    BOOST_THROW_EXCEPTION(std::invalid_argument("AsfStrategy does not accept parameters"));
+    processParams(parsed.parameters);
   }
+
   if (parsed.version && *parsed.version != getStrategyName()[-1].toVersion()) {
     BOOST_THROW_EXCEPTION(std::invalid_argument(
       "AsfStrategy does not support version " + to_string(*parsed.version)));
   }
   this->setInstanceName(makeInstanceName(name, getStrategyName()));
+
+  NFD_LOG_DEBUG("Probing interval=" << m_probing.getProbingInterval()
+                << ", Num silent timeouts=" << m_maxSilentTimeouts);
 }
 
 const Name&
 AsfStrategy::getStrategyName()
 {
-  static Name strategyName("/localhost/nfd/strategy/asf/%FD%02");
+  static Name strategyName("/localhost/nfd/strategy/asf/%FD%03");
   return strategyName;
 }
 
 void
+AsfStrategy::processParams(const PartialName& parsed)
+{
+  for (const auto& component : parsed) {
+    std::string parsedStr(reinterpret_cast<const char*>(component.value()), component.value_size());
+    auto n = parsedStr.find("~");
+    if (n == std::string::npos) {
+      BOOST_THROW_EXCEPTION(std::invalid_argument("Format is <parameter>~<value>"));
+    }
+
+    auto f = parsedStr.substr(0, n);
+    auto s = parsedStr.substr(n + 1);
+    if (f == "probing-interval") {
+      m_probing.setProbingInterval(getParamValue(f, s));
+    }
+    else if (f == "n-silent-timeouts") {
+      m_maxSilentTimeouts = getParamValue(f, s);
+    }
+    else {
+      BOOST_THROW_EXCEPTION(std::invalid_argument("Parameter should be probing-interval or n-silent-timeouts"));
+    }
+  }
+}
+
+uint64_t
+AsfStrategy::getParamValue(const std::string& param, const std::string& value)
+{
+  try {
+    if (!value.empty() && value[0] == '-')
+      BOOST_THROW_EXCEPTION(boost::bad_lexical_cast());
+
+    return boost::lexical_cast<uint64_t>(value);
+  }
+  catch (const boost::bad_lexical_cast&) {
+    BOOST_THROW_EXCEPTION(std::invalid_argument("Value of " + param + " must be a non-negative integer"));
+  }
+}
+
+void
 AsfStrategy::afterReceiveInterest(const Face& inFace, const Interest& interest,
                                   const shared_ptr<pit::Entry>& pitEntry)
 {
@@ -132,7 +175,7 @@
   faceInfo->recordRtt(pitEntry, inFace);
 
   // Extend lifetime for measurements associated with Face
-  namespaceInfo->extendFaceInfoLifetime(*faceInfo, inFace);
+  namespaceInfo->extendFaceInfoLifetime(*faceInfo, inFace.getId());
 
   if (faceInfo->isTimeoutScheduled()) {
     faceInfo->cancelTimeoutEvent(data.getName());
@@ -169,11 +212,11 @@
     this->sendInterest(pitEntry, outFace, interest);
   }
 
-  FaceInfo& faceInfo = m_measurements.getOrCreateFaceInfo(fibEntry, interest, outFace);
+  FaceInfo& faceInfo = m_measurements.getOrCreateFaceInfo(fibEntry, interest, outFace.getId());
 
   // Refresh measurements since Face is being used for forwarding
   NamespaceInfo& namespaceInfo = m_measurements.getOrCreateNamespaceInfo(fibEntry, interest);
-  namespaceInfo.extendFaceInfoLifetime(faceInfo, outFace);
+  namespaceInfo.extendFaceInfoLifetime(faceInfo, outFace.getId());
 
   if (!faceInfo.isTimeoutScheduled()) {
     // Estimate and schedule timeout
@@ -251,7 +294,7 @@
       continue;
     }
 
-    FaceInfo* info = m_measurements.getFaceInfo(fibEntry, interest, hopFace);
+    FaceInfo* info = m_measurements.getFaceInfo(fibEntry, interest, hopFace.getId());
 
     if (info == nullptr) {
       FaceStats stats = {&hopFace,
@@ -278,10 +321,8 @@
 }
 
 void
-AsfStrategy::onTimeout(const Name& interestName, face::FaceId faceId)
+AsfStrategy::onTimeout(const Name& interestName, const face::FaceId faceId)
 {
-  NFD_LOG_TRACE("FaceId: " << faceId << " for " << interestName << " has timed-out");
-
   NamespaceInfo* namespaceInfo = m_measurements.getNamespaceInfo(interestName);
 
   if (namespaceInfo == nullptr) {
@@ -296,7 +337,23 @@
   }
 
   FaceInfo& faceInfo = it->second;
-  faceInfo.recordTimeout(interestName);
+
+  faceInfo.setNSilentTimeouts(faceInfo.getNSilentTimeouts() + 1);
+
+  if (faceInfo.getNSilentTimeouts() <= m_maxSilentTimeouts) {
+    NFD_LOG_TRACE("FaceId " << faceId << " for " << interestName << " has timed-out "
+                  << faceInfo.getNSilentTimeouts() << " time(s), ignoring");
+    // Extend lifetime for measurements associated with Face
+    namespaceInfo->extendFaceInfoLifetime(faceInfo, faceId);
+
+    if (faceInfo.isTimeoutScheduled()) {
+      faceInfo.cancelTimeoutEvent(interestName);
+    }
+  }
+  else {
+    NFD_LOG_TRACE("FaceId " << faceId << " for " << interestName << " has timed-out");
+    faceInfo.recordTimeout(interestName);
+  }
 }
 
 void
diff --git a/daemon/fw/asf-strategy.hpp b/daemon/fw/asf-strategy.hpp
index eb1c65c..b58e656 100644
--- a/daemon/fw/asf-strategy.hpp
+++ b/daemon/fw/asf-strategy.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+/*
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -51,15 +51,15 @@
   getStrategyName();
 
 public: // triggers
-  virtual void
+  void
   afterReceiveInterest(const Face& inFace, const Interest& interest,
                        const shared_ptr<pit::Entry>& pitEntry) override;
 
-  virtual void
+  void
   beforeSatisfyInterest(const shared_ptr<pit::Entry>& pitEntry,
                         const Face& inFace, const Data& data) override;
 
-  virtual void
+  void
   afterReceiveNack(const Face& inFace, const lp::Nack& nack,
                    const shared_ptr<pit::Entry>& pitEntry) override;
 
@@ -75,14 +75,21 @@
   getBestFaceForForwarding(const fib::Entry& fibEntry, const Interest& interest, const Face& inFace);
 
   void
-  onTimeout(const Name& interestName, face::FaceId faceId);
+  onTimeout(const Name& interestName, const FaceId faceId);
 
   void
   sendNoRouteNack(const Face& inFace, const Interest& interest, const shared_ptr<pit::Entry>& pitEntry);
 
+  void
+  processParams(const PartialName& parsed);
+
+  static uint64_t
+  getParamValue(const std::string& param, const std::string& value);
+
 private:
   AsfMeasurements m_measurements;
   ProbingModule m_probing;
+  size_t m_maxSilentTimeouts;
 
 private:
   RetxSuppressionExponential m_retxSuppression;
diff --git a/docs/conf.py b/docs/conf.py
index 4b0e9e1..6cf5ccb 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -258,6 +258,7 @@
     ('manpages/ndn-autoconfig.conf', 'ndn-autoconfig.conf',
         u'NDN auto-configuration client configuration file', None, 5),
     ('manpages/nfd-autoreg', 'nfd-autoreg', u'NFD Auto-registration Server', None, 1),
+    ('manpages/nfd-asf-strategy', 'nfd-asf-strategy', u'NFD ASF Strategy', None, 7),
 ]
 
 
diff --git a/docs/manpages.rst b/docs/manpages.rst
index 0dd78eb..e2e0582 100644
--- a/docs/manpages.rst
+++ b/docs/manpages.rst
@@ -18,4 +18,5 @@
    manpages/ndn-autoconfig-server
    misc/local-prefix-discovery
    manpages/nfd-autoreg
+   manpages/nfd-asf-strategy
    :maxdepth: 1
diff --git a/docs/manpages/nfd-asf-strategy.rst b/docs/manpages/nfd-asf-strategy.rst
new file mode 100644
index 0000000..5a7d785
--- /dev/null
+++ b/docs/manpages/nfd-asf-strategy.rst
@@ -0,0 +1,46 @@
+nfd-asf-strategy
+================
+
+SYNOPSIS
+--------
+| nfdc strategy set prefix <PREFIX> strategy /localhost/nfd/strategy/asf/%FD%02[/probing-interval~<PROBING-INTERVAL>][/n-silent-timeouts~<N-SILENT-TIMEOUTS>]
+
+DESCRIPTION
+-----------
+
+ASF is an Adaptive Smoothed RTT-based Forwarding Strategy that chooses the best next hop based on SRTT measurement, and also periodically probes other next hops to learn their RTTs.
+
+OPTIONS
+-------
+<PROBING-INTERVAL>
+    Tells ASF how often to send a probe to determine alternative paths.
+    The value is specified in milliseconds (non-negative integer)
+    Lower value means high overhead but faster reaction.
+    Default value is 1 minute and minimum value is 1 second.
+    It is optional to specify probing-interval.
+
+<N-SILENT-TIMEOUTS>
+    ASF switches immediately to another appropriate face (if available) upon timeout.
+    This behavior may be too sensitive for application use and appropriate only for link
+    failures and not transient timeouts. So this parameter makes ASF switch paths
+    only after it has encountered the specified number of timeouts (non-negative integer).
+    Default and minimum value is 0 i.e. switch immediately.
+    It is optional to specify n-silent-timeouts.
+
+EXAMPLES
+--------
+nfdc strategy set prefix /ndn strategy /localhost/nfd/strategy/asf
+    Use the default values.
+
+nfdc strategy set prefix /ndn strategy /localhost/nfd/strategy/asf/%FD%03/probing-interval~30000
+    Set probing interval as 30 seconds.
+
+nfdc strategy set prefix /ndn strategy /localhost/nfd/strategy/asf/%FD%03/n-silent-timeouts~5
+    Set n-silent-timeouts as 5.
+
+nfdc strategy set prefix /ndn strategy /localhost/nfd/strategy/asf/%FD%03/probing-interval~30000/n-silent-timeouts~5
+    Set probing interval as 30 seconds and n-silent-timeouts as 5.
+
+SEE ALSO
+--------
+nfdc(1), nfdc-strategy(1)
\ No newline at end of file
diff --git a/docs/manpages/nfdc-strategy.rst b/docs/manpages/nfdc-strategy.rst
index 20e33f1..a9f6886 100644
--- a/docs/manpages/nfdc-strategy.rst
+++ b/docs/manpages/nfdc-strategy.rst
@@ -71,4 +71,4 @@
 
 SEE ALSO
 --------
-nfd(1), nfdc(1)
+nfd(1), nfdc(1), nfd-asf-strategy(7)
diff --git a/tests/daemon/fw/asf-strategy.t.cpp b/tests/daemon/fw/asf-strategy.t.cpp
index 750effd..5759e68 100644
--- a/tests/daemon/fw/asf-strategy.t.cpp
+++ b/tests/daemon/fw/asf-strategy.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+/*
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -46,7 +46,8 @@
 class AsfGridFixture : public UnitTestTimeFixture
 {
 protected:
-  AsfGridFixture()
+  AsfGridFixture(Name parameters = AsfStrategy::getStrategyName())
+    : parameters(parameters)
   {
     /*
      *                  +---------+
@@ -69,10 +70,10 @@
     nodeC = topo.addForwarder("C");
     nodeD = topo.addForwarder("D");
 
-    topo.setStrategy<fw::AsfStrategy>(nodeA);
-    topo.setStrategy<fw::AsfStrategy>(nodeB);
-    topo.setStrategy<fw::AsfStrategy>(nodeC);
-    topo.setStrategy<fw::AsfStrategy>(nodeD);
+    topo.setStrategy<AsfStrategy>(nodeA, Name("ndn:/"), parameters);
+    topo.setStrategy<AsfStrategy>(nodeB, Name("ndn:/"), parameters);
+    topo.setStrategy<AsfStrategy>(nodeC, Name("ndn:/"), parameters);
+    topo.setStrategy<AsfStrategy>(nodeD, Name("ndn:/"), parameters);
 
     linkAB = topo.addLink("AB", time::milliseconds(10), {nodeA, nodeB});
     linkAD = topo.addLink("AD", time::milliseconds(100), {nodeA, nodeD});
@@ -89,13 +90,14 @@
   }
 
   void
-  runConsumer()
+  runConsumer(int numInterests = 30)
   {
-    topo.addIntervalConsumer(consumer->getClientFace(), PRODUCER_PREFIX, time::seconds(1), 30);
-    this->advanceClocks(time::milliseconds(10), time::seconds(30));
+    topo.addIntervalConsumer(consumer->getClientFace(), PRODUCER_PREFIX, time::seconds(1), numInterests);
+    this->advanceClocks(time::milliseconds(10), time::seconds(numInterests));
   }
 
 protected:
+  Name parameters;
   TopologyTester topo;
 
   TopologyNode nodeA;
@@ -114,6 +116,17 @@
   static const Name PRODUCER_PREFIX;
 };
 
+class AsfStrategyParametersGridFixture : public AsfGridFixture
+{
+protected:
+  AsfStrategyParametersGridFixture()
+    : AsfGridFixture(Name(AsfStrategy::getStrategyName())
+                     .append("probing-interval~30000")
+                     .append("n-silent-timeouts~5"))
+  {
+  }
+};
+
 const Name AsfGridFixture::PRODUCER_PREFIX = Name("ndn:/hr/C");
 
 BOOST_FIXTURE_TEST_CASE(Basic, AsfGridFixture)
@@ -214,7 +227,7 @@
                nodeD = topo.addForwarder("D");
 
   for (TopologyNode node : {nodeA, nodeB, nodeC, nodeD}) {
-    topo.setStrategy<fw::AsfStrategy>(node);
+    topo.setStrategy<AsfStrategy>(node);
   }
 
   shared_ptr<TopologyLink> linkAB = topo.addLink("AB", time::milliseconds(15), {nodeA, nodeB}),
@@ -304,6 +317,91 @@
   }
 }
 
+BOOST_FIXTURE_TEST_CASE(IgnoreTimeouts, AsfStrategyParametersGridFixture)
+{
+  // Both nodeB and nodeD have FIB entries to reach the producer
+  topo.registerPrefix(nodeB, linkBC->getFace(nodeB), PRODUCER_PREFIX);
+  topo.registerPrefix(nodeD, linkCD->getFace(nodeD), PRODUCER_PREFIX);
+
+  // Send 15 interests let it change to use the 10 ms link
+  runConsumer(15);
+
+  int outInterestsBeforeFailure = linkAD->getFace(nodeA).getCounters().nOutInterests;
+
+  // Bring down 10 ms link
+  linkAB->fail();
+
+  // Send 6 interests, first 5 will be ignored and on the 6th it will record the timeout
+  // ready to switch for the next interest
+  runConsumer(6);
+
+  // Check that link has not been switched to 100 ms because n-silent-timeouts = 5
+  BOOST_CHECK_EQUAL(linkAD->getFace(nodeA).getCounters().nOutInterests - outInterestsBeforeFailure, 0);
+
+  // Send 5 interests, check that 100 ms link is used
+  runConsumer(5);
+
+  BOOST_CHECK_EQUAL(linkAD->getFace(nodeA).getCounters().nOutInterests - outInterestsBeforeFailure, 5);
+}
+
+BOOST_FIXTURE_TEST_CASE(ProbingInterval, AsfStrategyParametersGridFixture)
+{
+  // Both nodeB and nodeD have FIB entries to reach the producer
+  topo.registerPrefix(nodeB, linkBC->getFace(nodeB), PRODUCER_PREFIX);
+  topo.registerPrefix(nodeD, linkCD->getFace(nodeD), PRODUCER_PREFIX);
+
+  // Send 6 interests let it change to use the 10 ms link
+  runConsumer(6);
+
+  shared_ptr<TopologyLink> linkAC = topo.addLink("AC", time::milliseconds(5), {nodeA, nodeD});
+  topo.registerPrefix(nodeA, linkAC->getFace(nodeA), PRODUCER_PREFIX, 1);
+
+  BOOST_CHECK_EQUAL(linkAC->getFace(nodeA).getCounters().nOutInterests, 0);
+
+  // After 30 seconds a probe would be sent that would switch make ASF switch
+  runConsumer(30);
+
+  BOOST_CHECK_EQUAL(linkAC->getFace(nodeA).getCounters().nOutInterests, 1);
+}
+
+class ParametersFixture
+{
+public:
+  void
+  checkValidity(std::string parameters, bool isCorrect)
+  {
+    Name strategyName(Name(AsfStrategy::getStrategyName()).append(parameters));
+    if (isCorrect) {
+      BOOST_CHECK_NO_THROW(make_unique<AsfStrategy>(forwarder, strategyName));
+    }
+    else {
+      BOOST_CHECK_THROW(make_unique<AsfStrategy>(forwarder, strategyName), std::invalid_argument);
+    }
+  }
+
+protected:
+  Forwarder forwarder;
+};
+
+BOOST_FIXTURE_TEST_CASE(InstantiationTest, ParametersFixture)
+{
+  checkValidity("/probing-interval~30000/n-silent-timeouts~5", true);
+  checkValidity("/n-silent-timeouts~5/probing-interval~30000", true);
+  checkValidity("/probing-interval~30000", true);
+  checkValidity("/n-silent-timeouts~5", true);
+  checkValidity("", true);
+
+  checkValidity("/probing-interval~500", false); // At least 1 seconds
+  checkValidity("/probing-interval~-5000", false);
+  checkValidity("/n-silent-timeouts~-5", false);
+  checkValidity("/n-silent-timeouts~-5/probing-interval~-30000", false);
+  checkValidity("/n-silent-timeouts", false);
+  checkValidity("/probing-interval~", false);
+  checkValidity("/~1000", false);
+  checkValidity("/probing-interval~foo", false);
+  checkValidity("/n-silent-timeouts~1~2", false);
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestAsfStrategy
 BOOST_AUTO_TEST_SUITE_END() // Fw
 
diff --git a/tests/daemon/fw/strategy-instantiation.t.cpp b/tests/daemon/fw/strategy-instantiation.t.cpp
index 391c761..cde39bb 100644
--- a/tests/daemon/fw/strategy-instantiation.t.cpp
+++ b/tests/daemon/fw/strategy-instantiation.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2017,  Regents of the University of California,
+/*
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -75,7 +75,7 @@
 
 using Tests = boost::mpl::vector<
   Test<AccessStrategy, false, 1>,
-  Test<AsfStrategy, true, 2>,
+  Test<AsfStrategy, true, 3>,
   Test<BestRouteStrategy, false, 1>,
   Test<BestRouteStrategy2, false, 5>,
   Test<ClientControlStrategy, false, 2>,
diff --git a/tests/daemon/fw/topology-tester.hpp b/tests/daemon/fw/topology-tester.hpp
index 892dc01..cbd8fd9 100644
--- a/tests/daemon/fw/topology-tester.hpp
+++ b/tests/daemon/fw/topology-tester.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2017,  Regents of the University of California,
+/*
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -209,10 +209,11 @@
    */
   template<typename S>
   void
-  setStrategy(TopologyNode i, Name prefix = Name("ndn:/"))
+  setStrategy(TopologyNode i, Name prefix = Name("ndn:/"),
+              Name instanceName = S::getStrategyName())
   {
     Forwarder& forwarder = this->getForwarder(i);
-    choose<S>(forwarder, prefix);
+    choose<S>(forwarder, prefix, instanceName);
   }
 
   /** \brief makes a link that interconnects two or more forwarders