tools: Registering /localhop/ndn-autoconf/hub prefix towards all UDP4 multicast faces

Change-Id: I0cd619f9c600862e4402bd6a1811751706223575
Refs: #1595
diff --git a/tools/ndn-autoconfig.cpp b/tools/ndn-autoconfig.cpp
index c996566..a3c8041 100644
--- a/tools/ndn-autoconfig.cpp
+++ b/tools/ndn-autoconfig.cpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * Copyright (c) 2014,  Regents of the University of California,
+ *                      Arizona Board of Regents,
+ *                      Colorado State University,
+ *                      University Pierre & Marie Curie, Sorbonne University,
+ *                      Washington University in St. Louis,
+ *                      Beijing Institute of Technology,
+ *                      The University of Memphis
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -20,13 +21,17 @@
  *
  * You should have received a copy of the GNU General Public License along with
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- **/
+ */
 
 #include "version.hpp"
 
+#include "core/face-uri.hpp"
+
 #include <ndn-cxx/face.hpp>
 #include <ndn-cxx/management/nfd-controller.hpp>
+#include <ndn-cxx/management/nfd-face-status.hpp>
 #include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/encoding/buffer-stream.hpp>
 
 #include <boost/lexical_cast.hpp>
 
@@ -39,6 +44,7 @@
 #include <arpa/nameser_compat.h>
 #endif
 
