mgmt: Initial fib manager with support for fib/add-nexthop

fw: Added FaceId to Face conversion method

Linked InternalFace's sendInterest to FibManager's
onFibRequest

refs: #1138

Change-Id: I0b18f2d41c9ba9d8749c586e3553b51a1e8b1269
diff --git a/daemon/mgmt/fib-manager.cpp b/daemon/mgmt/fib-manager.cpp
new file mode 100644
index 0000000..214bf0b
--- /dev/null
+++ b/daemon/mgmt/fib-manager.cpp
@@ -0,0 +1,185 @@
+/* -*- 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 "fib-manager.hpp"
+
+#include "table/fib.hpp"
+#include "fw/forwarder.hpp"
+#include "mgmt/internal-face.hpp"
+#include "mgmt/app-face.hpp"
+
+#include <ndn-cpp-dev/management/fib-management-options.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("FibManager");
+
+const Name FibManager::FIB_MANAGER_REQUEST_PREFIX = "/localhost/nfd/fib";
+
+// In the future this should be 3 (Signature TLV, Signature Info TLV, Timestamp)
+const size_t FibManager::FIB_MANAGER_REQUEST_SIGNED_INTEREST_NCOMPS = 0;
+
+const size_t FibManager::FIB_MANAGER_REQUEST_COMMAND_MIN_NCOMPS =
+  FibManager::FIB_MANAGER_REQUEST_PREFIX.size() +
+  1 + // verb
+  1 + // verb options
+  FibManager::FIB_MANAGER_REQUEST_SIGNED_INTEREST_NCOMPS;
+
+const Name::Component FibManager::FIB_MANAGER_REQUEST_VERB_INSERT = "insert";
+const Name::Component FibManager::FIB_MANAGER_REQUEST_VERB_DELETE = "delete";
+const Name::Component FibManager::FIB_MANAGER_REQUEST_VERB_ADD_NEXTHOP = "add-nexthop";
+const Name::Component FibManager::FIB_MANAGER_REQUEST_VERB_REMOVE_NEXTHOP = "remove-nexthop";
+const Name::Component FibManager::FIB_MANAGER_REQUEST_VERB_STRATEGY = "strategy";
+
+
+const FibManager::VerbAndProcessor FibManager::FIB_MANAGER_REQUEST_VERBS[] =
+  {
+    // Unsupported
+
+    // VerbAndProcessor(
+    //                  FibManager::FIB_MANAGER_REQUEST_VERB_INSERT,
+    //                  &FibManager::fibInsert
+    //                  ),
+
+    // VerbAndProcessor(
+    //                  FibManager::FIB_MANAGER_REQUEST_VERB_DELETE,
+    //                  &FibManager::fibDelete
+    //                  ),
+
+    VerbAndProcessor(
+                     FibManager::FIB_MANAGER_REQUEST_VERB_ADD_NEXTHOP,
+                     &FibManager::fibAddNextHop
+                     ),
+
+    // Unsupported
+
+    // VerbAndProcessor(
+    //                  FibManager::FIB_MANAGER_REQUEST_VERB_REMOVE_NEXTHOP,
+    //                  &FibManager::fibRemoveNextHop
+    //                  ),
+
+    // VerbAndProcessor(
+    //                  FibManager::FIB_MANAGER_REQUEST_VERB_STRATEGY,
+    //                  &FibManager::fibStrategy
+    //                  )
+
+  };
+
+FibManager::FibManager(Fib& fib,
+                       function<shared_ptr<Face>(FaceId)> getFace)
+  : ManagerBase(shared_ptr<AppFace>(new InternalFace(*this))),
+    m_managedFib(fib),
+    m_getFace(getFace),
+    m_verbDispatch(FIB_MANAGER_REQUEST_VERBS,
+                   FIB_MANAGER_REQUEST_VERBS +
+                   (sizeof(FIB_MANAGER_REQUEST_VERBS) / sizeof(VerbAndProcessor)))
+{
+
+}
+
+void
+FibManager::onFibRequest(const Interest& request)
+{
+  const Name& requestCommand = request.getName();
+
+  /// \todo Separate out response status codes 400 and 401
+
+  if (requestCommand.size() < FIB_MANAGER_REQUEST_COMMAND_MIN_NCOMPS ||
+      !FIB_MANAGER_REQUEST_PREFIX.isPrefixOf(requestCommand))
+    {
+      NFD_LOG_INFO("Malformed command: " << requestCommand);
+      sendResponse(request.getName(), 404, "MALFORMED");
+      return;
+    }
+
+  const Name::Component& requestVerb = requestCommand.get(FIB_MANAGER_REQUEST_PREFIX.size());
+
+  VerbDispatchTable::const_iterator verbProcessor = m_verbDispatch.find (requestVerb);
+  if (verbProcessor == m_verbDispatch.end())
+    {
+      NFD_LOG_INFO("Unsupported command verb: " << requestVerb);
+      sendResponse(request.getName(), 404, "UNSUPPORTED");
+
+    }
+  else
+    {
+      NFD_LOG_INFO("Processing command verb: " << requestVerb);
+      (verbProcessor->second)(this, request);
+    }
+
+}
+
+void
+FibManager::fibInsert(const Interest& request)
+{
+
+}
+
+void
+FibManager::fibDelete(const Interest& request)
+{
+
+}
+
+void
+FibManager::fibAddNextHop(const Interest& request)
+{
+  ndn::FibManagementOptions options;
+  const size_t optionCompIndex =
+    FIB_MANAGER_REQUEST_PREFIX.size() + 1;
+
+  const ndn::Buffer &optionBuffer =
+    request.getName()[optionCompIndex].getValue();
+  shared_ptr<const ndn::Buffer> tmpOptionBuffer(new ndn::Buffer(optionBuffer));
+  Block rawOptions(tmpOptionBuffer);
+
+  options.wireDecode(rawOptions);
+
+  /// \todo authorize command
+  if (false)
+    {
+      NFD_LOG_INFO("Unauthorized command attempt");
+
+      sendResponse(request.getName(), 403, "");
+      return;
+    }
+
+  NFD_LOG_INFO("add-nexthop Name: " << options.getName()
+               << " FaceId: " << options.getFaceId()
+               << " Cost: " << options.getCost());
+
+  shared_ptr<Face> nextHopFace = m_getFace(options.getFaceId());
+  if (nextHopFace)
+    {
+      std::pair<shared_ptr<fib::Entry>, bool> insertResult = m_managedFib.insert(options.getName());
+      insertResult.first->addNextHop(nextHopFace, options.getCost());
+      sendResponse(request.getName(), 200, "OK");
+    }
+  else
+    {
+      sendResponse(request.getName(), 400, "INCORRECT");
+    }
+}
+
+void
+FibManager::fibRemoveNextHop(const Interest& request)
+{
+
+}
+
+void
+FibManager::fibStrategy(const Interest& request)
+{
+
+}
+
+// void
+// FibManager::onConfig(ConfigFile::Node section, bool isDryRun)
+// {
+
+// }
+
+} // namespace nfd
diff --git a/daemon/mgmt/fib-manager.hpp b/daemon/mgmt/fib-manager.hpp
new file mode 100644
index 0000000..214b426
--- /dev/null
+++ b/daemon/mgmt/fib-manager.hpp
@@ -0,0 +1,88 @@
+/* -*- 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 NFD_MGMT_FIB_MANAGER_HPP
+#define NFD_MGMT_FIB_MANAGER_HPP
+
+#include "common.hpp"
+#include "face/face.hpp"
+#include "mgmt/manager-base.hpp"
+
+namespace nfd {
+
+class AppFace;
+class Face;
+class Strategy;
+class Forwarder;
+class Fib;
+
+class FibManager : public ManagerBase
+{
+public:
+
+  FibManager(Fib& fib, function<shared_ptr<Face>(FaceId)> getFace);
+
+  void
+  onFibRequest(const Interest& request);
+
+  const Name&
+  getRequestPrefix() const { return FIB_MANAGER_REQUEST_PREFIX; }
+
+private:
+
+  void
+  fibInsert(const Interest& request);
+
+  void
+  fibDelete(const Interest& request);
+
+  void
+  fibAddNextHop(const Interest& request);
+
+  void
+  fibRemoveNextHop(const Interest& request);
+
+  void
+  fibStrategy(const Interest& request);
+
+  // void
+  // onConfig(ConfigFile::Node section, bool isDryRun);
+
+private:
+
+  Fib& m_managedFib;
+  function<shared_ptr<Face>(FaceId)> m_getFace;
+  std::map<Name, shared_ptr<Strategy> > m_namespaceToStrategyMap;
+
+
+
+  typedef function<void(FibManager*,
+                        const Interest&)> VerbProcessor;
+
+  typedef std::map<Name::Component, VerbProcessor> VerbDispatchTable;
+
+  typedef std::pair<Name::Component, VerbProcessor> VerbAndProcessor;
+
+
+  const VerbDispatchTable m_verbDispatch;
+
+  static const Name FIB_MANAGER_REQUEST_PREFIX;
+  static const size_t FIB_MANAGER_REQUEST_COMMAND_MIN_NCOMPS;
+  static const size_t FIB_MANAGER_REQUEST_SIGNED_INTEREST_NCOMPS;
+
+  static const Name::Component FIB_MANAGER_REQUEST_VERB_INSERT;
+  static const Name::Component FIB_MANAGER_REQUEST_VERB_DELETE;
+  static const Name::Component FIB_MANAGER_REQUEST_VERB_ADD_NEXTHOP;
+  static const Name::Component FIB_MANAGER_REQUEST_VERB_REMOVE_NEXTHOP;
+  static const Name::Component FIB_MANAGER_REQUEST_VERB_STRATEGY;
+
+  static const VerbAndProcessor FIB_MANAGER_REQUEST_VERBS[];
+
+};
+
+} // namespace nfd
+
+#endif // NFD_MGMT_FIB_MANAGER_HPP
diff --git a/daemon/mgmt/internal-face.cpp b/daemon/mgmt/internal-face.cpp
index e3daeca..b207dd2 100644
--- a/daemon/mgmt/internal-face.cpp
+++ b/daemon/mgmt/internal-face.cpp
@@ -5,10 +5,12 @@
  */
 
 #include "internal-face.hpp"
