Merge branch for issue #1138
Change-Id: Id4c2ddb3707983cc07177ebd834f8fb3a40f9606
diff --git a/daemon/fw/forwarder.cpp b/daemon/fw/forwarder.cpp
index d9850df..1ce3b8f 100644
--- a/daemon/fw/forwarder.cpp
+++ b/daemon/fw/forwarder.cpp
@@ -49,6 +49,13 @@
m_fib.removeNextHopFromAllEntries(face);
}
+shared_ptr<Face>
+Forwarder::getFace(FaceId id)
+{
+ std::map<FaceId, shared_ptr<Face> >::iterator i = m_faces.find(id);
+ return (i == m_faces.end()) ? (shared_ptr<Face>()) : (i->second);
+}
+
void
Forwarder::onInterest(Face& face, const Interest& interest)
{
diff --git a/daemon/fw/forwarder.hpp b/daemon/fw/forwarder.hpp
index 1359737..0ea838b 100644
--- a/daemon/fw/forwarder.hpp
+++ b/daemon/fw/forwarder.hpp
@@ -49,6 +49,9 @@
Cs&
getCs();
+ shared_ptr<Face>
+ getFace(FaceId id);
+
private: // pipelines
/** \brief incoming Interest pipeline
*/
@@ -135,7 +138,6 @@
return m_cs;
}
-
} // namespace nfd
#endif // NFD_FW_FORWARDER_HPP
diff --git a/daemon/mgmt/app-face.cpp b/daemon/mgmt/app-face.cpp
new file mode 100644
index 0000000..79bf39f
--- /dev/null
+++ b/daemon/mgmt/app-face.cpp
@@ -0,0 +1,17 @@
+/* -*- 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 "app-face.hpp"
+
+namespace nfd {
+
+void
+AppFace::sign(Data& data)
+{
+ m_keyChain.sign(data);
+}
+
+} // namespace nfd
diff --git a/daemon/mgmt/app-face.hpp b/daemon/mgmt/app-face.hpp
index af58cdf..b6e2ab5 100644
--- a/daemon/mgmt/app-face.hpp
+++ b/daemon/mgmt/app-face.hpp
@@ -9,9 +9,11 @@
#include "common.hpp"
+#include <ndn-cpp-dev/security/key-chain.hpp>
+
namespace nfd {
-typedef ndn::func_lib::function<void(const Name&, const Interest&)> OnInterest;
+typedef function<void(const Name&, const Interest&)> OnInterest;
class AppFace
{
@@ -21,10 +23,16 @@
OnInterest onInterest) = 0;
virtual void
+ sign(Data& data);
+
+ virtual void
put(const Data& data) = 0;
virtual
~AppFace() { }
+
+protected:
+ ndn::KeyChain m_keyChain;
};
} // namespace nfd
diff --git a/daemon/mgmt/fib-manager.cpp b/daemon/mgmt/fib-manager.cpp
new file mode 100644
index 0000000..69be19f
--- /dev/null
+++ b/daemon/mgmt/fib-manager.cpp
@@ -0,0 +1,204 @@
+/* -*- 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>
+#include <ndn-cpp-dev/encoding/tlv.hpp>
+
+#include <ndn-cpp-dev/management/fib-management-options.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("FibManager");
+
+const Name FibManager::FIB_MANAGER_COMMAND_PREFIX = "/localhost/nfd/fib";
+
+const size_t FibManager::FIB_MANAGER_COMMAND_UNSIGNED_NCOMPS =
+ FibManager::FIB_MANAGER_COMMAND_PREFIX.size() +
+ 1 + // verb
+ 1; // verb options
+
+const size_t FibManager::FIB_MANAGER_COMMAND_SIGNED_NCOMPS =
+ FibManager::FIB_MANAGER_COMMAND_UNSIGNED_NCOMPS +
+ 0; // No signed Interest support in mock, otherwise 3 (timestamp, signed info tlv, signature tlv)
+
+const FibManager::VerbAndProcessor FibManager::FIB_MANAGER_COMMAND_VERBS[] =
+ {
+ // Unsupported
+
+ // VerbAndProcessor(
+ // "insert",
+ // &FibManager::fibInsert
+ // ),
+
+ // VerbAndProcessor(
+ // "delete",
+ // &FibManager::fibDelete
+ // ),
+
+ VerbAndProcessor(
+ "add-nexthop",
+ &FibManager::fibAddNextHop
+ ),
+
+ // Unsupported
+
+ // VerbAndProcessor(
+ // "remove-nexthop",
+ // &FibManager::fibRemoveNextHop
+ // ),
+
+ // VerbAndProcessor(
+ // "strategy",
+ // &FibManager::fibStrategy
+ // )
+
+ };
+
+FibManager::FibManager(Fib& fib,
+ function<shared_ptr<Face>(FaceId)> getFace,
+ shared_ptr<AppFace> face)
+ : ManagerBase(face),
+ m_managedFib(fib),
+ m_getFace(getFace),
+ m_verbDispatch(FIB_MANAGER_COMMAND_VERBS,
+ FIB_MANAGER_COMMAND_VERBS +
+ (sizeof(FIB_MANAGER_COMMAND_VERBS) / sizeof(VerbAndProcessor)))
+{
+ face->setInterestFilter("/localhost/nfd/fib",
+ bind(&FibManager::onFibRequest, this, _2));
+}
+
+void
+FibManager::onFibRequest(const Interest& request)
+{
+ const Name& command = request.getName();
+ const size_t commandNComps = command.size();
+
+ /// \todo Separate out response status codes 400 and 401
+
+ if (FIB_MANAGER_COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
+ commandNComps < FIB_MANAGER_COMMAND_SIGNED_NCOMPS)
+ {
+ NFD_LOG_INFO("Unsigned command: " << command);
+ sendResponse(command, 401, "Signature required");
+
+ return;
+ }
+ else if (commandNComps < FIB_MANAGER_COMMAND_SIGNED_NCOMPS ||
+ !FIB_MANAGER_COMMAND_PREFIX.isPrefixOf(command))
+ {
+ NFD_LOG_INFO("Malformed command: " << command);
+ sendResponse(command, 400, "Malformed command");
+ return;
+ }
+
+
+ const Name::Component& verb = command.get(FIB_MANAGER_COMMAND_PREFIX.size());
+
+ VerbDispatchTable::const_iterator verbProcessor = m_verbDispatch.find (verb);
+ if (verbProcessor != m_verbDispatch.end())
+ {
+ NFD_LOG_INFO("Processing command verb: " << verb);
+ (verbProcessor->second)(this, request);
+ }
+ else
+ {
+ NFD_LOG_INFO("Unsupported command verb: " << verb);
+ sendResponse(request.getName(), 501, "Unsupported command");
+ }
+
+}
+
+void
+FibManager::fibInsert(const Interest& request)
+{
+
+}
+
+void
+FibManager::fibDelete(const Interest& request)
+{
+
+}
+
+void
+FibManager::fibAddNextHop(const Interest& request)
+{
+ const Name& command = request.getName();
+ ndn::FibManagementOptions options;
+ const size_t optionCompIndex =
+ FIB_MANAGER_COMMAND_PREFIX.size() + 1;
+
+ const ndn::Buffer& optionBuffer =
+ request.getName()[optionCompIndex].getValue();
+ shared_ptr<const ndn::Buffer> tmpOptionBuffer(make_shared<ndn::Buffer>(optionBuffer));
+
+ try
+ {
+ Block rawOptions(tmpOptionBuffer);
+ options.wireDecode(rawOptions);
+ }
+ catch (const ndn::Tlv::Error& e)
+ {
+ NFD_LOG_INFO("Bad command option parse: " << command);
+ sendResponse(request.getName(), 400, "Malformed command");
+ return;
+ }
+
+ /// \todo authorize command
+ if (false)
+ {
+ NFD_LOG_INFO("Unauthorized command attempt: " << command);
+ sendResponse(request.getName(), 403, "Unauthorized command");
+ return;
+ }
+
+ NFD_LOG_INFO("add-nexthop Name: " << options.getName()
+ << " FaceId: " << options.getFaceId()
+ << " Cost: " << options.getCost());
+
+ shared_ptr<Face> nextHopFace = m_getFace(options.getFaceId());
+ if (static_cast<bool>(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
+ {
+ NFD_LOG_INFO("Unknown FaceId: " << command);
+ sendResponse(request.getName(), 400, "Malformed command");
+ }
+}
+
+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..d77a2f2
--- /dev/null
+++ b/daemon/mgmt/fib-manager.hpp
@@ -0,0 +1,84 @@
+/* -*- 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/app-face.hpp"
+#include "fw/strategy.hpp"
+#include "mgmt/manager-base.hpp"
+
+namespace nfd {
+
+class Forwarder;
+class Fib;
+
+class FibManager : public ManagerBase
+{
+public:
+
+ FibManager(Fib& fib,
+ function<shared_ptr<Face>(FaceId)> getFace,
+ shared_ptr<AppFace> face);
+
+ void
+ onFibRequest(const Interest& request);
+
+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<fw::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_COMMAND_PREFIX; // /localhost/nfd/fib
+
+ // number of components in an invalid, but not malformed, unsigned command.
+ // (/localhost/nfd/fib + verb + options) = 5
+ static const size_t FIB_MANAGER_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 FIB_MANAGER_COMMAND_SIGNED_NCOMPS;
+
+ static const VerbAndProcessor FIB_MANAGER_COMMAND_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..7af328f 100644
--- a/daemon/mgmt/internal-face.cpp
+++ b/daemon/mgmt/internal-face.cpp
@@ -8,6 +8,8 @@
namespace nfd {
+NFD_LOG_INIT("InternalFace");
+
InternalFace::InternalFace()
{
@@ -16,12 +18,71 @@
void
InternalFace::sendInterest(const Interest& interest)
{
- static const Name prefixRegPrefix("/localhost/nfd/prefixreg");
- const Name& interestName = interest.getName();
-
- if (prefixRegPrefix.isPrefixOf(interestName))
+ if (m_interestFilters.size() == 0)
{
- //invoke FibManager
+ NFD_LOG_DEBUG("no Interest filters to match against");
+ return;
+ }
+
+ const Name& interestName(interest.getName());
+ NFD_LOG_DEBUG("received Interest: " << interestName);
+
+ std::map<Name, OnInterest>::const_iterator filter =
+ m_interestFilters.lower_bound(interestName);
+
+ // lower_bound gives us the first Name that is
+ // an exact match OR ordered after interestName.
+ //
+ // If we reach the end of the map, then we need
+ // only check if the before-end element is a match.
+ //
+ // If we match an element, then the current
+ // position or the previous element are potential
+ // matches.
+ //
+ // If we hit begin, the element is either an exact
+ // match or there is no matching prefix in the map.
+
+
+ if (filter == m_interestFilters.end() && filter != m_interestFilters.begin())
+ {
+ // We hit the end, check if the previous element
+ // is a match
+ --filter;
+ if (filter->first.isPrefixOf(interestName))
+ {
+ NFD_LOG_DEBUG("found Interest filter for " << filter->first << " (before end match)");
+ filter->second(interestName, interest);
+ }
+ else
+ {
+ NFD_LOG_DEBUG("no Interest filter found for " << interestName << " (before end)");
+ }
+ }
+ else if (filter->first == interestName)
+ {
+ NFD_LOG_DEBUG("found Interest filter for " << filter->first << " (exact match)");
+ filter->second(interestName, interest);
+ }
+ else if (filter != m_interestFilters.begin())
+ {
+ // the element we found is canonically
+ // ordered after interestName.
+ // Check the previous element.
+ --filter;
+ if (filter->first.isPrefixOf(interestName))
+ {
+ NFD_LOG_DEBUG("found Interest filter for " << filter->first << " (previous match)");
+ filter->second(interestName, interest);
+ }
+ else
+ {
+ NFD_LOG_DEBUG("no Interest filter found for " << interestName << " (previous)");
+ }
+ }
+ else
+ {
+ NFD_LOG_DEBUG("no Interest filter found for " << interestName << " (begin)");
}
//Drop Interest
}
@@ -36,7 +97,8 @@
InternalFace::setInterestFilter(const Name& filter,
OnInterest onInterest)
{
-
+ NFD_LOG_INFO("registering callback for " << filter);
+ m_interestFilters[filter] = onInterest;
}
void
diff --git a/daemon/mgmt/internal-face.hpp b/daemon/mgmt/internal-face.hpp
index 7798143..9539a9e 100644
--- a/daemon/mgmt/internal-face.hpp
+++ b/daemon/mgmt/internal-face.hpp
@@ -44,7 +44,6 @@
// onConfig(ConfigFile::Node section, bool isDryRun);
std::map<Name, OnInterest> m_interestFilters;
-
};
} // namespace nfd
diff --git a/daemon/mgmt/manager-base.cpp b/daemon/mgmt/manager-base.cpp
new file mode 100644
index 0000000..55787b6
--- /dev/null
+++ b/daemon/mgmt/manager-base.cpp
@@ -0,0 +1,48 @@
+/* -*- 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 {
+
+NFD_LOG_INIT("ManagerBase");
+
+ManagerBase::ManagerBase(shared_ptr<AppFace> face)
+ : m_face(face)
+{
+
+}
+
+ManagerBase::~ManagerBase()
+{
+
+}
+
+void
+ManagerBase::sendResponse(const Name& name,
+ uint32_t code,
+ const std::string& text)
+{
+ ndn::ControlResponse control(code, text);
+ const Block& encodedControl = control.wireEncode();
+
+ NFD_LOG_DEBUG("sending control response"
+ << " Name: " << name
+ << " code: " << code
+ << " text: " << text);
+
+ Data response(name);
+ response.setContent(encodedControl);
+
+ m_face->sign(response);
+ 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..d62b1c0
--- /dev/null
+++ b/daemon/mgmt/manager-base.hpp
@@ -0,0 +1,40 @@
+/* -*- 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:
+
+ void
+ sendResponse(const Name& name,
+ uint32_t code,
+ const std::string& text);
+
+protected:
+ shared_ptr<AppFace> m_face;
+};
+
+
+} // namespace nfd
+
+#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..b07d11b
--- /dev/null
+++ b/tests/mgmt/fib-manager.cpp
@@ -0,0 +1,456 @@
+/* -*- 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 "table/fib-nexthop.hpp"
+#include "face/face.hpp"
+#include "mgmt/internal-face.hpp"
+#include "../face/dummy-face.hpp"
+
+#include <algorithm>
+
+#include <ndn-cpp-dev/management/fib-management-options.hpp>
+#include <ndn-cpp-dev/management/control-response.hpp>
+
+#include <boost/test/unit_test.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("FibManagerTest");
+
+class FibManagerFixture
+{
+public:
+
+ FibManagerFixture()
+ : m_callbackFired(false)
+ {
+
+ }
+
+ shared_ptr<Face>
+ getFace(FaceId id)
+ {
+ if (id > 0 && id <= m_faces.size())
+ {
+ return m_faces[id-1];
+ }
+ NFD_LOG_DEBUG("No face found returning NULL");
+ return shared_ptr<DummyFace>();
+ }
+
+ void
+ addFace(shared_ptr<Face> face)
+ {
+ m_faces.push_back(face);
+ }
+
+ void
+ validateControlResponse(const Data& response,
+ uint32_t expectedCode,
+ const std::string& expectedText)
+ {
+ m_callbackFired = true;
+ Block controlRaw = response.getContent().blockFromValue();
+
+ ndn::ControlResponse control;
+ control.wireDecode(controlRaw);
+
+ NFD_LOG_DEBUG("received control response"
+ << " Name: " << response.getName()
+ << " code: " << control.getCode()
+ << " text: " << control.getText());
+
+ BOOST_REQUIRE(control.getCode() == expectedCode);
+ BOOST_REQUIRE(control.getText() == expectedText);
+ }
+
+ bool
+ didCallbackFire()
+ {
+ return m_callbackFired;
+ }
+
+ void
+ resetCallbackFired()
+ {
+ m_callbackFired = false;
+ }
+
+private:
+ std::vector<shared_ptr<Face> > m_faces;
+ bool m_callbackFired;
+};
+
+
+BOOST_AUTO_TEST_SUITE(MgmtFibManager)
+
+
+
+bool
+foundNextHop(FaceId id, uint32_t cost, const fib::NextHop& next)
+{
+ return id == next.getFace()->getId() && next.getCost() == cost;
+}
+
+bool
+addedNextHopWithCost(const Fib& fib, const Name& prefix, size_t oldSize, uint32_t cost)
+{
+ shared_ptr<fib::Entry> entry = fib.findLongestPrefixMatch(prefix);
+
+ if (static_cast<bool>(entry))
+ {
+ const fib::NextHopList& hops = entry->getNextHops();
+ return hops.size() == oldSize + 1 &&
+ std::find_if(hops.begin(), hops.end(), bind(&foundNextHop, -1, cost, _1)) != hops.end();
+ }
+ return false;
+}
+
+BOOST_AUTO_TEST_CASE(TestFireInterestFilter)
+{
+ FibManagerFixture fixture;
+ shared_ptr<InternalFace> face(make_shared<InternalFace>());
+ Fib fib;
+ FibManager manager(fib,
+ bind(&FibManagerFixture::getFace,
+ &fixture, _1),
+ face);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, &fixture, _1, 400, "Malformed command");
+
+ Interest command("/localhost/nfd/fib");
+ face->sendInterest(command);
+}
+
+BOOST_AUTO_TEST_CASE(MalformedCommmand)
+{
+ FibManagerFixture fixture;
+ shared_ptr<InternalFace> face(make_shared<InternalFace>());
+ Fib fib;
+ FibManager manager(fib,
+ bind(&FibManagerFixture::getFace,
+ &fixture, _1),
+ face);
+
+ BOOST_REQUIRE(fixture.didCallbackFire() == false);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, &fixture, _1, 400, "Malformed command");
+
+ Interest command("/localhost/nfd/fib");
+ manager.onFibRequest(command);
+
+ BOOST_REQUIRE(fixture.didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(UnsupportedVerb, FibManagerFixture)
+{
+ shared_ptr<InternalFace> face(make_shared<InternalFace>());
+ Fib fib;
+ FibManager manager(fib,
+ bind(&FibManagerFixture::getFace,
+ this, _1),
+ face);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1, 501, "Unsupported command");
+
+ ndn::FibManagementOptions options;
+ options.setName("/hello");
+ options.setFaceId(1);
+ options.setCost(1);
+
+ Block encodedOptions(options.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("unsupported");
+ commandName.append(encodedOptions);
+
+ Interest command(commandName);
+ manager.onFibRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(UnsignedCommand, FibManagerFixture)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(make_shared<InternalFace>());
+ Fib fib;
+ FibManager manager(fib,
+ bind(&FibManagerFixture::getFace,
+ this, _1),
+ face);
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1, 200, "OK");
+ /// \todo enable once sig checking implement
+ // bind(&FibManagerFixture::validateControlResponse, this, _1, 401, "SIGREG");
+
+ ndn::FibManagementOptions options;
+ options.setName("/hello");
+ options.setFaceId(1);
+ options.setCost(101);
+
+ Block encodedOptions(options.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedOptions);
+
+ Interest command(commandName);
+ manager.onFibRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(addedNextHopWithCost(fib, "/hello", 0, 101));
+}
+
+BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand, FibManagerFixture)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(make_shared<InternalFace>());
+ Fib fib;
+ FibManager manager(fib,
+ bind(&FibManagerFixture::getFace,
+ this, _1),
+ face);
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1, 200, "OK");
+ /// \todo enable once sig checking implement
+ // bind(&FibManagerFixture::validateControlResponse, this, _1, 403, "Unauthorized command");
+
+ ndn::FibManagementOptions options;
+ options.setName("/hello");
+ options.setFaceId(1);
+ options.setCost(101);
+
+ Block encodedOptions(options.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedOptions);
+
+ Interest command(commandName);
+ manager.onFibRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(addedNextHopWithCost(fib, "/hello", 0, 101));
+}
+
+BOOST_FIXTURE_TEST_CASE(BadOptionParse, FibManagerFixture)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(make_shared<InternalFace>());
+ Fib fib;
+ FibManager manager(fib,
+ bind(&FibManagerFixture::getFace,
+ this, _1),
+ face);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1, 400, "Malformed command");
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append("NotReallyOptions");
+
+ Interest command(commandName);
+ manager.onFibRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(UnknownFaceId, FibManagerFixture)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(make_shared<InternalFace>());
+ Fib fib;
+ FibManager manager(fib,
+ bind(&FibManagerFixture::getFace,
+ this, _1),
+ face);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1, 400, "Malformed command");
+
+ ndn::FibManagementOptions options;
+ options.setName("/hello");
+ options.setFaceId(1000);
+ options.setCost(101);
+
+ Block encodedOptions(options.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedOptions);
+
+ Interest command(commandName);
+ manager.onFibRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(addedNextHopWithCost(fib, "/hello", 0, 101) == false);
+}
+
+BOOST_FIXTURE_TEST_CASE(AddNextHopVerbInitialAdd, FibManagerFixture)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(make_shared<InternalFace>());
+ Fib fib;
+ FibManager manager(fib,
+ bind(&FibManagerFixture::getFace,
+ this, _1),
+ face);
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1, 200, "OK");
+
+ ndn::FibManagementOptions options;
+ options.setName("/hello");
+ options.setFaceId(1);
+ options.setCost(101);
+
+ Block encodedOptions(options.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedOptions);
+
+ Interest command(commandName);
+ manager.onFibRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(addedNextHopWithCost(fib, "/hello", 0, 101));
+}
+
+BOOST_FIXTURE_TEST_CASE(AddNextHopVerbAddToExisting, FibManagerFixture)
+{
+ addFace(make_shared<DummyFace>());
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(make_shared<InternalFace>());
+ Fib fib;
+ FibManager manager(fib,
+ bind(&FibManagerFixture::getFace,
+ this, _1),
+ face);
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1, 200, "OK");
+
+ // Add faces with cost == FaceID for the name /hello
+ // This test assumes:
+ // FaceIDs are -1 because we don't add them to a forwarder
+
+ for (int i = 1; i <= 2; i++)
+ {
+ ndn::FibManagementOptions options;
+ options.setName("/hello");
+ options.setFaceId(i);
+ options.setCost(100 + i);
+
+ Block encodedOptions(options.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedOptions);
+
+ Interest command(commandName);
+ manager.onFibRequest(command);
+ BOOST_REQUIRE(didCallbackFire());
+ resetCallbackFired();
+
+ shared_ptr<fib::Entry> entry = fib.findLongestPrefixMatch("/hello");
+
+ if (static_cast<bool>(entry))
+ {
+ const fib::NextHopList& hops = entry->getNextHops();
+ for (int j = 1; j <= i; j++)
+ {
+ BOOST_REQUIRE(addedNextHopWithCost(fib, "/hello", i - 1, 100 + j));
+ }
+ }
+ else
+ {
+ BOOST_FAIL("Failed to find expected fib entry");
+ }
+ }
+}
+
+BOOST_FIXTURE_TEST_CASE(AddNextHopVerbUpdateFaceCost, FibManagerFixture)
+{
+ FibManagerFixture fixture;
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(make_shared<InternalFace>());
+ Fib fib;
+ FibManager manager(fib,
+ bind(&FibManagerFixture::getFace,
+ this, _1),
+ face);
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1, 200, "OK");
+
+ ndn::FibManagementOptions options;
+ options.setName("/hello");
+ options.setFaceId(1);
+
+ {
+ options.setCost(1);
+
+ Block encodedOptions(options.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedOptions);
+
+ Interest command(commandName);
+ manager.onFibRequest(command);
+ BOOST_REQUIRE(didCallbackFire());
+ resetCallbackFired();
+ }
+
+ {
+ options.setCost(102);
+
+ Block encodedOptions(options.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedOptions);
+
+ Interest command(commandName);
+ manager.onFibRequest(command);
+ BOOST_REQUIRE(didCallbackFire());
+ }
+
+ shared_ptr<fib::Entry> entry = fib.findLongestPrefixMatch("/hello");
+
+ // Add faces with cost == FaceID for the name /hello
+ // This test assumes:
+ // FaceIDs are -1 because we don't add them to a forwarder
+ if (static_cast<bool>(entry))
+ {
+ const fib::NextHopList& hops = entry->getNextHops();
+ BOOST_REQUIRE(hops.size() == 1);
+ BOOST_REQUIRE(std::find_if(hops.begin(),
+ hops.end(),
+ bind(&foundNextHop, -1, 102, _1)) != hops.end());
+ }
+ 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..5953fc6 100644
--- a/tests/mgmt/internal-face.cpp
+++ b/tests/mgmt/internal-face.cpp
@@ -5,25 +5,241 @@
*/
#include "mgmt/internal-face.hpp"
+#include "mgmt/fib-manager.hpp"
+#include "table/fib.hpp"
+#include "../face/dummy-face.hpp"
+
+#include <ndn-cpp-dev/management/fib-management-options.hpp>
+#include <ndn-cpp-dev/management/control-response.hpp>
+#include <ndn-cpp-dev/encoding/block.hpp>
#include <boost/test/unit_test.hpp>
namespace nfd {
+NFD_LOG_INIT("InternalFaceTest");
+
+class InternalFaceFixture
+{
+public:
+
+ InternalFaceFixture()
+ : m_onInterestFired(false),
+ m_noOnInterestFired(false)
+ {
+
+ }
+
+ shared_ptr<Face>
+ getFace(FaceId id)
+ {
+ if (m_faces.size() < id)
+ {
+ BOOST_FAIL("Attempted to access invalid FaceId: " << id);
+ }
+ return m_faces[id-1];
+ }
+
+ void
+ validateOnInterestCallback(const Name& name, const Interest& interest)
+ {
+ m_onInterestFired = true;
+ }
+
+ void
+ validateNoOnInterestCallback(const Name& name, const Interest& interest)
+ {
+ m_noOnInterestFired = true;
+ }
+
+ void
+ addFace(shared_ptr<Face> face)
+ {
+ m_faces.push_back(face);
+ }
+
+ bool
+ didOnInterestFire()
+ {
+ return m_onInterestFired;
+ }
+
+ bool
+ didNoOnInterestFire()
+ {
+ return m_noOnInterestFired;
+ }
+
+ void
+ resetOnInterestFired()
+ {
+ m_onInterestFired = false;
+ }
+
+ void
+ resetNoOnInterestFired()
+ {
+ m_noOnInterestFired = false;
+ }
+
+private:
+ std::vector<shared_ptr<Face> > m_faces;
+ bool m_onInterestFired;
+ bool m_noOnInterestFired;
+};
+
BOOST_AUTO_TEST_SUITE(MgmtInternalFace)
-BOOST_AUTO_TEST_CASE(ValidPrefixRegistration)
+void
+validatePutData(bool& called, const Name& expectedName, const Data& data)
{
- InternalFace internal;
- Interest regInterest("/localhost/nfd/prefixreg/hello/world");
- internal.sendInterest(regInterest);
+ called = true;
+ BOOST_CHECK_EQUAL(expectedName, data.getName());
}
-BOOST_AUTO_TEST_CASE(InvalidPrefixRegistration)
+BOOST_FIXTURE_TEST_CASE(PutData, InternalFaceFixture)
{
- InternalFace internal;
- Interest nonRegInterest("/hello/world");
- internal.sendInterest(nonRegInterest);
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(new InternalFace);
+ Fib fib;
+ FibManager manager(fib,
+ bind(&InternalFaceFixture::getFace,
+ this, _1),
+ face);
+
+ bool didPutData = false;
+ Name dataName("/hello");
+ face->onReceiveData += bind(&validatePutData, boost::ref(didPutData), dataName, _1);
+
+ Data testData(dataName);
+ face->sign(testData);
+ face->put(testData);
+
+ BOOST_REQUIRE(didPutData);
+}
+
+BOOST_FIXTURE_TEST_CASE(SendInterestHitEnd, InternalFaceFixture)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(new InternalFace);
+ Fib fib;
+ FibManager manager(fib,
+ bind(&InternalFaceFixture::getFace,
+ this, _1),
+ face);
+
+ face->setInterestFilter("/localhost/nfd/fib",
+ bind(&InternalFaceFixture::validateOnInterestCallback,
+ this, _1, _2));
+
+ // generate command whose name is canonically
+ // ordered after /localhost/nfd/fib so that
+ // we hit the end of the std::map
+
+ Name commandName("/localhost/nfd/fib/end");
+ Interest command(commandName);
+ face->sendInterest(command);
+
+ BOOST_REQUIRE(didOnInterestFire());
+ BOOST_REQUIRE(didNoOnInterestFire() == false);
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE(SendInterestHitBegin, InternalFaceFixture)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(new InternalFace);
+ Fib fib;
+ FibManager manager(fib,
+ bind(&InternalFaceFixture::getFace,
+ this, _1),
+ face);
+
+ face->setInterestFilter("/localhost/nfd/fib",
+ bind(&InternalFaceFixture::validateNoOnInterestCallback,
+ this, _1, _2));
+
+ // generate command whose name is canonically
+ // ordered before /localhost/nfd/fib so that
+ // we hit the beginning of the std::map
+
+ Name commandName("/localhost/nfd");
+ Interest command(commandName);
+ face->sendInterest(command);
+
+ BOOST_REQUIRE(didNoOnInterestFire() == false);
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE(SendInterestHitExact, InternalFaceFixture)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(new InternalFace);
+ Fib fib;
+ FibManager manager(fib,
+ bind(&InternalFaceFixture::getFace,
+ this, _1),
+ face);
+
+ face->setInterestFilter("/localhost/nfd/eib",
+ bind(&InternalFaceFixture::validateNoOnInterestCallback,
+ this, _1, _2));
+
+ face->setInterestFilter("/localhost/nfd/fib",
+ bind(&InternalFaceFixture::validateOnInterestCallback,
+ this, _1, _2));
+
+ face->setInterestFilter("/localhost/nfd/gib",
+ bind(&InternalFaceFixture::validateNoOnInterestCallback,
+ this, _1, _2));
+
+ // generate command whose name exactly matches
+ // /localhost/nfd/fib
+
+ Name commandName("/localhost/nfd/fib");
+ Interest command(commandName);
+ face->sendInterest(command);
+
+ BOOST_REQUIRE(didOnInterestFire());
+ BOOST_REQUIRE(didNoOnInterestFire() == false);
+}
+
+
+
+BOOST_FIXTURE_TEST_CASE(SendInterestHitPrevious, InternalFaceFixture)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(new InternalFace);
+ Fib fib;
+ FibManager manager(fib,
+ bind(&InternalFaceFixture::getFace,
+ this, _1),
+ face);
+
+ face->setInterestFilter("/localhost/nfd/fib",
+ bind(&InternalFaceFixture::validateOnInterestCallback,
+ this, _1, _2));
+
+ face->setInterestFilter("/localhost/nfd/fib/zzzzzzzzzzzzz/",
+ bind(&InternalFaceFixture::validateNoOnInterestCallback,
+ this, _1, _2));
+
+ // generate command whose name exactly matches
+ // an Interest filter
+
+ Name commandName("/localhost/nfd/fib/previous");
+ Interest command(commandName);
+ face->sendInterest(command);
+
+ BOOST_REQUIRE(didOnInterestFire());
+ BOOST_REQUIRE(didNoOnInterestFire() == false);
}
BOOST_AUTO_TEST_SUITE_END()