+namespace ndn {
 namespace tools {
 
 void
@@ -75,34 +81,167 @@
   {
   }
 
-  // Start to look for a hub (NDN hub discovery first stage)
+  void
+  run()
+  {
+    m_face.processEvents();
+  }
+
+  void
+  fetchSegments(const Data& data, const shared_ptr<OBufferStream>& buffer,
+                void (NdnAutoconfig::*onDone)(const shared_ptr<OBufferStream>&))
+  {
+    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(&NdnAutoconfig::fetchSegments, this, _2, buffer, onDone),
+                               bind(&NdnAutoconfig::discoverHubStage2, this, "Timeout"));
+      }
+    else
+      {
+        return (this->*onDone)(buffer);
+      }
+  }
+
   void
   discoverHubStage1()
   {
-    ndn::Interest interest(ndn::Name("/localhop/ndn-autoconf/hub"));
-    interest.setInterestLifetime(ndn::time::milliseconds(4000)); // 4 seconds
+    shared_ptr<OBufferStream> buffer = make_shared<OBufferStream>();
+
+    Interest interest("/localhost/nfd/faces/list");
+    interest.setChildSelector(1);
+    interest.setMustBeFresh(true);
+
+    m_face.expressInterest(interest,
+                           bind(&NdnAutoconfig::fetchSegments, this, _2, buffer,
+                                &NdnAutoconfig::discoverHubStage1_registerLocalhubNdnAutoconfHub),
+                           bind(&NdnAutoconfig::discoverHubStage2, this, "Timeout"));
+  }
+
+  void
+  discoverHubStage1_registerLocalhubNdnAutoconfHub(const shared_ptr<OBufferStream>& buffer)
+  {
+    ConstBufferPtr buf = buffer->buf();
+    std::vector<uint64_t> multicastFaces;
+
+    size_t offset = 0;
+    while (offset < buf->size())
+      {
+        Block block;
+        bool ok = Block::fromBuffer(buf, offset, block);
+        if (!ok)
+          {
+            std::cerr << "ERROR: cannot decode FaceStatus TLV" << std::endl;
+            break;
+          }
+
+        offset += block.size();
+
+        nfd::FaceStatus faceStatus(block);
+
+        ::nfd::FaceUri uri(faceStatus.getRemoteUri());
+        if (uri.getScheme() == "udp4") {
+          namespace ip = boost::asio::ip;
+          boost::system::error_code ec;
+          ip::address address = ip::address::from_string(uri.getHost(), ec);
+
+          if (!ec && address.is_multicast()) {
+            multicastFaces.push_back(faceStatus.getFaceId());
+          }
+          else
+            continue;
+        }
+      }
+
+    if (multicastFaces.empty()) {
+      discoverHubStage2("No multicast faces available, skipping stage 1");
+    }
+    else {
+      shared_ptr<nfd::Controller> controller = make_shared<nfd::Controller>(ref(m_face));
+      shared_ptr<std::pair<size_t, size_t> > nRegistrations =
+        make_shared<std::pair<size_t, size_t> >(0, 0);
+
+      nfd::ControlParameters parameters;
+      parameters
+        .setName("/localhop/ndn-autoconf/hub")
+        .setCost(1);
+
+      nRegistrations->first = multicastFaces.size();
+
+      for (std::vector<uint64_t>::iterator i = multicastFaces.begin();
+           i != multicastFaces.end(); ++i) {
+        parameters.setFaceId(*i);
+
+        controller->start<nfd::RibRegisterCommand>(parameters,
+          bind(&NdnAutoconfig::discoverHubStage1_onRegisterSuccess,
+               this, controller, nRegistrations),
+          bind(&NdnAutoconfig::discoverHubStage1_onRegisterFailure,
+               this, _1, _2, controller, nRegistrations));
+      }
+    }
+  }
+
+  void
+  discoverHubStage1_onRegisterSuccess(const shared_ptr<nfd::Controller>& controller,
+                                      const shared_ptr<std::pair<size_t, size_t> >& nRegistrations)
+  {
+    nRegistrations->second++;
+
+    if (nRegistrations->first == nRegistrations->second) {
+      discoverHubStage1_requestHubData();
+    }
+  }
+
+  void
+  discoverHubStage1_onRegisterFailure(uint32_t code, const std::string& error,
+                                      const shared_ptr<nfd::Controller> controller,
+                                      const shared_ptr<std::pair<size_t, size_t> >& nRegistrations)
+  {
+    std::cerr << "ERROR: " << error << " (code: " << code << ")" << std::endl;
+    nRegistrations->first--;
+
+    if (nRegistrations->first == nRegistrations->second) {
+      if (nRegistrations->first > 0) {
+        discoverHubStage1_requestHubData();
+      } else {
+        discoverHubStage2("Failed to register /localhop/ndn-autoconf/hub for all multicast faces");
+      }
+    }
+  }
+
+  // Start to look for a hub (NDN hub discovery first stage)
+  void
+  discoverHubStage1_requestHubData()
+  {
+    Interest interest(Name("/localhop/ndn-autoconf/hub"));
+    interest.setInterestLifetime(time::milliseconds(4000)); // 4 seconds
     interest.setMustBeFresh(true);
 
     std::cerr << "Stage 1: Trying multicast discovery..." << std::endl;
     m_face.expressInterest(interest,
-                           ndn::bind(&NdnAutoconfig::onDiscoverHubStage1Success, this, _1, _2),
-                           ndn::bind(&NdnAutoconfig::discoverHubStage2, this, _1, "Timeout"));
-
-    m_face.processEvents();
+                           bind(&NdnAutoconfig::onDiscoverHubStage1Success, this, _1, _2),
+                           bind(&NdnAutoconfig::discoverHubStage2, this, "Timeout"));
   }
 
   // First stage OnData Callback
   void
