rib: Verify face before route registration

refs: #1811

Change-Id: If99f59734b29a46eca4f6139d35361a8563a9010
diff --git a/rib/rib-manager.cpp b/rib/rib-manager.cpp
index 993d84b..318198a 100644
--- a/rib/rib-manager.cpp
+++ b/rib/rib-manager.cpp
@@ -27,6 +27,7 @@
 #include "core/global-io.hpp"
 #include "core/logger.hpp"
 #include "core/scheduler.hpp"
+#include <ndn-cxx/management/nfd-face-status.hpp>
 
 namespace nfd {
 namespace rib {
@@ -35,6 +36,7 @@
 
 const Name RibManager::COMMAND_PREFIX = "/localhost/nfd/rib";
 const Name RibManager::REMOTE_COMMAND_PREFIX = "/localhop/nfd/rib";
+const Name RibManager::FACES_LIST_DATASET_PREFIX = "/localhost/nfd/faces/list";
 
 const size_t RibManager::COMMAND_UNSIGNED_NCOMPS =
   RibManager::COMMAND_PREFIX.size() +
@@ -118,6 +120,8 @@
   NFD_LOG_INFO("Start monitoring face create/destroy events");
   m_faceMonitor.addSubscriber(bind(&RibManager::onNotification, this, _1));
   m_faceMonitor.startNotifications();
+
+  fetchActiveFaces();
 }
 
 void
@@ -240,8 +244,12 @@
   if (!validateParameters(command, parameters))
     {
       NFD_LOG_DEBUG("register result: FAIL reason: malformed");
+
       if (static_cast<bool>(request))
-        sendResponse(request->getName(), 400, "Malformed command");
+        {
+          sendResponse(request->getName(), 400, "Malformed command");
+        }
+
       return;
     }
 
@@ -250,6 +258,19 @@
       parameters.setFaceId(request->getIncomingFaceId());
     }
 
+  // Is the face valid?
+  if (activeFaces.find(parameters.getFaceId()) == activeFaces.end())
+    {
+      NFD_LOG_DEBUG("register result: FAIL reason: unknown faceId");
+
+      if (static_cast<bool>(request))
+        {
+          sendResponse(request->getName(), 410, "Face not found");
+        }
+
+      return;
+    }
+
   FaceEntry faceEntry;
   faceEntry.faceId = parameters.getFaceId();
   faceEntry.origin = parameters.getOrigin();
@@ -311,6 +332,19 @@
       parameters.setFaceId(request->getIncomingFaceId());
     }
 
+  // Is the face valid?
+  if (activeFaces.find(parameters.getFaceId()) == activeFaces.end())
+    {
+      NFD_LOG_DEBUG("register result: FAIL reason: unknown faceId");
+
+      if (static_cast<bool>(request))
+        {
+          sendResponse(request->getName(), 410, "Face not found");
+        }
+
+      return;
+    }
+
   FaceEntry faceEntry;
   faceEntry.faceId = parameters.getFaceId();
   faceEntry.origin = parameters.getOrigin();
@@ -603,8 +637,17 @@
 {
   /// \todo A notification can be missed, in this case check Facelist
   NFD_LOG_TRACE("onNotification: " << notification);
-  if (notification.getKind() == ndn::nfd::FACE_EVENT_DESTROYED) //face destroyed
+
+  if (notification.getKind() == ndn::nfd::FACE_EVENT_CREATED)
     {
+      NFD_LOG_DEBUG("Received notification for created faceId: " << notification.getFaceId());
+      activeFaces.insert(notification.getFaceId());
+    }
+  else if (notification.getKind() == ndn::nfd::FACE_EVENT_DESTROYED)
+    {
+      NFD_LOG_DEBUG("Received notification for destroyed faceId: " << notification.getFaceId());
+      activeFaces.erase(notification.getFaceId());
+
       scheduler::schedule(time::seconds(0),
                           bind(&RibManager::processErasureAfterNotification, this,
                                notification.getFaceId()));
@@ -722,5 +765,75 @@
   m_ribStatusPublisher.publish();
 }
 
+void
+RibManager::fetchActiveFaces()
+{
+  NFD_LOG_DEBUG("Fetching active faces");
+
+  Interest interest(FACES_LIST_DATASET_PREFIX);
+  interest.setChildSelector(1);
+  interest.setMustBeFresh(true);
+
+  shared_ptr<ndn::OBufferStream> buffer = make_shared<ndn::OBufferStream>();
+
+  m_face.expressInterest(interest,
+                         bind(&RibManager::fetchSegments, this, _2, buffer),
+                         bind(&RibManager::onFetchFaceStatusTimeout, this));
+}
+
+void
+RibManager::fetchSegments(const Data& data, shared_ptr<ndn::OBufferStream> buffer)
+{
+  buffer->write(reinterpret_cast<const char*>(data.getContent().value()),
+                data.getContent().value_size());
+
+  uint64_t currentSegment = data.getName().get(-1).toSegment();
+
+  const name::Component& finalBlockId = data.getMetaInfo().getFinalBlockId();
+  if (finalBlockId.empty() || finalBlockId.toSegment() > currentSegment)
+    {
+      m_face.expressInterest(data.getName().getPrefix(-1).appendSegment(currentSegment+1),
+                             bind(&RibManager::fetchSegments, this, _2, buffer),
+                             bind(&RibManager::onFetchFaceStatusTimeout, this));
+    }
+  else
+    {
+      updateActiveFaces(buffer);
+    }
+}
+
+void
+RibManager::updateActiveFaces(shared_ptr<ndn::OBufferStream> buffer)
+{
+  NFD_LOG_DEBUG("Updating active faces");
+
+  ndn::ConstBufferPtr buf = buffer->buf();
+
+  Block block;
+  size_t offset = 0;
+
+  while (offset < buf->size())
+    {
+      if (!Block::fromBuffer(buf, offset, block))
+        {
+          std::cerr << "ERROR: cannot decode FaceStatus TLV" << std::endl;
+          break;
+        }
+
+      offset += block.size();
+
+      ndn::nfd::FaceStatus status(block);
+
+      NFD_LOG_DEBUG("Adding faceId: " << status.getFaceId() << " to activeFaces");
+      activeFaces.insert(status.getFaceId());
+    }
+}
+
+void
+RibManager::onFetchFaceStatusTimeout()
+{
+  std::cerr << "Face Status Dataset request timed out" << std::endl;
+}
+
 } // namespace rib
 } // namespace nfd
diff --git a/rib/rib-manager.hpp b/rib/rib-manager.hpp
index a19d211..c31256e 100644
--- a/rib/rib-manager.hpp
+++ b/rib/rib-manager.hpp
@@ -202,6 +202,18 @@
   void
   listEntries(const Interest& request);
 
+  void
+  fetchActiveFaces();
+
+  void
+  fetchSegments(const Data& data, shared_ptr<ndn::OBufferStream> buffer);
+
+  void
+  updateActiveFaces(shared_ptr<ndn::OBufferStream> buffer);
+
+  void
+  onFetchFaceStatusTimeout();
+
 private:
   Rib m_managedRib;
   ndn::Face& m_face;
@@ -263,6 +275,11 @@
 
   static const Name LIST_COMMAND_PREFIX;
   static const size_t LIST_COMMAND_NCOMPS;
+
+  static const Name FACES_LIST_DATASET_PREFIX;
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  std::set<int> activeFaces;
 };
 
 } // namespace rib