route: Fix FIB next hop removal bug
refs: #2018
Change-Id: Id107c04d4cdce9cc756acad5d262e5c1e0cc29a8
diff --git a/src/conf-parameter.hpp b/src/conf-parameter.hpp
index b8b5b72..8f2965b 100644
--- a/src/conf-parameter.hpp
+++ b/src/conf-parameter.hpp
@@ -289,12 +289,12 @@
}
void
- setMaxFacesPerPrefix(int32_t mfpp)
+ setMaxFacesPerPrefix(uint32_t mfpp)
{
m_maxFacesPerPrefix = mfpp;
}
- int32_t
+ uint32_t
getMaxFacesPerPrefix() const
{
return m_maxFacesPerPrefix;
@@ -353,7 +353,7 @@
double m_corR;
double m_corTheta;
- int32_t m_maxFacesPerPrefix;
+ uint32_t m_maxFacesPerPrefix;
std::string m_logDir;
std::string m_seqFileDir;
diff --git a/src/nlsr.hpp b/src/nlsr.hpp
index 240fffb..00b7877 100644
--- a/src/nlsr.hpp
+++ b/src/nlsr.hpp
@@ -79,7 +79,7 @@
, m_isRouteCalculationScheduled(false)
, m_isRoutingTableCalculating(false)
, m_routingTable(scheduler)
- , m_fib(*this, m_nlsrFace, scheduler)
+ , m_fib(m_nlsrFace, scheduler, m_adjacencyList, m_confParam)
, m_namePrefixTable(*this)
, m_syncLogicHandler(m_nlsrFace, m_nlsrLsdb, m_confParam)
, m_helloProtocol(*this, scheduler)
diff --git a/src/route/fib.cpp b/src/route/fib.cpp
index 2010858..1815b27 100644
--- a/src/route/fib.cpp
+++ b/src/route/fib.cpp
@@ -24,14 +24,13 @@
#include <cmath>
#include <ndn-cxx/common.hpp>
-#include "nlsr.hpp"
+#include "adjacency-list.hpp"
+#include "conf-parameter.hpp"
#include "nexthop-list.hpp"
#include "face-map.hpp"
#include "fib.hpp"
#include "logger.hpp"
-
-
namespace nlsr {
INIT_LOGGER("Fib");
@@ -44,7 +43,7 @@
static bool
fibEntryNameCompare(const FibEntry& fibEntry, const ndn::Name& name)
{
- return fibEntry.getName() == name ;
+ return fibEntry.getName() == name;
}
void
@@ -93,84 +92,102 @@
}
void
-Fib::addNextHopsToFibEntryAndNfd(FibEntry& entry, NexthopList& nextHopList)
+Fib::addNextHopsToFibEntryAndNfd(FibEntry& entry, NexthopList& hopsToAdd)
{
const ndn::Name& name = entry.getName();
- nextHopList.sort();
-
- int numFaces = 0;
- int maxFaces = getNumberOfFacesForName(nextHopList,
- m_nlsr.getConfParameter().getMaxFacesPerPrefix());
-
- for (NexthopList::iterator hopIt = nextHopList.begin();
- hopIt != nextHopList.end() && numFaces < maxFaces; ++hopIt, ++numFaces)
+ for (NexthopList::iterator it = hopsToAdd.begin(); it != hopsToAdd.end(); ++it)
{
// Add nexthop to FIB entry
- entry.getNexthopList().addNextHop(*hopIt);
+ entry.getNexthopList().addNextHop(*it);
if (isPrefixUpdatable(name)) {
// Add nexthop to NDN-FIB
- registerPrefix(name, hopIt->getConnectingFaceUri(),
- hopIt->getRouteCostAsAdjustedInteger(),
+ registerPrefix(name, it->getConnectingFaceUri(),
+ it->getRouteCostAsAdjustedInteger(),
ndn::time::seconds(m_refreshTime + GRACE_PERIOD),
ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
}
}
+
+ entry.getNexthopList().sort();
+}
+
+void
+Fib::removeOldNextHopsFromFibEntryAndNfd(FibEntry& entry, const NexthopList& installedHops)
+{
+ _LOG_DEBUG("Fib::removeOldNextHopsFromFibEntryAndNfd Called");
+
+ const ndn::Name& name = entry.getName();
+ NexthopList& entryHopList = entry.getNexthopList();
+
+ for (NexthopList::iterator it = entryHopList.begin(); it != entryHopList.end();) {
+
+ const std::string& faceUri = it->getConnectingFaceUri();
+
+ // See if the nexthop is installed in NFD's FIB
+ NexthopList::const_iterator foundIt = std::find_if(installedHops.cbegin(),
+ installedHops.cend(),
+ bind(&compareFaceUri, _1, faceUri));
+
+ // The next hop is not installed
+ if (foundIt == installedHops.cend()) {
+
+ if (isPrefixUpdatable(name)) {
+ // Remove the nexthop from NDN's FIB
+ unregisterPrefix(name, it->getConnectingFaceUri());
+ }
+
+ // Remove the next hop from the FIB entry
+ _LOG_DEBUG("Removing " << it->getConnectingFaceUri() << " from " << name);
+ // Since the iterator will be invalidated on removal, dereference the original
+ // and increment the copy
+ entryHopList.removeNextHop(*(it++));
+ }
+ else {
+ ++it;
+ }
+ }
}
void
-Fib::removeOldNextHopsFromFibEntryAndNfd(FibEntry& entry, NexthopList& newHopList)
+Fib::update(const ndn::Name& name, NexthopList& allHops)
{
- _LOG_DEBUG("Fib::removeOldNextHopsFromFibEntryAndNfd Called");
- const ndn::Name& name = entry.getName();
- NexthopList& entryHopList = entry.getNexthopList();
- NexthopList itHopList = entryHopList;
+ _LOG_DEBUG("Fib::update called");
- for (NexthopList::iterator it = itHopList.begin(); it != itHopList.end(); ++it) {
+ // Sort all of the next hops so lower cost hops are prioritized
+ allHops.sort();
- // See if the nexthop is in the new nexthop list
- const std::string& faceUri = it->getConnectingFaceUri();
- NexthopList::iterator foundIt = std::find_if(newHopList.begin(),
- newHopList.end(),
- bind(&compareFaceUri, _1, faceUri));
+ // Get the max possible faces which is the minumum of the configuration setting and
+ // the length of the list of all next hops.
+ unsigned int maxFaces = getNumberOfFacesForName(allHops);
- // The next hop is not in the new nexthop list
- if (foundIt == newHopList.end()) {
- // Remove the next hop from the FIB entry
- _LOG_DEBUG("Removing " << it->getConnectingFaceUri() << " from " << name);
- entryHopList.removeNextHop(*it);
+ NexthopList hopsToAdd;
+ unsigned int nFaces = 0;
- if (isPrefixUpdatable(name)) {
- // Remove the nexthop from NDN-FIB
- unregisterPrefix(name, it->getConnectingFaceUri());
- }
- }
- }
-}
-
-void
-Fib::update(const ndn::Name& name, NexthopList& nextHopList)
-{
- _LOG_DEBUG("Fib::updateFib Called");
+ // Create a list of next hops to be installed with length == maxFaces
+ for (NexthopList::iterator it = allHops.begin(); it != allHops.end() && nFaces < maxFaces;
+ ++it, ++nFaces)
+ {
+ hopsToAdd.addNextHop(*it);
+ }
std::list<FibEntry>::iterator entryIt = std::find_if(m_table.begin(),
m_table.end(),
bind(&fibEntryNameCompare, _1, name));
+
// New FIB entry
if (entryIt == m_table.end()) {
_LOG_DEBUG("New FIB Entry");
// Don't create an entry for a name with no nexthops
- if (nextHopList.getSize() == 0) {
+ if (hopsToAdd.getSize() == 0) {
return;
}
FibEntry entry(name);
- addNextHopsToFibEntryAndNfd(entry, nextHopList);
-
- entry.getNexthopList().sort();
+ addNextHopsToFibEntryAndNfd(entry, hopsToAdd);
// Set entry's expiration time point and sequence number
entry.setExpirationTimePoint(ndn::time::system_clock::now() +
@@ -189,15 +206,14 @@
FibEntry& entry = *entryIt;
// Remove empty FIB entry
- if (nextHopList.getSize() == 0) {
+ if (hopsToAdd.getSize() == 0) {
remove(name);
return;
}
- addNextHopsToFibEntryAndNfd(entry, nextHopList);
- removeOldNextHopsFromFibEntryAndNfd(entry, nextHopList);
+ addNextHopsToFibEntryAndNfd(entry, hopsToAdd);
- entry.getNexthopList().sort();
+ removeOldNextHopsFromFibEntryAndNfd(entry, hopsToAdd);
// Set entry's expiration time point
entry.setExpirationTimePoint(ndn::time::system_clock::now() +
@@ -205,7 +221,7 @@
// Increment sequence number
entry.setSeqNo(entry.getSeqNo() + 1);
- // Cancel previosuly scheduled event
+ // Cancel previously scheduled event
m_scheduler.cancelEvent(entry.getExpiringEventId());
// Schedule entry to be refreshed
@@ -234,23 +250,24 @@
}
}
-int
-Fib::getNumberOfFacesForName(NexthopList& nextHopList,
- uint32_t maxFacesPerPrefix)
+unsigned int
+Fib::getNumberOfFacesForName(NexthopList& nextHopList)
{
- int endFace = 0;
- if ((maxFacesPerPrefix == 0) || (nextHopList.getSize() <= maxFacesPerPrefix)) {
- return nextHopList.getSize();
+ uint32_t nNextHops = static_cast<uint32_t>(nextHopList.getNextHops().size());
+ uint32_t nMaxFaces = m_confParameter.getMaxFacesPerPrefix();
+
+ // Allow all faces
+ if (nMaxFaces == 0) {
+ return nNextHops;
}
else {
- return maxFacesPerPrefix;
+ return std::min(nNextHops, nMaxFaces);
}
- return endFace;
}
bool
Fib::isPrefixUpdatable(const ndn::Name& name) {
- if (!m_nlsr.getAdjacencyList().isNeighbor(name)) {
+ if (!m_adjacencyList.isNeighbor(name)) {
return true;
}
@@ -313,7 +330,7 @@
uint64_t faceCost, const ndn::time::milliseconds& timeout,
uint64_t flags, uint8_t times)
{
- uint64_t faceId = m_nlsr.getAdjacencyList().getFaceId(faceUri);
+ uint64_t faceId = m_adjacencyList.getFaceId(faceUri);
if (faceId != 0) {
ndn::nfd::ControlParameters faceParameters;
faceParameters
diff --git a/src/route/fib.hpp b/src/route/fib.hpp
index 9893ff2..e49727b 100644
--- a/src/route/fib.hpp
+++ b/src/route/fib.hpp
@@ -30,27 +30,30 @@
#include <ndn-cxx/util/time.hpp>
#include "face-map.hpp"
#include "fib-entry.hpp"
+#include "test-access-control.hpp"
namespace nlsr {
typedef ndn::function<void(const ndn::nfd::ControlParameters&)> CommandSucceedCallback;
typedef ndn::function<void(uint32_t/*code*/,const std::string&/*reason*/)> CommandFailCallback;
-class Nlsr;
-
+class AdjacencyList;
+class ConfParameter;
class Fib
{
public:
- Fib(Nlsr& nlsr, ndn::Face& face, ndn::Scheduler& scheduler)
- : m_nlsr(nlsr)
- , m_scheduler(scheduler)
+ Fib(ndn::Face& face, ndn::Scheduler& scheduler, AdjacencyList& adjacencyList, ConfParameter& conf)
+ : m_scheduler(scheduler)
, m_table()
, m_refreshTime(0)
, m_controller(face)
, m_faceMap()
+ , m_adjacencyList(adjacencyList)
+ , m_confParameter(conf)
{
}
+
~Fib()
{
}
@@ -59,7 +62,7 @@
remove(const ndn::Name& name);
void
- update(const ndn::Name& name, NexthopList& nextHopList);
+ update(const ndn::Name& name, NexthopList& allHops);
void
clean();
@@ -75,17 +78,17 @@
isPrefixUpdatable(const ndn::Name& name);
void
- addNextHopsToFibEntryAndNfd(FibEntry& entry, NexthopList& nextHopList);
+ addNextHopsToFibEntryAndNfd(FibEntry& entry, NexthopList& hopsToAdd);
void
- removeOldNextHopsFromFibEntryAndNfd(FibEntry& entry, NexthopList& newHopList);
+ removeOldNextHopsFromFibEntryAndNfd(FibEntry& entry, const NexthopList& installedHops);
void
removeHop(NexthopList& nl, const std::string& doNotRemoveHopFaceUri,
const ndn::Name& name);
- int
- getNumberOfFacesForName(NexthopList& nextHopList, uint32_t maxFacesPerPrefix);
+ unsigned int
+ getNumberOfFacesForName(NexthopList& nextHopList);
ndn::EventId
scheduleEntryExpiration(const ndn::Name& name, int32_t feSeqNum,
@@ -178,14 +181,19 @@
const std::string& message);
private:
- Nlsr& m_nlsr;
ndn::Scheduler& m_scheduler;
std::list<FibEntry> m_table;
int32_t m_refreshTime;
ndn::nfd::Controller m_controller;
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
FaceMap m_faceMap;
+private:
+ AdjacencyList& m_adjacencyList;
+ ConfParameter& m_confParameter;
+
static const uint64_t GRACE_PERIOD;
};
diff --git a/src/route/nexthop-list.hpp b/src/route/nexthop-list.hpp
index 7c2011e..faf13f0 100644
--- a/src/route/nexthop-list.hpp
+++ b/src/route/nexthop-list.hpp
@@ -73,6 +73,7 @@
}
typedef std::list<NextHop>::iterator iterator;
+ typedef std::list<NextHop>::const_iterator const_iterator;
iterator
begin()
@@ -86,6 +87,18 @@
return m_nexthopList.end();
}
+ const_iterator
+ cbegin() const
+ {
+ return m_nexthopList.begin();
+ }
+
+ const_iterator
+ cend() const
+ {
+ return m_nexthopList.end();
+ }
+
void
writeLog();
diff --git a/src/test-access-control.hpp b/src/test-access-control.hpp
new file mode 100644
index 0000000..f1667e3
--- /dev/null
+++ b/src/test-access-control.hpp
@@ -0,0 +1,39 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014 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/>
+ *
+ **/
+
+#ifndef NLSR_TEST_ACCESS_CONTROL_HPP
+#define NLSR_TEST_ACCESS_CONTROL_HPP
+
+#include "config.hpp"
+
+#ifdef WITH_TESTS
+#define VIRTUAL_WITH_TESTS virtual
+#define PUBLIC_WITH_TESTS_ELSE_PROTECTED public
+#define PUBLIC_WITH_TESTS_ELSE_PRIVATE public
+#define PROTECTED_WITH_TESTS_ELSE_PRIVATE protected
+#else
+#define VIRTUAL_WITH_TESTS
+#define PUBLIC_WITH_TESTS_ELSE_PROTECTED protected
+#define PUBLIC_WITH_TESTS_ELSE_PRIVATE private
+#define PROTECTED_WITH_TESTS_ELSE_PRIVATE private
+#endif
+
+#endif //NLSR_TEST_ACCESS_CONTROL_HPP
diff --git a/tests/control-commands.hpp b/tests/control-commands.hpp
new file mode 100644
index 0000000..e84deea
--- /dev/null
+++ b/tests/control-commands.hpp
@@ -0,0 +1,56 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014 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/>.
+ *
+ *
+ **/
+
+#ifndef NLSR_TEST_CONTROL_COMMANDS_HPP
+#define NLSR_TEST_CONTROL_COMMANDS_HPP
+
+#include <ndn-cxx/interest.hpp>
+#include <ndn-cxx/management/nfd-control-parameters.hpp>
+
+namespace nlsr {
+namespace test {
+
+inline void
+extractParameters(const ndn::Interest& interest,
+ ndn::Name::Component& verb,
+ ndn::nfd::ControlParameters& extractedParameters,
+ const ndn::Name& commandPrefix)
+{
+ const ndn::Name& name = interest.getName();
+ verb = name[commandPrefix.size()];
+ const ndn::Name::Component& parameterComponent = name[commandPrefix.size() + 1];
+
+ ndn::Block rawParameters = parameterComponent.blockFromValue();
+ extractedParameters.wireDecode(rawParameters);
+}
+
+inline void
+extractRibCommandParameters(const ndn::Interest& interest, ndn::Name::Component& verb,
+ ndn::nfd::ControlParameters& extractedParameters)
+{
+ extractParameters(interest, verb, extractedParameters, ndn::Name("/localhost/nfd/rib"));
+}
+
+} // namespace test
+} // namespace nlsr
+
+#endif // NLSR_TEST_CONTROL_COMMANDS_HPP
diff --git a/tests/test-common.hpp b/tests/test-common.hpp
index dc60c90..b996540 100644
--- a/tests/test-common.hpp
+++ b/tests/test-common.hpp
@@ -25,6 +25,7 @@
#include <boost/asio.hpp>
#include <boost/test/unit_test.hpp>
+
#include <ndn-cxx/util/scheduler.hpp>
namespace nlsr {
@@ -46,4 +47,4 @@
} // namespace test
} // namespace nlsr
-#endif
\ No newline at end of file
+#endif // NLSR_TEST_COMMON_HPP
diff --git a/tests/test-fib.cpp b/tests/test-fib.cpp
new file mode 100644
index 0000000..4ade674
--- /dev/null
+++ b/tests/test-fib.cpp
@@ -0,0 +1,311 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014 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"
+#include "control-commands.hpp"
+#include "dummy-face.hpp"
+
+#include "route/fib.hpp"
+
+#include "adjacency-list.hpp"
+#include "conf-parameter.hpp"
+
+namespace nlsr {
+namespace test {
+
+using ndn::DummyFace;
+using ndn::shared_ptr;
+
+class FibFixture : public BaseFixture
+{
+public:
+ FibFixture()
+ : face(ndn::makeDummyFace())
+ , interests(face->m_sentInterests)
+ {
+ INIT_LOGGERS("/tmp", "DEBUG");
+
+ Adjacent neighbor1(router1Name, router1FaceUri, 0, Adjacent::STATUS_ACTIVE, 0, router1FaceId);
+ adjacencies.insert(neighbor1);
+
+ Adjacent neighbor2(router2Name, router2FaceUri, 0, Adjacent::STATUS_ACTIVE, 0, router2FaceId);
+ adjacencies.insert(neighbor2);
+
+ Adjacent neighbor3(router3Name, router3FaceUri, 0, Adjacent::STATUS_ACTIVE, 0, router3FaceId);
+ adjacencies.insert(neighbor3);
+
+ conf.setMaxFacesPerPrefix(2);
+
+ fib = ndn::make_shared<Fib>(ndn::ref(*face), ndn::ref(g_scheduler),ndn::ref(adjacencies),
+ ndn::ref(conf));
+
+ fib->m_faceMap.update(router1FaceUri, router1FaceId);
+ fib->m_faceMap.update(router2FaceUri, router2FaceId);
+ fib->m_faceMap.update(router3FaceUri, router3FaceId);
+ }
+
+public:
+ shared_ptr<ndn::DummyFace> face;
+ shared_ptr<Fib> fib;
+
+ AdjacencyList adjacencies;
+ ConfParameter conf;
+ std::vector<ndn::Interest>& interests;
+
+ static const ndn::Name router1Name;
+ static const ndn::Name router2Name;
+ static const ndn::Name router3Name;
+
+ static const std::string router1FaceUri;
+ static const std::string router2FaceUri;
+ static const std::string router3FaceUri;
+
+ static const uint32_t router1FaceId;
+ static const uint32_t router2FaceId;
+ static const uint32_t router3FaceId;
+};
+
+const ndn::Name FibFixture::router1Name = "/ndn/router1";
+const ndn::Name FibFixture::router2Name = "/ndn/router2";
+const ndn::Name FibFixture::router3Name = "/ndn/router3";
+
+const std::string FibFixture::router1FaceUri = "uri://face1";
+const std::string FibFixture::router2FaceUri = "uri://face2";
+const std::string FibFixture::router3FaceUri = "uri://face3";
+
+const uint32_t FibFixture::router1FaceId = 1;
+const uint32_t FibFixture::router2FaceId = 2;
+const uint32_t FibFixture::router3FaceId = 3;
+
+BOOST_FIXTURE_TEST_SUITE(TestFib, FibFixture)
+
+BOOST_AUTO_TEST_CASE(NextHopsAdd)
+{
+ NextHop hop1(router1FaceUri, 10);
+ NextHop hop2(router2FaceUri, 20);
+
+ NexthopList hops;
+ hops.addNextHop(hop1);
+ hops.addNextHop(hop2);
+
+ fib->update("/ndn/name", hops);
+ face->processEvents(ndn::time::milliseconds(1));
+
+ // Should register faces 1 and 2 for /ndn/name
+ BOOST_REQUIRE_EQUAL(interests.size(), 2);
+
+ ndn::nfd::ControlParameters extractedParameters;
+ ndn::Name::Component verb;
+ std::vector<ndn::Interest>::iterator it = interests.begin();
+
+ extractRibCommandParameters(*it, verb, extractedParameters);
+
+ BOOST_CHECK(extractedParameters.getName() == "/ndn/name" &&
+ extractedParameters.getFaceId() == router1FaceId &&
+ verb == ndn::Name::Component("register"));
+
+ ++it;
+ extractRibCommandParameters(*it, verb, extractedParameters);
+
+ BOOST_CHECK(extractedParameters.getName() == "/ndn/name" &&
+ extractedParameters.getFaceId() == router2FaceId &&
+ verb == ndn::Name::Component("register"));
+}
+
+
+BOOST_AUTO_TEST_CASE(NextHopsNoChange)
+{
+ NextHop hop1(router1FaceUri, 10);
+ NextHop hop2(router2FaceUri, 20);
+
+ NexthopList oldHops;
+ oldHops.addNextHop(hop1);
+ oldHops.addNextHop(hop2);
+
+ fib->update("/ndn/name", oldHops);
+ face->processEvents(ndn::time::milliseconds(1));
+
+ BOOST_REQUIRE_EQUAL(interests.size(), 2);
+ interests.clear();
+
+ fib->update("/ndn/name", oldHops);
+ face->processEvents(ndn::time::milliseconds(1));
+
+ // Should register face 1 and 2 for /ndn/name
+ BOOST_REQUIRE_EQUAL(interests.size(), 2);
+
+ ndn::nfd::ControlParameters extractedParameters;
+ ndn::Name::Component verb;
+ std::vector<ndn::Interest>::iterator it = interests.begin();
+
+ extractRibCommandParameters(*it, verb, extractedParameters);
+
+ BOOST_CHECK(extractedParameters.getName() == "/ndn/name" &&
+ extractedParameters.getFaceId() == router1FaceId &&
+ verb == ndn::Name::Component("register"));
+
+ ++it;
+ extractRibCommandParameters(*it, verb, extractedParameters);
+
+ BOOST_CHECK(extractedParameters.getName() == "/ndn/name" &&
+ extractedParameters.getFaceId() == router2FaceId &&
+ verb == ndn::Name::Component("register"));
+}
+
+BOOST_AUTO_TEST_CASE(NextHopsRemoveAll)
+{
+ NextHop hop1(router1FaceUri, 10);
+ NextHop hop2(router2FaceUri, 20);
+
+ NexthopList oldHops;
+ oldHops.addNextHop(hop1);
+ oldHops.addNextHop(hop2);
+
+ fib->update("/ndn/name", oldHops);
+ face->processEvents(ndn::time::milliseconds(1));
+
+ BOOST_REQUIRE_EQUAL(interests.size(), 2);
+ interests.clear();
+
+ NexthopList empty;
+
+ fib->update("/ndn/name", empty);
+ face->processEvents(ndn::time::milliseconds(1));
+
+ // Should unregister faces 1 and 2 for /ndn/name
+ BOOST_CHECK_EQUAL(interests.size(), 2);
+
+ ndn::nfd::ControlParameters extractedParameters;
+ ndn::Name::Component verb;
+ std::vector<ndn::Interest>::iterator it = interests.begin();
+
+ extractRibCommandParameters(*it, verb, extractedParameters);
+
+ BOOST_CHECK(extractedParameters.getName() == "/ndn/name" &&
+ extractedParameters.getFaceId() == router1FaceId &&
+ verb == ndn::Name::Component("unregister"));
+
+ ++it;
+ extractRibCommandParameters(*it, verb, extractedParameters);
+
+ BOOST_CHECK(extractedParameters.getName() == "/ndn/name" &&
+ extractedParameters.getFaceId() == router2FaceId &&
+ verb == ndn::Name::Component("unregister"));
+}
+
+BOOST_AUTO_TEST_CASE(NextHopsMaxPrefixes)
+{
+ NextHop hop1(router1FaceUri, 10);
+ NextHop hop2(router2FaceUri, 20);
+ NextHop hop3(router3FaceUri, 30);
+
+ NexthopList hops;
+ hops.addNextHop(hop1);
+ hops.addNextHop(hop2);
+ hops.addNextHop(hop3);
+
+ fib->update("/ndn/name", hops);
+ face->processEvents(ndn::time::milliseconds(1));
+
+ // Should only register faces 1 and 2 for /ndn/name
+ BOOST_CHECK_EQUAL(interests.size(), 2);
+
+ ndn::nfd::ControlParameters extractedParameters;
+ ndn::Name::Component verb;
+ std::vector<ndn::Interest>::iterator it = interests.begin();
+
+ extractRibCommandParameters(*it, verb, extractedParameters);
+
+ BOOST_CHECK(extractedParameters.getName() == "/ndn/name" &&
+ extractedParameters.getFaceId() == router1FaceId &&
+ verb == ndn::Name::Component("register"));
+
+ ++it;
+ extractRibCommandParameters(*it, verb, extractedParameters);
+
+ BOOST_CHECK(extractedParameters.getName() == "/ndn/name" &&
+ extractedParameters.getFaceId() == router2FaceId &&
+ verb == ndn::Name::Component("register"));
+}
+
+BOOST_AUTO_TEST_CASE(NextHopsMaxPrefixesAfterRecalculation)
+{
+ NextHop hop1(router1FaceUri, 10);
+ NextHop hop2(router2FaceUri, 20);
+
+ NexthopList hops;
+ hops.addNextHop(hop1);
+ hops.addNextHop(hop2);
+
+ fib->update("/ndn/name", hops);
+ face->processEvents(ndn::time::milliseconds(1));
+
+ // FIB
+ // Name NextHops
+ // /ndn/name (faceId=1, cost=10), (faceId=2, cost=20)
+ BOOST_REQUIRE_EQUAL(interests.size(), 2);
+ interests.clear();
+
+ // Routing table is recalculated; a new more optimal path is found
+ NextHop hop3(router3FaceUri, 5);
+ hops.addNextHop(hop3);
+
+ fib->update("/ndn/name", hops);
+ face->processEvents(ndn::time::milliseconds(1));
+
+ // To maintain a max 2 face requirement, face 3 should be registered and face 2 should be
+ // unregistered. Face 1 will also be re-registered.
+ //
+ // FIB
+ // Name NextHops
+ // /ndn/name (faceId=3, cost=5), (faceId=1, cost=10)
+
+ BOOST_CHECK_EQUAL(interests.size(), 3);
+
+ ndn::nfd::ControlParameters extractedParameters;
+ ndn::Name::Component verb;
+ std::vector<ndn::Interest>::iterator it = interests.begin();
+
+ extractRibCommandParameters(*it, verb, extractedParameters);
+
+ BOOST_CHECK(extractedParameters.getName() == "/ndn/name" &&
+ extractedParameters.getFaceId() == router3FaceId &&
+ verb == ndn::Name::Component("register"));
+
+ ++it;
+ extractRibCommandParameters(*it, verb, extractedParameters);
+
+ BOOST_CHECK(extractedParameters.getName() == "/ndn/name" &&
+ extractedParameters.getFaceId() == router1FaceId &&
+ verb == ndn::Name::Component("register"));
+
+ ++it;
+ extractRibCommandParameters(*it, verb, extractedParameters);
+
+ BOOST_CHECK(extractedParameters.getName() == "/ndn/name" &&
+ extractedParameters.getFaceId() == router2FaceId &&
+ verb == ndn::Name::Component("unregister"));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} //namespace test
+} //namespace nlsr