nlsr: discover Faces from NFD

refs: #2954

Change-Id: I072972d88bce0e1012e96f33577657048b7df1e1
diff --git a/src/adjacent.hpp b/src/adjacent.hpp
index 4aded01..29d7567 100644
--- a/src/adjacent.hpp
+++ b/src/adjacent.hpp
@@ -115,7 +115,7 @@
   }
 
   uint64_t
-  getFaceId()
+  getFaceId() const
   {
     return m_faceId;
   }
diff --git a/src/conf-file-processor.cpp b/src/conf-file-processor.cpp
index 789efc4..5362a41 100644
--- a/src/conf-file-processor.cpp
+++ b/src/conf-file-processor.cpp
@@ -454,6 +454,33 @@
   if (!adjLsaBuildInterval.parseFromConfigSection(section)) {
     return false;
   }
+  // Set the retry count for fetching the FaceStatus dataset
+  ConfigurationVariable<uint32_t> faceDatasetFetchTries("face-dataset-fetch-tries",
+                                                        std::bind(&ConfParameter::setFaceDatasetFetchTries,
+                                                                  &m_nlsr.getConfParameter(),
+                                                                  _1));
+
+  faceDatasetFetchTries.setMinAndMaxValue(FACE_DATASET_FETCH_TRIES_MIN,
+                                          FACE_DATASET_FETCH_TRIES_MAX);
+  faceDatasetFetchTries.setOptional(FACE_DATASET_FETCH_TRIES_DEFAULT);
+
+  if (!faceDatasetFetchTries.parseFromConfigSection(section)) {
+    return false;
+  }
+
+  // Set the interval between FaceStatus dataset fetch attempts.
+  ConfigurationVariable<ndn::time::seconds> faceDatasetFetchInterval("face-dataset-fetch-interval",
+                                                           bind(&ConfParameter::setFaceDatasetFetchInterval,
+                                                                &m_nlsr.getConfParameter(),
+                                                                _1));
+
+  faceDatasetFetchInterval.setMinAndMaxValue(ndn::time::seconds(FACE_DATASET_FETCH_INTERVAL_MIN),
+                                             ndn::time::seconds(FACE_DATASET_FETCH_INTERVAL_MAX));
+  faceDatasetFetchInterval.setOptional(ndn::time::seconds(FACE_DATASET_FETCH_INTERVAL_DEFAULT));
+
+  if (!faceDatasetFetchInterval.parseFromConfigSection(section)) {
+    return false;
+  }
 
   // first-hello-interval
   ConfigurationVariable<uint32_t> firstHelloInterval("first-hello-interval",
@@ -469,8 +496,7 @@
   for (ConfigSection::const_iterator tn =
            section.begin(); tn != section.end(); ++tn) {
 
-    if (tn->first == "neighbor")
-    {
+    if (tn->first == "neighbor") {
       try {
         ConfigSection CommandAttriTree = tn->second;
         std::string name = CommandAttriTree.get<std::string>("name");
@@ -479,7 +505,7 @@
         ndn::util::FaceUri faceUri;
         try {
           faceUri = ndn::util::FaceUri(uriString);
-        } catch (ndn::util::FaceUri::Error e) {
+        } catch (const ndn::util::FaceUri::Error& e) {
           std::cerr << "Malformed face-uri <" << uriString << "> for " << name << std::endl;
           return false;
         }
diff --git a/src/conf-parameter.hpp b/src/conf-parameter.hpp
index 9bcd95c..6ec21f5 100644
--- a/src/conf-parameter.hpp
+++ b/src/conf-parameter.hpp
@@ -62,6 +62,19 @@
   ROUTING_CALC_INTERVAL_MAX = 15
 };
 
+
+enum {
+  FACE_DATASET_FETCH_TRIES_MIN = 1,
+  FACE_DATASET_FETCH_TRIES_MAX = 10,
+  FACE_DATASET_FETCH_TRIES_DEFAULT = 3
+};
+
+enum {
+  FACE_DATASET_FETCH_INTERVAL_MIN = 1800,
+  FACE_DATASET_FETCH_INTERVAL_MAX = 5400,
+  FACE_DATASET_FETCH_INTERVAL_DEFAULT = 3600
+};
+
 enum {
   HELLO_RETRIES_MIN = 1,
   HELLO_RETRIES_DEFAULT = 3,
@@ -261,6 +274,30 @@
   }
 
   void
+  setFaceDatasetFetchTries(uint32_t count)
+  {
+    m_faceDatasetFetchTries = count;
+  }
+
+  uint32_t
+  getFaceDatasetFetchTries() const
+  {
+    return m_faceDatasetFetchTries;
+  }
+
+  void
+  setFaceDatasetFetchInterval(ndn::time::seconds interval)
+  {
+    m_faceDatasetFetchInterval = interval;
+  }
+
+  const ndn::time::seconds
+  getFaceDatasetFetchInterval() const
+  {
+    return m_faceDatasetFetchInterval;
+  }
+
+  void
   setLogLevel(const std::string& logLevel)
   {
     m_logLevel = logLevel;
@@ -423,6 +460,9 @@
   uint32_t m_firstHelloInterval;
   uint32_t m_routingCalcInterval;
 
+  uint32_t m_faceDatasetFetchTries;
+  ndn::time::seconds m_faceDatasetFetchInterval;
+
   ndn::time::seconds m_lsaInterestLifetime;
   uint32_t  m_routerDeadInterval;
   std::string m_logLevel;
diff --git a/src/hello-protocol.cpp b/src/hello-protocol.cpp
index 52e8997..4a995de 100644
--- a/src/hello-protocol.cpp
+++ b/src/hello-protocol.cpp
@@ -17,8 +17,6 @@
  * You should have received a copy of the GNU General Public License along with
  * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  *
- * \author A K M Mahmudul Hoque <ahoque1@memphis.edu>
- *
  **/
 
 #include "nlsr.hpp"
@@ -68,13 +66,6 @@
                       m_nlsr.getConfParameter().getInterestResendTime());
       _LOG_DEBUG("Sending scheduled interest: " << interestName);
     }
-    // If it does not have a Face, we need to give it one.  A
-    // successful registration prompts a callback that sends the hello
-    // Interest to the new Face.
-    else {
-      registerPrefixes((*it).getName(), (*it).getFaceUri().toString(),
-                       (*it).getLinkCost(), ndn::time::milliseconds::max());
-    }
   }
   scheduleInterest(m_nlsr.getConfParameter().getInfoInterestInterval());
 }
@@ -126,12 +117,6 @@
         expressInterest(interestName,
                         m_nlsr.getConfParameter().getInterestResendTime());
       }