+#include "fib-manager.hpp"
 
 namespace nfd {
 
-InternalFace::InternalFace()
+InternalFace::InternalFace(FibManager& manager)
+  : m_fibManager(manager)
 {
 
 }
@@ -16,12 +18,11 @@
 void
 InternalFace::sendInterest(const Interest& interest)
 {
-  static const Name prefixRegPrefix("/localhost/nfd/prefixreg");
   const Name& interestName = interest.getName();
 
-  if (prefixRegPrefix.isPrefixOf(interestName))
+  if (m_fibManager.getRequestPrefix().isPrefixOf(interestName))
     {
-      //invoke FibManager
+      m_fibManager.onFibRequest(interest);
     }
   //Drop Interest
 }
diff --git a/daemon/mgmt/internal-face.hpp b/daemon/mgmt/internal-face.hpp
index 7798143..4b16cc1 100644
--- a/daemon/mgmt/internal-face.hpp
+++ b/daemon/mgmt/internal-face.hpp
@@ -12,11 +12,14 @@
 
 namespace nfd {
 
+class FibManager;
+
 class InternalFace : public Face, public AppFace
 {
 public:
 
-  InternalFace();
+  explicit
+  InternalFace(FibManager& manager);
 
   // Overridden Face methods for forwarder
 
@@ -44,6 +47,7 @@
   // onConfig(ConfigFile::Node section, bool isDryRun);
 
   std::map<Name, OnInterest> m_interestFilters;
+  FibManager& m_fibManager; // for mock only
 
 };
 
diff --git a/daemon/mgmt/manager-base.cpp b/daemon/mgmt/manager-base.cpp
new file mode 100644
index 0000000..1802018
--- /dev/null
+++ b/daemon/mgmt/manager-base.cpp
@@ -0,0 +1,37 @@
+/* -*- 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 "manager-base.hpp"
+#include "mgmt/app-face.hpp"
+
+#include <ndn-cpp-dev/management/control-response.hpp>
+
+namespace nfd {
+
+ManagerBase::ManagerBase(shared_ptr<AppFace> face)
+  : m_face(face)
+{
+
+}
+
+ManagerBase::~ManagerBase()
+{
+
+}
+
+void
+ManagerBase::sendResponse(const Name& name,
+                            uint32_t code,
+                            const std::string& text)
+{
+  Data response(name);
+
+  response.setContent(ndn::ControlResponse(code, text).wireEncode());
+  m_face->put(response);
+}
+
+
+} // namespace nfd
diff --git a/daemon/mgmt/manager-base.hpp b/daemon/mgmt/manager-base.hpp
new file mode 100644
index 0000000..97f4c05
--- /dev/null
+++ b/daemon/mgmt/manager-base.hpp
@@ -0,0 +1,39 @@
+/* -*- 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 NFD_MGMT_MANAGER_BASE_HPP
+#define NFD_MGMT_MANAGER_BASE_HPP
+
+#include "common.hpp"
+
+namespace nfd {
+
+class AppFace;
+
+class ManagerBase
+{
+public:
+  ManagerBase(shared_ptr<AppFace> face);
+
+  virtual
+  ~ManagerBase();
+
+protected:
+
+  virtual void
+  sendResponse(const Name& name,
+                 uint32_t code,
+                 const std::string& text);
+
+protected:
+  shared_ptr<AppFace> m_face;
+};
+
+
+} // namespace ndf
+
+#endif // NFD_MGMT_MANAGER_BASE_HPP
+
diff --git a/tests/mgmt/fib-manager.cpp b/tests/mgmt/fib-manager.cpp
new file mode 100644
index 0000000..bbcce4b
--- /dev/null
+++ b/tests/mgmt/fib-manager.cpp
@@ -0,0 +1,210 @@
+/* -*- 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 "mgmt/fib-manager.hpp"
+#include "fw/forwarder.hpp"
+#include "table/fib.hpp"
+#include "face/face.hpp"
+#include "../face/dummy-face.hpp"
+
+#include <ndn-cpp-dev/management/fib-management-options.hpp>
+
+#include <boost/test/unit_test.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("FibManagerTest");
+
+FaceId g_faceCount = 1;
+std::vector<shared_ptr<Face> > g_faces;
+
+shared_ptr<Face>
+getFace(FaceId id)
+{
+  if (g_faces.size() < id)
+    {
+      BOOST_FAIL("Attempted to access invalid FaceId: " << id);
+    }
+  return g_faces[id-1];
+}
+
+BOOST_AUTO_TEST_SUITE(MgmtFibManager)
+
+BOOST_AUTO_TEST_CASE(MalformedCommmand)
+{
+  Fib fib;
+  FibManager manager(fib, &getFace);
+
+  Interest command(manager.getRequestPrefix());
+  manager.onFibRequest(command);
+}
+
+BOOST_AUTO_TEST_CASE(UnsupportedVerb)
+{
+  Fib fib;
+  FibManager manager(fib, &getFace);
+
+  Name commandName(manager.getRequestPrefix());
+  commandName.append("unsupported");
+
+  Interest command(commandName);
+  manager.onFibRequest(command);
+}
+
+BOOST_AUTO_TEST_CASE(AddNextHopVerbInitialAdd)
+{
+  g_faceCount = 1;
+  g_faces.clear();
+  g_faces.push_back(make_shared<DummyFace>());
+
+  Fib fib;
+
+  FibManager manager(fib, &getFace);
+
+  ndn::FibManagementOptions options;
+  options.setName("/hello");
+  options.setFaceId(1);
+  options.setCost(1);
+
+  Block encodedOptions(options.wireEncode());
+
+  Name commandName(manager.getRequestPrefix());
+  commandName.append("add-nexthop");
+  commandName.append(encodedOptions);
+
+  Interest command(commandName);
+  manager.onFibRequest(command);
+
+  shared_ptr<fib::Entry> entry = fib.findLongestPrefixMatch("/hello");
+
+  if (entry)
+    {
+      const fib::NextHopList& hops = entry->getNextHops();
+      BOOST_REQUIRE(hops.size() == 1);
+      //      BOOST_CHECK(hops[0].getFace()->getFaceId() == 1);
+      BOOST_CHECK(hops[0].getCost() == 1);
+    }
+  else
+    {
+      BOOST_FAIL("Failed to find expected fib entry");
+    }
+}
+
+BOOST_AUTO_TEST_CASE(AddNextHopVerbAddToExisting)
+{
+  g_faceCount = 1;
+  g_faces.clear();
+  g_faces.push_back(make_shared<DummyFace>());
+  g_faces.push_back(make_shared<DummyFace>());
+
+  Fib fib;
+
+  FibManager manager(fib, &getFace);
+
+  // Add faces with cost == FaceID for the name /hello
+  // This test assumes:
+  //   FaceIDs are assigned from 1 to N
+  //   Faces are store sequentially in the NextHopList
+  //   NextHopList supports random access
+
+  for (int i = 1; i <= 2; i++)
+    {
+      ndn::FibManagementOptions options;
+      options.setName("/hello");
+      options.setFaceId(i);
+      options.setCost(i);
+
+      Block encodedOptions(options.wireEncode());
+
+      Name commandName(manager.getRequestPrefix());
+      commandName.append("add-nexthop");
+      commandName.append(encodedOptions);
+
+      Interest command(commandName);
+      manager.onFibRequest(command);
+
+      shared_ptr<fib::Entry> entry = fib.findLongestPrefixMatch("/hello");
+
+      if (entry)
+        {
+          const fib::NextHopList& hops = entry->getNextHops();
+          for (int j = 1; j <= i; j++)
+            {
+              BOOST_REQUIRE(hops.size() == i);
+              // BOOST_CHECK(hops[j-1].getFace()->getFaceId() == j);
+              BOOST_CHECK(hops[j-1].getCost() == j);
+            }
+        }
+      else
+        {
+          BOOST_FAIL("Failed to find expected fib entry");
+        }
+    }
+}
+
+BOOST_AUTO_TEST_CASE(AddNextHopVerbUpdateFaceCost)
+{
+  g_faceCount = 1;
+  g_faces.clear();
+  g_faces.push_back(make_shared<DummyFace>());
+
+  Fib fib;
+
+  FibManager manager(fib, &getFace);
+
+  ndn::FibManagementOptions options;
+  options.setName("/hello");
+  options.setFaceId(1);
+
+  {
+    options.setCost(1);
+
+    Block encodedOptions(options.wireEncode());
+
+    Name commandName(manager.getRequestPrefix());
+    commandName.append("add-nexthop");
+    commandName.append(encodedOptions);
+
+    Interest command(commandName);
+    manager.onFibRequest(command);
+  }
+
+  {
+    options.setCost(2);
+
+    Block encodedOptions(options.wireEncode());
+
+    Name commandName(manager.getRequestPrefix());
+    commandName.append("add-nexthop");
+    commandName.append(encodedOptions);
+
+    Interest command(commandName);
+    manager.onFibRequest(command);
+  }
+
+  shared_ptr<fib::Entry> entry = fib.findLongestPrefixMatch("/hello");
+
+  // Add faces with cost == FaceID for the name /hello
+  // This test assumes:
+  //   FaceIDs are assigned from 1 to N
+  //   Faces are store sequentially in the NextHopList
+  //   NextHopList supports random access
+  if (entry)
+    {
+      const fib::NextHopList& hops = entry->getNextHops();
+      BOOST_REQUIRE(hops.size() == 1);
+      // BOOST_CHECK(hops[0].getFace()->getFaceId() == 1);
+      BOOST_CHECK(hops[0].getCost() == 2);
+    }
+  else
+    {
+      BOOST_FAIL("Failed to find expected fib entry");
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace nfd
diff --git a/tests/mgmt/internal-face.cpp b/tests/mgmt/internal-face.cpp
index 0ece5b6..861ca97 100644
--- a/tests/mgmt/internal-face.cpp
+++ b/tests/mgmt/internal-face.cpp
@@ -5,6 +5,9 @@
  */
 
 #include "mgmt/internal-face.hpp"
+#include "mgmt/fib-manager.hpp"
+#include "table/fib.hpp"
+
 
 #include <boost/test/unit_test.hpp>
 
@@ -12,16 +15,29 @@
 
 BOOST_AUTO_TEST_SUITE(MgmtInternalFace)
 
+shared_ptr<Face>
+getFace(FaceId id)
+{
+  return shared_ptr<Face>();
+}
+
 BOOST_AUTO_TEST_CASE(ValidPrefixRegistration)
 {
-  InternalFace internal;
-  Interest regInterest("/localhost/nfd/prefixreg/hello/world");
+  Fib fib;
+  FibManager manager(fib, &getFace);
+  InternalFace internal(manager);
+
+  Name regName(manager.getRequestPrefix());
+  regName.append("hello").append("world");
+  Interest regInterest(regName);
   internal.sendInterest(regInterest);
 }
 
 BOOST_AUTO_TEST_CASE(InvalidPrefixRegistration)
 {
-  InternalFace internal;
+  Fib fib;
+  FibManager manager(fib, &getFace);
+  InternalFace internal(manager);
   Interest nonRegInterest("/hello/world");
   internal.sendInterest(nonRegInterest);
 }