nlsr: discover Faces from NFD

refs: #2954

Change-Id: I072972d88bce0e1012e96f33577657048b7df1e1
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()