-      // If the originator of the Interest currently lacks a Face, we
-      // need to give it one.
-      else {
-        registerPrefixes(adjacent->getName(), adjacent->getFaceUri().toString(),
-                         adjacent->getLinkCost(), ndn::time::milliseconds::max());
-      }
     }
   }
 }
@@ -235,82 +220,4 @@
   _LOG_DEBUG("Validation Error: " << msg);
 }
 
-
-  // Asks the FIB to register the supplied adjacency (in other words,
-  // create a Face for it).
-void
-HelloProtocol::registerPrefixes(const ndn::Name& adjName, const std::string& faceUri,
-                               double linkCost, const ndn::time::milliseconds& timeout)
-{
-  m_nlsr.getFib().registerPrefix(adjName, faceUri, linkCost, timeout,
-                                 ndn::nfd::ROUTE_FLAG_CAPTURE, 0,
-                                 std::bind(&HelloProtocol::onRegistrationSuccess,
-                                           this, _1, adjName,timeout),
-                                 std::bind(&HelloProtocol::onRegistrationFailure,
-                                           this, _1, adjName));
-}
-
-  // After we create a new Face, we need to set it up for use. This
-  // function sets the controlling strategy, registers prefixes in
-  // sync, broadcast, and LSA.
-void
-HelloProtocol::onRegistrationSuccess(const ndn::nfd::ControlParameters& commandSuccessResult,
-                                     const ndn::Name& neighbor,const ndn::time::milliseconds& timeout)
-{
-  auto adjacent = m_nlsr.getAdjacencyList().findAdjacent(neighbor);
-  if (adjacent != m_nlsr.getAdjacencyList().end()){
-    adjacent->setFaceId(commandSuccessResult.getFaceId());
-    ndn::Name broadcastKeyPrefix = DEFAULT_BROADCAST_PREFIX;
-    broadcastKeyPrefix.append("KEYS");
-    std::string faceUri = adjacent->getFaceUri().toString();
-    double linkCost = adjacent->getLinkCost();
-    m_nlsr.getFib().registerPrefix(m_nlsr.getConfParameter().getChronosyncPrefix(),
-                                 faceUri, linkCost, timeout,
-                                 ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
-    m_nlsr.getFib().registerPrefix(m_nlsr.getConfParameter().getLsaPrefix(),
-                                 faceUri, linkCost, timeout,
-                                 ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
-    m_nlsr.getFib().registerPrefix(broadcastKeyPrefix,
-                                 faceUri, linkCost, timeout,
-                                 ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
-
-    // Sends a Hello Interest to determine status before the next scheduled.
-    // interest name: /<neighbor>/NLSR/INFO/<router>
-    ndn::Name interestName(neighbor);
-    interestName.append(NLSR_COMPONENT);
-    interestName.append(INFO_COMPONENT);
-    interestName.append(m_nlsr.getConfParameter().getRouterPrefix().wireEncode());
-    expressInterest(interestName,
-                    m_nlsr.getConfParameter().getInterestResendTime());
-  }
-}
-
-void
-HelloProtocol::onRegistrationFailure(const ndn::nfd::ControlResponse& response,
-                                     const ndn::Name& name)
-{
-  _LOG_DEBUG(response.getText() << " (code: " << response.getCode() << ")");
-  /*
-  * If NLSR can not create face for given faceUri then it will treat this
-  * failure as one INFO interest timed out. So that NLSR can move on with
-  * building Adj Lsa and calculate routing table. NLSR does not build Adj
-  * Lsa unless all the neighbors are ACTIVE or DEAD. For considering the
-  * missconfigured(link) neighbour dead this is required.
-  */
-  auto adjacent = m_nlsr.getAdjacencyList().findAdjacent(name);
-  if (adjacent != m_nlsr.getAdjacencyList().end()) {
-    adjacent->setInterestTimedOutNo(adjacent->getInterestTimedOutNo() + 1);
-    Adjacent::Status status = adjacent->getStatus();
-    uint32_t infoIntTimedOutCount = adjacent->getInterestTimedOutNo();
-
-    if (infoIntTimedOutCount == m_nlsr.getConfParameter().getInterestRetryNumber()) {
-      if (status == Adjacent::STATUS_ACTIVE) {
-        adjacent->setStatus(Adjacent::STATUS_INACTIVE);
-      }
-
-      m_nlsr.getLsdb().scheduleAdjLsaBuild();
-    }
-  }
-}
-
 } // namespace nlsr
diff --git a/src/hello-protocol.hpp b/src/hello-protocol.hpp
index 1817850..808b843 100644
--- a/src/hello-protocol.hpp
+++ b/src/hello-protocol.hpp
@@ -117,9 +117,6 @@
   onRegistrationSuccess(const ndn::nfd::ControlParameters& commandSuccessResult,
                         const ndn::Name& neighbor, const ndn::time::milliseconds& timeout);
 
-  void
-  registerPrefixes(const ndn::Name& adjName, const std::string& faceUri,
-                   double linkCost, const ndn::time::milliseconds& timeout);
 private:
   Nlsr& m_nlsr;
   ndn::Scheduler& m_scheduler;
diff --git a/src/nlsr-runner.cpp b/src/nlsr-runner.cpp
index 29e4737..827593a 100644
--- a/src/nlsr-runner.cpp
+++ b/src/nlsr-runner.cpp
@@ -75,7 +75,6 @@
     std::cerr << "ERROR: " << e.what() << std::endl;
 
     m_nlsr.getFib().clean();
-    m_nlsr.destroyFaces();
   }
 }
 
diff --git a/src/nlsr.cpp b/src/nlsr.cpp
index ca829e0..5709313 100644
--- a/src/nlsr.cpp
+++ b/src/nlsr.cpp
@@ -29,6 +29,7 @@
 #include "adjacent.hpp"
 #include "logger.hpp"
 
