nrd: Basic version of nrd

refs #1324

Change-Id: I797922e8367e0473bb8ed39840e44d038295f148
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