nrd: Basic version of nrd

refs #1324

Change-Id: I797922e8367e0473bb8ed39840e44d038295f148
diff --git a/src/common.hpp b/src/common.hpp
index e19ead0..b09fc63 100644
--- a/src/common.hpp
+++ b/src/common.hpp
@@ -10,6 +10,18 @@
 #include <iostream>
 #include <list>
 
+#include <ndn-cpp-dev/face.hpp>
+#include <ndn-cpp-dev/security/key-chain.hpp>
+#include <ndn-cpp-dev/util/scheduler.hpp>
+
+#include <ndn-cpp-dev/management/nfd-fib-management-options.hpp>
+#include <ndn-cpp-dev/management/nfd-face-management-options.hpp>
+#include <ndn-cpp-dev/management/nfd-controller.hpp>
+#include <ndn-cpp-dev/management/nfd-control-response.hpp>
 #include <ndn-cpp-dev/management/nrd-prefix-reg-options.hpp>
 
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/regex_find_format.hpp>
+
 #endif // NRD_COMMON_HPP
diff --git a/src/main.cpp b/src/main.cpp
index 0ec8e7a..ce89f3f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -4,8 +4,18 @@
  * See COPYING for copyright and distribution information.
  */
 
+#include "nrd.hpp"
+
 int
 main(int argc, char** argv)
 {
+  try {
+    ndn::nrd::Nrd nrd;
+    nrd.enableLocalControlHeader();
+    nrd.listen();
+  }
+  catch (std::exception& e) {
+    std::cerr << "ERROR: " << e.what() << std::endl;
+  }
   return 0;
 }