+#include <ndn-cxx/util/face-uri.hpp>
 
 namespace nlsr {
 
@@ -63,9 +64,12 @@
                          m_routerNameDispatcher,
                          m_nlsrFace,
                          m_keyChain)
+
   , m_helloProtocol(*this, scheduler)
   , m_certificateCache(new ndn::CertificateCacheTtl(ioService))
   , m_validator(m_nlsrFace, DEFAULT_BROADCAST_PREFIX, m_certificateCache, m_certStore)
+  , m_controller(m_nlsrFace, m_keyChain, m_validator)
+  , m_faceDatasetController(m_nlsrFace, m_keyChain)
   , m_prefixUpdateProcessor(m_nlsrFace,
                             m_namePrefixList,
                             m_nlsrLsdb,
@@ -252,6 +256,9 @@
   setInfoInterestFilter();
   setLsaInterestFilter();
 
+  initializeFaces(std::bind(&Nlsr::processFaceDataset, this, _1),
+                  std::bind(&Nlsr::onFaceDatasetFetchTimeout, this, _1, _2, 0));
+
   // Set event intervals
   setFirstHelloInterval(m_confParam.getFirstHelloInterval());
   m_nlsrLsdb.setAdjLsaBuildInterval(m_confParam.getAdjLsaBuildInterval());
@@ -377,78 +384,195 @@
 }
 
 void
