nlsr: discover Faces from NFD
refs: #2954
Change-Id: I072972d88bce0e1012e96f33577657048b7df1e1
diff --git a/tests/test-common.cpp b/tests/test-common.cpp
new file mode 100644
index 0000000..e6b5e78
--- /dev/null
+++ b/tests/test-common.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2017, The University of Memphis,
+ * Regents of the University of California
+ *
+ * This file is part of NLSR (Named-data Link State Routing).
+ * See AUTHORS.md for complete list of NLSR authors and contributors.
+ *
+ * NLSR is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NLSR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NLSR, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "test-common.hpp"
+
+namespace nlsr {
+namespace test {
+
+ndn::Data&
+signData(ndn::Data& data)
+{
+ ndn::SignatureSha256WithRsa fakeSignature;
+ fakeSignature.setValue(ndn::encoding::makeEmptyBlock(ndn::tlv::SignatureValue));
+ data.setSignature(fakeSignature);
+ data.wireEncode();
+
+ return data;
+}
+
+MockNfdMgmtFixture::MockNfdMgmtFixture()
+ : face(std::make_shared<ndn::util::DummyClientFace>(g_ioService))
+{
+}
+
+void
+MockNfdMgmtFixture::signDatasetReply(ndn::Data& data)
+{
+ signData(data);
+}
+
+void
+UnitTestTimeFixture::advanceClocks(const ndn::time::nanoseconds& tick, size_t nTicks)
+{
+ for (size_t i = 0; i < nTicks; ++i) {
+ steadyClock->advance(tick);
+ systemClock->advance(tick);
+
+ if (g_ioService.stopped()) {
+ g_ioService.reset();
+ }
+
+ g_ioService.poll();
+ }
+}
+
+} // namespace test
+} // namespace nlsr
diff --git a/tests/test-common.hpp b/tests/test-common.hpp
index 817b77e..dcba6c0 100644
--- a/tests/test-common.hpp
+++ b/tests/test-common.hpp
@@ -17,7 +17,6 @@
* You should have received a copy of the GNU General Public License along with
* NLSR, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*
- *
**/
#ifndef NLSR_TEST_COMMON_HPP
@@ -32,9 +31,25 @@
#include <ndn-cxx/security/key-chain.hpp>
#include <ndn-cxx/util/time-unit-test-clock.hpp>
+#include <ndn-cxx/security/signature-sha256-with-rsa.hpp>
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
namespace nlsr {
namespace test {
+ndn::Data&
+signData(ndn::Data& data);
+
+/** \brief add a fake signature to Data
+ */
+inline shared_ptr<ndn::Data>
+signData(shared_ptr<ndn::Data> data)
+{
+ signData(*data);
+ return data;
+}
+
class BaseFixture
{
public:
@@ -65,25 +80,89 @@
}
void
- advanceClocks(const ndn::time::nanoseconds& tick, size_t nTicks = 1)
- {
- for (size_t i = 0; i < nTicks; ++i) {
- steadyClock->advance(tick);
- systemClock->advance(tick);
-
- if (g_ioService.stopped()) {
- g_ioService.reset();
- }
-
- g_ioService.poll();
- }
- }
+ advanceClocks(const ndn::time::nanoseconds& tick, size_t nTicks = 1);
protected:
std::shared_ptr<ndn::time::UnitTestSteadyClock> steadyClock;
std::shared_ptr<ndn::time::UnitTestSystemClock> systemClock;
};
+class MockNfdMgmtFixture : public UnitTestTimeFixture
+{
+public:
+ MockNfdMgmtFixture();
+
+ /** \brief send one WireEncodable in reply to StatusDataset request
+ * \param prefix dataset prefix without version and segment
+ * \param payload payload block
+ * \note payload must fit in one Data
+ * \pre Interest for dataset has been expressed, sendDataset has not been invoked
+ */
+ template<typename T>
+ void
+ sendDataset(const ndn::Name& prefix, const T& payload)
+ {
+ BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T>));
+
+ this->sendDatasetReply(prefix, payload.wireEncode());
+ }
+
+ /** \brief send two WireEncodables in reply to StatusDataset request
+ * \param prefix dataset prefix without version and segment
+ * \param payload1 first vector item
+ * \param payload2 second vector item
+ * \note all payloads must fit in one Data
+ * \pre Interest for dataset has been expressed, sendDataset has not been invoked
+ */
+ template<typename T1, typename T2>
+ void
+ sendDataset(const ndn::Name& prefix, const T1& payload1, const T2& payload2)
+ {
+ BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T1>));
+ BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T2>));
+
+ ndn::encoding::EncodingBuffer buffer;
+ payload2.wireEncode(buffer);
+ payload1.wireEncode(buffer);
+
+ this->sendDatasetReply(prefix, buffer.buf(), buffer.size());
+ }
+
+ /** \brief send a payload in reply to StatusDataset request
+ * \param name dataset prefix without version and segment
+ * \param contentArgs passed to Data::setContent
+ */
+ template<typename ...ContentArgs>
+ void
+ sendDatasetReply(ndn::Name name, ContentArgs&&... contentArgs)
+ {
+ name.appendVersion().appendSegment(0);
+
+ // These warnings assist in debugging when nfdc does not receive StatusDataset.
+ // They usually indicate a misspelled prefix or incorrect timing in the test case.
+ if (face->sentInterests.empty()) {
+ BOOST_WARN_MESSAGE(false, "no Interest expressed");
+ }
+ else {
+ BOOST_WARN_MESSAGE(face->sentInterests.back().getName().isPrefixOf(name),
+ "last Interest " << face->sentInterests.back().getName() <<
+ " cannot be satisfied by this Data " << name);
+ }
+
+ auto data = make_shared<ndn::Data>(name);
+ data->setFinalBlockId(name[-1]);
+ data->setContent(std::forward<ContentArgs>(contentArgs)...);
+ this->signDatasetReply(*data);
+ face->receive(*data);
+ }
+
+ virtual void
+ signDatasetReply(ndn::Data& data);
+
+public:
+ std::shared_ptr<ndn::util::DummyClientFace> face;
+};
+
} // namespace test
} // namespace nlsr
diff --git a/tests/test-nlsr.cpp b/tests/test-nlsr.cpp
index 78e0fb9..2cfe46f 100644
--- a/tests/test-nlsr.cpp
+++ b/tests/test-nlsr.cpp
@@ -25,21 +25,21 @@
#include "nlsr.hpp"
#include <ndn-cxx/mgmt/nfd/face-event-notification.hpp>
-#include <ndn-cxx/util/dummy-client-face.hpp>
namespace nlsr {
namespace test {
using std::shared_ptr;
-class NlsrFixture : public UnitTestTimeFixture
+class NlsrFixture : public MockNfdMgmtFixture
{
public:
NlsrFixture()
- : face(std::make_shared<ndn::util::DummyClientFace>(g_ioService))
- , nlsr(g_ioService, g_scheduler, std::ref(*face), g_keyChain)
+ : nlsr(g_ioService, g_scheduler, std::ref(*face), g_keyChain)
, lsdb(nlsr.getLsdb())
, neighbors(nlsr.getAdjacencyList())
+ , nSuccessCallbacks(0)
+ , nFailureCallbacks(0)
{
}
@@ -55,10 +55,12 @@
}
public:
- std::shared_ptr<ndn::util::DummyClientFace> face;
Nlsr nlsr;
Lsdb& lsdb;
AdjacencyList& neighbors;
+ uint32_t nSuccessCallbacks;
+ uint32_t nFailureCallbacks;
+
};
BOOST_FIXTURE_TEST_SUITE(TestNlsr, NlsrFixture)
@@ -124,6 +126,92 @@
BOOST_CHECK_EQUAL(rt.getRoutingCalcInterval(), ndn::time::seconds(9));
}
+BOOST_AUTO_TEST_CASE(FaceCreateEvent)
+{
+ // Setting constants for the unit test
+ const uint32_t faceId = 1;
+ const std::string faceUri = "udp4://10.0.0.1:6363";
+ Adjacent neighbor("/ndn/neighborA", ndn::util::FaceUri(faceUri), 10, Adjacent::STATUS_INACTIVE, 0, 0);
+ BOOST_REQUIRE_EQUAL(nlsr.getAdjacencyList().insert(neighbor), 0);
+
+ this->advanceClocks(ndn::time::milliseconds(1));
+
+ // Build, sign, and send the Face Event
+ ndn::nfd::FaceEventNotification event;
+ event.setKind(ndn::nfd::FACE_EVENT_CREATED)
+ .setRemoteUri(faceUri)
+ .setFaceId(faceId);
+ std::shared_ptr<ndn::Data> data = std::make_shared<ndn::Data>("/localhost/nfd/faces/events/%FE%00");
+ data->setContent(event.wireEncode());
+ nlsr.getKeyChain().sign(*data);
+ face->receive(*data);
+
+ // Move the clocks forward so that the Face processes the event.
+ this->advanceClocks(ndn::time::milliseconds(1));
+
+ // Need to explicitly provide a FaceUri object, because the
+ // conversion will attempt to create Name objects.
+ auto iterator = nlsr.getAdjacencyList().findAdjacent(ndn::util::FaceUri(faceUri));
+ BOOST_REQUIRE(iterator != nlsr.getAdjacencyList().end());
+ BOOST_CHECK_EQUAL(iterator->getFaceId(), faceId);
+}
+
+BOOST_AUTO_TEST_CASE(FaceCreateEventNoMatch)
+{
+ // Setting constants for the unit test
+ const uint32_t faceId = 1;
+ const std::string eventUri = "udp4://10.0.0.1:6363";
+ const std::string neighborUri = "udp4://10.0.0.2:6363";
+ Adjacent neighbor("/ndn/neighborA", ndn::util::FaceUri(neighborUri), 10, Adjacent::STATUS_INACTIVE, 0, 0);
+ nlsr.getAdjacencyList().insert(neighbor);
+
+ // Build, sign, and send the Face Event
+ ndn::nfd::FaceEventNotification event;
+ event.setKind(ndn::nfd::FACE_EVENT_CREATED)
+ .setRemoteUri(eventUri)
+ .setFaceId(faceId);
+ std::shared_ptr<ndn::Data> data = std::make_shared<ndn::Data>("/localhost/nfd/faces/events/%FE%00");
+ data->setContent(event.wireEncode());
+ nlsr.getKeyChain().sign(*data);
+ face->receive(*data);
+
+ // Move the clocks forward so that the Face processes the event.
+ this->advanceClocks(ndn::time::milliseconds(1));
+
+ // The Face URIs did not match, so this neighbor should be unconfigured.
+ auto iterator = nlsr.getAdjacencyList().findAdjacent(ndn::util::FaceUri(neighborUri));
+ BOOST_REQUIRE(iterator != nlsr.getAdjacencyList().end());
+ BOOST_CHECK_EQUAL(iterator->getFaceId(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FaceCreateEventAlreadyConfigured)
+{
+ // Setting constants for the unit test
+ const uint32_t eventFaceId = 1;
+ const uint32_t neighborFaceId = 2;
+ const std::string faceUri = "udp4://10.0.0.1:6363";
+ Adjacent neighbor("/ndn/neighborA", ndn::util::FaceUri(faceUri), 10, Adjacent::STATUS_ACTIVE, 0, neighborFaceId);
+ nlsr.getAdjacencyList().insert(neighbor);
+
+ // Build, sign, and send the Face Event
+ ndn::nfd::FaceEventNotification event;
+ event.setKind(ndn::nfd::FACE_EVENT_CREATED)
+ .setRemoteUri(faceUri)
+ .setFaceId(eventFaceId);
+ std::shared_ptr<ndn::Data> data = std::make_shared<ndn::Data>("/localhost/nfd/faces/events/%FE%00");
+ data->setContent(event.wireEncode());
+ nlsr.getKeyChain().sign(*data);
+ face->receive(*data);
+
+ // Move the clocks forward so that the Face processes the event.
+ this->advanceClocks(ndn::time::milliseconds(1));
+
+ // Since the neighbor was already configured, this (simply erroneous) event should have no effect.
+ auto iterator = nlsr.getAdjacencyList().findAdjacent(ndn::util::FaceUri(faceUri));
+ BOOST_REQUIRE(iterator != nlsr.getAdjacencyList().end());
+ BOOST_CHECK_EQUAL(iterator->getFaceId(), neighborFaceId);
+}
+
BOOST_FIXTURE_TEST_CASE(FaceDestroyEvent, UnitTestTimeFixture)
{
std::shared_ptr<ndn::util::DummyClientFace> face = std::make_shared<ndn::util::DummyClientFace>(g_ioService);
@@ -480,6 +568,134 @@
ndn::util::FaceUri("udp4://10.0.0.2:6363"));
}
+BOOST_AUTO_TEST_CASE(FaceDatasetFetchSuccess)
+{
+ bool hasResult = false;
+ nlsr.m_validator.m_shouldValidate = false;
+
+ nlsr.initializeFaces([&hasResult] (const std::vector<ndn::nfd::FaceStatus>& faces) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(faces.size(), 2);
+ BOOST_CHECK_EQUAL(faces.front().getFaceId(), 25401);
+ BOOST_CHECK_EQUAL(faces.back().getFaceId(), 25402);
+ },
+ [] (uint32_t code, const std::string& reason) {});
+
+ this->advanceClocks(ndn::time::milliseconds(500));
+
+ ndn::nfd::FaceStatus payload1;
+ payload1.setFaceId(25401);
+ ndn::nfd::FaceStatus payload2;
+ payload2.setFaceId(25402);
+ this->sendDataset("/localhost/nfd/faces/list", payload1, payload2);
+
+ this->advanceClocks(ndn::time::milliseconds(500));
+ BOOST_CHECK(hasResult);
+}
+
+BOOST_AUTO_TEST_CASE(FaceDatasetFetchFailure)
+{
+ nlsr.m_validator.m_shouldValidate = false;
+ nlsr.initializeFaces([](const std::vector<ndn::nfd::FaceStatus>& faces) {},
+ [this](uint32_t code, const std::string& reason){
+ this->nFailureCallbacks++;
+ });
+ this->advanceClocks(ndn::time::milliseconds(500));
+
+ ndn::Name payload;
+ this->sendDataset("/localhost/nfd/faces/list", payload);
+ this->advanceClocks(ndn::time::milliseconds(500));
+
+ BOOST_CHECK_EQUAL(nFailureCallbacks, 1);
+ BOOST_CHECK_EQUAL(nSuccessCallbacks, 0);
+}
+
+BOOST_AUTO_TEST_CASE(FaceDatasetProcess)
+{
+ Adjacent neighborA("/ndn/neighborA", ndn::util::FaceUri("udp4://192.168.0.100:6363"), 25, Adjacent::STATUS_INACTIVE, 0, 0);
+ neighbors.insert(neighborA);
+
+ Adjacent neighborB("/ndn/neighborB", ndn::util::FaceUri("udp4://192.168.0.101:6363"), 10, Adjacent::STATUS_INACTIVE, 0, 0);
+ neighbors.insert(neighborB);
+
+ ndn::nfd::FaceStatus payload1;
+ payload1.setFaceId(1)
+ .setRemoteUri("udp4://192.168.0.100:6363");
+ ndn::nfd::FaceStatus payload2;
+ payload2.setFaceId(2)
+ .setRemoteUri("udp4://192.168.0.101:6363");
+ std::vector<ndn::nfd::FaceStatus> faceStatuses = {payload1, payload2};
+
+ nlsr.processFaceDataset(faceStatuses);
+ this->advanceClocks(ndn::time::milliseconds(100));
+
+ AdjacencyList adjList = nlsr.getAdjacencyList();
+
+ BOOST_CHECK_EQUAL(adjList.getAdjacent("/ndn/neighborA").getFaceId(), payload1.getFaceId());
+ BOOST_CHECK_EQUAL(adjList.getAdjacent("/ndn/neighborB").getFaceId(), payload2.getFaceId());
+}
+
+BOOST_AUTO_TEST_CASE(UnconfiguredNeighbor)
+{
+ Adjacent neighborA("/ndn/neighborA", ndn::util::FaceUri("udp4://192.168.0.100:6363"), 25, Adjacent::STATUS_INACTIVE, 0, 0);
+ neighbors.insert(neighborA);
+
+ ndn::nfd::FaceStatus payload;
+ payload.setFaceId(1)
+ .setRemoteUri("udp4://192.168.0.101:6363"); // Note dissimilar Face URI.
+ std::vector<ndn::nfd::FaceStatus> faceStatuses = {payload};
+
+ nlsr.processFaceDataset(faceStatuses);
+ this->advanceClocks(ndn::time::milliseconds(100));
+
+ AdjacencyList adjList = nlsr.getAdjacencyList();
+
+ BOOST_CHECK_EQUAL(adjList.getAdjacent("/ndn/neighborA").getFaceId(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FaceDatasetPeriodicFetch)
+{
+ int nNameMatches = 0;
+ ndn::Name datasetPrefix("/localhost/nfd/faces/list");
+ ndn::nfd::CommandOptions options;
+ ndn::time::milliseconds defaultTimeout = options.getTimeout();
+
+ ndn::time::seconds fetchInterval(1);
+ ConfParameter& conf = nlsr.getConfParameter();
+ conf.setFaceDatasetFetchInterval(fetchInterval);
+ conf.setFaceDatasetFetchTries(0);
+
+ nlsr.initializeFaces(std::bind(&Nlsr::processFaceDataset, &nlsr, _1),
+ std::bind(&Nlsr::onFaceDatasetFetchTimeout, &nlsr, _1, _2, 0));
+
+ // Elapse the default timeout time of the interest.
+ this->advanceClocks(defaultTimeout);
+
+ // Check that we have one interest for face list in the sent interests.
+ for (const ndn::Interest& interest : face->sentInterests) {
+ if (datasetPrefix.isPrefixOf(interest.getName())) {
+ nNameMatches++;
+ }
+ }
+ BOOST_CHECK_EQUAL(nNameMatches, 1);
+
+ // Elapse the clock by the reschedule time (that we set)
+ this->advanceClocks(fetchInterval);
+ // Elapse the default timeout on the interest.
+ this->advanceClocks(defaultTimeout);
+ // Plus a little more to let the events process.
+ this->advanceClocks(ndn::time::seconds(1));
+
+ // Check that we now have two interests
+ nNameMatches = 0;
+ for (const ndn::Interest& interest : face->sentInterests) {
+ if (datasetPrefix.isPrefixOf(interest.getName())) {
+ nNameMatches++;
+ }
+ }
+ BOOST_CHECK_EQUAL(nNameMatches, 2);
+}
+
BOOST_AUTO_TEST_SUITE_END()
} // namespace test
diff --git a/tests/utility/test-face-controller.cpp b/tests/utility/test-face-controller.cpp
deleted file mode 100644
index 7684256..0000000
--- a/tests/utility/test-face-controller.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2017, The University of Memphis,
- * Regents of the University of California,
- * Arizona Board of Regents.
- *
- * This file is part of NLSR (Named-data Link State Routing).
- * See AUTHORS.md for complete list of NLSR authors and contributors.
- *
- * NLSR is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NLSR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NLSR, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-#include "tests/test-common.hpp"
-#include "tests/control-commands.hpp"
-
-#include "utility/face-controller.hpp"
-
-#include <ndn-cxx/security/key-chain.hpp>
-#include <ndn-cxx/mgmt/nfd/controller.hpp>
-#include <ndn-cxx/util/dummy-client-face.hpp>
-
-namespace nlsr {
-namespace test {
-
-using std::bind;
-using std::shared_ptr;
-using ndn::Interest;
-
-class FaceControllerFixture : public BaseFixture
-{
-public:
- FaceControllerFixture()
- : face(std::make_shared<ndn::util::DummyClientFace>())
- , interests(face->sentInterests)
- , controller(*face, keyChain)
- , faceController(g_ioService, controller)
- {
- }
-
- void
- onFailure(const ndn::nfd::ControlResponse& response)
- {
- BOOST_CHECK_EQUAL(response.getCode(), 408);
- }
-
-public:
- std::shared_ptr<ndn::util::DummyClientFace> face;
- ndn::KeyChain keyChain;
- std::vector<Interest>& interests;
- ndn::nfd::Controller controller;
- util::FaceController faceController;
-};
-
-BOOST_FIXTURE_TEST_SUITE(TestFaceController, FaceControllerFixture)
-
-BOOST_AUTO_TEST_CASE(FaceCreateCanonizeSuccess)
-{
- const std::string uri("udp4://192.0.2.1:6363");
- faceController.createFace(uri, nullptr, nullptr);
-
- face->processEvents(ndn::time::milliseconds(1));
-
- BOOST_REQUIRE_EQUAL(interests.size(), 1);
- Interest interest = interests.front();
-
- ndn::nfd::ControlParameters extractedParameters;
- ndn::Name::Component verb;
-
- extractFaceCommandParameters(interest, verb, extractedParameters);
-
- BOOST_CHECK_EQUAL(verb, ndn::Name::Component("create"));
- BOOST_CHECK_EQUAL(uri, extractedParameters.getUri());
-}
-
-BOOST_AUTO_TEST_CASE(FaceCreateCanonizeFailure)
-{
- faceController.createFace("invalid://256.0.0.1:6363",
- nullptr,
- std::bind(&FaceControllerFixture::onFailure, this, _1));
-
- face->processEvents(ndn::time::milliseconds(1));
-
- BOOST_CHECK_EQUAL(interests.size(), 0);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-} // namespace test
-} // namespace nlsr