diff --git a/src/nrd.cpp b/src/nrd.cpp
new file mode 100644
index 0000000..0928a63
--- /dev/null
+++ b/src/nrd.cpp
@@ -0,0 +1,260 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "nrd.hpp"
+
+namespace ndn {
+namespace nrd {
+
+const Name Nrd::COMMAND_PREFIX = "/localhost/nrd";
+
+const size_t Nrd::COMMAND_UNSIGNED_NCOMPS =
+  Nrd::COMMAND_PREFIX.size() +
+  1 + // verb
+  1;  // verb options
+
+const size_t Nrd::COMMAND_SIGNED_NCOMPS =
+  Nrd::COMMAND_UNSIGNED_NCOMPS +
+  4; // (timestamp, nonce, signed info tlv, signature tlv)
+
+const Nrd::VerbAndProcessor Nrd::COMMAND_VERBS[] =
+  {
+    VerbAndProcessor(
+                     Name::Component("register"),
+                     &Nrd::insertEntry
+                     ),
+
+    VerbAndProcessor(
+                     Name::Component("unregister"),
+                     &Nrd::deleteEntry
+                     ),
+  };
+
+void
+setInterestFilterFailed(const Name& name, const std::string& msg) 
+{
+  std::cerr << "Error in setting interest filter (" << name << "): " << msg << std::endl;
+}
+
+Nrd::Nrd()
+  : m_nfdController(new nfd::Controller(m_face)),
+    m_verbDispatch(COMMAND_VERBS,
+                   COMMAND_VERBS + (sizeof(COMMAND_VERBS) / sizeof(VerbAndProcessor)))
+{
+  m_face.setController(m_nfdController);
+  m_face.setInterestFilter("/localhost/nrd",
+                           bind(&Nrd::onRibRequest, this, _2),
+                           bind(&setInterestFilterFailed, _1, _2));
+}
+
+void
+Nrd::sendResponse(const Name& name,
+                  const nfd::ControlResponse& response)
+{
+  const Block& encodedControl = response.wireEncode();
+
+  Data responseData(name);
+  responseData.setContent(encodedControl);
+
+  m_keyChain.sign(responseData);
+  m_face.put(responseData);
+}
+
+void
+Nrd::sendResponse(const Name& name,
+                  uint32_t code,
+                  const std::string& text)
+{
+  nfd::ControlResponse response(code, text);
+  sendResponse(name, response);
+}
+
+void
+Nrd::onRibRequest(const Interest& request)
+{
+  const Name& command = request.getName();
+  const size_t commandNComps = command.size();
+
+  if (COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
+      commandNComps < COMMAND_SIGNED_NCOMPS)
+    {
+      std::cerr << "Error: Signature required" << std::endl;
+      sendResponse(command, 401, "Signature required");
+      return;
+    }
+  else if (commandNComps < COMMAND_SIGNED_NCOMPS ||
+           !COMMAND_PREFIX.isPrefixOf(command))
+    {
+      std::cerr << "Error: Malformed Command" << std::endl;
+      sendResponse(command, 400, "Malformed command");
+      return;
+    }
+
+  const Name::Component& verb = command.get(COMMAND_PREFIX.size());
+  VerbDispatchTable::const_iterator verbProcessor = m_verbDispatch.find(verb);
+
+  if (verbProcessor != m_verbDispatch.end())
+    {
+      PrefixRegOptions options;
+      if (!extractOptions(request, options))
+        {
+          sendResponse(command, 400, "Malformed command");
+          return;
+        }
+
+      /// \todo authorize command
+      if (false)
+        {
+          sendResponse(request.getName(), 403, "Unauthorized command");
+          return;
+        }
+
+      // \todo add proper log support
+      std::cout << "Received options (name, faceid, cost): " << options.getName() << 
+                   ", " << options.getFaceId() << ", "  << options.getCost() << std::endl;
+
+      nfd::ControlResponse response;
+      (verbProcessor->second)(this, request, options);
+    }
+  else
+    {
+      sendResponse(request.getName(), 501, "Unsupported command");
+    }
+}
+
+bool
+Nrd::extractOptions(const Interest& request,
+                    PrefixRegOptions& extractedOptions)
+{
+  const Name& command = request.getName();
+  const size_t optionCompIndex = COMMAND_PREFIX.size() + 1;
+
+  try
+    {
+      Block rawOptions = request.getName()[optionCompIndex].blockFromValue();
+      extractedOptions.wireDecode(rawOptions);
+    }
+  catch (const ndn::Tlv::Error& e)
+    {
+      return false;
+    }
+
+  if (extractedOptions.getFaceId() == 0)
+    {
+      std::cout <<"IncomingFaceId: " << request.getIncomingFaceId() << std::endl;
+      extractedOptions.setFaceId(request.getIncomingFaceId());
+    }
+  return true;
+}
+
+void
+Nrd::onCommandError(const std::string& error, 
+                    const Interest& request, 
+                    const PrefixRegOptions& options)
+{
+  nfd::ControlResponse response;
+  
+  response.setCode(400);
+  response.setText(error);
+  
+  std::cout << "Error: " << error << std::endl;
+  sendResponse(request.getName(), response);
+  m_managedRib.erase(options);
+}
+
+void
+Nrd::onUnRegSuccess(const Interest& request, const PrefixRegOptions& options)
+{
+  nfd::ControlResponse response;
+  
+  response.setCode(200);
+  response.setText("Success");
+  response.setBody(options.wireEncode());
+ 
+  std::cout << "Success: Name unregistered (" << 
+                options.getName() << ", " << 
+                options.getFaceId() << ")" << std::endl;
+  sendResponse(request.getName(), response);
+  m_managedRib.erase(options);
+}
+
+void
+Nrd::onRegSuccess(const Interest& request, const PrefixRegOptions& options)
+{
+  nfd::ControlResponse response;
+  
+  response.setCode(200);
+  response.setText("Success");
+  response.setBody(options.wireEncode());
+  
+  std::cout << "Success: Name registered (" << options.getName() << ", " << 
+                                               options.getFaceId() << ")" << std::endl;
+  sendResponse(request.getName(), response);
+}
+
+
+void
+Nrd::insertEntry(const Interest& request, const PrefixRegOptions& options)
+{
+  // For right now, just pass the options to fib as it is,
+  // without processing flags. Later options will be first added to
+  // Rib tree, then nrd will generate fib updates based on flags and then
+  // will add next hops one by one..
+  m_managedRib.insert(options);
+  m_nfdController->fibAddNextHop(options.getName(), options.getFaceId(),
+                                 options.getCost(),
+                                 bind(&Nrd::onRegSuccess, this, request, options),
+                                 bind(&Nrd::onCommandError, this, _1, request, options));
+}
+
+
+void
+Nrd::deleteEntry(const Interest& request, const PrefixRegOptions& options)
+{
+  m_nfdController->fibRemoveNextHop(options.getName(),
+                                    options.getFaceId(),
+                                    bind(&Nrd::onUnRegSuccess, this, request, options),
+                                    bind(&Nrd::onCommandError, this, _1, request, options));
+}
+
+
+void
+Nrd::listen()
+{
+  std::cout << "NRD started: listening for incoming interests" << std::endl;
+  m_face.processEvents();
+}
+
+
+void
+Nrd::onControlHeaderSuccess()
+{
+  std::cout << "Local control header enabled" << std::endl;
+}
+
+
+void
+Nrd::onControlHeaderError()
+{
+  std::cout << "Error: couldn't enable local control header" << std::endl;
+  m_face.shutdown();
+}
+
+
+void
+Nrd::enableLocalControlHeader()
+{
+  Name enable("/localhost/nfd/control-header/in-faceid/enable");
+  Interest enableCommand(enable);
+ 
+  m_keyChain.sign(enableCommand);
+  m_face.expressInterest(enableCommand,
+                         ndn::bind(&Nrd::onControlHeaderSuccess, this),
+                         ndn::bind(&Nrd::onControlHeaderError, this));
+}
+
+} // namespace nrd
+} // namespace ndn
diff --git a/src/nrd.hpp b/src/nrd.hpp
new file mode 100644
index 0000000..210af74
--- /dev/null
+++ b/src/nrd.hpp
@@ -0,0 +1,98 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NRD_HPP
+#define NRD_HPP
+
+#include "rib.hpp"
+
+namespace ndn {
+namespace nrd {
+
+class Nrd
+{
+public:
+  Nrd();
+
+  void
+  onRibRequest(const Interest& request);
+
+  void
+  enableLocalControlHeader();
+
+  void
+  listen();
+
+private:
+  void
+  sendResponse(const Name& name,
+               const nfd::ControlResponse& response);
+
+  void
+  sendResponse(const Name& name,
+               uint32_t code,
+               const std::string& text);
+  void
+  onCommandError(const std::string& error, 
+                 const ndn::Interest& interest, 
+                 const PrefixRegOptions& options);
+ 
+  void
+  onRegSuccess(const ndn::Interest& interest, const PrefixRegOptions& options);
+
+  void
+  onUnRegSuccess(const ndn::Interest& interest, const PrefixRegOptions& options);
+
+  void
+  onControlHeaderSuccess();
+
+  void
+  onControlHeaderError();
+
+  void
+  insertEntry(const Interest& request, const PrefixRegOptions& options);
+
+  void
+  deleteEntry(const Interest& request, const PrefixRegOptions& options);
+
+  bool
+  extractOptions(const Interest& request,
+                 PrefixRegOptions& extractedOptions);
+private:
+  Rib m_managedRib;
+  ndn::Face m_face;
+  ndn::KeyChain m_keyChain;
+  shared_ptr<nfd::Controller> m_nfdController;
+
+  typedef boost::function<void(Nrd*,
+                               const Interest&,
+                               const PrefixRegOptions&)> VerbProcessor;
+
+  typedef std::map<Name::Component, VerbProcessor> VerbDispatchTable;
+
+  typedef std::pair<Name::Component, VerbProcessor> VerbAndProcessor;
+
+
+  const VerbDispatchTable m_verbDispatch;
+
+  static const Name COMMAND_PREFIX; // /localhost/nrd
+
+  // number of components in an invalid, but not malformed, unsigned command.
+  // (/localhost/nrd + verb + options) = 4
+  static const size_t COMMAND_UNSIGNED_NCOMPS;
+
+  // number of components in a valid signed Interest.
+  // 5 in mock (see UNSIGNED_NCOMPS), 8 with signed Interest support.
+  static const size_t COMMAND_SIGNED_NCOMPS;
+
+  static const VerbAndProcessor COMMAND_VERBS[];
+};
+
+} // namespace nrd
+} // namespace ndn
+
+#endif // NRD_HPP
+
diff --git a/src/rib.cpp b/src/rib.cpp
index 0e0cbfe..3fc9399 100644
--- a/src/rib.cpp
+++ b/src/rib.cpp
@@ -13,7 +13,7 @@
 {
 }
 
-//
+
 Rib::~Rib()
 {
 }
@@ -24,7 +24,7 @@
   return opt1.getName() == opt2.getName();
 }
 
-//
+
 Rib::const_iterator
 Rib::find(const PrefixRegOptions& options) const
 {
@@ -38,7 +38,7 @@
     return it;
 }
 
-//
+
 void
 Rib::insert(const PrefixRegOptions& options)
 {
@@ -50,7 +50,7 @@
     }
 }
 
-//
+
 void
 Rib::erase(const PrefixRegOptions& options)
 {