-  onDiscoverHubStage1Success(const ndn::Interest& interest, ndn::Data& data)
+  onDiscoverHubStage1Success(const Interest& interest, Data& data)
   {
-    const ndn::Block& content = data.getContent();
+    const Block& content = data.getContent();
     content.parse();
 
     // Get Uri
-    ndn::Block::element_const_iterator blockValue = content.find(ndn::tlv::nfd::Uri);
+    Block::element_const_iterator blockValue = content.find(tlv::nfd::Uri);
     if (blockValue == content.elements_end())
     {
-      discoverHubStage2(interest, "Incorrect reply to stage1");
+      discoverHubStage2("Incorrect reply to stage1");
       return;
     }
     std::string faceMgmtUri(reinterpret_cast<const char*>(blockValue->value()),
@@ -112,7 +251,7 @@
 
   // First stage OnTimeout callback - start 2nd stage
   void
-  discoverHubStage2(const ndn::Interest& interest, const std::string& message)
+  discoverHubStage2(const std::string& message)
   {
     std::cerr << message << std::endl;
     std::cerr << "Stage 2: Trying DNS query with default suffix..." << std::endl;
@@ -151,11 +290,11 @@
     std::cerr << message << std::endl;
     std::cerr << "Stage 3: Trying to find home router..." << std::endl;
 
-    ndn::KeyChain keyChain;
-    ndn::Name identity = keyChain.getDefaultIdentity();
+    KeyChain keyChain;
+    Name identity = keyChain.getDefaultIdentity();
     std::string serverName = "_ndn._udp.";
 
-    for (ndn::Name::const_reverse_iterator i = identity.rbegin(); i != identity.rend(); i++)
+    for (Name::const_reverse_iterator i = identity.rbegin(); i != identity.rend(); i++)
     {
       serverName.append(i->toUri());
       serverName.append(".");
@@ -195,8 +334,8 @@
   {
     std::cerr << "about to connect to: " << uri << std::endl;
 
-    m_controller.start<ndn::nfd::FaceCreateCommand>(
-      ndn::nfd::ControlParameters()
+    m_controller.start<nfd::FaceCreateCommand>(
+      nfd::ControlParameters()
         .setUri(uri),
       bind(&NdnAutoconfig::onHubConnectSuccess, this, _1),
       bind(&NdnAutoconfig::onHubConnectError, this, _1, _2)
@@ -204,17 +343,17 @@
   }
 
   void
-  onHubConnectSuccess(const ndn::nfd::ControlParameters& resp)
+  onHubConnectSuccess(const nfd::ControlParameters& resp)
   {
     std::cerr << "Successfully created face: " << resp << std::endl;
 
     // Register a prefix in RIB
-    ndn::nfd::ControlParameters ribParameters;
+    nfd::ControlParameters ribParameters;
     ribParameters
       .setName("/ndn")
       .setFaceId(resp.getFaceId());
 
-    m_controller.start<ndn::nfd::RibRegisterCommand>(
+    m_controller.start<nfd::RibRegisterCommand>(
       ribParameters,
       bind(&NdnAutoconfig::onPrefixRegistrationSuccess, this, _1),
       bind(&NdnAutoconfig::onPrefixRegistrationError, this, _1, _2));
@@ -303,7 +442,7 @@
   }
 
   void
-  onPrefixRegistrationSuccess(const ndn::nfd::ControlParameters& commandSuccessResult)
+  onPrefixRegistrationSuccess(const nfd::ControlParameters& commandSuccessResult)
   {
     std::cerr << "Successful in name registration: " << commandSuccessResult << std::endl;
   }
@@ -317,11 +456,12 @@
   }
 
 private:
-  ndn::Face m_face;
-  ndn::nfd::Controller m_controller;
+  Face m_face;
+  nfd::Controller m_controller;
 };
 
 } // namespace tools
+} // namespace ndn
 
 int
 main(int argc, char** argv)
@@ -332,7 +472,7 @@
   while ((opt = getopt(argc, argv, "hV")) != -1) {
     switch (opt) {
     case 'h':
-      tools::usage(programName);
+      ndn::tools::usage(programName);
       return 0;
     case 'V':
       std::cout << NFD_VERSION_BUILD_STRING << std::endl;
@@ -341,9 +481,10 @@
   }
 
   try {
-    tools::NdnAutoconfig autoConfigInstance;
+    ndn::tools::NdnAutoconfig autoConfigInstance;
 
     autoConfigInstance.discoverHubStage1();
+    autoConfigInstance.run();
   }
   catch (const std::exception& error) {
     std::cerr << "ERROR: " << error.what() << std::endl;