-Nlsr::onDestroyFaceSuccess(const ndn::nfd::ControlParameters& commandSuccessResult)
-{
-}
-
-void
-Nlsr::onDestroyFaceFailure(const ndn::nfd::ControlResponse& response)
-{
-  std::cerr << response.getText() << " (code: " << response.getCode() << ")";
-  BOOST_THROW_EXCEPTION(Error("Error: Face destruction failed"));
-}
-
-void
-Nlsr::destroyFaces()
-{
-  std::list<Adjacent>& adjacents = m_adjacencyList.getAdjList();
-  for (std::list<Adjacent>::iterator it = adjacents.begin();
-       it != adjacents.end(); it++) {
-    m_fib.destroyFace((*it).getFaceUri().toString(),
-                      std::bind(&Nlsr::onDestroyFaceSuccess, this, _1),
-                      std::bind(&Nlsr::onDestroyFaceFailure, this, _1));
-  }
-}
-
-void
 Nlsr::onFaceEventNotification(const ndn::nfd::FaceEventNotification& faceEventNotification)
 {
   _LOG_TRACE("Nlsr::onFaceEventNotification called");
-  ndn::nfd::FaceEventKind kind = faceEventNotification.getKind();
 
-  if (kind == ndn::nfd::FACE_EVENT_DESTROYED) {
-    uint64_t faceId = faceEventNotification.getFaceId();
+  switch (faceEventNotification.getKind()) {
+    case ndn::nfd::FACE_EVENT_DESTROYED: {
+      uint64_t faceId = faceEventNotification.getFaceId();
 
-    auto adjacent = m_adjacencyList.findAdjacent(faceId);
+      auto adjacent = m_adjacencyList.findAdjacent(faceId);
 
-    if (adjacent != m_adjacencyList.end()) {
-      _LOG_DEBUG("Face to " << adjacent->getName() << " with face id: " << faceId << " destroyed");
+      if (adjacent != m_adjacencyList.end()) {
+        _LOG_DEBUG("Face to " << adjacent->getName() << " with face id: " << faceId << " destroyed");
 
-      adjacent->setFaceId(0);
+        adjacent->setFaceId(0);
 
-      // Only trigger an Adjacency LSA build if this node is changing
-      // from ACTIVE to INACTIVE since this rebuild will effectively
-      // cancel the previous Adjacency LSA refresh event and schedule
-      // a new one further in the future.
-      //
-      // Continuously scheduling the refresh in the future will block
-      // the router from refreshing its Adjacency LSA. Since other
-      // routers' Name prefixes' expiration times are updated when
-      // this router refreshes its Adjacency LSA, the other routers'
-      // prefixes will expire and be removed from the RIB.
-      //
-      // This check is required to fix Bug #2733 for now. This check
-      // would be unnecessary to fix Bug #2733 when Issue #2732 is
-      // completed, but the check also helps with optimization so it
-      // can remain even when Issue #2732 is implemented.
-      if (adjacent->getStatus() == Adjacent::STATUS_ACTIVE) {
-        adjacent->setStatus(Adjacent::STATUS_INACTIVE);
+        // Only trigger an Adjacency LSA build if this node is changing
+        // from ACTIVE to INACTIVE since this rebuild will effectively
+        // cancel the previous Adjacency LSA refresh event and schedule
+        // a new one further in the future.
+        //
+        // Continuously scheduling the refresh in the future will block
+        // the router from refreshing its Adjacency LSA. Since other
+        // routers' Name prefixes' expiration times are updated when
+        // this router refreshes its Adjacency LSA, the other routers'
+        // prefixes will expire and be removed from the RIB.
+        //
+        // This check is required to fix Bug #2733 for now. This check
+        // would be unnecessary to fix Bug #2733 when Issue #2732 is
+        // completed, but the check also helps with optimization so it
+        // can remain even when Issue #2732 is implemented.
+        if (adjacent->getStatus() == Adjacent::STATUS_ACTIVE) {
+          adjacent->setStatus(Adjacent::STATUS_INACTIVE);
 
-        // A new adjacency LSA cannot be built until the neighbor is marked INACTIVE and
-        // has met the HELLO retry threshold
-        adjacent->setInterestTimedOutNo(m_confParam.getInterestRetryNumber());
+          // A new adjacency LSA cannot be built until the neighbor is marked INACTIVE and
+          // has met the HELLO retry threshold
+          adjacent->setInterestTimedOutNo(m_confParam.getInterestRetryNumber());
 
-        if (m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_OFF) {
-          getRoutingTable().scheduleRoutingTableCalculation(*this);
-        }
-        else {
-          m_nlsrLsdb.scheduleAdjLsaBuild();
+          if (m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_OFF) {
+            getRoutingTable().scheduleRoutingTableCalculation(*this);
+          }
+          else {
+            m_nlsrLsdb.scheduleAdjLsaBuild();
+          }
         }
       }
+      break;
     }
+    case ndn::nfd::FACE_EVENT_CREATED: {
+      // Find the neighbor in our adjacency list
+      _LOG_DEBUG("Face created event received.");
+      auto adjacent = m_adjacencyList.findAdjacent(
+        ndn::util::FaceUri(faceEventNotification.getRemoteUri()));
+      // If we have a neighbor by that FaceUri and it has no FaceId, we
+      // have a match.
+      if (adjacent != m_adjacencyList.end()) {
+        _LOG_DEBUG("Face creation event matches neighbor: " << adjacent->getName()
+                   << ". New Face ID: " << faceEventNotification.getFaceId()
+                   << ". Registering prefixes.");
+        adjacent->setFaceId(faceEventNotification.getFaceId());
+
+        registerAdjacencyPrefixes(*adjacent, ndn::time::milliseconds::max());
+      }
+      break;
+    }
+    default:
+      break;
   }
 }
 
+void
+Nlsr::initializeFaces(const FetchDatasetCallback& onFetchSuccess,
+                      const FetchDatasetTimeoutCallback& onFetchFailure)
+{
+  _LOG_TRACE("Initializing Faces...");
+
+  m_faceDatasetController.fetch<ndn::nfd::FaceDataset>(onFetchSuccess, onFetchFailure);
+
+}
+
+void
+Nlsr::processFaceDataset(const std::vector<ndn::nfd::FaceStatus>& faces)
+{
+  // Iterate over each neighbor listed in nlsr.conf
+  bool anyFaceChanged = false;
+  for (auto&& adjacency : m_adjacencyList.getAdjList()) {
+
+    const std::string faceUriString = adjacency.getFaceUri().toString();
+    // Check the list of FaceStatus objects we got for a match
+    for (const ndn::nfd::FaceStatus& faceStatus : faces) {
+
+      // Set the adjacency FaceID if we find a URI match and it was
+      // previously unset. Change the boolean to true.
+      if (adjacency.getFaceId() == 0 && faceUriString == faceStatus.getRemoteUri()) {
+        adjacency.setFaceId(faceStatus.getFaceId());
+        anyFaceChanged = true;
+        // Register the prefixes for each neighbor
+        this->registerAdjacencyPrefixes(adjacency, ndn::time::milliseconds::max());
+      }
+    }
+    // If this adjacency has no information in this dataset, then one
+    // of two things is happening: 1. NFD is starting slowly and this
+    // Face wasn't ready yet, or 2. NFD is configured
+    // incorrectly and this Face isn't available.
+    if (adjacency.getFaceId() == 0) {
+      _LOG_WARN("The adjacency " << adjacency.getName() <<
+                " has no Face information in this dataset.");
+    }
+  }
+
+  if (anyFaceChanged) {
+    // Only do these things if something has changed.  Schedule an
+    // adjacency LSA build to update with all of the new neighbors if
+    // HR is off.
+    if (m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_OFF) {
+      getRoutingTable().scheduleRoutingTableCalculation(*this);
+    }
+    else {
+      m_nlsrLsdb.scheduleAdjLsaBuild();
+    }
+
+    // Begin the Hello Protocol loop immediately, so we don't wait.
+    m_helloProtocol.sendScheduledInterest(0);
+  }
+
+  scheduleDatasetFetch();
+}
+
+void
+Nlsr::registerAdjacencyPrefixes(const Adjacent& adj,
+                                const ndn::time::milliseconds& timeout)
+{
+    std::string faceUri = adj.getFaceUri().toString();
+    double linkCost = adj.getLinkCost();
+
+    m_fib.registerPrefix(adj.getName(), faceUri, linkCost,
+                         timeout, ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
+
+    m_fib.registerPrefix(m_confParam.getChronosyncPrefix(),
+                                 faceUri, linkCost, timeout,
+                                 ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
+
+    m_fib.registerPrefix(m_confParam.getLsaPrefix(),
+                                 faceUri, linkCost, timeout,
+                                 ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
+
+    ndn::Name broadcastKeyPrefix = DEFAULT_BROADCAST_PREFIX;
+    broadcastKeyPrefix.append("KEYS");
+    m_fib.registerPrefix(broadcastKeyPrefix,
+                                 faceUri, linkCost, timeout,
+                                 ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
+}
+
+void
+Nlsr::onFaceDatasetFetchTimeout(uint32_t code,
+                                const std::string& msg,
+                                uint32_t nRetriesSoFar)
+{
+  // If we have exceeded the maximum attempt count, do not try again.
+  if (nRetriesSoFar++ < m_confParam.getFaceDatasetFetchTries()) {
+    _LOG_DEBUG("Failed to fetch dataset: " << msg << ". Attempting retry #" << nRetriesSoFar);
+    m_faceDatasetController.fetch<ndn::nfd::FaceDataset>(std::bind(&Nlsr::processFaceDataset,
+                                                        this, _1),
+                                              std::bind(&Nlsr::onFaceDatasetFetchTimeout,
+                                                        this, _1, _2, nRetriesSoFar));
+  }
+  else {
+    _LOG_ERROR("Failed to fetch dataset: " << msg << ". Exceeded limit of " <<
+               m_confParam.getFaceDatasetFetchTries() << ", so not trying again this time.");
+    // If we fail to fetch it, just do nothing until the next
+    // interval.  Since this is a backup mechanism, we aren't as
+    // concerned with retrying.
+    scheduleDatasetFetch();
+  }
+}
+
+void
+Nlsr::scheduleDatasetFetch()
+{
+  m_scheduler.scheduleEvent(m_confParam.getFaceDatasetFetchInterval(),
+    [this] {
+      this->initializeFaces(
+        [this] (const std::vector<ndn::nfd::FaceStatus>& faces) {
+         this->processFaceDataset(faces);
+        },
+        [this] (uint32_t code, const std::string& msg) {
+         this->onFaceDatasetFetchTimeout(code, msg, 0);
+        });
+  });
+}
 
 void
 Nlsr::startEventLoop()
diff --git a/src/nlsr.hpp b/src/nlsr.hpp
index 81629ae..bb67f2d 100644
--- a/src/nlsr.hpp
+++ b/src/nlsr.hpp
@@ -33,6 +33,9 @@
 #include <ndn-cxx/mgmt/nfd/face-event-notification.hpp>
 #include <ndn-cxx/mgmt/nfd/face-monitor.hpp>
 #include <ndn-cxx/mgmt/dispatcher.hpp>
+#include <ndn-cxx/mgmt/nfd/face-status.hpp>
+#include <ndn-cxx/data.hpp>
+#include <ndn-cxx/encoding/block.hpp>
 
 #include "adjacency-list.hpp"
 #include "common.hpp"
@@ -57,7 +60,9 @@
 
 class Nlsr
 {
-  friend class NlsrRunner;
+public:
+  using FetchDatasetCallback = std::function<void(const std::vector<ndn::nfd::FaceStatus>&)>;
+  using FetchDatasetTimeoutCallback = std::function<void(uint32_t, const std::string&)>;
 
   class Error : public std::runtime_error
   {
@@ -69,7 +74,6 @@
     }
   };
 
-public:
   Nlsr(boost::asio::io_service& ioService, ndn::Scheduler& scheduler, ndn::Face& face, ndn::KeyChain& keyChain);
 
   void
@@ -225,6 +229,51 @@
   void
   initialize();
 
+  /*! \brief Initializes neighbors' Faces using information from NFD.
+   * \sa Nlsr::initialize()
+   * \sa Nlsr::processFaceDataset()
+   *
+   * This function serves as the entry-point for initializing the
+   * neighbors listed in nlsr.conf during Nlsr::initialize(). NLSR
+   * will attempt to fetch a dataset of Faces from NFD, and configure
+   * each of its neighbors using information from that dataset. The
+   * explicit callbacks allow for better testability.
+   */
+  void
+  initializeFaces(const FetchDatasetCallback& onFetchSuccess,
+                  const FetchDatasetTimeoutCallback& onFetchFailure);
+
+  void
+  onFaceDatasetFetchTimeout(uint32_t code,
+                            const std::string& reason,
+                            uint32_t nRetriesSoFar);
+
+  /*! \brief Consumes a Face StatusDataset to configure NLSR neighbors.
+   * \sa Nlsr::initializeFaces
+   * \param faces A Face Dataset that should conform to FaceMgmt specifications.
+   *
+   * This function processes a Face StatusDataset that should conform
+   * to the FaceMgmt specifications listed
+   * [here](https://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Face-Dataset).
+   * Any newly configured neighbors will have prefixes registered with NFD
+   * and be sent Hello Interests as well.
+   */
+  void
+  processFaceDataset(const std::vector<ndn::nfd::FaceStatus>& faces);
+
+  /*! \brief Registers NLSR-specific prefixes for a neighbor (Adjacent)
+   * \sa Nlsr::initializeFaces
+   * \param adj A reference to the neighbor to register prefixes for
+   * \param timeout The amount of time to give NFD to respond to *each* registration request.
+   *
+   * Registers the prefixes in NFD that NLSR needs to route with a
+   * neighbor. The timeout given is how long to set the timeout for
+   * *each* registration request that is made.
+   */
+  void
+  registerAdjacencyPrefixes(const Adjacent& adj,
+                            const ndn::time::milliseconds& timeout);
+
   void
   initializeKey();
 
@@ -297,14 +346,6 @@
   }
 
   void
-  createFace(const std::string& faceUri,
-             const CommandSucceedCallback& onSuccess,
-             const CommandFailCallback& onFailure);
-
-  void
-  destroyFaces();
-
-  void
   setStrategies();
 
   void
@@ -316,8 +357,7 @@
     return m_firstHelloInterval;
   }
 
-  /**
-   * \brief Canonize the URI for this and all proceeding neighbors in a list.
+  /*! \brief Canonize the URI for this and all proceeding neighbors in a list.
    *
    * This function canonizes the URI of the Adjacent object pointed to
    * by currentNeighbor. It then executes the then callback, providing
@@ -365,12 +405,6 @@
   onKeyPrefixRegSuccess(const ndn::Name& name);
 
   void
-  onDestroyFaceSuccess(const ndn::nfd::ControlParameters& commandSuccessResult);
-
-  void
-  onDestroyFaceFailure(const ndn::nfd::ControlResponse& response);
-
-  void
   onFaceEventNotification(const ndn::nfd::FaceEventNotification& faceEventNotification);
 
   void
@@ -379,8 +413,7 @@
     m_firstHelloInterval = interval;
   }
 
-  /**
-   * \brief Continues canonizing neighbor URIs.
+  /*! \brief Continues canonizing neighbor URIs.
    *
    * For testability reasons, we want what each instance of
    * canonization does after completion to be controllable. The best
@@ -390,6 +423,9 @@
   void
   canonizeContinuation(std::list<Adjacent>::iterator iterator);
 
+  void
+  scheduleDatasetFetch();
+
 public:
   static const ndn::Name LOCALHOST_PREFIX;
 
@@ -421,7 +457,13 @@
 private:
   std::shared_ptr<ndn::CertificateCacheTtl> m_certificateCache;
   security::CertificateStore m_certStore;
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   Validator m_validator;
+
+private:
+  ndn::nfd::Controller m_controller;
+  ndn::nfd::Controller m_faceDatasetController;
   ndn::security::SigningInfo m_signingInfo;
   ndn::Name m_defaultCertName;
   update::PrefixUpdateProcessor m_prefixUpdateProcessor;
@@ -430,6 +472,8 @@
   ndn::nfd::FaceMonitor m_faceMonitor;
 
   uint32_t m_firstHelloInterval;
+
+  friend class NlsrRunner;
 };
 
 } // namespace nlsr
diff --git a/src/route/fib.cpp b/src/route/fib.cpp
index 73cde75..49ce647 100644
--- a/src/route/fib.cpp
+++ b/src/route/fib.cpp
@@ -209,37 +209,6 @@
 }
 
 void
-Fib::createFace(const std::string& faceUri,
-                const CommandSucceedCallback& onSuccess,
-                const CommandFailCallback& onFailure)
-{
-  m_faceController.createFace(faceUri, onSuccess, onFailure);
-}
-
-void
-Fib::destroyFace(const std::string& faceUri,
-                 const CommandSucceedCallback& onSuccess,
-                 const CommandFailCallback& onFailure)
-{
-  createFace(faceUri,
-             std::bind(&Fib::destroyFaceInNfd, this, _1, onSuccess, onFailure),
-             onFailure);
-}
-
-void
-Fib::destroyFaceInNfd(const ndn::nfd::ControlParameters& faceDestroyResult,
-                        const CommandSucceedCallback& onSuccess,
-                        const CommandFailCallback& onFailure)
-{
-  ndn::nfd::ControlParameters faceParameters;
-  faceParameters
-    .setFaceId(faceDestroyResult.getFaceId());
-  m_controller.start<ndn::nfd::FaceDestroyCommand>(faceParameters,
-                                                   onSuccess,
-                                                   onFailure);
-}
-
-void
 Fib::registerPrefix(const ndn::Name& namePrefix, const std::string& faceUri,
                     uint64_t faceCost, const ndn::time::milliseconds& timeout,
                     uint64_t flags, uint8_t times)
@@ -264,35 +233,6 @@
   }
 }
 
-typedef void(Fib::*RegisterPrefixCallback)(const ndn::nfd::ControlParameters&,
-                                           const ndn::nfd::ControlParameters&, uint8_t,
-                                           const CommandSucceedCallback&,
-                                           const CommandFailCallback&);
-
-void
-Fib::registerPrefix(const ndn::Name& namePrefix,
-                    const std::string& faceUri,
-                    uint64_t faceCost,
-                    const ndn::time::milliseconds& timeout,
-                    uint64_t flags,
-                    uint8_t times,
-                    const CommandSucceedCallback& onSuccess,
-                    const CommandFailCallback& onFailure)
-
-{
-  ndn::nfd::ControlParameters parameters;
-  parameters
-    .setName(namePrefix)
-    .setFlags(flags)
-    .setCost(faceCost)
-    .setExpirationPeriod(timeout)
-    .setOrigin(ndn::nfd::ROUTE_ORIGIN_NLSR);
-  createFace(faceUri,
-             std::bind(static_cast<RegisterPrefixCallback>(&Fib::registerPrefixInNfd),
-                       this, _1, parameters, times, onSuccess, onFailure),
-             onFailure);
-}
-
 void
 Fib::registerPrefixInNfd(ndn::nfd::ControlParameters& parameters,
                          const std::string& faceUri,
@@ -304,33 +244,13 @@
                                                              "Successful in name registration",
                                                              faceUri),
                                                    std::bind(&Fib::onRegistrationFailure,
-                                                             this, _1,
+                                                            this, _1,
                                                              "Failed in name registration",
                                                              parameters,
                                                              faceUri, times));
 }
 
 void
-Fib::registerPrefixInNfd(const ndn::nfd::ControlParameters& faceCreateResult,
-                         const ndn::nfd::ControlParameters& parameters,
-                         uint8_t times,
-                         const CommandSucceedCallback& onSuccess,
-                         const CommandFailCallback& onFailure)
-{
-  ndn::nfd::ControlParameters controlParameters;
-  controlParameters
-    .setName(parameters.getName())
-    .setFaceId(faceCreateResult.getFaceId())
-    .setCost(parameters.getCost())
-    .setFlags(parameters.getFlags())
-    .setExpirationPeriod(parameters.getExpirationPeriod())
-    .setOrigin(ndn::nfd::ROUTE_ORIGIN_NLSR);
-  m_controller.start<ndn::nfd::RibRegisterCommand>(controlParameters,
-                                                   onSuccess,
-                                                   onFailure);
-}
-
-void
 Fib::unregisterPrefix(const ndn::Name& namePrefix, const std::string& faceUri)
 {
   uint32_t faceId = m_faceMap.getFaceId(faceUri);
@@ -374,6 +294,9 @@
   _LOG_DEBUG("Register successful Prefix: " << commandSuccessResult.getName() <<
              " Face Uri: " << faceUri);
 
+  // Update the adjacency list with the new Face ID
+  m_adjacencyList.findAdjacent(faceUri)->setFaceId(commandSuccessResult.getFaceId());
+  // Update the fast-access FaceMap with the new Face ID, too
   m_faceMap.update(faceUri, commandSuccessResult.getFaceId());
   m_faceMap.writeLog();
 }
diff --git a/src/route/fib.hpp b/src/route/fib.hpp
index 10aa4fa..2705e47 100644
--- a/src/route/fib.hpp
+++ b/src/route/fib.hpp
@@ -26,15 +26,12 @@
 #include "face-map.hpp"
 #include "fib-entry.hpp"
 #include "test-access-control.hpp"
-#include "utility/face-controller.hpp"
 
 #include <ndn-cxx/mgmt/nfd/controller.hpp>
 #include <ndn-cxx/util/time.hpp>
 
 namespace nlsr {
 
-typedef ndn::nfd::Controller::CommandSucceedCallback CommandSucceedCallback;
-typedef ndn::nfd::Controller::CommandFailCallback CommandFailCallback;
 typedef std::function<void(FibEntry&)> afterRefreshCallback;
 
 class AdjacencyList;
@@ -49,7 +46,6 @@
     : m_scheduler(scheduler)
     , m_refreshTime(0)
     , m_controller(face, keyChain)
-    , m_faceController(face.getIoService(), m_controller)
     , m_adjacencyList(adjacencyList)
     , m_confParameter(conf)
   {
@@ -82,26 +78,11 @@
                  uint8_t times);
 
   void
-  registerPrefix(const ndn::Name& namePrefix,
-                 const std::string& faceUri,
-                 uint64_t faceCost,
-                 const ndn::time::milliseconds& timeout,
-                 uint64_t flags,
-                 uint8_t times,
-                 const CommandSucceedCallback& onSuccess,
-                 const CommandFailCallback& onFailure);
-
-  void
   setStrategy(const ndn::Name& name, const std::string& strategy, uint32_t count);
 
   void
   writeLog();
 
-  void
-  destroyFace(const std::string& faceUri,
-              const CommandSucceedCallback& onSuccess,
-              const CommandFailCallback& onFailure);
-
 private:
   bool
   isPrefixUpdatable(const ndn::Name& name);
@@ -113,28 +94,11 @@
   getNumberOfFacesForName(NexthopList& nextHopList);
 
   void
-  createFace(const std::string& faceUri,
-             const CommandSucceedCallback& onSuccess,
-             const CommandFailCallback& onFailure);
-
-  void
   registerPrefixInNfd(ndn::nfd::ControlParameters& parameters,
                       const std::string& faceUri,
                       uint8_t times);
 
   void
-  registerPrefixInNfd(const ndn::nfd::ControlParameters& faceCreateResult,
-                      const ndn::nfd::ControlParameters& parameters,
-                      uint8_t times,
-                      const CommandSucceedCallback& onSuccess,
-                      const CommandFailCallback& onFailure);
-
-  void
-  destroyFaceInNfd(const ndn::nfd::ControlParameters& faceDestroyResult,
-                   const CommandSucceedCallback& onSuccess,
-                   const CommandFailCallback& onFailure);
-
-  void
   unregisterPrefix(const ndn::Name& namePrefix, const std::string& faceUri);
 
   void
@@ -184,7 +148,6 @@
   ndn::Scheduler& m_scheduler;
   int32_t m_refreshTime;
   ndn::nfd::Controller m_controller;
-  util::FaceController m_faceController;
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   FaceMap m_faceMap;
diff --git a/src/utility/face-controller.cpp b/src/utility/face-controller.cpp
deleted file mode 100644
index e6c0a8b..0000000
--- a/src/utility/face-controller.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2017,  The University of Memphis,
- *                           Regents of the University of California
- *
- * This file is part of NLSR (Named-data Link State Routing).
- * See AUTHORS.md for complete list of NLSR authors and contributors.
- *
- * NLSR is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NLSR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- *
- **/
-
-#include "face-controller.hpp"
-
-#include "common.hpp"
-#include "logger.hpp"
-
-namespace nlsr {
-namespace util {
-
-INIT_LOGGER("FaceController");
-
-using ndn::util::FaceUri;
-
-void
-FaceController::createFace(const std::string& request,
-                           const CommandSuccessCallback& onSuccess,
-                           const CommandFailureCallback& onFailure)
-{
-  FaceUri uri(request);
-
-  _LOG_TRACE("Converting " << uri << " to canonical form");
-  uri.canonize(std::bind(&FaceController::onCanonizeSuccess, this, _1, onSuccess, onFailure, uri),
-               std::bind(&FaceController::onCanonizeFailure, this, _1, onSuccess, onFailure, uri),
-               m_ioService, TIME_ALLOWED_FOR_CANONIZATION);
-}
-
-void
-FaceController::createFaceInNfd(const FaceUri& uri,
-                                const CommandSuccessCallback& onSuccess,
-                                const CommandFailureCallback& onFailure)
-{
-  ndn::nfd::ControlParameters faceParameters;
-  faceParameters.setUri(uri.toString());
-
-  _LOG_DEBUG("Creating Face in NFD with face-uri: " << uri);
-  m_controller.start<ndn::nfd::FaceCreateCommand>(faceParameters, onSuccess,
-                                                  [onSuccess, onFailure, uri] (const ndn::nfd::ControlResponse& response) {
-                                                    ndn::nfd::ControlParameters faceParams(response.getBody());
-                                                    if (response.getCode() == 409 && faceParams.getUri() == uri.toString()) {
-                                                      _LOG_DEBUG("Got 409 - treating as success");
-                                                      onSuccess(faceParams);
-                                                    }
-                                                    else {
-                                                      onFailure(response);
-                                                    }
-                                                  }
-                                                 );
-}
-
-void
-FaceController::onCanonizeSuccess(const FaceUri& uri,
-                                  const CommandSuccessCallback& onSuccess,
-                                  const CommandFailureCallback& onFailure,
-                                  const FaceUri& request)
-{
-  _LOG_DEBUG("Converted " << request << " to canonical form: " << uri);
-
-  createFaceInNfd(uri, onSuccess, onFailure);
-}
-
-void
-FaceController::onCanonizeFailure(const std::string& reason,
-                                  const CommandSuccessCallback& onSuccess,
-                                  const CommandFailureCallback& onFailure,
-                                  const FaceUri& request)
-{
-  _LOG_WARN("Could not convert " << request << " to canonical form: " << reason);
-  onFailure(ndn::nfd::ControlResponse(CANONIZE_ERROR_CODE,
-                                      "Could not canonize face-uri: " + request.toString()));
-}
-
-} // namespace util
-} // namespace nlsr
diff --git a/src/utility/face-controller.hpp b/src/utility/face-controller.hpp
deleted file mode 100644
index d44c2ec..0000000
--- a/src/utility/face-controller.hpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2017,  The University of Memphis,
- *                           Regents of the University of California,
- *                           Arizona Board of Regents.
- *
- * This file is part of NLSR (Named-data Link State Routing).
- * See AUTHORS.md for complete list of NLSR authors and contributors.
- *
- * NLSR is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NLSR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- **/
-
-#ifndef NLSR_UTIL_FACE_CONTROLLER_HPP
-#define NLSR_UTIL_FACE_CONTROLLER_HPP
-
-#include <ndn-cxx/util/face-uri.hpp>
-#include <ndn-cxx/mgmt/nfd/controller.hpp>
-
-namespace nlsr {
-namespace util {
-
-class FaceController : boost::noncopyable
-{
-public:
-  typedef ndn::nfd::Controller::CommandSucceedCallback CommandSuccessCallback;
-  typedef ndn::nfd::Controller::CommandFailCallback CommandFailureCallback;
-
-  FaceController(boost::asio::io_service& io, ndn::nfd::Controller& controller)
-    : m_ioService(io)
-    , m_controller(controller)
-  {
-  }
-
-  void
-  createFace(const std::string& request,
-             const CommandSuccessCallback& onSuccess,
-             const CommandFailureCallback& onFailure);
-
-private:
-  void
-  createFaceInNfd(const ndn::util::FaceUri& uri,
-                  const CommandSuccessCallback& onSuccess,
-                  const CommandFailureCallback& onFailure);
-
-  void
-  onCanonizeSuccess(const ndn::util::FaceUri& uri,
-                    const CommandSuccessCallback& onSuccess,
-                    const CommandFailureCallback& onFailure,
-                    const ndn::util::FaceUri& request);
-
-  void
-  onCanonizeFailure(const std::string& reason,
-                    const CommandSuccessCallback& onSuccess,
-                    const CommandFailureCallback& onFailure,
-                    const ndn::util::FaceUri& request);
-
-private:
-  boost::asio::io_service& m_ioService;
-  ndn::nfd::Controller& m_controller;
-
-  static const uint32_t CANONIZE_ERROR_CODE = 408;
-};
-
-} // namespace util
-} // namespace nlsr
-
-#endif // NLSR_UTIL_FACE_CONTROLLER_HPP
diff --git a/src/validator.cpp b/src/validator.cpp
new file mode 100644
index 0000000..a73c9ee
--- /dev/null
+++ b/src/validator.cpp
@@ -0,0 +1,89 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2017,  The University of Memphis,
+ *                           Regents of the University of California
+ *
+ * This file is part of NLSR (Named-data Link State Routing).
+ * See AUTHORS.md for complete list of NLSR authors and contributors.
+ *
+ * NLSR is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NLSR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "validator.hpp"
+
+namespace nlsr {
+
+void
+Validator::checkPolicy(const ndn::Data& data, int nSteps, const ndn::OnDataValidated& onValidated,
+                       const ndn::OnDataValidationFailed& onValidationFailed,
+                       std::vector<shared_ptr<ndn::ValidationRequest>>& nextSteps)
+{
+  if (!m_shouldValidate) {
+    onValidated(data.shared_from_this());
+  }
+  else {
+    ValidatorConfig::checkPolicy(data, nSteps, onValidated, onValidationFailed, nextSteps);
+  }
+}
+
+void
+Validator::afterCheckPolicy(const NextSteps& nextSteps, const OnFailure& onFailure)
+{
+  if (m_face == nullptr) {
+    onFailure("Require more information to validate the packet!");
+    return;
+  }
+
+  for (const std::shared_ptr<ndn::ValidationRequest>& request : nextSteps) {
+
+    ndn::Interest& interest = request->m_interest;
+
+    // Look for certificate in permanent storage
+    std::shared_ptr<const ndn::IdentityCertificate> cert = m_certStore.find(interest.getName());
+
+    if (cert != nullptr) {
+      // If the certificate is found, no reason to express interest
+      std::shared_ptr<ndn::Data> data = std::make_shared<ndn::Data>(interest.getName());
+      data->setContent(cert->wireEncode());
+
+      Validator::onData(interest, *data, request);
+    }
+    else {
+      // Prepend broadcast prefix to interest name
+      ndn::Name broadcastName = m_broadcastPrefix;
+      broadcastName.append(interest.getName());
+      interest.setName(broadcastName);
+
+      // Attempt to fetch the certificate
+      m_face->expressInterest(interest,
+                              std::bind(&Validator::onData, this, _1, _2, request),
+                              std::bind(&Validator::onTimeout, // Nack
+                                        this, _1, request->m_nRetries,
+                                        onFailure,
+                                        request),
+                              std::bind(&Validator::onTimeout,
+                                        this, _1, request->m_nRetries,
+                                        onFailure,
+                                        request));
+    }
+  }
+}
+
+std::shared_ptr<const ndn::Data>
+Validator::preCertificateValidation(const ndn::Data& data)
+{
+  std::shared_ptr<ndn::Data> internalData = std::make_shared<ndn::Data>();
+  internalData->wireDecode(data.getContent().blockFromValue());
+  return internalData;
+}
+
+} // namespace nlsr
diff --git a/src/validator.hpp b/src/validator.hpp
index 4387ea5..c4f2a19 100644
--- a/src/validator.hpp
+++ b/src/validator.hpp
@@ -49,6 +49,7 @@
             security::CertificateStore& certStore,
             const int stepLimit = 10)
     : ndn::ValidatorConfig(face, cache, ndn::ValidatorConfig::DEFAULT_GRACE_INTERVAL, stepLimit)
+    , m_shouldValidate(true)
     , m_broadcastPrefix(broadcastPrefix)
     , m_certStore(certStore)
   {
@@ -75,57 +76,22 @@
 protected:
   typedef std::vector<std::shared_ptr<ndn::ValidationRequest>> NextSteps;
 
-  virtual void
+  void
+  checkPolicy(const ndn::Data& data,
+              int nSteps,
+              const ndn::OnDataValidated& onValidated,
+              const ndn::OnDataValidationFailed& onValidationFailed,
+              std::vector<shared_ptr<ndn::ValidationRequest>>& nextSteps) override;
+
+  void
   afterCheckPolicy(const NextSteps& nextSteps,
-                   const OnFailure& onFailure)
-  {
-    if (m_face == nullptr) {
-      onFailure("Require more information to validate the packet!");
-      return;
-    }
+                   const OnFailure& onFailure) override;
 
-    for (const std::shared_ptr<ndn::ValidationRequest>& request : nextSteps) {
+  std::shared_ptr<const ndn::Data>
+  preCertificateValidation(const ndn::Data& data) override;
 
-      ndn::Interest& interest = request->m_interest;
-
-      // Look for certificate in permanent storage
-      std::shared_ptr<const ndn::IdentityCertificate> cert = m_certStore.find(interest.getName());
-
-      if (cert != nullptr) {
-        // If the certificate is found, no reason to express interest
-        std::shared_ptr<ndn::Data> data = std::make_shared<ndn::Data>(interest.getName());
-        data->setContent(cert->wireEncode());
-
-        Validator::onData(interest, *data, request);
-      }
-      else {
-        // Prepend broadcast prefix to interest name
-        ndn::Name broadcastName = m_broadcastPrefix;
-        broadcastName.append(interest.getName());
-        interest.setName(broadcastName);
-
-        // Attempt to fetch the certificate
-        m_face->expressInterest(interest,
-                                std::bind(&Validator::onData, this, _1, _2, request),
-                                std::bind(&Validator::onTimeout, // Nack
-                                          this, _1, request->m_nRetries,
-                                          onFailure,
-                                          request),
-                                std::bind(&Validator::onTimeout,
-                                          this, _1, request->m_nRetries,
-                                          onFailure,
-                                          request));
-      }
-    }
-  }
-
-  virtual std::shared_ptr<const ndn::Data>
-  preCertificateValidation(const ndn::Data& data)
-  {
-    std::shared_ptr<ndn::Data> internalData = std::make_shared<ndn::Data>();
-    internalData->wireDecode(data.getContent().blockFromValue());
-    return internalData;
-  }
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  bool m_shouldValidate;
 
 private:
   ndn::Name m_broadcastPrefix;