rib: move entire subdir to daemon/rib
Refs: #4528
Change-Id: I7de03631ddef0f014f12f979373aa449f42486d1
diff --git a/daemon/rib/fib-update.cpp b/daemon/rib/fib-update.cpp
new file mode 100644
index 0000000..25e2a62
--- /dev/null
+++ b/daemon/rib/fib-update.cpp
@@ -0,0 +1,57 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "fib-update.hpp"
+
+namespace nfd {
+namespace rib {
+
+FibUpdate
+FibUpdate::createAddUpdate(const Name& name, const uint64_t faceId, const uint64_t cost)
+{
+ FibUpdate update;
+
+ update.name = name;
+ update.faceId = faceId;
+ update.cost = cost;
+ update.action = ADD_NEXTHOP;
+
+ return update;
+}
+
+FibUpdate
+FibUpdate::createRemoveUpdate(const Name& name, const uint64_t faceId)
+{
+ FibUpdate update;
+
+ update.name = name;
+ update.faceId = faceId;
+ update.action = REMOVE_NEXTHOP;
+
+ return update;
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/daemon/rib/fib-update.hpp b/daemon/rib/fib-update.hpp
new file mode 100644
index 0000000..5339a37
--- /dev/null
+++ b/daemon/rib/fib-update.hpp
@@ -0,0 +1,96 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_RIB_FIB_UPDATE_HPP
+#define NFD_DAEMON_RIB_FIB_UPDATE_HPP
+
+#include "core/common.hpp"
+
+namespace nfd {
+namespace rib {
+
+/** \class FibUpdate
+ * \brief represents a FIB update
+ */
+class FibUpdate
+{
+public:
+ FibUpdate()
+ : faceId(0)
+ , cost(0)
+ {
+ }
+
+ bool
+ operator==(const FibUpdate& other) const
+ {
+ return (this->name == other.name &&
+ this->faceId == other.faceId &&
+ this->cost == other.cost &&
+ this->action == other.action);
+ }
+
+ static FibUpdate
+ createAddUpdate(const Name& name, const uint64_t faceId, const uint64_t cost);
+
+ static FibUpdate
+ createRemoveUpdate(const Name& name, const uint64_t faceId);
+
+ enum Action {
+ ADD_NEXTHOP = 0,
+ REMOVE_NEXTHOP = 1
+ };
+
+public:
+ Name name;
+ uint64_t faceId;
+ uint64_t cost;
+ Action action;
+};
+
+inline std::ostream&
+operator<<(std::ostream& os, const FibUpdate& update)
+{
+ os << "FibUpdate("
+ << " Name: " << update.name << ", "
+ << "faceId: " << update.faceId << ", ";
+
+ if (update.action == FibUpdate::ADD_NEXTHOP) {
+ os << "cost: " << update.cost << ", "
+ << "action: ADD_NEXTHOP";
+ }
+ else {
+ os << "action: REMOVE_NEXTHOP";
+ }
+
+ os << ")";
+
+ return os;
+}
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_DAEMON_RIB_FIB_UPDATE_HPP
diff --git a/daemon/rib/fib-updater.cpp b/daemon/rib/fib-updater.cpp
new file mode 100644
index 0000000..574b5f9
--- /dev/null
+++ b/daemon/rib/fib-updater.cpp
@@ -0,0 +1,716 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "fib-updater.hpp"
+#include "core/logger.hpp"
+
+#include <ndn-cxx/mgmt/nfd/control-parameters.hpp>
+
+namespace nfd {
+namespace rib {
+
+NFD_LOG_INIT(FibUpdater);
+
+using ndn::nfd::ControlParameters;
+
+const unsigned int FibUpdater::MAX_NUM_TIMEOUTS = 10;
+const uint32_t FibUpdater::ERROR_FACE_NOT_FOUND = 410;
+
+FibUpdater::FibUpdater(Rib& rib, ndn::nfd::Controller& controller)
+ : m_rib(rib)
+ , m_controller(controller)
+{
+ rib.setFibUpdater(this);
+}
+
+void
+FibUpdater::computeAndSendFibUpdates(const RibUpdateBatch& batch,
+ const FibUpdateSuccessCallback& onSuccess,
+ const FibUpdateFailureCallback& onFailure)
+{
+ m_batchFaceId = batch.getFaceId();
+
+ // Erase previously calculated inherited routes
+ m_inheritedRoutes.clear();
+
+ // Erase previously calculated FIB updates
+ m_updatesForBatchFaceId.clear();
+ m_updatesForNonBatchFaceId.clear();
+
+ computeUpdates(batch);
+
+ sendUpdatesForBatchFaceId(onSuccess, onFailure);
+}
+
+void
+FibUpdater::computeUpdates(const RibUpdateBatch& batch)
+{
+ NFD_LOG_DEBUG("Computing updates for batch with faceID: " << batch.getFaceId());
+
+ // Compute updates and add to m_fibUpdates
+ for (const RibUpdate& update : batch) {
+ switch (update.getAction()) {
+ case RibUpdate::REGISTER:
+ computeUpdatesForRegistration(update);
+ break;
+ case RibUpdate::UNREGISTER:
+ computeUpdatesForUnregistration(update);
+ break;
+ case RibUpdate::REMOVE_FACE:
+ computeUpdatesForUnregistration(update);
+
+ // Do not apply updates with the same face ID as the destroyed face
+ // since they will be rejected by the FIB
+ m_updatesForBatchFaceId.clear();
+ break;
+ }
+ }
+}
+
+void
+FibUpdater::computeUpdatesForRegistration(const RibUpdate& update)
+{
+ const Name& prefix = update.getName();
+ const Route& route = update.getRoute();
+
+ Rib::const_iterator it = m_rib.find(prefix);
+
+ // Name prefix exists
+ if (it != m_rib.end()) {
+ shared_ptr<const RibEntry> entry(it->second);
+
+ RibEntry::const_iterator existingRoute = entry->findRoute(route);
+
+ // Route will be new
+ if (existingRoute == entry->end()) {
+ // Will the new route change the namespace's capture flag?
+ bool willCaptureBeTurnedOn = (entry->hasCapture() == false && route.isRibCapture());
+
+ createFibUpdatesForNewRoute(*entry, route, willCaptureBeTurnedOn);
+ }
+ else {
+ // Route already exists
+ RibEntry entryCopy = *entry;
+
+ Route& routeToUpdate = *(entryCopy.findRoute(route));
+
+ routeToUpdate.flags = route.flags;
+ routeToUpdate.cost = route.cost;
+ routeToUpdate.expires = route.expires;
+
+ createFibUpdatesForUpdatedRoute(entryCopy, route, *existingRoute);
+ }
+ }
+ else {
+ // New name in RIB
+ // Find prefix's parent
+ shared_ptr<RibEntry> parent = m_rib.findParent(prefix);
+
+ Rib::RibEntryList descendants = m_rib.findDescendantsForNonInsertedName(prefix);
+ Rib::RibEntryList children;
+
+ for (const auto& descendant : descendants) {
+ // If the child has the same parent as the new entry,
+ // the new entry must be the child's new parent
+ if (descendant->getParent() == parent) {
+ children.push_back(descendant);
+ }
+ }
+
+ createFibUpdatesForNewRibEntry(prefix, route, children);
+ }
+}
+
+void
+FibUpdater::computeUpdatesForUnregistration(const RibUpdate& update)
+{
+ const Name& prefix = update.getName();
+ const Route& route = update.getRoute();
+
+ Rib::const_iterator ribIt = m_rib.find(prefix);
+
+ // Name prefix exists
+ if (ribIt != m_rib.end()) {
+ shared_ptr<const RibEntry> entry(ribIt->second);
+
+ const bool hadCapture = entry->hasCapture();
+
+ RibEntry::const_iterator existing = entry->findRoute(route);
+
+ if (existing != entry->end()) {
+ RibEntry temp = *entry;
+
+ // Erase route in temp entry
+ temp.eraseRoute(route);
+
+ const bool captureWasTurnedOff = (hadCapture && !temp.hasCapture());
+
+ createFibUpdatesForErasedRoute(temp, *existing, captureWasTurnedOff);
+
+ // The RibEntry still has the face ID; need to update FIB
+ // with lowest cost for the same face instead of removing the face from the FIB
+ const Route* next = entry->getRouteWithSecondLowestCostByFaceId(route.faceId);
+
+ if (next != nullptr) {
+ createFibUpdatesForNewRoute(temp, *next, false);
+ }
+
+ // The RibEntry will be empty after this removal
+ if (entry->getNRoutes() == 1) {
+ createFibUpdatesForErasedRibEntry(*entry);
+ }
+ }
+ }
+}
+
+void
+FibUpdater::sendUpdates(const FibUpdateList& updates,
+ const FibUpdateSuccessCallback& onSuccess,
+ const FibUpdateFailureCallback& onFailure)
+{
+ std::string updateString = (updates.size() == 1) ? " update" : " updates";
+ NFD_LOG_DEBUG("Applying " << updates.size() << updateString << " to FIB");
+
+ for (const FibUpdate& update : updates) {
+ NFD_LOG_DEBUG("Sending FIB update: " << update);
+
+ if (update.action == FibUpdate::ADD_NEXTHOP) {
+ sendAddNextHopUpdate(update, onSuccess, onFailure);
+ }
+ else if (update.action == FibUpdate::REMOVE_NEXTHOP) {
+ sendRemoveNextHopUpdate(update, onSuccess, onFailure);
+ }
+ }
+}
+
+void
+FibUpdater::sendUpdatesForBatchFaceId(const FibUpdateSuccessCallback& onSuccess,
+ const FibUpdateFailureCallback& onFailure)
+{
+ if (m_updatesForBatchFaceId.size() > 0) {
+ sendUpdates(m_updatesForBatchFaceId, onSuccess, onFailure);
+ }
+ else {
+ sendUpdatesForNonBatchFaceId(onSuccess, onFailure);
+ }
+}
+
+void
+FibUpdater::sendUpdatesForNonBatchFaceId(const FibUpdateSuccessCallback& onSuccess,
+ const FibUpdateFailureCallback& onFailure)
+{
+ if (m_updatesForNonBatchFaceId.size() > 0) {
+ sendUpdates(m_updatesForNonBatchFaceId, onSuccess, onFailure);
+ }
+ else {
+ onSuccess(m_inheritedRoutes);
+ }
+}
+
+void
+FibUpdater::sendAddNextHopUpdate(const FibUpdate& update,
+ const FibUpdateSuccessCallback& onSuccess,
+ const FibUpdateFailureCallback& onFailure,
+ uint32_t nTimeouts)
+{
+ m_controller.start<ndn::nfd::FibAddNextHopCommand>(
+ ControlParameters()
+ .setName(update.name)
+ .setFaceId(update.faceId)
+ .setCost(update.cost),
+ bind(&FibUpdater::onUpdateSuccess, this, update, onSuccess, onFailure),
+ bind(&FibUpdater::onUpdateError, this, update, onSuccess, onFailure, _1, nTimeouts));
+}
+
+void
+FibUpdater::sendRemoveNextHopUpdate(const FibUpdate& update,
+ const FibUpdateSuccessCallback& onSuccess,
+ const FibUpdateFailureCallback& onFailure,
+ uint32_t nTimeouts)
+{
+ m_controller.start<ndn::nfd::FibRemoveNextHopCommand>(
+ ControlParameters()
+ .setName(update.name)
+ .setFaceId(update.faceId),
+ bind(&FibUpdater::onUpdateSuccess, this, update, onSuccess, onFailure),
+ bind(&FibUpdater::onUpdateError, this, update, onSuccess, onFailure, _1, nTimeouts));
+}
+
+void
+FibUpdater::onUpdateSuccess(const FibUpdate update,
+ const FibUpdateSuccessCallback& onSuccess,
+ const FibUpdateFailureCallback& onFailure)
+{
+ if (update.faceId == m_batchFaceId) {
+ m_updatesForBatchFaceId.remove(update);
+
+ if (m_updatesForBatchFaceId.size() == 0) {
+ sendUpdatesForNonBatchFaceId(onSuccess, onFailure);
+ }
+ }
+ else {
+ m_updatesForNonBatchFaceId.remove(update);
+
+ if (m_updatesForNonBatchFaceId.size() == 0) {
+ onSuccess(m_inheritedRoutes);
+ }
+ }
+}
+
+void
+FibUpdater::onUpdateError(const FibUpdate update,
+ const FibUpdateSuccessCallback& onSuccess,
+ const FibUpdateFailureCallback& onFailure,
+ const ndn::nfd::ControlResponse& response, uint32_t nTimeouts)
+{
+ uint32_t code = response.getCode();
+ NFD_LOG_DEBUG("Failed to apply " << update <<
+ " (code: " << code << ", error: " << response.getText() << ")");
+
+ if (code == ndn::nfd::Controller::ERROR_TIMEOUT && nTimeouts < MAX_NUM_TIMEOUTS) {
+ sendAddNextHopUpdate(update, onSuccess, onFailure, ++nTimeouts);
+ }
+ else if (code == ERROR_FACE_NOT_FOUND) {
+ if (update.faceId == m_batchFaceId) {
+ onFailure(code, response.getText());
+ }
+ else {
+ m_updatesForNonBatchFaceId.remove(update);
+
+ if (m_updatesForNonBatchFaceId.size() == 0) {
+ onSuccess(m_inheritedRoutes);
+ }
+ }
+ }
+ else {
+ NDN_THROW(Error("Non-recoverable error: " + response.getText() + " code: " + to_string(code)));
+ }
+}
+
+void
+FibUpdater::addFibUpdate(FibUpdate update)
+{
+ FibUpdateList& updates = (update.faceId == m_batchFaceId) ? m_updatesForBatchFaceId :
+ m_updatesForNonBatchFaceId;
+
+ // If an update with the same name and route already exists,
+ // replace it
+ FibUpdateList::iterator it = std::find_if(updates.begin(), updates.end(),
+ [&update] (const FibUpdate& other) {
+ return update.name == other.name && update.faceId == other.faceId;
+ });
+
+ if (it != updates.end()) {
+ FibUpdate& existingUpdate = *it;
+ existingUpdate.action = update.action;
+ existingUpdate.cost = update.cost;
+ }
+ else {
+ updates.push_back(update);
+ }
+}
+
+void
+FibUpdater::addInheritedRoutes(const RibEntry& entry, const Rib::RouteSet& routesToAdd)
+{
+ for (const Route& route : routesToAdd) {
+ // Don't add an ancestor faceId if the namespace has an entry for that faceId
+ if (!entry.hasFaceId(route.faceId)) {
+ // Create a record of the inherited route so it can be added to the RIB later
+ addInheritedRoute(entry.getName(), route);
+
+ addFibUpdate(FibUpdate::createAddUpdate(entry.getName(), route.faceId, route.cost));
+ }
+ }
+}
+
+void
+FibUpdater::addInheritedRoutes(const Name& name, const Rib::RouteSet& routesToAdd,
+ const Route& ignore)
+{
+ for (const Route& route : routesToAdd) {
+ if (route.faceId != ignore.faceId) {
+ // Create a record of the inherited route so it can be added to the RIB later
+ addInheritedRoute(name, route);
+
+ addFibUpdate(FibUpdate::createAddUpdate(name, route.faceId, route.cost));
+ }
+ }
+}
+
+void
+FibUpdater::removeInheritedRoutes(const RibEntry& entry, const Rib::Rib::RouteSet& routesToRemove)
+{
+ for (const Route& route : routesToRemove) {
+ // Only remove if the route has been inherited
+ if (entry.hasInheritedRoute(route)) {
+ removeInheritedRoute(entry.getName(), route);
+ addFibUpdate(FibUpdate::createRemoveUpdate(entry.getName(), route.faceId));
+ }
+ }
+}
+
+void
+FibUpdater::createFibUpdatesForNewRibEntry(const Name& name, const Route& route,
+ const Rib::RibEntryList& children)
+{
+ // Create FIB update for new entry
+ addFibUpdate(FibUpdate::createAddUpdate(name, route.faceId, route.cost));
+
+ // No flags are set
+ if (!route.isChildInherit() && !route.isRibCapture()) {
+ // Add ancestor routes to self
+ addInheritedRoutes(name, m_rib.getAncestorRoutes(name), route);
+ }
+ else if (route.isChildInherit() && route.isRibCapture()) {
+ // Add route to children
+ Rib::RouteSet routesToAdd;
+ routesToAdd.insert(route);
+
+ // Remove routes blocked by capture and add self to children
+ modifyChildrensInheritedRoutes(children, routesToAdd, m_rib.getAncestorRoutes(name));
+ }
+ else if (route.isChildInherit()) {
+ Rib::RouteSet ancestorRoutes = m_rib.getAncestorRoutes(name);
+
+ // Add ancestor routes to self
+ addInheritedRoutes(name, ancestorRoutes, route);
+
+ // If there is an ancestor route which is the same as the new route, replace it
+ // with the new route
+ Rib::RouteSet::iterator it = ancestorRoutes.find(route);
+
+ // There is a route that needs to be overwritten, erase and then replace
+ if (it != ancestorRoutes.end()) {
+ ancestorRoutes.erase(it);
+ }
+
+ // Add new route to ancestor list so it can be added to children
+ ancestorRoutes.insert(route);
+
+ // Add ancestor routes to children
+ modifyChildrensInheritedRoutes(children, ancestorRoutes, Rib::RouteSet());
+ }
+ else if (route.isRibCapture()) {
+ // Remove routes blocked by capture
+ modifyChildrensInheritedRoutes(children, Rib::RouteSet(), m_rib.getAncestorRoutes(name));
+ }
+}
+
+void
+FibUpdater::createFibUpdatesForNewRoute(const RibEntry& entry, const Route& route,
+ bool captureWasTurnedOn)
+{
+ // Only update if the new route has a lower cost than a previously installed route
+ const Route* prevRoute = entry.getRouteWithLowestCostAndChildInheritByFaceId(route.faceId);
+
+ Rib::RouteSet routesToAdd;
+ if (route.isChildInherit()) {
+ // Add to children if this new route doesn't override a previous lower cost, or
+ // add to children if this new route is lower cost than a previous route.
+ // Less than equal, since entry may find this route
+ if (prevRoute == nullptr || route.cost <= prevRoute->cost) {
+ // Add self to children
+ routesToAdd.insert(route);
+ }
+ }
+
+ Rib::RouteSet routesToRemove;
+ if (captureWasTurnedOn) {
+ // Capture flag on
+ routesToRemove = m_rib.getAncestorRoutes(entry);
+
+ // Remove ancestor routes from self
+ removeInheritedRoutes(entry, routesToRemove);
+ }
+
+ modifyChildrensInheritedRoutes(entry.getChildren(), routesToAdd, routesToRemove);
+
+ // If another route with same faceId and lower cost exists, don't update.
+ // Must be done last so that add updates replace removal updates
+ // Create FIB update for new entry
+ const Route* other = entry.getRouteWithLowestCostByFaceId(route.faceId);
+
+ if (other == nullptr || route.cost <= other->cost) {
+ addFibUpdate(FibUpdate::createAddUpdate(entry.getName(), route.faceId, route.cost));
+ }
+}
+
+void
+FibUpdater::createFibUpdatesForUpdatedRoute(const RibEntry& entry, const Route& route,
+ const Route& existingRoute)
+{
+ const bool costDidChange = (route.cost != existingRoute.cost);
+
+ // Look for an installed route with the lowest cost and child inherit set
+ const Route* prevRoute = entry.getRouteWithLowestCostAndChildInheritByFaceId(route.faceId);
+
+ // No flags changed and cost didn't change, no change in FIB
+ if (route.flags == existingRoute.flags && !costDidChange) {
+ return;
+ }
+
+ // Cost changed so create update for the entry itself
+ if (costDidChange) {
+ // Create update if this route's cost is lower than other routes
+ if (route.cost <= entry.getRouteWithLowestCostByFaceId(route.faceId)->cost) {
+ // Create FIB update for the updated entry
+ addFibUpdate(FibUpdate::createAddUpdate(entry.getName(), route.faceId, route.cost));
+ }
+ else if (existingRoute.cost < entry.getRouteWithLowestCostByFaceId(route.faceId)->cost) {
+ // Create update if this route used to be the lowest route but is no longer
+ // the lowest cost route.
+ addFibUpdate(FibUpdate::createAddUpdate(entry.getName(), prevRoute->faceId, prevRoute->cost));
+ }
+
+ // If another route with same faceId and lower cost and ChildInherit exists,
+ // don't update children.
+ if (prevRoute == nullptr || route.cost <= prevRoute->cost) {
+ // If no flags changed but child inheritance is set, need to update children
+ // with new cost
+ if ((route.flags == existingRoute.flags) && route.isChildInherit()) {
+ // Add self to children
+ Rib::RouteSet routesToAdd;
+ routesToAdd.insert(route);
+ modifyChildrensInheritedRoutes(entry.getChildren(), routesToAdd, Rib::RouteSet());
+
+ return;
+ }
+ }
+ }
+
+ // Child inherit was turned on
+ if (!existingRoute.isChildInherit() && route.isChildInherit()) {
+ // If another route with same faceId and lower cost and ChildInherit exists,
+ // don't update children.
+ if (prevRoute == nullptr || route.cost <= prevRoute->cost) {
+ // Add self to children
+ Rib::RouteSet routesToAdd;
+ routesToAdd.insert(route);
+ modifyChildrensInheritedRoutes(entry.getChildren(), routesToAdd, Rib::RouteSet());
+ }
+ } // Child inherit was turned off
+ else if (existingRoute.isChildInherit() && !route.isChildInherit()) {
+ // Remove self from children
+ Rib::RouteSet routesToRemove;
+ routesToRemove.insert(route);
+
+ Rib::RouteSet routesToAdd;
+ // If another route with same faceId and ChildInherit exists, update children with this route.
+ if (prevRoute != nullptr) {
+ routesToAdd.insert(*prevRoute);
+ }
+ else {
+ // Look for an ancestor that was blocked previously
+ const Rib::RouteSet ancestorRoutes = m_rib.getAncestorRoutes(entry);
+ Rib::RouteSet::iterator it = ancestorRoutes.find(route);
+
+ // If an ancestor is found, add it to children
+ if (it != ancestorRoutes.end()) {
+ routesToAdd.insert(*it);
+ }
+ }
+
+ modifyChildrensInheritedRoutes(entry.getChildren(), routesToAdd, routesToRemove);
+ }
+
+ // Capture was turned on
+ if (!existingRoute.isRibCapture() && route.isRibCapture()) {
+ Rib::RouteSet ancestorRoutes = m_rib.getAncestorRoutes(entry);
+
+ // Remove ancestor routes from self
+ removeInheritedRoutes(entry, ancestorRoutes);
+
+ // Remove ancestor routes from children
+ modifyChildrensInheritedRoutes(entry.getChildren(), Rib::RouteSet(), ancestorRoutes);
+ } // Capture was turned off
+ else if (existingRoute.isRibCapture() && !route.isRibCapture()) {
+ Rib::RouteSet ancestorRoutes = m_rib.getAncestorRoutes(entry);
+
+ // Add ancestor routes to self
+ addInheritedRoutes(entry, ancestorRoutes);
+
+ // Add ancestor routes to children
+ modifyChildrensInheritedRoutes(entry.getChildren(), ancestorRoutes, Rib::RouteSet());
+ }
+}
+
+void
+FibUpdater::createFibUpdatesForErasedRoute(const RibEntry& entry, const Route& route,
+ const bool captureWasTurnedOff)
+{
+ addFibUpdate(FibUpdate::createRemoveUpdate(entry.getName(), route.faceId));
+
+ if (route.isChildInherit() && route.isRibCapture()) {
+ // Remove self from children
+ Rib::RouteSet routesToRemove;
+ routesToRemove.insert(route);
+
+ // If capture is turned off for the route and another route is installed in the RibEntry,
+ // add ancestors to self
+ Rib::RouteSet routesToAdd;
+ if (captureWasTurnedOff && entry.getNRoutes() != 0) {
+ // Look for an ancestors that were blocked previously
+ routesToAdd = m_rib.getAncestorRoutes(entry);
+
+ // Add ancestor routes to self
+ addInheritedRoutes(entry, routesToAdd);
+ }
+
+ modifyChildrensInheritedRoutes(entry.getChildren(), routesToAdd, routesToRemove);
+ }
+ else if (route.isChildInherit()) {
+ // If not blocked by capture, add inherited routes to children
+ Rib::RouteSet routesToAdd;
+ if (!entry.hasCapture()) {
+ routesToAdd = m_rib.getAncestorRoutes(entry);
+ }
+
+ Rib::RouteSet routesToRemove;
+ routesToRemove.insert(route);
+
+ // Add ancestor routes to children
+ modifyChildrensInheritedRoutes(entry.getChildren(), routesToAdd, routesToRemove);
+ }
+ else if (route.isRibCapture()) {
+ // If capture is turned off for the route and another route is installed in the RibEntry,
+ // add ancestors to self
+ Rib::RouteSet routesToAdd;
+ if (captureWasTurnedOff && entry.getNRoutes() != 0) {
+ // Look for an ancestors that were blocked previously
+ routesToAdd = m_rib.getAncestorRoutes(entry);
+
+ // Add ancestor routes to self
+ addInheritedRoutes(entry, routesToAdd);
+ }
+
+ modifyChildrensInheritedRoutes(entry.getChildren(), routesToAdd, Rib::RouteSet());
+ }
+
+ // Need to check if the removed route was blocking an inherited route
+ Rib::RouteSet ancestorRoutes = m_rib.getAncestorRoutes(entry);
+
+ // If the current entry has capture set or is pending removal, don't add inherited route
+ if (!entry.hasCapture() && entry.getNRoutes() != 0) {
+ // If there is an ancestor route which is the same as the erased route, add that route
+ // to the current entry
+ Rib::RouteSet::iterator it = ancestorRoutes.find(route);
+
+ if (it != ancestorRoutes.end()) {
+ addInheritedRoute(entry.getName(), *it);
+ addFibUpdate(FibUpdate::createAddUpdate(entry.getName(), it->faceId, it->cost));
+ }
+ }
+}
+
+void
+FibUpdater::createFibUpdatesForErasedRibEntry(const RibEntry& entry)
+{
+ for (const Route& route : entry.getInheritedRoutes()) {
+ addFibUpdate(FibUpdate::createRemoveUpdate(entry.getName(), route.faceId));
+ }
+}
+
+void
+FibUpdater::modifyChildrensInheritedRoutes(const Rib::RibEntryList& children,
+ const Rib::RouteSet& routesToAdd,
+ const Rib::RouteSet& routesToRemove)
+{
+ for (const auto& child : children) {
+ traverseSubTree(*child, routesToAdd, routesToRemove);
+ }
+}
+
+void
+FibUpdater::traverseSubTree(const RibEntry& entry, Rib::Rib::RouteSet routesToAdd,
+ Rib::Rib::RouteSet routesToRemove)
+{
+ // If a route on the namespace has the capture flag set, ignore self and children
+ if (entry.hasCapture()) {
+ return;
+ }
+
+ // Remove inherited routes from current namespace
+ for (auto removeIt = routesToRemove.begin(); removeIt != routesToRemove.end(); ) {
+ // If a route on the namespace has the same face ID and child inheritance set,
+ // ignore this route
+ if (entry.hasChildInheritOnFaceId(removeIt->faceId)) {
+ removeIt = routesToRemove.erase(removeIt);
+ continue;
+ }
+
+ // Only remove route if it removes an existing inherited route
+ if (entry.hasInheritedRoute(*removeIt)) {
+ removeInheritedRoute(entry.getName(), *removeIt);
+ addFibUpdate(FibUpdate::createRemoveUpdate(entry.getName(), removeIt->faceId));
+ }
+
+ ++removeIt;
+ }
+
+ // Add inherited routes to current namespace
+ for (auto addIt = routesToAdd.begin(); addIt != routesToAdd.end(); ) {
+ // If a route on the namespace has the same face ID and child inherit set, ignore this face
+ if (entry.hasChildInheritOnFaceId(addIt->faceId)) {
+ addIt = routesToAdd.erase(addIt);
+ continue;
+ }
+
+ // Only add route if it does not override an existing route
+ if (!entry.hasFaceId(addIt->faceId)) {
+ addInheritedRoute(entry.getName(), *addIt);
+ addFibUpdate(FibUpdate::createAddUpdate(entry.getName(), addIt->faceId, addIt->cost));
+ }
+
+ ++addIt;
+ }
+
+ modifyChildrensInheritedRoutes(entry.getChildren(), routesToAdd, routesToRemove);
+}
+
+void
+FibUpdater::addInheritedRoute(const Name& name, const Route& route)
+{
+ RibUpdate update;
+ update.setAction(RibUpdate::REGISTER)
+ .setName(name)
+ .setRoute(route);
+
+ m_inheritedRoutes.push_back(update);
+}
+
+void
+FibUpdater::removeInheritedRoute(const Name& name, const Route& route)
+{
+ RibUpdate update;
+ update.setAction(RibUpdate::UNREGISTER)
+ .setName(name)
+ .setRoute(route);
+
+ m_inheritedRoutes.push_back(update);
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/daemon/rib/fib-updater.hpp b/daemon/rib/fib-updater.hpp
new file mode 100644
index 0000000..a404853
--- /dev/null
+++ b/daemon/rib/fib-updater.hpp
@@ -0,0 +1,275 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_RIB_FIB_UPDATER_HPP
+#define NFD_DAEMON_RIB_FIB_UPDATER_HPP
+
+#include "core/common.hpp"
+#include "fib-update.hpp"
+#include "rib.hpp"
+#include "rib-update-batch.hpp"
+
+#include <ndn-cxx/mgmt/nfd/controller.hpp>
+
+namespace nfd {
+namespace rib {
+
+/** \brief computes FibUpdates based on updates to the RIB and sends them to NFD
+ */
+class FibUpdater : noncopyable
+{
+public:
+ class Error : public std::runtime_error
+ {
+ public:
+ using std::runtime_error::runtime_error;
+ };
+
+public:
+ typedef std::list<FibUpdate> FibUpdateList;
+
+ typedef std::function<void(RibUpdateList inheritedRoutes)> FibUpdateSuccessCallback;
+ typedef std::function<void(uint32_t code, const std::string& error)> FibUpdateFailureCallback;
+
+ FibUpdater(Rib& rib, ndn::nfd::Controller& controller);
+
+ /** \brief computes FibUpdates using the provided RibUpdateBatch and then sends the
+ * updates to NFD's FIB
+ *
+ * \note Caller must guarantee that the previous batch has either succeeded or failed
+ * before calling this method
+ */
+ void
+ computeAndSendFibUpdates(const RibUpdateBatch& batch,
+ const FibUpdateSuccessCallback& onSuccess,
+ const FibUpdateFailureCallback& onFailure);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ /** \brief determines the type of action that will be performed on the RIB and calls the
+ * corresponding computation method
+ */
+ void
+ computeUpdates(const RibUpdateBatch& batch);
+
+ /** \brief sends the passed updates to NFD
+ *
+ * onSuccess or onFailure will be called based on the results in
+ * onUpdateSuccess or onUpdateFailure
+ *
+ * \see FibUpdater::onUpdateSuccess
+ * \see FibUpdater::onUpdateFailure
+ */
+ void
+ sendUpdates(const FibUpdateList& updates,
+ const FibUpdateSuccessCallback& onSuccess,
+ const FibUpdateFailureCallback& onFailure);
+
+ /** \brief sends the updates in m_updatesForBatchFaceId to NFD if any exist,
+ * otherwise calls FibUpdater::sendUpdatesForNonBatchFaceId.
+ */
+ void
+ sendUpdatesForBatchFaceId(const FibUpdateSuccessCallback& onSuccess,
+ const FibUpdateFailureCallback& onFailure);
+
+ /** \brief sends the updates in m_updatesForNonBatchFaceId to NFD if any exist,
+ * otherwise calls onSuccess.
+ */
+ void
+ sendUpdatesForNonBatchFaceId(const FibUpdateSuccessCallback& onSuccess,
+ const FibUpdateFailureCallback& onFailure);
+
+ /** \brief sends a FibAddNextHopCommand to NFD using the parameters supplied by
+ * the passed update
+ *
+ * \param nTimeouts the number of times this FibUpdate has failed due to timeout
+ */
+ void
+ sendAddNextHopUpdate(const FibUpdate& update,
+ const FibUpdateSuccessCallback& onSuccess,
+ const FibUpdateFailureCallback& onFailure,
+ uint32_t nTimeouts = 0);
+
+ /** \brief sends a FibRemoveNextHopCommand to NFD using the parameters supplied by
+ * the passed update
+ *
+ * \param nTimeouts the number of times this FibUpdate has failed due to timeout
+ */
+ void
+ sendRemoveNextHopUpdate(const FibUpdate& update,
+ const FibUpdateSuccessCallback& onSuccess,
+ const FibUpdateFailureCallback& onFailure,
+ uint32_t nTimeouts = 0);
+
+private:
+ /** \brief calculates the FibUpdates generated by a RIB registration
+ */
+ void
+ computeUpdatesForRegistration(const RibUpdate& update);
+
+ /** \brief calculates the FibUpdates generated by a RIB unregistration
+ */
+ void
+ computeUpdatesForUnregistration(const RibUpdate& update);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ /** \brief callback used by NfdController when a FibAddNextHopCommand or FibRemoveNextHopCommand
+ * is successful.
+ *
+ * If the update has the same Face ID as the batch being processed, the update is
+ * removed from m_updatesForBatchFaceId. If m_updatesForBatchFaceId becomes empty,
+ * the updates with a different Face ID than the batch are sent to NFD.
+ *
+ * If the update has a different Face ID than the batch being processed, the update is
+ * removed from m_updatesForBatchNonFaceId. If m_updatesForBatchNonFaceId becomes empty,
+ * the FIB update process is considered a success.
+ */
+ void
+ onUpdateSuccess(const FibUpdate update,
+ const FibUpdateSuccessCallback& onSuccess,
+ const FibUpdateFailureCallback& onFailure);
+
+ /** \brief callback used by NfdController when a FibAddNextHopCommand or FibRemoveNextHopCommand
+ * is successful.
+ *
+ * If the update has not reached the max number of timeouts allowed, the update
+ * is retried.
+ *
+ * If the update failed due to a non-existent face and the update has the same Face ID
+ * as the update batch, the FIB update process fails.
+ *
+ * If the update failed due to a non-existent face and the update has a different
+ * face than the update batch, the update is not retried and the error is
+ * ignored.
+ *
+ * Otherwise, a non-recoverable error has occurred and an exception is thrown.
+ */
+ void
+ onUpdateError(const FibUpdate update,
+ const FibUpdateSuccessCallback& onSuccess,
+ const FibUpdateFailureCallback& onFailure,
+ const ndn::nfd::ControlResponse& response, uint32_t nTimeouts);
+
+private:
+ /** \brief adds the update to an update list based on its Face ID
+ *
+ * If the update has the same Face ID as the update batch, the update is added
+ * to m_updatesForBatchFaceId.
+ *
+ * Otherwise, the update is added to m_updatesForBatchNonFaceId.
+ */
+ void
+ addFibUpdate(const FibUpdate update);
+
+ /** \brief creates records of the passed routes added to the entry and creates FIB updates
+ */
+ void
+ addInheritedRoutes(const RibEntry& entry, const Rib::RouteSet& routesToAdd);
+
+ /** \brief creates records of the passed routes added to the name and creates FIB updates.
+ * Routes in routesToAdd with the same Face ID as ignore will be not be considered.
+ */
+ void
+ addInheritedRoutes(const Name& name, const Rib::RouteSet& routesToAdd, const Route& ignore);
+
+ /** \brief creates records of the passed routes removed from the entry and creates FIB updates
+ */
+ void
+ removeInheritedRoutes(const RibEntry& entry, const Rib::RouteSet& routesToRemove);
+
+ /** \brief calculates updates for a name that will create a new RIB entry
+ */
+ void
+ createFibUpdatesForNewRibEntry(const Name& name, const Route& route,
+ const Rib::RibEntryList& children);
+
+ /** \brief calculates updates for a new route added to a RIB entry
+ */
+ void
+ createFibUpdatesForNewRoute(const RibEntry& entry, const Route& route,
+ const bool captureWasTurnedOn);
+
+ /** \brief calculates updates for changes to an existing route for a RIB entry
+ */
+ void
+ createFibUpdatesForUpdatedRoute(const RibEntry& entry, const Route& route,
+ const Route& existingRoute);
+
+ /** \brief calculates updates for a an existing route removed from a RIB entry
+ */
+ void
+ createFibUpdatesForErasedRoute(const RibEntry& entry, const Route& route,
+ const bool captureWasTurnedOff);
+
+ /** \brief calculates updates for an entry that will be removed from the RIB
+ */
+ void
+ createFibUpdatesForErasedRibEntry(const RibEntry& entry);
+
+ /** \brief adds and removes passed routes to children's inherited routes
+ */
+ void
+ modifyChildrensInheritedRoutes(const Rib::RibEntryList& children,
+ const Rib::RouteSet& routesToAdd,
+ const Rib::RouteSet& routesToRemove);
+
+ /** \brief traverses the entry's children adding and removing the passed routes
+ */
+ void
+ traverseSubTree(const RibEntry& entry, Rib::RouteSet routesToAdd, Rib::RouteSet routesToRemove);
+
+private:
+ /** \brief creates a record of a calculated inherited route that should be added to the entry
+ */
+ void
+ addInheritedRoute(const Name& name, const Route& route);
+
+ /** \brief creates a record of an existing inherited route that should be removed from the entry
+ */
+ void
+ removeInheritedRoute(const Name& name, const Route& route);
+
+private:
+ const Rib& m_rib;
+ ndn::nfd::Controller& m_controller;
+ uint64_t m_batchFaceId;
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ FibUpdateList m_updatesForBatchFaceId;
+ FibUpdateList m_updatesForNonBatchFaceId;
+
+ /** \brief list of inherited routes generated during FIB update calculation;
+ * passed to the RIB when updates are completed successfully
+ */
+ RibUpdateList m_inheritedRoutes;
+
+private:
+ static const unsigned int MAX_NUM_TIMEOUTS;
+ static const uint32_t ERROR_FACE_NOT_FOUND;
+};
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_DAEMON_RIB_FIB_UPDATER_HPP
diff --git a/daemon/rib/readvertise/client-to-nlsr-readvertise-policy.cpp b/daemon/rib/readvertise/client-to-nlsr-readvertise-policy.cpp
new file mode 100644
index 0000000..ca54132
--- /dev/null
+++ b/daemon/rib/readvertise/client-to-nlsr-readvertise-policy.cpp
@@ -0,0 +1,49 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "client-to-nlsr-readvertise-policy.hpp"
+
+namespace nfd {
+namespace rib {
+
+optional<ReadvertiseAction>
+ClientToNlsrReadvertisePolicy::handleNewRoute(const RibRouteRef& ribRoute) const
+{
+ if (ribRoute.route->origin == ndn::nfd::ROUTE_ORIGIN_CLIENT) {
+ return ReadvertiseAction{ribRoute.entry->getName(), ndn::security::SigningInfo()};
+ }
+ else {
+ return nullopt;
+ }
+}
+
+time::milliseconds
+ClientToNlsrReadvertisePolicy::getRefreshInterval() const
+{
+ return 1_h;
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/daemon/rib/readvertise/client-to-nlsr-readvertise-policy.hpp b/daemon/rib/readvertise/client-to-nlsr-readvertise-policy.hpp
new file mode 100644
index 0000000..b1c5e29
--- /dev/null
+++ b/daemon/rib/readvertise/client-to-nlsr-readvertise-policy.hpp
@@ -0,0 +1,54 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_RIB_READVERTISE_CLIENT_TO_NLSR_READVERTISE_POLICY_HPP
+#define NFD_DAEMON_RIB_READVERTISE_CLIENT_TO_NLSR_READVERTISE_POLICY_HPP
+
+#include "readvertise-policy.hpp"
+
+namespace nfd {
+namespace rib {
+
+/** \brief a policy to readvertise routes registered by end hosts into NLSR
+ */
+class ClientToNlsrReadvertisePolicy : public ReadvertisePolicy
+{
+public:
+ /** \brief advertise if the route's origin is client
+ *
+ * If the route origin is "client" (typically from auto prefix propagation), readvertise it
+ * using the default signing identity.
+ */
+ optional<ReadvertiseAction>
+ handleNewRoute(const RibRouteRef& ribRoute) const override;
+
+ time::milliseconds
+ getRefreshInterval() const override;
+};
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_DAEMON_RIB_READVERTISE_CLIENT_TO_NLSR_READVERTISE_POLICY_HPP
diff --git a/daemon/rib/readvertise/host-to-gateway-readvertise-policy.cpp b/daemon/rib/readvertise/host-to-gateway-readvertise-policy.cpp
new file mode 100644
index 0000000..2d508f8
--- /dev/null
+++ b/daemon/rib/readvertise/host-to-gateway-readvertise-policy.cpp
@@ -0,0 +1,91 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "host-to-gateway-readvertise-policy.hpp"
+#include "core/scope-prefix.hpp"
+#include "rib/rib-manager.hpp"
+
+#include <ndn-cxx/security/pib/identity.hpp>
+#include <ndn-cxx/security/signing-helpers.hpp>
+
+namespace nfd {
+namespace rib {
+
+static const name::Component IGNORE_COMPONENT("nrd");
+static const time::seconds DEFAULT_REFRESH_INTERVAL = 25_s;
+
+HostToGatewayReadvertisePolicy::HostToGatewayReadvertisePolicy(const ndn::KeyChain& keyChain,
+ const ConfigSection& section)
+ : m_keyChain(keyChain)
+{
+ auto interval = section.get_optional<uint64_t>("refresh_interval");
+ m_refreshInterval = interval ? time::seconds(*interval) : DEFAULT_REFRESH_INTERVAL;
+}
+
+optional<ReadvertiseAction>
+HostToGatewayReadvertisePolicy::handleNewRoute(const RibRouteRef& ribRoute) const
+{
+ auto ribEntryName = ribRoute.entry->getName();
+ if (scope_prefix::LOCALHOST.isPrefixOf(ribEntryName) ||
+ ribEntryName == RibManager::LOCALHOP_TOP_PREFIX) {
+ return nullopt;
+ }
+
+ // find out the shortest identity whose name is a prefix of the RIB entry name
+ auto prefixToAdvertise = ribEntryName;
+ ndn::security::pib::Identity signingIdentity;
+ bool isFound = false;
+
+ for (const auto& identity : m_keyChain.getPib().getIdentities()) {
+ auto prefix = identity.getName();
+
+ // ignore the identity name's last component if it is "nrd"
+ if (!prefix.empty() && IGNORE_COMPONENT == prefix.at(-1)) {
+ prefix = prefix.getPrefix(-1);
+ }
+
+ if (prefix.isPrefixOf(prefixToAdvertise)) {
+ isFound = true;
+ prefixToAdvertise = prefix;
+ signingIdentity = identity;
+ }
+ }
+
+ if (isFound) {
+ return ReadvertiseAction{prefixToAdvertise, ndn::security::signingByIdentity(signingIdentity)};
+ }
+ else {
+ return nullopt;
+ }
+}
+
+time::milliseconds
+HostToGatewayReadvertisePolicy::getRefreshInterval() const
+{
+ return m_refreshInterval;
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/daemon/rib/readvertise/host-to-gateway-readvertise-policy.hpp b/daemon/rib/readvertise/host-to-gateway-readvertise-policy.hpp
new file mode 100644
index 0000000..69e914a
--- /dev/null
+++ b/daemon/rib/readvertise/host-to-gateway-readvertise-policy.hpp
@@ -0,0 +1,60 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_RIB_READVERTISE_HOST_TO_GATEWAY_READVERTISE_POLICY_HPP
+#define NFD_DAEMON_RIB_READVERTISE_HOST_TO_GATEWAY_READVERTISE_POLICY_HPP
+
+#include "readvertise-policy.hpp"
+#include "core/config-file.hpp"
+
+#include <ndn-cxx/security/key-chain.hpp>
+
+namespace nfd {
+namespace rib {
+
+/** \brief a policy to readvertise routes registered by local applications into remote gateway
+ */
+class HostToGatewayReadvertisePolicy : public ReadvertisePolicy
+{
+public:
+ HostToGatewayReadvertisePolicy(const ndn::KeyChain& keyChain,
+ const ConfigSection& section);
+
+public:
+ optional<ReadvertiseAction>
+ handleNewRoute(const RibRouteRef& ribRoute) const override;
+
+ time::milliseconds
+ getRefreshInterval() const override;
+
+private:
+ const ndn::KeyChain& m_keyChain;
+ time::seconds m_refreshInterval;
+};
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_DAEMON_RIB_READVERTISE_HOST_TO_GATEWAY_READVERTISE_POLICY_HPP
diff --git a/daemon/rib/readvertise/nfd-rib-readvertise-destination.cpp b/daemon/rib/readvertise/nfd-rib-readvertise-destination.cpp
new file mode 100644
index 0000000..f0a0482
--- /dev/null
+++ b/daemon/rib/readvertise/nfd-rib-readvertise-destination.cpp
@@ -0,0 +1,110 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "nfd-rib-readvertise-destination.hpp"
+#include "core/logger.hpp"
+
+#include <ndn-cxx/mgmt/nfd/control-command.hpp>
+#include <ndn-cxx/mgmt/nfd/control-response.hpp>
+
+namespace nfd {
+namespace rib {
+
+NFD_LOG_INIT(NfdRibReadvertiseDestination);
+
+using ndn::nfd::ControlResponse;
+
+NfdRibReadvertiseDestination::NfdRibReadvertiseDestination(ndn::nfd::Controller& controller,
+ Rib& rib,
+ const ndn::nfd::CommandOptions& options,
+ const ndn::nfd::ControlParameters& parameters)
+ : m_controller(controller)
+ , m_commandOptions(options)
+ , m_controlParameters(parameters)
+{
+ m_ribInsertConn = rib.afterInsertEntry.connect(
+ std::bind(&NfdRibReadvertiseDestination::handleRibInsert, this, _1));
+ m_ribEraseConn = rib.afterEraseEntry.connect(
+ std::bind(&NfdRibReadvertiseDestination::handleRibErase, this, _1));
+}
+
+void
+NfdRibReadvertiseDestination::advertise(const nfd::rib::ReadvertisedRoute& rr,
+ std::function<void()> successCb,
+ std::function<void(const std::string&)> failureCb)
+{
+ NFD_LOG_DEBUG("advertise " << rr.prefix << " on " << m_commandOptions.getPrefix());
+
+ m_controller.start<ndn::nfd::RibRegisterCommand>(
+ getControlParameters().setName(rr.prefix),
+ [=] (const ControlParameters& cp) { successCb(); },
+ [=] (const ControlResponse& cr) { failureCb(cr.getText()); },
+ getCommandOptions().setSigningInfo(rr.signer));
+}
+
+void
+NfdRibReadvertiseDestination::withdraw(const nfd::rib::ReadvertisedRoute& rr,
+ std::function<void()> successCb,
+ std::function<void(const std::string&)> failureCb)
+{
+ NFD_LOG_DEBUG("withdraw " << rr.prefix << " on " << m_commandOptions.getPrefix());
+
+ m_controller.start<ndn::nfd::RibUnregisterCommand>(
+ getControlParameters().setName(rr.prefix),
+ [=] (const ControlParameters& cp) { successCb(); },
+ [=] (const ControlResponse& cr) { failureCb(cr.getText()); },
+ getCommandOptions().setSigningInfo(rr.signer));
+}
+
+ndn::nfd::ControlParameters
+NfdRibReadvertiseDestination::getControlParameters()
+{
+ return m_controlParameters;
+}
+
+ndn::nfd::CommandOptions
+NfdRibReadvertiseDestination::getCommandOptions()
+{
+ return m_commandOptions;
+}
+
+void
+NfdRibReadvertiseDestination::handleRibInsert(const ndn::Name& name)
+{
+ if (name.isPrefixOf(m_commandOptions.getPrefix())) {
+ setAvailability(true);
+ }
+}
+
+void
+NfdRibReadvertiseDestination::handleRibErase(const ndn::Name& name)
+{
+ if (name.isPrefixOf(m_commandOptions.getPrefix())) {
+ setAvailability(false);
+ }
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/daemon/rib/readvertise/nfd-rib-readvertise-destination.hpp b/daemon/rib/readvertise/nfd-rib-readvertise-destination.hpp
new file mode 100644
index 0000000..9c5bce6
--- /dev/null
+++ b/daemon/rib/readvertise/nfd-rib-readvertise-destination.hpp
@@ -0,0 +1,92 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_RIB_READVERTISE_NFD_RIB_READVERTISE_DESTINATION_HPP
+#define NFD_DAEMON_RIB_READVERTISE_NFD_RIB_READVERTISE_DESTINATION_HPP
+
+#include "readvertise-destination.hpp"
+#include "rib/rib.hpp"
+
+#include <ndn-cxx/mgmt/nfd/command-options.hpp>
+#include <ndn-cxx/mgmt/nfd/controller.hpp>
+#include <ndn-cxx/mgmt/nfd/control-parameters.hpp>
+
+namespace nfd {
+namespace rib {
+
+/** \brief a readvertise destination using NFD RIB management protocol
+ */
+class NfdRibReadvertiseDestination : public ReadvertiseDestination
+{
+public:
+ NfdRibReadvertiseDestination(ndn::nfd::Controller& controller,
+ Rib& rib,
+ const ndn::nfd::CommandOptions& options = ndn::nfd::CommandOptions(),
+ const ndn::nfd::ControlParameters& parameters =
+ ndn::nfd::ControlParameters()
+ .setOrigin(ndn::nfd::ROUTE_ORIGIN_CLIENT));
+
+ /** \brief add a name prefix into NFD RIB
+ */
+ void
+ advertise(const ReadvertisedRoute& rr,
+ std::function<void()> successCb,
+ std::function<void(const std::string&)> failureCb) override;
+
+ /** \brief remove a name prefix from NFD RIB
+ */
+ void
+ withdraw(const ReadvertisedRoute& rr,
+ std::function<void()> successCb,
+ std::function<void(const std::string&)> failureCb) override;
+
+protected:
+ ndn::nfd::ControlParameters
+ getControlParameters();
+
+ ndn::nfd::CommandOptions
+ getCommandOptions();
+
+private:
+ void
+ handleRibInsert(const Name& name);
+
+ void
+ handleRibErase(const Name& name);
+
+private:
+ ndn::nfd::Controller& m_controller;
+
+ signal::ScopedConnection m_ribInsertConn;
+ signal::ScopedConnection m_ribEraseConn;
+
+ ndn::nfd::CommandOptions m_commandOptions;
+ ndn::nfd::ControlParameters m_controlParameters;
+};
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_DAEMON_RIB_READVERTISE_NFD_RIB_READVERTISE_DESTINATION_HPP
diff --git a/daemon/rib/readvertise/readvertise-destination.cpp b/daemon/rib/readvertise/readvertise-destination.cpp
new file mode 100644
index 0000000..a687a8a
--- /dev/null
+++ b/daemon/rib/readvertise/readvertise-destination.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "readvertise-destination.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+namespace rib {
+
+NFD_LOG_INIT(ReadvertiseDestination);
+
+void
+ReadvertiseDestination::setAvailability(bool isAvailable)
+{
+ if (m_isAvailable != isAvailable) {
+ if (isAvailable) {
+ NFD_LOG_DEBUG("Destination has become available.");
+ }
+ else {
+ NFD_LOG_DEBUG("Destinatino has become unavailable.");
+ }
+ m_isAvailable = isAvailable;
+ afterAvailabilityChange(isAvailable);
+ }
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/daemon/rib/readvertise/readvertise-destination.hpp b/daemon/rib/readvertise/readvertise-destination.hpp
new file mode 100644
index 0000000..1c4fb76
--- /dev/null
+++ b/daemon/rib/readvertise/readvertise-destination.hpp
@@ -0,0 +1,74 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_RIB_READVERTISE_READVERTISE_DESTINATION_HPP
+#define NFD_DAEMON_RIB_READVERTISE_READVERTISE_DESTINATION_HPP
+
+#include "readvertised-route.hpp"
+
+namespace nfd {
+namespace rib {
+
+/** \brief a destination to readvertise into
+ */
+class ReadvertiseDestination : noncopyable
+{
+public:
+ virtual
+ ~ReadvertiseDestination() = default;
+
+ virtual void
+ advertise(const ReadvertisedRoute& rr,
+ std::function<void()> successCb,
+ std::function<void(const std::string&)> failureCb) = 0;
+
+ virtual void
+ withdraw(const ReadvertisedRoute& rr,
+ std::function<void()> successCb,
+ std::function<void(const std::string&)> failureCb) = 0;
+
+ bool
+ isAvailable() const
+ {
+ return m_isAvailable;
+ }
+
+protected:
+ void
+ setAvailability(bool isAvailable);
+
+public:
+ /** \brief signals when the destination becomes available or unavailable
+ */
+ signal::Signal<ReadvertiseDestination, bool> afterAvailabilityChange;
+
+private:
+ bool m_isAvailable = false;
+};
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_DAEMON_RIB_READVERTISE_READVERTISE_DESTINATION_HPP
diff --git a/daemon/rib/readvertise/readvertise-policy.hpp b/daemon/rib/readvertise/readvertise-policy.hpp
new file mode 100644
index 0000000..99deccb
--- /dev/null
+++ b/daemon/rib/readvertise/readvertise-policy.hpp
@@ -0,0 +1,66 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_RIB_READVERTISE_READVERTISE_POLICY_HPP
+#define NFD_DAEMON_RIB_READVERTISE_READVERTISE_POLICY_HPP
+
+#include "rib/rib.hpp"
+
+#include <ndn-cxx/security/signing-info.hpp>
+
+namespace nfd {
+namespace rib {
+
+/** \brief a decision made by readvertise policy
+ */
+struct ReadvertiseAction
+{
+ Name prefix; ///< the prefix that should be readvertised
+ ndn::security::SigningInfo signer; ///< credentials for command signing
+};
+
+/** \brief a policy to decide whether to readvertise a route, and what prefix to readvertise
+ */
+class ReadvertisePolicy : noncopyable
+{
+public:
+ virtual
+ ~ReadvertisePolicy() = default;
+
+ /** \brief decide whether to readvertise a route, and what prefix to readvertise
+ */
+ virtual optional<ReadvertiseAction>
+ handleNewRoute(const RibRouteRef& ribRoute) const = 0;
+
+ /** \return how often readvertisements made by this policy should be refreshed.
+ */
+ virtual time::milliseconds
+ getRefreshInterval() const = 0;
+};
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_DAEMON_RIB_READVERTISE_READVERTISE_POLICY_HPP
diff --git a/daemon/rib/readvertise/readvertise.cpp b/daemon/rib/readvertise/readvertise.cpp
new file mode 100644
index 0000000..454ab20
--- /dev/null
+++ b/daemon/rib/readvertise/readvertise.cpp
@@ -0,0 +1,201 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "readvertise.hpp"
+#include "core/logger.hpp"
+
+#include <ndn-cxx/util/random.hpp>
+
+namespace nfd {
+namespace rib {
+
+NFD_LOG_INIT(Readvertise);
+
+const time::milliseconds Readvertise::RETRY_DELAY_MIN = 50_s;
+const time::milliseconds Readvertise::RETRY_DELAY_MAX = 3600_s;
+
+static time::milliseconds
+randomizeTimer(time::milliseconds baseTimer)
+{
+ std::uniform_int_distribution<> dist(-5, 5);
+ auto newTime = baseTimer + time::milliseconds(dist(ndn::random::getRandomNumberEngine()));
+ return std::max(newTime, 0_ms);
+}
+
+Readvertise::Readvertise(Rib& rib, ndn::util::Scheduler& scheduler,
+ unique_ptr<ReadvertisePolicy> policy,
+ unique_ptr<ReadvertiseDestination> destination)
+ : m_scheduler(scheduler)
+ , m_policy(std::move(policy))
+ , m_destination(std::move(destination))
+{
+ m_addRouteConn = rib.afterAddRoute.connect([this] (const auto& r) { this->afterAddRoute(r); });
+ m_removeRouteConn = rib.beforeRemoveRoute.connect([this] (const auto& r) { this->beforeRemoveRoute(r); });
+
+ m_destination->afterAvailabilityChange.connect([this] (bool isAvailable) {
+ if (isAvailable) {
+ this->afterDestinationAvailable();
+ }
+ else {
+ this->afterDestinationUnavailable();
+ }
+ });
+}
+
+void
+Readvertise::afterAddRoute(const RibRouteRef& ribRoute)
+{
+ optional<ReadvertiseAction> action = m_policy->handleNewRoute(ribRoute);
+ if (!action) {
+ NFD_LOG_DEBUG("add-route " << ribRoute.entry->getName() << '(' << ribRoute.route->faceId <<
+ ',' << ribRoute.route->origin << ") not-readvertising");
+ return;
+ }
+
+ ReadvertisedRouteContainer::iterator rrIt;
+ bool isNew = false;
+ std::tie(rrIt, isNew) = m_rrs.emplace(action->prefix);
+
+ if (!isNew && rrIt->signer != action->signer) {
+ NFD_LOG_WARN("add-route " << ribRoute.entry->getName() << '(' << ribRoute.route->faceId <<
+ ',' << ribRoute.route->origin << ") readvertising-as " << action->prefix <<
+ " old-signer " << rrIt->signer << " new-signer " << action->signer);
+ }
+ rrIt->signer = action->signer;
+
+ RouteRrIndex::iterator indexIt;
+ std::tie(indexIt, isNew) = m_routeToRr.emplace(ribRoute, rrIt);
+ BOOST_ASSERT(isNew);
+
+ if (rrIt->nRibRoutes++ > 0) {
+ NFD_LOG_DEBUG("add-route " << ribRoute.entry->getName() << '(' << ribRoute.route->faceId <<
+ ',' << ribRoute.route->origin << ") already-readvertised-as " << action->prefix);
+ return;
+ }
+
+ NFD_LOG_DEBUG("add-route " << ribRoute.entry->getName() << '(' << ribRoute.route->faceId <<
+ ',' << ribRoute.route->origin << ") readvertising-as " << action->prefix <<
+ " signer " << action->signer);
+ rrIt->retryDelay = RETRY_DELAY_MIN;
+ this->advertise(rrIt);
+}
+
+void
+Readvertise::beforeRemoveRoute(const RibRouteRef& ribRoute)
+{
+ auto indexIt = m_routeToRr.find(ribRoute);
+ if (indexIt == m_routeToRr.end()) {
+ NFD_LOG_DEBUG("remove-route " << ribRoute.entry->getName() << '(' << ribRoute.route->faceId <<
+ ',' << ribRoute.route->origin << ") not-readvertised");
+ return;
+ }
+
+ auto rrIt = indexIt->second;
+ m_routeToRr.erase(indexIt);
+
+ if (--rrIt->nRibRoutes > 0) {
+ NFD_LOG_DEBUG("remove-route " << ribRoute.entry->getName() << '(' << ribRoute.route->faceId <<
+ ',' << ribRoute.route->origin << ") needed-by " << rrIt->nRibRoutes);
+ return;
+ }
+
+ rrIt->retryDelay = RETRY_DELAY_MIN;
+ this->withdraw(rrIt);
+}
+
+void
+Readvertise::afterDestinationAvailable()
+{
+ for (auto rrIt = m_rrs.begin(); rrIt != m_rrs.end(); ++rrIt) {
+ rrIt->retryDelay = RETRY_DELAY_MIN;
+ this->advertise(rrIt);
+ }
+}
+
+void
+Readvertise::afterDestinationUnavailable()
+{
+ for (auto rrIt = m_rrs.begin(); rrIt != m_rrs.end();) {
+ if (rrIt->nRibRoutes > 0) {
+ rrIt->retryEvt.cancel(); // stop retrying or refreshing
+ ++rrIt;
+ }
+ else {
+ rrIt = m_rrs.erase(rrIt); // assume withdraw has completed
+ }
+ }
+}
+
+void
+Readvertise::advertise(ReadvertisedRouteContainer::iterator rrIt)
+{
+ BOOST_ASSERT(rrIt->nRibRoutes > 0);
+
+ if (!m_destination->isAvailable()) {
+ NFD_LOG_DEBUG("advertise " << rrIt->prefix << " destination-unavailable");
+ return;
+ }
+
+ m_destination->advertise(*rrIt,
+ [=] {
+ NFD_LOG_DEBUG("advertise " << rrIt->prefix << " success");
+ rrIt->retryDelay = RETRY_DELAY_MIN;
+ rrIt->retryEvt = m_scheduler.scheduleEvent(randomizeTimer(m_policy->getRefreshInterval()),
+ [=] { advertise(rrIt); });
+ },
+ [=] (const std::string& msg) {
+ NFD_LOG_DEBUG("advertise " << rrIt->prefix << " failure " << msg);
+ rrIt->retryDelay = std::min(RETRY_DELAY_MAX, rrIt->retryDelay * 2);
+ rrIt->retryEvt = m_scheduler.scheduleEvent(randomizeTimer(rrIt->retryDelay),
+ [=] { advertise(rrIt); });
+ });
+}
+
+void
+Readvertise::withdraw(ReadvertisedRouteContainer::iterator rrIt)
+{
+ BOOST_ASSERT(rrIt->nRibRoutes == 0);
+
+ if (!m_destination->isAvailable()) {
+ NFD_LOG_DEBUG("withdraw " << rrIt->prefix << " destination-unavailable");
+ m_rrs.erase(rrIt);
+ return;
+ }
+
+ m_destination->withdraw(*rrIt,
+ [=] {
+ NFD_LOG_DEBUG("withdraw " << rrIt->prefix << " success");
+ m_rrs.erase(rrIt);
+ },
+ [=] (const std::string& msg) {
+ NFD_LOG_DEBUG("withdraw " << rrIt->prefix << " failure " << msg);
+ rrIt->retryDelay = std::min(RETRY_DELAY_MAX, rrIt->retryDelay * 2);
+ rrIt->retryEvt = m_scheduler.scheduleEvent(randomizeTimer(rrIt->retryDelay),
+ [=] { withdraw(rrIt); });
+ });
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/daemon/rib/readvertise/readvertise.hpp b/daemon/rib/readvertise/readvertise.hpp
new file mode 100644
index 0000000..80dbbc7
--- /dev/null
+++ b/daemon/rib/readvertise/readvertise.hpp
@@ -0,0 +1,94 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_RIB_READVERTISE_READVERTISE_HPP
+#define NFD_DAEMON_RIB_READVERTISE_READVERTISE_HPP
+
+#include "readvertise-destination.hpp"
+#include "readvertise-policy.hpp"
+#include "readvertised-route.hpp"
+#include "rib/rib.hpp"
+
+namespace nfd {
+namespace rib {
+
+/** \brief readvertise a subset of routes to a destination according to a policy
+ *
+ * The Readvertise class allows RIB routes to be readvertised to a destination such as a routing
+ * protocol daemon or another NFD-RIB. It monitors the RIB for route additions and removals,
+ * asks the ReadvertisePolicy to make decision on whether to readvertise each new route and what
+ * prefix to readvertise as, and invokes a ReadvertiseDestination to send the commands.
+ */
+class Readvertise : noncopyable
+{
+
+public:
+ Readvertise(Rib& rib,
+ ndn::util::Scheduler& scheduler,
+ unique_ptr<ReadvertisePolicy> policy,
+ unique_ptr<ReadvertiseDestination> destination);
+
+private:
+ void
+ afterAddRoute(const RibRouteRef& ribRoute);
+
+ void
+ beforeRemoveRoute(const RibRouteRef& ribRoute);
+
+ void
+ afterDestinationAvailable();
+
+ void
+ afterDestinationUnavailable();
+
+ void
+ advertise(ReadvertisedRouteContainer::iterator rrIt);
+
+ void
+ withdraw(ReadvertisedRouteContainer::iterator rrIt);
+
+private:
+ /** \brief maps from RIB route to readvertised route derived from RIB route(s)
+ */
+ using RouteRrIndex = std::map<RibRouteRef, ReadvertisedRouteContainer::iterator>;
+
+ static const time::milliseconds RETRY_DELAY_MIN;
+ static const time::milliseconds RETRY_DELAY_MAX;
+
+ ndn::util::Scheduler& m_scheduler;
+ unique_ptr<ReadvertisePolicy> m_policy;
+ unique_ptr<ReadvertiseDestination> m_destination;
+
+ ReadvertisedRouteContainer m_rrs;
+ RouteRrIndex m_routeToRr;
+
+ signal::ScopedConnection m_addRouteConn;
+ signal::ScopedConnection m_removeRouteConn;
+};
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_DAEMON_RIB_READVERTISE_READVERTISE_HPP
diff --git a/daemon/rib/readvertise/readvertised-route.hpp b/daemon/rib/readvertise/readvertised-route.hpp
new file mode 100644
index 0000000..7dc02ae
--- /dev/null
+++ b/daemon/rib/readvertise/readvertised-route.hpp
@@ -0,0 +1,69 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_RIB_READVERTISE_READVERTISED_ROUTE_HPP
+#define NFD_DAEMON_RIB_READVERTISE_READVERTISED_ROUTE_HPP
+
+#include "core/common.hpp"
+
+#include <ndn-cxx/security/signing-info.hpp>
+#include <ndn-cxx/util/scheduler.hpp>
+
+namespace nfd {
+namespace rib {
+
+/** \brief state of a readvertised route
+ */
+class ReadvertisedRoute : noncopyable
+{
+public:
+ explicit
+ ReadvertisedRoute(const Name& prefix)
+ : prefix(prefix)
+ , nRibRoutes(0)
+ , retryDelay(0)
+ {
+ }
+
+public:
+ Name prefix; ///< readvertised prefix
+ mutable ndn::security::SigningInfo signer; ///< signer for commands
+ mutable size_t nRibRoutes; ///< number of RIB routes that cause the readvertisement
+ mutable time::milliseconds retryDelay; ///< retry interval (not used for refresh)
+ mutable ndn::util::scheduler::ScopedEventId retryEvt; ///< retry or refresh event
+};
+
+inline bool
+operator<(const ReadvertisedRoute& lhs, const ReadvertisedRoute& rhs)
+{
+ return lhs.prefix < rhs.prefix;
+}
+
+using ReadvertisedRouteContainer = std::set<ReadvertisedRoute>;
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_DAEMON_RIB_READVERTISE_READVERTISED_ROUTE_HPP
diff --git a/daemon/rib/rib-entry.cpp b/daemon/rib/rib-entry.cpp
new file mode 100644
index 0000000..008ac4f
--- /dev/null
+++ b/daemon/rib/rib-entry.cpp
@@ -0,0 +1,290 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "rib-entry.hpp"
+#include "core/logger.hpp"
+
+#include <ndn-cxx/mgmt/nfd/control-command.hpp>
+
+namespace nfd {
+namespace rib {
+
+NFD_LOG_INIT(RibEntry);
+
+RibEntry::RouteList::iterator
+RibEntry::findRoute(const Route& route)
+{
+ return std::find_if(begin(), end(), bind(&compareFaceIdAndOrigin, _1, route));
+}
+
+RibEntry::RouteList::const_iterator
+RibEntry::findRoute(const Route& route) const
+{
+ return std::find_if(begin(), end(), bind(&compareFaceIdAndOrigin, _1, route));
+}
+
+std::pair<RibEntry::iterator, bool>
+RibEntry::insertRoute(const Route& route)
+{
+ iterator it = findRoute(route);
+
+ if (it == end()) {
+ if (route.flags & ndn::nfd::ROUTE_FLAG_CAPTURE) {
+ m_nRoutesWithCaptureSet++;
+ }
+
+ m_routes.push_back(route);
+ return {std::prev(m_routes.end()), true};
+ }
+
+ return {it, false};
+}
+
+void
+RibEntry::eraseRoute(const Route& route)
+{
+ RibEntry::iterator it = findRoute(route);
+ eraseRoute(it);
+}
+
+bool
+RibEntry::hasRoute(const Route& route)
+{
+ RibEntry::const_iterator it = findRoute(route);
+
+ return it != end();
+}
+
+bool
+RibEntry::hasFaceId(const uint64_t faceId) const
+{
+ RibEntry::const_iterator it = std::find_if(begin(), end(), bind(&compareFaceId, _1, faceId));
+
+ return it != end();
+}
+
+size_t
+RibEntry::getNRoutes() const
+{
+ return m_routes.size();
+}
+
+void
+RibEntry::addChild(shared_ptr<RibEntry> child)
+{
+ BOOST_ASSERT(!child->getParent());
+ child->setParent(this->shared_from_this());
+ m_children.push_back(std::move(child));
+}
+
+void
+RibEntry::removeChild(shared_ptr<RibEntry> child)
+{
+ BOOST_ASSERT(child->getParent().get() == this);
+ child->setParent(nullptr);
+ m_children.remove(child);
+}
+
+RibEntry::RouteList::iterator
+RibEntry::eraseRoute(RouteList::iterator route)
+{
+ if (route != m_routes.end()) {
+ if (route->flags & ndn::nfd::ROUTE_FLAG_CAPTURE) {
+ m_nRoutesWithCaptureSet--;
+ }
+
+ // Cancel any scheduled event
+ NFD_LOG_TRACE("Cancelling expiration event: " << route->getExpirationEvent());
+ route->cancelExpirationEvent();
+
+ return m_routes.erase(route);
+ }
+
+ return m_routes.end();
+}
+
+void
+RibEntry::addInheritedRoute(const Route& route)
+{
+ m_inheritedRoutes.push_back(route);
+}
+
+void
+RibEntry::removeInheritedRoute(const Route& route)
+{
+ m_inheritedRoutes.remove_if(bind(&compareFaceId, _1, route.faceId));
+}
+
+RibEntry::RouteList::const_iterator
+RibEntry::findInheritedRoute(const Route& route) const
+{
+ return std::find_if(m_inheritedRoutes.begin(), m_inheritedRoutes.end(),
+ bind(&compareFaceId, _1, route.faceId));
+}
+
+bool
+RibEntry::hasInheritedRoute(const Route& route) const
+{
+ return findInheritedRoute(route) != m_inheritedRoutes.end();
+}
+
+bool
+RibEntry::hasCapture() const
+{
+ return m_nRoutesWithCaptureSet > 0;
+}
+
+bool
+RibEntry::hasChildInheritOnFaceId(uint64_t faceId) const
+{
+ for (const Route& route : m_routes) {
+ if (route.faceId == faceId && (route.flags & ndn::nfd::ROUTE_FLAG_CHILD_INHERIT)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+const Route*
+RibEntry::getRouteWithLowestCostByFaceId(uint64_t faceId) const
+{
+ const Route* candidate = nullptr;
+
+ for (const Route& route : m_routes) {
+ // Matching face ID
+ if (route.faceId == faceId) {
+ // If this is the first route with this Face ID found
+ if (candidate == nullptr) {
+ candidate = &route;
+ }
+ else if (route.cost < candidate->cost) {
+ // Found a route with a lower cost
+ candidate = &route;
+ }
+ }
+ }
+
+ return candidate;
+}
+
+const Route*
+RibEntry::getRouteWithSecondLowestCostByFaceId(uint64_t faceId) const
+{
+ std::vector<const Route*> matches;
+
+ // Copy routes which have faceId
+ for (const Route& route : m_routes) {
+ if (route.faceId == faceId) {
+ matches.push_back(&route);
+ }
+ }
+
+ // If there are less than 2 routes, there is no second lowest
+ if (matches.size() < 2) {
+ return nullptr;
+ }
+
+ // Get second lowest cost
+ std::nth_element(matches.begin(), matches.begin() + 1, matches.end(),
+ [] (const Route* lhs, const Route* rhs) { return lhs->cost < rhs->cost; });
+
+ return matches.at(1);
+}
+
+const Route*
+RibEntry::getRouteWithLowestCostAndChildInheritByFaceId(uint64_t faceId) const
+{
+ const Route* candidate = nullptr;
+
+ for (const Route& route : m_routes) {
+ // Correct face ID and Child Inherit flag set
+ if (route.faceId == faceId &&
+ (route.flags & ndn::nfd::ROUTE_FLAG_CHILD_INHERIT) == ndn::nfd::ROUTE_FLAG_CHILD_INHERIT)
+ {
+ // If this is the first route with this Face ID found
+ if (candidate == nullptr) {
+ candidate = &route;
+ }
+ else if (route.cost < candidate->cost) {
+ // Found a route with a lower cost
+ candidate = &route;
+ }
+ }
+ }
+
+ return candidate;
+}
+
+ndn::PrefixAnnouncement
+RibEntry::getPrefixAnnouncement(time::milliseconds minExpiration,
+ time::milliseconds maxExpiration) const
+{
+ const Route* bestAnnRoute = nullptr;
+ auto entryExpiry = time::steady_clock::TimePoint::min();
+
+ for (const Route& route : *this) {
+ if (route.expires) {
+ entryExpiry = std::max(entryExpiry, *route.expires);
+ if (route.announcement) {
+ if (bestAnnRoute == nullptr || *route.expires > *bestAnnRoute->expires) {
+ bestAnnRoute = &route;
+ }
+ }
+ }
+ else {
+ entryExpiry = time::steady_clock::TimePoint::max();
+ }
+ }
+
+ if (bestAnnRoute != nullptr) {
+ return *bestAnnRoute->announcement;
+ }
+
+ ndn::PrefixAnnouncement ann;
+ ann.setAnnouncedName(m_name);
+ ann.setExpiration(ndn::clamp(
+ time::duration_cast<time::milliseconds>(entryExpiry - time::steady_clock::now()),
+ minExpiration, maxExpiration));
+ return ann;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const RibEntry& entry)
+{
+ os << "RibEntry {\n";
+ os << " Name: " << entry.getName() << "\n";
+
+ for (const Route& route : entry) {
+ os << " " << route << "\n";
+ }
+
+ os << "}";
+
+ return os;
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/daemon/rib/rib-entry.hpp b/daemon/rib/rib-entry.hpp
new file mode 100644
index 0000000..45d5a87
--- /dev/null
+++ b/daemon/rib/rib-entry.hpp
@@ -0,0 +1,288 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_RIB_RIB_ENTRY_HPP
+#define NFD_DAEMON_RIB_RIB_ENTRY_HPP
+
+#include "route.hpp"
+
+#include <list>
+
+namespace nfd {
+namespace rib {
+
+/** \brief Represents a RIB entry, which contains one or more Routes with the same prefix.
+ */
+class RibEntry : public std::enable_shared_from_this<RibEntry>
+{
+public:
+ typedef std::list<Route> RouteList;
+ typedef RouteList::iterator iterator;
+ typedef RouteList::const_iterator const_iterator;
+
+ RibEntry()
+ : m_nRoutesWithCaptureSet(0)
+ {
+ }
+
+ void
+ setName(const Name& prefix);
+
+ const Name&
+ getName() const;
+
+ shared_ptr<RibEntry>
+ getParent() const;
+
+ bool
+ hasParent() const;
+
+ void
+ addChild(shared_ptr<RibEntry> child);
+
+ void
+ removeChild(shared_ptr<RibEntry> child);
+
+ const std::list<shared_ptr<RibEntry>>&
+ getChildren() const;
+
+ bool
+ hasChildren() const;
+
+ /** \brief inserts a new route into the entry's route list
+ * If another route already exists with the same faceId and origin,
+ * the new route is not inserted.
+ * \return a pair, whose first element is the iterator to the newly
+ * inserted element if the insert succeeds and to the
+ * previously-existing element otherwise, and whose second element
+ * is true if the insert succeeds and false otherwise.
+ */
+ std::pair<RibEntry::iterator, bool>
+ insertRoute(const Route& route);
+
+ /** \brief erases a Route with the same faceId and origin
+ */
+ void
+ eraseRoute(const Route& route);
+
+ /** \brief erases a Route with the passed iterator
+ * \return{ an iterator to the element that followed the erased iterator }
+ */
+ iterator
+ eraseRoute(RouteList::iterator route);
+
+ bool
+ hasFaceId(const uint64_t faceId) const;
+
+ const RouteList&
+ getRoutes() const;
+
+ size_t
+ getNRoutes() const;
+
+ iterator
+ findRoute(const Route& route);
+
+ const_iterator
+ findRoute(const Route& route) const;
+
+ bool
+ hasRoute(const Route& route);
+
+ void
+ addInheritedRoute(const Route& route);
+
+ void
+ removeInheritedRoute(const Route& route);
+
+ /** \brief Returns the routes this namespace has inherited.
+ * The inherited routes returned represent inherited routes this namespace has in the FIB.
+ * \return{ routes inherited by this namespace }
+ */
+ const RouteList&
+ getInheritedRoutes() const;
+
+ /** \brief Finds an inherited route with a matching face ID.
+ * \return{ An iterator to the matching route if one is found;
+ * otherwise, an iterator to the end of the entry's
+ * inherited route list }
+ */
+ RouteList::const_iterator
+ findInheritedRoute(const Route& route) const;
+
+ /** \brief Determines if the entry has an inherited route with a matching face ID.
+ * \return{ True, if a matching inherited route is found; otherwise, false. }
+ */
+ bool
+ hasInheritedRoute(const Route& route) const;
+
+ bool
+ hasCapture() const;
+
+ /** \brief Determines if the entry has an inherited route with the passed
+ * face ID and its child inherit flag set.
+ * \return{ True, if a matching inherited route is found; otherwise, false. }
+ */
+ bool
+ hasChildInheritOnFaceId(uint64_t faceId) const;
+
+ /** \brief Returns the route with the lowest cost that has the passed face ID.
+ * \return{ The route with the lowest cost that has the passed face ID}
+ */
+ const Route*
+ getRouteWithLowestCostByFaceId(uint64_t faceId) const;
+
+ const Route*
+ getRouteWithSecondLowestCostByFaceId(uint64_t faceId) const;
+
+ /** \brief Returns the route with the lowest cost that has the passed face ID
+ * and its child inherit flag set.
+ * \return{ The route with the lowest cost that has the passed face ID
+ * and its child inherit flag set }
+ */
+ const Route*
+ getRouteWithLowestCostAndChildInheritByFaceId(uint64_t faceId) const;
+
+ /** \brief Retrieve a prefix announcement suitable for readvertising this route.
+ *
+ * If one or more routes in this RIB entry contains a prefix announcement, this method returns
+ * the announcement from the route that expires last.
+ *
+ * If this RIB entry does not have a route containing a prefix announcement, this method creates
+ * a new announcement. Its expiration period reflects the remaining lifetime of this RIB entry,
+ * confined within [\p minExpiration, \p maxExpiration] range. The caller is expected to sign
+ * this announcement.
+ *
+ * \warning (minExpiration > maxExpiration) triggers undefined behavior.
+ */
+ ndn::PrefixAnnouncement
+ getPrefixAnnouncement(time::milliseconds minExpiration = 15_s,
+ time::milliseconds maxExpiration = 1_h) const;
+
+ const_iterator
+ begin() const;
+
+ const_iterator
+ end() const;
+
+ iterator
+ begin();
+
+ iterator
+ end();
+
+private:
+ void
+ setParent(shared_ptr<RibEntry> parent);
+
+private:
+ Name m_name;
+ std::list<shared_ptr<RibEntry>> m_children;
+ shared_ptr<RibEntry> m_parent;
+ RouteList m_routes;
+ RouteList m_inheritedRoutes;
+
+ /** \brief The number of routes on this namespace with the capture flag set.
+ *
+ * This count is used to check if the namespace will block inherited routes.
+ * If the number is greater than zero, a route on the namespace has its capture
+ * flag set which means the namespace should not inherit any routes.
+ */
+ uint64_t m_nRoutesWithCaptureSet;
+};
+
+inline void
+RibEntry::setName(const Name& prefix)
+{
+ m_name = prefix;
+}
+
+inline const Name&
+RibEntry::getName() const
+{
+ return m_name;
+}
+
+inline void
+RibEntry::setParent(shared_ptr<RibEntry> parent)
+{
+ m_parent = parent;
+}
+
+inline shared_ptr<RibEntry>
+RibEntry::getParent() const
+{
+ return m_parent;
+}
+
+inline const std::list<shared_ptr<RibEntry>>&
+RibEntry::getChildren() const
+{
+ return m_children;
+}
+
+inline const RibEntry::RouteList&
+RibEntry::getRoutes() const
+{
+ return m_routes;
+}
+
+inline const RibEntry::RouteList&
+RibEntry::getInheritedRoutes() const
+{
+ return m_inheritedRoutes;
+}
+
+inline RibEntry::const_iterator
+RibEntry::begin() const
+{
+ return m_routes.begin();
+}
+
+inline RibEntry::const_iterator
+RibEntry::end() const
+{
+ return m_routes.end();
+}
+
+inline RibEntry::iterator
+RibEntry::begin()
+{
+ return m_routes.begin();
+}
+
+inline RibEntry::iterator
+RibEntry::end()
+{
+ return m_routes.end();
+}
+
+std::ostream&
+operator<<(std::ostream& os, const RibEntry& entry);
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_DAEMON_RIB_RIB_ENTRY_HPP
diff --git a/daemon/rib/rib-manager.cpp b/daemon/rib/rib-manager.cpp
new file mode 100644
index 0000000..4477ebc
--- /dev/null
+++ b/daemon/rib/rib-manager.cpp
@@ -0,0 +1,500 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "rib-manager.hpp"
+
+#include "core/fib-max-depth.hpp"
+#include "core/logger.hpp"
+
+#include <ndn-cxx/lp/tags.hpp>
+#include <ndn-cxx/mgmt/nfd/control-command.hpp>
+#include <ndn-cxx/mgmt/nfd/control-parameters.hpp>
+#include <ndn-cxx/mgmt/nfd/control-response.hpp>
+#include <ndn-cxx/mgmt/nfd/face-status.hpp>
+#include <ndn-cxx/mgmt/nfd/rib-entry.hpp>
+
+namespace nfd {
+namespace rib {
+
+NFD_LOG_INIT(RibManager);
+
+static const std::string MGMT_MODULE_NAME = "rib";
+static const Name LOCALHOST_TOP_PREFIX = "/localhost/nfd";
+static const time::seconds ACTIVE_FACE_FETCH_INTERVAL = 5_min;
+
+const Name RibManager::LOCALHOP_TOP_PREFIX = "/localhop/nfd";
+
+RibManager::RibManager(Rib& rib, ndn::Face& face, ndn::KeyChain& keyChain,
+ ndn::nfd::Controller& nfdController, Dispatcher& dispatcher,
+ ndn::util::Scheduler& scheduler)
+ : ManagerBase(dispatcher, MGMT_MODULE_NAME)
+ , m_rib(rib)
+ , m_keyChain(keyChain)
+ , m_nfdController(nfdController)
+ , m_dispatcher(dispatcher)
+ , m_scheduler(scheduler)
+ , m_faceMonitor(face)
+ , m_localhostValidator(face)
+ , m_localhopValidator(face)
+ , m_isLocalhopEnabled(false)
+{
+ registerCommandHandler<ndn::nfd::RibRegisterCommand>("register",
+ bind(&RibManager::registerEntry, this, _2, _3, _4, _5));
+ registerCommandHandler<ndn::nfd::RibUnregisterCommand>("unregister",
+ bind(&RibManager::unregisterEntry, this, _2, _3, _4, _5));
+
+ registerStatusDatasetHandler("list", bind(&RibManager::listEntries, this, _1, _2, _3));
+}
+
+void
+RibManager::applyLocalhostConfig(const ConfigSection& section, const std::string& filename)
+{
+ m_localhostValidator.load(section, filename);
+}
+
+void
+RibManager::enableLocalhop(const ConfigSection& section, const std::string& filename)
+{
+ m_localhopValidator.load(section, filename);
+ m_isLocalhopEnabled = true;
+}
+
+void
+RibManager::disableLocalhop()
+{
+ m_isLocalhopEnabled = false;
+}
+
+void
+RibManager::registerWithNfd()
+{
+ registerTopPrefix(LOCALHOST_TOP_PREFIX);
+
+ if (m_isLocalhopEnabled) {
+ registerTopPrefix(LOCALHOP_TOP_PREFIX);
+ }
+
+ NFD_LOG_INFO("Start monitoring face create/destroy events");
+ m_faceMonitor.onNotification.connect(bind(&RibManager::onNotification, this, _1));
+ m_faceMonitor.start();
+
+ scheduleActiveFaceFetch(ACTIVE_FACE_FETCH_INTERVAL);
+}
+
+void
+RibManager::enableLocalFields()
+{
+ m_nfdController.start<ndn::nfd::FaceUpdateCommand>(
+ ControlParameters().setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED, true),
+ [] (const ControlParameters&) {
+ NFD_LOG_DEBUG("Local fields enabled");
+ },
+ [] (const ControlResponse& res) {
+ NDN_THROW(Error("Couldn't enable local fields (" + to_string(res.getCode()) +
+ " " + res.getText() + ")"));
+ });
+}
+
+void
+RibManager::beginAddRoute(const Name& name, Route route, optional<time::nanoseconds> expires,
+ const std::function<void(RibUpdateResult)>& done)
+{
+ if (expires) {
+ route.expires = time::steady_clock::now() + *expires;
+ }
+ else if (route.expires) {
+ expires = *route.expires - time::steady_clock::now();
+ }
+
+ if (expires && *expires <= 0_s) {
+ m_rib.onRouteExpiration(name, route);
+ return done(RibUpdateResult::EXPIRED);
+ }
+
+ NFD_LOG_INFO("Adding route " << name << " nexthop=" << route.faceId <<
+ " origin=" << route.origin << " cost=" << route.cost);
+
+ if (expires) {
+ auto event = m_scheduler.scheduleEvent(*expires, [=] { m_rib.onRouteExpiration(name, route); });
+ route.setExpirationEvent(event);
+ NFD_LOG_TRACE("Scheduled unregistration at: " << *route.expires);
+ }
+
+ m_registeredFaces.insert(route.faceId);
+
+ RibUpdate update;
+ update.setAction(RibUpdate::REGISTER)
+ .setName(name)
+ .setRoute(route);
+ beginRibUpdate(update, done);
+}
+
+void
+RibManager::beginRemoveRoute(const Name& name, const Route& route,
+ const std::function<void(RibUpdateResult)>& done)
+{
+ NFD_LOG_INFO("Removing route " << name << " nexthop=" << route.faceId <<
+ " origin=" << route.origin);
+
+ RibUpdate update;
+ update.setAction(RibUpdate::UNREGISTER)
+ .setName(name)
+ .setRoute(route);
+ beginRibUpdate(update, done);
+}
+
+void
+RibManager::beginRibUpdate(const RibUpdate& update,
+ const std::function<void(RibUpdateResult)>& done)
+{
+ m_rib.beginApplyUpdate(update,
+ [=] {
+ NFD_LOG_DEBUG("RIB update succeeded for " << update);
+ done(RibUpdateResult::OK);
+ },
+ [=] (uint32_t code, const std::string& error) {
+ NFD_LOG_DEBUG("RIB update failed for " << update << " (" << code << " " << error << ")");
+
+ // Since the FIB rejected the update, clean up invalid routes
+ scheduleActiveFaceFetch(1_s);
+
+ done(RibUpdateResult::ERROR);
+ });
+}
+
+void
+RibManager::registerTopPrefix(const Name& topPrefix)
+{
+ // add FIB nexthop
+ m_nfdController.start<ndn::nfd::FibAddNextHopCommand>(
+ ControlParameters().setName(Name(topPrefix).append(MGMT_MODULE_NAME))
+ .setFaceId(0),
+ [=] (const ControlParameters& res) {
+ NFD_LOG_DEBUG("Successfully registered " << topPrefix << " with NFD");
+
+ // Routes must be inserted into the RIB so route flags can be applied
+ Route route;
+ route.faceId = res.getFaceId();
+ route.origin = ndn::nfd::ROUTE_ORIGIN_APP;
+ route.flags = ndn::nfd::ROUTE_FLAG_CHILD_INHERIT;
+
+ m_rib.insert(topPrefix, route);
+
+ m_registeredFaces.insert(route.faceId);
+ },
+ [=] (const ControlResponse& res) {
+ NDN_THROW(Error("Cannot add FIB entry " + topPrefix.toUri() + " (" +
+ to_string(res.getCode()) + " " + res.getText() + ")"));
+ });
+
+ // add top prefix to the dispatcher without prefix registration
+ m_dispatcher.addTopPrefix(topPrefix, false);
+}
+
+void
+RibManager::registerEntry(const Name& topPrefix, const Interest& interest,
+ ControlParameters parameters,
+ const ndn::mgmt::CommandContinuation& done)
+{
+ if (parameters.getName().size() > FIB_MAX_DEPTH) {
+ done(ControlResponse(414, "Route prefix cannot exceed " + ndn::to_string(FIB_MAX_DEPTH) +
+ " components"));
+ return;
+ }
+
+ setFaceForSelfRegistration(interest, parameters);
+
+ // Respond since command is valid and authorized
+ done(ControlResponse(200, "Success").setBody(parameters.wireEncode()));
+
+ Route route;
+ route.faceId = parameters.getFaceId();
+ route.origin = parameters.getOrigin();
+ route.cost = parameters.getCost();
+ route.flags = parameters.getFlags();
+
+ optional<time::nanoseconds> expires;
+ if (parameters.hasExpirationPeriod() &&
+ parameters.getExpirationPeriod() != time::milliseconds::max()) {
+ expires = time::duration_cast<time::nanoseconds>(parameters.getExpirationPeriod());
+ }
+
+ beginAddRoute(parameters.getName(), std::move(route), expires, [] (RibUpdateResult) {});
+}
+
+void
+RibManager::unregisterEntry(const Name& topPrefix, const Interest& interest,
+ ControlParameters parameters,
+ const ndn::mgmt::CommandContinuation& done)
+{
+ setFaceForSelfRegistration(interest, parameters);
+
+ // Respond since command is valid and authorized
+ done(ControlResponse(200, "Success").setBody(parameters.wireEncode()));
+
+ Route route;
+ route.faceId = parameters.getFaceId();
+ route.origin = parameters.getOrigin();
+
+ beginRemoveRoute(parameters.getName(), route, [] (RibUpdateResult) {});
+}
+
+void
+RibManager::listEntries(const Name& topPrefix, const Interest& interest,
+ ndn::mgmt::StatusDatasetContext& context)
+{
+ auto now = time::steady_clock::now();
+ for (const auto& kv : m_rib) {
+ const RibEntry& entry = *kv.second;
+ ndn::nfd::RibEntry item;
+ item.setName(entry.getName());
+ for (const Route& route : entry.getRoutes()) {
+ ndn::nfd::Route r;
+ r.setFaceId(route.faceId);
+ r.setOrigin(route.origin);
+ r.setCost(route.cost);
+ r.setFlags(route.flags);
+ if (route.expires) {
+ r.setExpirationPeriod(time::duration_cast<time::milliseconds>(*route.expires - now));
+ }
+ item.addRoute(r);
+ }
+ context.append(item.wireEncode());
+ }
+ context.end();
+}
+
+void
+RibManager::setFaceForSelfRegistration(const Interest& request, ControlParameters& parameters)
+{
+ bool isSelfRegistration = (parameters.getFaceId() == 0);
+ if (isSelfRegistration) {
+ shared_ptr<lp::IncomingFaceIdTag> incomingFaceIdTag = request.getTag<lp::IncomingFaceIdTag>();
+ // NDNLPv2 says "application MUST be prepared to receive a packet without IncomingFaceId field",
+ // but it's fine to assert IncomingFaceId is available, because InternalFace lives inside NFD
+ // and is initialized synchronously with IncomingFaceId field enabled.
+ BOOST_ASSERT(incomingFaceIdTag != nullptr);
+ parameters.setFaceId(*incomingFaceIdTag);
+ }
+}
+
+ndn::mgmt::Authorization
+RibManager::makeAuthorization(const std::string& verb)
+{
+ return [this] (const Name& prefix, const Interest& interest,
+ const ndn::mgmt::ControlParameters* params,
+ const ndn::mgmt::AcceptContinuation& accept,
+ const ndn::mgmt::RejectContinuation& reject) {
+ BOOST_ASSERT(params != nullptr);
+ BOOST_ASSERT(typeid(*params) == typeid(ndn::nfd::ControlParameters));
+ BOOST_ASSERT(prefix == LOCALHOST_TOP_PREFIX || prefix == LOCALHOP_TOP_PREFIX);
+
+ ndn::ValidatorConfig& validator = prefix == LOCALHOST_TOP_PREFIX ?
+ m_localhostValidator : m_localhopValidator;
+ validator.validate(interest,
+ bind([&interest, this, accept] { extractRequester(interest, accept); }),
+ bind([reject] { reject(ndn::mgmt::RejectReply::STATUS403); }));
+ };
+}
+
+std::ostream&
+operator<<(std::ostream& os, RibManager::SlAnnounceResult res)
+{
+ switch (res) {
+ case RibManager::SlAnnounceResult::OK:
+ return os << "OK";
+ case RibManager::SlAnnounceResult::ERROR:
+ return os << "ERROR";
+ case RibManager::SlAnnounceResult::VALIDATION_FAILURE:
+ return os << "VALIDATION_FAILURE";
+ case RibManager::SlAnnounceResult::EXPIRED:
+ return os << "EXPIRED";
+ case RibManager::SlAnnounceResult::NOT_FOUND:
+ return os << "NOT_FOUND";
+ }
+ BOOST_ASSERT_MSG(false, "bad SlAnnounceResult");
+ return os;
+}
+
+RibManager::SlAnnounceResult
+RibManager::getSlAnnounceResultFromRibUpdateResult(RibUpdateResult r)
+{
+ switch (r) {
+ case RibUpdateResult::OK:
+ return SlAnnounceResult::OK;
+ case RibUpdateResult::ERROR:
+ return SlAnnounceResult::ERROR;
+ case RibUpdateResult::EXPIRED:
+ return SlAnnounceResult::EXPIRED;
+ default:
+ BOOST_ASSERT(false);
+ return SlAnnounceResult::ERROR;
+ }
+}
+
+void
+RibManager::slAnnounce(const ndn::PrefixAnnouncement& pa, uint64_t faceId,
+ time::milliseconds maxLifetime, const SlAnnounceCallback& cb)
+{
+ BOOST_ASSERT(pa.getData());
+
+ if (!m_isLocalhopEnabled) {
+ NFD_LOG_INFO("slAnnounce " << pa.getAnnouncedName() << " " << faceId <<
+ ": localhop_security unconfigured");
+ cb(SlAnnounceResult::VALIDATION_FAILURE);
+ return;
+ }
+
+ m_localhopValidator.validate(*pa.getData(),
+ [=] (const Data&) {
+ Route route(pa, faceId);
+ route.expires = std::min(route.annExpires, time::steady_clock::now() + maxLifetime);
+ beginAddRoute(pa.getAnnouncedName(), route, nullopt,
+ [=] (RibUpdateResult ribRes) {
+ auto res = getSlAnnounceResultFromRibUpdateResult(ribRes);
+ NFD_LOG_INFO("slAnnounce " << pa.getAnnouncedName() << " " << faceId << ": " << res);
+ cb(res);
+ });
+ },
+ [=] (const Data&, ndn::security::v2::ValidationError err) {
+ NFD_LOG_INFO("slAnnounce " << pa.getAnnouncedName() << " " << faceId <<
+ " validation error: " << err);
+ cb(SlAnnounceResult::VALIDATION_FAILURE);
+ }
+ );
+}
+
+void
+RibManager::slRenew(const Name& name, uint64_t faceId, time::milliseconds maxLifetime,
+ const SlAnnounceCallback& cb)
+{
+ Route routeQuery;
+ routeQuery.faceId = faceId;
+ routeQuery.origin = ndn::nfd::ROUTE_ORIGIN_PREFIXANN;
+ Route* oldRoute = m_rib.findLongestPrefix(name, routeQuery);
+
+ if (oldRoute == nullptr || !oldRoute->announcement) {
+ NFD_LOG_DEBUG("slRenew " << name << " " << faceId << ": not found");
+ return cb(SlAnnounceResult::NOT_FOUND);
+ }
+ Name routeName = oldRoute->announcement->getAnnouncedName();
+
+ Route route = *oldRoute;
+ route.expires = std::min(route.annExpires, time::steady_clock::now() + maxLifetime);
+ beginAddRoute(routeName, route, nullopt,
+ [=] (RibUpdateResult ribRes) {
+ auto res = getSlAnnounceResultFromRibUpdateResult(ribRes);
+ NFD_LOG_INFO("slRenew " << name << " " << faceId << ": " << res << " " << routeName);
+ cb(res);
+ });
+}
+
+void
+RibManager::slFindAnn(const Name& name, const SlFindAnnCallback& cb) const
+{
+ shared_ptr<RibEntry> entry;
+ auto exactMatch = m_rib.find(name);
+ if (exactMatch != m_rib.end()) {
+ entry = exactMatch->second;
+ }
+ else {
+ entry = m_rib.findParent(name);
+ }
+ if (entry == nullptr) {
+ return cb(nullopt);
+ }
+
+ auto pa = entry->getPrefixAnnouncement();
+ pa.toData(m_keyChain);
+ cb(pa);
+}
+
+void
+RibManager::fetchActiveFaces()
+{
+ NFD_LOG_DEBUG("Fetching active faces");
+
+ m_nfdController.fetch<ndn::nfd::FaceDataset>(
+ bind(&RibManager::removeInvalidFaces, this, _1),
+ bind(&RibManager::onFetchActiveFacesFailure, this, _1, _2),
+ ndn::nfd::CommandOptions());
+}
+
+void
+RibManager::onFetchActiveFacesFailure(uint32_t code, const std::string& reason)
+{
+ NFD_LOG_DEBUG("Face Status Dataset request failure " << code << " " << reason);
+ scheduleActiveFaceFetch(ACTIVE_FACE_FETCH_INTERVAL);
+}
+
+void
+RibManager::onFaceDestroyedEvent(uint64_t faceId)
+{
+ m_rib.beginRemoveFace(faceId);
+ m_registeredFaces.erase(faceId);
+}
+
+void
+RibManager::scheduleActiveFaceFetch(const time::seconds& timeToWait)
+{
+ m_activeFaceFetchEvent = m_scheduler.scheduleEvent(timeToWait, [this] { fetchActiveFaces(); });
+}
+
+void
+RibManager::removeInvalidFaces(const std::vector<ndn::nfd::FaceStatus>& activeFaces)
+{
+ NFD_LOG_DEBUG("Checking for invalid face registrations");
+
+ FaceIdSet activeFaceIds;
+ for (const auto& faceStatus : activeFaces) {
+ activeFaceIds.insert(faceStatus.getFaceId());
+ }
+
+ // Look for face IDs that were registered but not active to find missed
+ // face destroyed events
+ for (auto faceId : m_registeredFaces) {
+ if (activeFaceIds.count(faceId) == 0) {
+ NFD_LOG_DEBUG("Removing invalid face ID: " << faceId);
+ m_scheduler.scheduleEvent(0_ns, [this, faceId] { this->onFaceDestroyedEvent(faceId); });
+ }
+ }
+
+ // Reschedule the check for future clean up
+ scheduleActiveFaceFetch(ACTIVE_FACE_FETCH_INTERVAL);
+}
+
+void
+RibManager::onNotification(const ndn::nfd::FaceEventNotification& notification)
+{
+ NFD_LOG_TRACE("onNotification: " << notification);
+
+ if (notification.getKind() == ndn::nfd::FACE_EVENT_DESTROYED) {
+ NFD_LOG_DEBUG("Received notification for destroyed faceId: " << notification.getFaceId());
+ m_scheduler.scheduleEvent(0_ns, [this, id = notification.getFaceId()] { onFaceDestroyedEvent(id); });
+ }
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/daemon/rib/rib-manager.hpp b/daemon/rib/rib-manager.hpp
new file mode 100644
index 0000000..503dc2f
--- /dev/null
+++ b/daemon/rib/rib-manager.hpp
@@ -0,0 +1,268 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_RIB_RIB_MANAGER_HPP
+#define NFD_DAEMON_RIB_RIB_MANAGER_HPP
+
+#include "fib-updater.hpp"
+#include "rib.hpp"
+
+#include "core/config-file.hpp"
+#include "core/manager-base.hpp"
+
+#include <ndn-cxx/security/validator-config.hpp>
+#include <ndn-cxx/mgmt/nfd/controller.hpp>
+#include <ndn-cxx/mgmt/nfd/face-event-notification.hpp>
+#include <ndn-cxx/mgmt/nfd/face-monitor.hpp>
+#include <ndn-cxx/util/scheduler.hpp>
+
+namespace nfd {
+namespace rib {
+
+/**
+ * @brief Serve commands and datasets in NFD RIB management protocol.
+ */
+class RibManager : public nfd::ManagerBase
+{
+public:
+ class Error : public std::runtime_error
+ {
+ public:
+ using std::runtime_error::runtime_error;
+ };
+
+ RibManager(Rib& rib, ndn::Face& face, ndn::KeyChain& keyChain, ndn::nfd::Controller& nfdController,
+ Dispatcher& dispatcher, ndn::util::Scheduler& scheduler);
+
+ /**
+ * @brief Apply localhost_security configuration.
+ */
+ void
+ applyLocalhostConfig(const ConfigSection& section, const std::string& filename);
+
+ /**
+ * @brief Apply localhop_security configuration and allow accepting commands on
+ * /localhop/nfd/rib prefix.
+ */
+ void
+ enableLocalhop(const ConfigSection& section, const std::string& filename);
+
+ /**
+ * @brief Disallow accepting commands on /localhop/nfd/rib prefix.
+ */
+ void
+ disableLocalhop();
+
+ /**
+ * @brief Start accepting commands and dataset requests.
+ */
+ void
+ registerWithNfd();
+
+ /**
+ * @brief Enable NDNLP IncomingFaceId field in order to support self-registration commands.
+ */
+ void
+ enableLocalFields();
+
+public: // self-learning support
+ enum class SlAnnounceResult {
+ OK, ///< RIB and FIB have been updated
+ ERROR, ///< unspecified error
+ VALIDATION_FAILURE, ///< the announcement cannot be verified against the trust schema
+ EXPIRED, ///< the announcement has expired
+ NOT_FOUND, ///< route does not exist (slRenew only)
+ };
+
+ using SlAnnounceCallback = std::function<void(SlAnnounceResult res)>;
+ using SlFindAnnCallback = std::function<void(optional<ndn::PrefixAnnouncement>)>;
+
+ /** \brief Insert a route by prefix announcement from self-learning strategy.
+ * \param pa A prefix announcement. It must contain the Data.
+ * \param faceId Face on which the announcement arrives.
+ * \param maxLifetime Maximum route lifetime as imposed by self-learning strategy.
+ * \param cb Callback to receive the operation result.
+ *
+ * If \p pa passes validation and is unexpired, inserts or replaces a route for the announced
+ * name and faceId whose lifetime is set to the earlier of now+maxLifetime or prefix
+ * announcement expiration time, updates FIB, and invokes \p cb with SlAnnounceResult::OK.
+ * In case \p pa expires when validation completes, invokes \p cb with SlAnnounceResult::EXPIRED.
+ * If \p pa cannot be verified by the trust schema given in rib.localhop_security config key,
+ * or the relevant config has not been loaded via \c enableLocalHop, invokes \p cb with
+ * SlAnnounceResult::VALIDATION_FAILURE.
+ *
+ * Self-learning strategy invokes this method after receiving a Data carrying a prefix
+ * announcement.
+ */
+ void
+ slAnnounce(const ndn::PrefixAnnouncement& pa, uint64_t faceId, time::milliseconds maxLifetime,
+ const SlAnnounceCallback& cb);
+
+ /** \brief Renew a route created by prefix announcement from self-learning strategy.
+ * \param name Data name, for finding RIB entry by longest-prefix-match.
+ * \param faceId Nexthop face.
+ * \param maxLifetime Maximum route lifetime as imposed by self-learning strategy.
+ * \param cb Callback to receive the operation result.
+ *
+ * If the specified route exists, prolongs its lifetime to the earlier of now+maxLifetime or
+ * prefix announcement expiration time, and invokes \p cb with SlAnnounceResult::OK.
+ * If the prefix announcement has expired, invokes \p cb with SlAnnounceResult::EXPIRED.
+ * If the route is not found, invokes \p cb with SlAnnounceResult::NOT_FOUND.
+ *
+ * Self-learning strategy invokes this method after an Interest forwarded via a learned route
+ * is satisfied.
+ *
+ * \bug In current implementation, if an slAnnounce operation is in progress to create a Route
+ * or replace a prefix announcement, slRenew could fail because Route does not exist in
+ * existing RIB, or overwrite the new prefix announcement with an old one.
+ */
+ void
+ slRenew(const Name& name, uint64_t faceId, time::milliseconds maxLifetime,
+ const SlAnnounceCallback& cb);
+
+ /** \brief Retrieve an outgoing prefix announcement for self-learning strategy.
+ * \param name Data name.
+ * \param cb Callback to receive a prefix announcement that announces a prefix of \p name, or
+ * nullopt if no RIB entry is found by longest-prefix-match of \p name.
+ *
+ * Self-learning strategy invokes this method before sending a Data in reply to a discovery
+ * Interest, so as to attach a prefix announcement onto that Data.
+ *
+ * \bug In current implementation, if an slAnnounce operation is in progress, slFindAnn does not
+ * wait for that operation to complete and its result reflects the prior RIB state.
+ */
+ void
+ slFindAnn(const Name& name, const SlFindAnnCallback& cb) const;
+
+private: // RIB and FibUpdater actions
+ enum class RibUpdateResult
+ {
+ OK,
+ ERROR,
+ EXPIRED,
+ };
+
+ static SlAnnounceResult
+ getSlAnnounceResultFromRibUpdateResult(RibUpdateResult r);
+
+ /** \brief Start adding a route to RIB and FIB.
+ * \param name route name
+ * \param route route parameters; may contain absolute expiration time
+ * \param expires relative expiration time; if specified, overwrites \c route.expires
+ * \param done completion callback
+ */
+ void
+ beginAddRoute(const Name& name, Route route, optional<time::nanoseconds> expires,
+ const std::function<void(RibUpdateResult)>& done);
+
+ /** \brief Start removing a route from RIB and FIB.
+ * \param name route name
+ * \param route route parameters
+ * \param done completion callback
+ */
+ void
+ beginRemoveRoute(const Name& name, const Route& route,
+ const std::function<void(RibUpdateResult)>& done);
+
+ void
+ beginRibUpdate(const RibUpdate& update, const std::function<void(RibUpdateResult)>& done);
+
+private: // management Dispatcher related
+ void
+ registerTopPrefix(const Name& topPrefix);
+
+ /** \brief Serve rib/register command.
+ */
+ void
+ registerEntry(const Name& topPrefix, const Interest& interest,
+ ControlParameters parameters,
+ const ndn::mgmt::CommandContinuation& done);
+
+ /** \brief Serve rib/unregister command.
+ */
+ void
+ unregisterEntry(const Name& topPrefix, const Interest& interest,
+ ControlParameters parameters,
+ const ndn::mgmt::CommandContinuation& done);
+
+ /** \brief Serve rib/list dataset.
+ */
+ void
+ listEntries(const Name& topPrefix, const Interest& interest,
+ ndn::mgmt::StatusDatasetContext& context);
+
+ void
+ setFaceForSelfRegistration(const Interest& request, ControlParameters& parameters);
+
+ ndn::mgmt::Authorization
+ makeAuthorization(const std::string& verb) override;
+
+private: // Face monitor
+ void
+ fetchActiveFaces();
+
+ void
+ onFetchActiveFacesFailure(uint32_t code, const std::string& reason);
+
+ void
+ onFaceDestroyedEvent(uint64_t faceId);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ void
+ scheduleActiveFaceFetch(const time::seconds& timeToWait);
+
+ void
+ removeInvalidFaces(const std::vector<ndn::nfd::FaceStatus>& activeFaces);
+
+ void
+ onNotification(const ndn::nfd::FaceEventNotification& notification);
+
+public:
+ static const Name LOCALHOP_TOP_PREFIX;
+
+private:
+ Rib& m_rib;
+ ndn::KeyChain& m_keyChain;
+ ndn::nfd::Controller& m_nfdController;
+ Dispatcher& m_dispatcher;
+ ndn::util::Scheduler& m_scheduler;
+
+ ndn::nfd::FaceMonitor m_faceMonitor;
+ ndn::ValidatorConfig m_localhostValidator;
+ ndn::ValidatorConfig m_localhopValidator;
+ bool m_isLocalhopEnabled;
+
+ ndn::util::scheduler::ScopedEventId m_activeFaceFetchEvent;
+ using FaceIdSet = std::set<uint64_t>;
+ FaceIdSet m_registeredFaces; ///< contains FaceIds with one or more Routes in the RIB
+};
+
+std::ostream&
+operator<<(std::ostream& os, RibManager::SlAnnounceResult res);
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_DAEMON_RIB_RIB_MANAGER_HPP
diff --git a/daemon/rib/rib-update-batch.cpp b/daemon/rib/rib-update-batch.cpp
new file mode 100644
index 0000000..e639ae3
--- /dev/null
+++ b/daemon/rib/rib-update-batch.cpp
@@ -0,0 +1,63 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "rib-update-batch.hpp"
+
+namespace nfd {
+namespace rib {
+
+RibUpdateBatch::RibUpdateBatch(uint64_t faceId)
+ : m_faceId(faceId)
+{
+}
+
+void
+RibUpdateBatch::add(const RibUpdate& update)
+{
+ BOOST_ASSERT(m_faceId == update.getRoute().faceId);
+
+ m_updates.push_back(update);
+}
+
+RibUpdateBatch::const_iterator
+RibUpdateBatch::begin() const
+{
+ return m_updates.begin();
+}
+
+RibUpdateBatch::const_iterator
+RibUpdateBatch::end() const
+{
+ return m_updates.end();
+}
+
+size_t
+RibUpdateBatch::size() const
+{
+ return m_updates.size();
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/daemon/rib/rib-update-batch.hpp b/daemon/rib/rib-update-batch.hpp
new file mode 100644
index 0000000..f72a9e8
--- /dev/null
+++ b/daemon/rib/rib-update-batch.hpp
@@ -0,0 +1,77 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_RIB_RIB_UPDATE_BATCH_HPP
+#define NFD_DAEMON_RIB_RIB_UPDATE_BATCH_HPP
+
+#include "rib-update.hpp"
+
+#include <list>
+
+namespace nfd {
+namespace rib {
+
+typedef std::list<RibUpdate> RibUpdateList;
+
+/** \brief Represents a collection of RibUpdates to be applied to a single FaceId.
+ */
+class RibUpdateBatch
+{
+public:
+ typedef RibUpdateList::const_iterator const_iterator;
+
+ explicit
+ RibUpdateBatch(uint64_t faceId);
+
+ uint64_t
+ getFaceId() const;
+
+ void
+ add(const RibUpdate& update);
+
+ const_iterator
+ begin() const;
+
+ const_iterator
+ end() const;
+
+ size_t
+ size() const;
+
+private:
+ uint64_t m_faceId;
+ RibUpdateList m_updates;
+};
+
+inline uint64_t
+RibUpdateBatch::getFaceId() const
+{
+ return m_faceId;
+}
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_DAEMON_RIB_RIB_UPDATE_BATCH_HPP
diff --git a/daemon/rib/rib-update.cpp b/daemon/rib/rib-update.cpp
new file mode 100644
index 0000000..8efe327
--- /dev/null
+++ b/daemon/rib/rib-update.cpp
@@ -0,0 +1,68 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "rib-update.hpp"
+
+namespace nfd {
+namespace rib {
+
+RibUpdate::RibUpdate()
+{
+
+}
+
+std::ostream&
+operator<<(std::ostream& os, const RibUpdate::Action action)
+{
+ switch (action) {
+ case RibUpdate::REGISTER:
+ os << "REGISTER";
+ break;
+ case RibUpdate::UNREGISTER:
+ os << "UNREGISTER";
+ break;
+ case RibUpdate::REMOVE_FACE:
+ os << "REMOVE_FACE";
+ break;
+ }
+
+ return os;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const RibUpdate& update)
+{
+ os << "RibUpdate {\n";
+ os << " Name: " << update.getName() << "\n";
+ os << " Action: " << update.getAction() << "\n";
+ os << " " << update.getRoute() << "\n";
+ os << "}";
+
+ return os;
+}
+
+
+} // namespace rib
+} // namespace nfd
diff --git a/daemon/rib/rib-update.hpp b/daemon/rib/rib-update.hpp
new file mode 100644
index 0000000..ad5a390
--- /dev/null
+++ b/daemon/rib/rib-update.hpp
@@ -0,0 +1,128 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_RIB_RIB_UPDATE_HPP
+#define NFD_DAEMON_RIB_RIB_UPDATE_HPP
+
+#include "core/common.hpp"
+#include "route.hpp"
+
+namespace nfd {
+namespace rib {
+
+/** RibUpdate
+ * \brief represents a route that will be added to or removed from a namespace
+ *
+ * \note This type is copyable so that it can be stored in STL containers.
+ */
+class RibUpdate
+{
+public:
+ enum Action {
+ REGISTER = 0,
+ UNREGISTER = 1,
+
+ /** \brief An update triggered by a face destruction notification
+ *
+ * \note indicates a Route needs to be removed after a face is destroyed
+ */
+ REMOVE_FACE = 2
+ };
+
+ RibUpdate();
+
+ RibUpdate&
+ setAction(Action action);
+
+ Action
+ getAction() const;
+
+ RibUpdate&
+ setName(const Name& name);
+
+ const Name&
+ getName() const;
+
+ RibUpdate&
+ setRoute(const Route& route);
+
+ const Route&
+ getRoute() const;
+
+private:
+ Action m_action;
+ Name m_name;
+ Route m_route;
+};
+
+inline RibUpdate&
+RibUpdate::setAction(Action action)
+{
+ m_action = action;
+ return *this;
+}
+
+inline RibUpdate::Action
+RibUpdate::getAction() const
+{
+ return m_action;
+}
+
+inline RibUpdate&
+RibUpdate::setName(const Name& name)
+{
+ m_name = name;
+ return *this;
+}
+
+inline const Name&
+RibUpdate::getName() const
+{
+ return m_name;
+}
+
+inline RibUpdate&
+RibUpdate::setRoute(const Route& route)
+{
+ m_route = route;
+ return *this;
+}
+
+inline const Route&
+RibUpdate::getRoute() const
+{
+ return m_route;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const RibUpdate::Action action);
+
+std::ostream&
+operator<<(std::ostream& os, const RibUpdate& update);
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_DAEMON_RIB_RIB_UPDATE_HPP
diff --git a/daemon/rib/rib.cpp b/daemon/rib/rib.cpp
new file mode 100644
index 0000000..bbd72f1
--- /dev/null
+++ b/daemon/rib/rib.cpp
@@ -0,0 +1,528 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "rib.hpp"
+#include "fib-updater.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+namespace rib {
+
+NFD_LOG_INIT(Rib);
+
+bool
+operator<(const RibRouteRef& lhs, const RibRouteRef& rhs)
+{
+ return std::tie(lhs.entry->getName(), lhs.route->faceId, lhs.route->origin) <
+ std::tie(rhs.entry->getName(), rhs.route->faceId, rhs.route->origin);
+}
+
+static inline bool
+sortRoutes(const Route& lhs, const Route& rhs)
+{
+ return lhs.faceId < rhs.faceId;
+}
+
+Rib::Rib()
+ : m_nItems(0)
+ , m_isUpdateInProgress(false)
+{
+}
+
+void
+Rib::setFibUpdater(FibUpdater* updater)
+{
+ m_fibUpdater = updater;
+}
+
+Rib::const_iterator
+Rib::find(const Name& prefix) const
+{
+ return m_rib.find(prefix);
+}
+
+Route*
+Rib::find(const Name& prefix, const Route& route) const
+{
+ auto ribIt = m_rib.find(prefix);
+
+ // Name prefix exists
+ if (ribIt != m_rib.end()) {
+ shared_ptr<RibEntry> entry = ribIt->second;
+ auto routeIt = entry->findRoute(route);
+ if (routeIt != entry->end()) {
+ return &*routeIt;
+ }
+ }
+
+ return nullptr;
+}
+
+Route*
+Rib::findLongestPrefix(const Name& prefix, const Route& route) const
+{
+ Route* existingRoute = find(prefix, route);
+ if (existingRoute == nullptr) {
+ auto parent = findParent(prefix);
+ if (parent) {
+ existingRoute = find(parent->getName(), route);
+ }
+ }
+
+ return existingRoute;
+}
+
+void
+Rib::insert(const Name& prefix, const Route& route)
+{
+ auto ribIt = m_rib.find(prefix);
+
+ // Name prefix exists
+ if (ribIt != m_rib.end()) {
+ shared_ptr<RibEntry> entry(ribIt->second);
+
+ RibEntry::iterator entryIt;
+ bool didInsert = false;
+ std::tie(entryIt, didInsert) = entry->insertRoute(route);
+
+ if (didInsert) {
+ // The route was new and we successfully inserted it.
+ m_nItems++;
+
+ afterAddRoute(RibRouteRef{entry, entryIt});
+
+ // Register with face lookup table
+ m_faceMap[route.faceId].push_back(entry);
+ }
+ else {
+ // Route exists, update fields
+ // First cancel old scheduled event, if any, then set the EventId to new one
+ if (entryIt->getExpirationEvent()) {
+ NFD_LOG_TRACE("Cancelling expiration event for " << entry->getName() << " " << *entryIt);
+ entryIt->cancelExpirationEvent();
+ }
+
+ *entryIt = route;
+ }
+ }
+ else {
+ // New name prefix
+ auto entry = make_shared<RibEntry>();
+
+ m_rib[prefix] = entry;
+ m_nItems++;
+
+ entry->setName(prefix);
+ auto routeIt = entry->insertRoute(route).first;
+
+ // Find prefix's parent
+ shared_ptr<RibEntry> parent = findParent(prefix);
+
+ // Add self to parent's children
+ if (parent != nullptr) {
+ parent->addChild(entry);
+ }
+
+ RibEntryList children = findDescendants(prefix);
+
+ for (const auto& child : children) {
+ if (child->getParent() == parent) {
+ // Remove child from parent and inherit parent's child
+ if (parent != nullptr) {
+ parent->removeChild(child);
+ }
+
+ entry->addChild(child);
+ }
+ }
+
+ // Register with face lookup table
+ m_faceMap[route.faceId].push_back(entry);
+
+ // do something after inserting an entry
+ afterInsertEntry(prefix);
+ afterAddRoute(RibRouteRef{entry, routeIt});
+ }
+}
+
+void
+Rib::erase(const Name& prefix, const Route& route)
+{
+ auto ribIt = m_rib.find(prefix);
+
+ // Name prefix exists
+ if (ribIt != m_rib.end()) {
+ shared_ptr<RibEntry> entry = ribIt->second;
+ auto routeIt = entry->findRoute(route);
+
+ if (routeIt != entry->end()) {
+ beforeRemoveRoute(RibRouteRef{entry, routeIt});
+
+ auto faceId = route.faceId;
+ entry->eraseRoute(routeIt);
+ m_nItems--;
+
+ // If this RibEntry no longer has this faceId, unregister from face lookup table
+ if (!entry->hasFaceId(faceId)) {
+ m_faceMap[faceId].remove(entry);
+ }
+
+ // If a RibEntry's route list is empty, remove it from the tree
+ if (entry->getRoutes().empty()) {
+ eraseEntry(ribIt);
+ }
+ }
+ }
+}
+
+void
+Rib::onRouteExpiration(const Name& prefix, const Route& route)
+{
+ NFD_LOG_DEBUG(route << " for " << prefix << " has expired");
+
+ RibUpdate update;
+ update.setAction(RibUpdate::UNREGISTER)
+ .setName(prefix)
+ .setRoute(route);
+
+ beginApplyUpdate(update, nullptr, nullptr);
+}
+
+shared_ptr<RibEntry>
+Rib::findParent(const Name& prefix) const
+{
+ for (int i = prefix.size() - 1; i >= 0; i--) {
+ auto it = m_rib.find(prefix.getPrefix(i));
+ if (it != m_rib.end()) {
+ return it->second;
+ }
+ }
+
+ return nullptr;
+}
+
+std::list<shared_ptr<RibEntry>>
+Rib::findDescendants(const Name& prefix) const
+{
+ std::list<shared_ptr<RibEntry>> children;
+
+ RibTable::const_iterator it = m_rib.find(prefix);
+ if (it != m_rib.end()) {
+ ++it;
+ for (; it != m_rib.end(); ++it) {
+ if (prefix.isPrefixOf(it->first)) {
+ children.push_back((it->second));
+ }
+ else {
+ break;
+ }
+ }
+ }
+
+ return children;
+}
+
+std::list<shared_ptr<RibEntry>>
+Rib::findDescendantsForNonInsertedName(const Name& prefix) const
+{
+ std::list<shared_ptr<RibEntry>> children;
+
+ for (const auto& pair : m_rib) {
+ if (prefix.isPrefixOf(pair.first)) {
+ children.push_back(pair.second);
+ }
+ }
+
+ return children;
+}
+
+Rib::RibTable::iterator
+Rib::eraseEntry(RibTable::iterator it)
+{
+ // Entry does not exist
+ if (it == m_rib.end()) {
+ return m_rib.end();
+ }
+
+ shared_ptr<RibEntry> entry(it->second);
+
+ shared_ptr<RibEntry> parent = entry->getParent();
+
+ // Remove self from parent's children
+ if (parent != nullptr) {
+ parent->removeChild(entry);
+ }
+
+ for (auto childIt = entry->getChildren().begin(); childIt != entry->getChildren().end(); ) {
+ shared_ptr<RibEntry> child = *childIt;
+
+ // Advance iterator so it is not invalidated by removal
+ ++childIt;
+
+ // Remove children from self
+ entry->removeChild(child);
+
+ // Update parent's children
+ if (parent != nullptr) {
+ parent->addChild(child);
+ }
+ }
+
+ auto nextIt = m_rib.erase(it);
+
+ // do something after erasing an entry.
+ afterEraseEntry(entry->getName());
+
+ return nextIt;
+}
+
+Rib::RouteSet
+Rib::getAncestorRoutes(const RibEntry& entry) const
+{
+ RouteSet ancestorRoutes(&sortRoutes);
+
+ shared_ptr<RibEntry> parent = entry.getParent();
+
+ while (parent != nullptr) {
+ for (const Route& route : parent->getRoutes()) {
+ if (route.isChildInherit()) {
+ ancestorRoutes.insert(route);
+ }
+ }
+
+ if (parent->hasCapture()) {
+ break;
+ }
+
+ parent = parent->getParent();
+ }
+
+ return ancestorRoutes;
+}
+
+Rib::RouteSet
+Rib::getAncestorRoutes(const Name& name) const
+{
+ RouteSet ancestorRoutes(&sortRoutes);
+
+ shared_ptr<RibEntry> parent = findParent(name);
+
+ while (parent != nullptr) {
+ for (const Route& route : parent->getRoutes()) {
+ if (route.isChildInherit()) {
+ ancestorRoutes.insert(route);
+ }
+ }
+
+ if (parent->hasCapture()) {
+ break;
+ }
+
+ parent = parent->getParent();
+ }
+
+ return ancestorRoutes;
+}
+
+void
+Rib::beginApplyUpdate(const RibUpdate& update,
+ const Rib::UpdateSuccessCallback& onSuccess,
+ const Rib::UpdateFailureCallback& onFailure)
+{
+ BOOST_ASSERT(m_fibUpdater != nullptr);
+
+ addUpdateToQueue(update, onSuccess, onFailure);
+
+ sendBatchFromQueue();
+}
+
+void
+Rib::beginRemoveFace(uint64_t faceId)
+{
+ for (const auto& nameAndRoute : findRoutesWithFaceId(faceId)) {
+ RibUpdate update;
+ update.setAction(RibUpdate::REMOVE_FACE)
+ .setName(nameAndRoute.first)
+ .setRoute(nameAndRoute.second);
+
+ addUpdateToQueue(update, nullptr, nullptr);
+ }
+
+ sendBatchFromQueue();
+}
+
+void
+Rib::addUpdateToQueue(const RibUpdate& update,
+ const Rib::UpdateSuccessCallback& onSuccess,
+ const Rib::UpdateFailureCallback& onFailure)
+{
+ RibUpdateBatch batch(update.getRoute().faceId);
+ batch.add(update);
+
+ UpdateQueueItem item{batch, onSuccess, onFailure};
+ m_updateBatches.push_back(std::move(item));
+}
+
+void
+Rib::sendBatchFromQueue()
+{
+ if (m_updateBatches.empty() || m_isUpdateInProgress) {
+ return;
+ }
+
+ m_isUpdateInProgress = true;
+
+ UpdateQueueItem item = std::move(m_updateBatches.front());
+ m_updateBatches.pop_front();
+
+ RibUpdateBatch& batch = item.batch;
+
+ // Until task #1698, each RibUpdateBatch contains exactly one RIB update
+ BOOST_ASSERT(batch.size() == 1);
+
+ auto fibSuccessCb = bind(&Rib::onFibUpdateSuccess, this, batch, _1, item.managerSuccessCallback);
+ auto fibFailureCb = bind(&Rib::onFibUpdateFailure, this, item.managerFailureCallback, _1, _2);
+
+#ifdef WITH_TESTS
+ if (mockFibResponse != nullptr) {
+ m_fibUpdater->computeAndSendFibUpdates(batch, bind([]{}), bind([]{}));
+ bool shouldFibSucceed = mockFibResponse(batch);
+ if (wantMockFibResponseOnce) {
+ mockFibResponse = nullptr;
+ }
+ if (shouldFibSucceed) {
+ fibSuccessCb(m_fibUpdater->m_inheritedRoutes);
+ }
+ else {
+ fibFailureCb(504, "mocked failure");
+ }
+ return;
+ }
+#endif
+
+ m_fibUpdater->computeAndSendFibUpdates(batch, fibSuccessCb, fibFailureCb);
+}
+
+void
+Rib::onFibUpdateSuccess(const RibUpdateBatch& batch,
+ const RibUpdateList& inheritedRoutes,
+ const Rib::UpdateSuccessCallback& onSuccess)
+{
+ for (const RibUpdate& update : batch) {
+ switch (update.getAction()) {
+ case RibUpdate::REGISTER:
+ insert(update.getName(), update.getRoute());
+ break;
+ case RibUpdate::UNREGISTER:
+ case RibUpdate::REMOVE_FACE:
+ erase(update.getName(), update.getRoute());
+ break;
+ }
+ }
+
+ // Add and remove precalculated inherited routes to RibEntries
+ modifyInheritedRoutes(inheritedRoutes);
+
+ m_isUpdateInProgress = false;
+
+ if (onSuccess != nullptr) {
+ onSuccess();
+ }
+
+ // Try to advance the batch queue
+ sendBatchFromQueue();
+}
+
+void
+Rib::onFibUpdateFailure(const Rib::UpdateFailureCallback& onFailure,
+ uint32_t code, const std::string& error)
+{
+ m_isUpdateInProgress = false;
+
+ if (onFailure != nullptr) {
+ onFailure(code, error);
+ }
+
+ // Try to advance the batch queue
+ sendBatchFromQueue();
+}
+
+void
+Rib::modifyInheritedRoutes(const RibUpdateList& inheritedRoutes)
+{
+ for (const RibUpdate& update : inheritedRoutes) {
+ auto ribIt = m_rib.find(update.getName());
+ BOOST_ASSERT(ribIt != m_rib.end());
+ shared_ptr<RibEntry> entry(ribIt->second);
+
+ switch (update.getAction()) {
+ case RibUpdate::REGISTER:
+ entry->addInheritedRoute(update.getRoute());
+ break;
+ case RibUpdate::UNREGISTER:
+ entry->removeInheritedRoute(update.getRoute());
+ break;
+ case RibUpdate::REMOVE_FACE:
+ break;
+ }
+ }
+}
+
+std::list<Rib::NameAndRoute>
+Rib::findRoutesWithFaceId(uint64_t faceId)
+{
+ std::list<NameAndRoute> routes;
+
+ auto lookupIt = m_faceMap.find(faceId);
+ if (lookupIt == m_faceMap.end()) {
+ // No RIB entries have this face
+ return routes;
+ }
+
+ // For each RIB entry that has faceId
+ for (const auto& entry : lookupIt->second) {
+ // Find the routes in the entry
+ for (const Route& route : *entry) {
+ if (route.faceId == faceId) {
+ routes.emplace_back(entry->getName(), route);
+ }
+ }
+ }
+
+ return routes;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Rib& rib)
+{
+ for (const auto& item : rib) {
+ os << *item.second << "\n";
+ }
+
+ return os;
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/daemon/rib/rib.hpp b/daemon/rib/rib.hpp
new file mode 100644
index 0000000..ea848f8
--- /dev/null
+++ b/daemon/rib/rib.hpp
@@ -0,0 +1,307 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_RIB_RIB_HPP
+#define NFD_DAEMON_RIB_RIB_HPP
+
+#include "rib-entry.hpp"
+#include "rib-update-batch.hpp"
+
+#include <ndn-cxx/mgmt/nfd/control-parameters.hpp>
+
+namespace nfd {
+namespace rib {
+
+using ndn::nfd::ControlParameters;
+
+class FibUpdater;
+
+/** \brief references a route
+ */
+struct RibRouteRef
+{
+ shared_ptr<RibEntry> entry;
+ RibEntry::const_iterator route;
+};
+
+bool
+operator<(const RibRouteRef& lhs, const RibRouteRef& rhs);
+
+/** \brief represents the Routing Information Base
+
+ The Routing Information Base contains a collection of Routes, each
+ represents a piece of static or dynamic routing information
+ registered by applications, operators, or NFD itself. Routes
+ associated with the same namespace are collected into a RIB entry.
+ */
+class Rib : noncopyable
+{
+public:
+ typedef std::list<shared_ptr<RibEntry>> RibEntryList;
+ typedef std::map<Name, shared_ptr<RibEntry>> RibTable;
+ typedef RibTable::const_iterator const_iterator;
+ typedef std::map<uint64_t, std::list<shared_ptr<RibEntry>>> FaceLookupTable;
+ typedef bool (*RouteComparePredicate)(const Route&, const Route&);
+ typedef std::set<Route, RouteComparePredicate> RouteSet;
+
+ Rib();
+
+ void
+ setFibUpdater(FibUpdater* updater);
+
+ const_iterator
+ find(const Name& prefix) const;
+
+ Route*
+ find(const Name& prefix, const Route& route) const;
+
+ Route*
+ findLongestPrefix(const Name& prefix, const Route& route) const;
+
+ const_iterator
+ begin() const;
+
+ const_iterator
+ end() const;
+
+ size_t
+ size() const;
+
+ bool
+ empty() const;
+
+ shared_ptr<RibEntry>
+ findParent(const Name& prefix) const;
+
+ /** \brief finds namespaces under the passed prefix
+ * \return{ a list of entries which are under the passed prefix }
+ */
+ std::list<shared_ptr<RibEntry>>
+ findDescendants(const Name& prefix) const;
+
+ /** \brief finds namespaces under the passed prefix
+ *
+ * \note Unlike findDescendants, needs to find where prefix would fit in tree
+ * before collecting list of descendant prefixes
+ *
+ * \return{ a list of entries which would be under the passed prefix if the prefix
+ * existed in the RIB }
+ */
+ std::list<shared_ptr<RibEntry>>
+ findDescendantsForNonInsertedName(const Name& prefix) const;
+
+public:
+ using UpdateSuccessCallback = std::function<void()>;
+ using UpdateFailureCallback = std::function<void(uint32_t code, const std::string& error)>;
+
+ /** \brief passes the provided RibUpdateBatch to FibUpdater to calculate and send FibUpdates.
+ *
+ * If the FIB is updated successfully, onFibUpdateSuccess() will be called, and the
+ * RIB will be updated
+ *
+ * If the FIB update fails, onFibUpdateFailure() will be called, and the RIB will not
+ * be updated.
+ */
+ void
+ beginApplyUpdate(const RibUpdate& update,
+ const UpdateSuccessCallback& onSuccess,
+ const UpdateFailureCallback& onFailure);
+
+ /** \brief starts the FIB update process when a face has been destroyed
+ */
+ void
+ beginRemoveFace(uint64_t faceId);
+
+ void
+ onFibUpdateSuccess(const RibUpdateBatch& batch,
+ const RibUpdateList& inheritedRoutes,
+ const Rib::UpdateSuccessCallback& onSuccess);
+
+ void
+ onFibUpdateFailure(const Rib::UpdateFailureCallback& onFailure,
+ uint32_t code, const std::string& error);
+
+ void
+ onRouteExpiration(const Name& prefix, const Route& route);
+
+ void
+ insert(const Name& prefix, const Route& route);
+
+private:
+ /** \brief adds the passed update to a RibUpdateBatch and adds the batch to
+ * the end of the update queue.
+ *
+ * If an update is not in progress, the front update batch in the queue will be
+ * processed by the RIB.
+ *
+ * If an update is in progress, the added update will eventually be processed
+ * when it reaches the front of the queue; after other update batches are
+ * processed, the queue is advanced.
+ */
+ void
+ addUpdateToQueue(const RibUpdate& update,
+ const Rib::UpdateSuccessCallback& onSuccess,
+ const Rib::UpdateFailureCallback& onFailure);
+
+ /** \brief Attempts to send the front update batch in the queue.
+ *
+ * If an update is not in progress, the front update batch in the queue will be
+ * sent to the RIB for processing.
+ *
+ * If an update is in progress, nothing will be done.
+ */
+ void
+ sendBatchFromQueue();
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+#ifdef WITH_TESTS
+ /** \brief In unit tests, mock FIB update result.
+ *
+ * If the callback is not nullptr, sendBatchFromQueue() immediately succeeds or fails according
+ * to the return value of callback function.
+ */
+ std::function<bool(const RibUpdateBatch&)> mockFibResponse;
+
+ bool wantMockFibResponseOnce = false; ///< if true, mockFibResponse is cleared after every use.
+#endif
+
+ void
+ erase(const Name& prefix, const Route& route);
+
+private:
+ RibTable::iterator
+ eraseEntry(RibTable::iterator it);
+
+ void
+ updateRib(const RibUpdateBatch& batch);
+
+ /** \brief returns routes inherited from the entry's ancestors
+ *
+ * \return{ a list of inherited routes }
+ */
+ RouteSet
+ getAncestorRoutes(const RibEntry& entry) const;
+
+ /** \brief returns routes inherited from the parent of the name and the parent's ancestors
+ *
+ * \note A parent is first found for the passed name before inherited routes are collected
+ *
+ * \return{ a list of inherited routes }
+ */
+ RouteSet
+ getAncestorRoutes(const Name& name) const;
+
+ /** \brief applies the passed inheritedRoutes and their actions to the corresponding RibEntries'
+ * inheritedRoutes lists
+ */
+ void
+ modifyInheritedRoutes(const RibUpdateList& inheritedRoutes);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ using NameAndRoute = std::pair<const Name&, const Route&>;
+
+ std::list<NameAndRoute>
+ findRoutesWithFaceId(uint64_t faceId);
+
+public:
+ /** \brief signals after a RIB entry is inserted
+ *
+ * A RIB entry is inserted when the first route associated with a
+ * certain namespace is added.
+ */
+ ndn::util::signal::Signal<Rib, Name> afterInsertEntry;
+
+ /** \brief signals after a RIB entry is erased
+ *
+ * A RIB entry is erased when the last route associated with a
+ * certain namespace is removed.
+ */
+
+ ndn::util::signal::Signal<Rib, Name> afterEraseEntry;
+
+ /** \brief signals after a Route is added
+ */
+ ndn::util::signal::Signal<Rib, RibRouteRef> afterAddRoute;
+
+ /** \brief signals before a route is removed
+ */
+ ndn::util::signal::Signal<Rib, RibRouteRef> beforeRemoveRoute;
+
+private:
+ RibTable m_rib;
+ FaceLookupTable m_faceMap;
+ FibUpdater* m_fibUpdater;
+
+ size_t m_nItems;
+
+ friend class FibUpdater;
+
+private:
+ struct UpdateQueueItem
+ {
+ RibUpdateBatch batch;
+ const Rib::UpdateSuccessCallback managerSuccessCallback;
+ const Rib::UpdateFailureCallback managerFailureCallback;
+ };
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ typedef std::list<UpdateQueueItem> UpdateQueue;
+ UpdateQueue m_updateBatches;
+
+private:
+ bool m_isUpdateInProgress;
+};
+
+inline Rib::const_iterator
+Rib::begin() const
+{
+ return m_rib.begin();
+}
+
+inline Rib::const_iterator
+Rib::end() const
+{
+ return m_rib.end();
+}
+
+inline size_t
+Rib::size() const
+{
+ return m_nItems;
+}
+
+inline bool
+Rib::empty() const
+{
+ return m_rib.empty();
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Rib& rib);
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_DAEMON_RIB_RIB_HPP
diff --git a/daemon/rib/route.cpp b/daemon/rib/route.cpp
new file mode 100644
index 0000000..04999bb
--- /dev/null
+++ b/daemon/rib/route.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "route.hpp"
+#include <ndn-cxx/util/string-helper.hpp>
+
+namespace nfd {
+namespace rib {
+
+const uint64_t PA_ROUTE_COST = 2048; ///< cost of route created by prefix announcement
+
+static time::steady_clock::TimePoint
+computeExpiration(const ndn::PrefixAnnouncement& ann)
+{
+ time::steady_clock::Duration validityEnd = time::steady_clock::Duration::max();
+ if (ann.getValidityPeriod()) {
+ auto now = time::system_clock::now();
+ if (!ann.getValidityPeriod()->isValid(now)) {
+ validityEnd = time::steady_clock::Duration::zero();
+ }
+ else {
+ validityEnd = ann.getValidityPeriod()->getPeriod().second - now;
+ }
+ }
+ return time::steady_clock::now() +
+ std::min(validityEnd, time::duration_cast<time::steady_clock::Duration>(ann.getExpiration()));
+}
+
+Route::Route(const ndn::PrefixAnnouncement& ann, uint64_t faceId)
+ : faceId(faceId)
+ , origin(ndn::nfd::ROUTE_ORIGIN_PREFIXANN)
+ , cost(PA_ROUTE_COST)
+ , flags(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT)
+ , expires(computeExpiration(ann))
+ , announcement(ann)
+ , annExpires(*expires)
+{
+}
+
+bool
+operator==(const Route& lhs, const Route& rhs)
+{
+ return lhs.faceId == rhs.faceId &&
+ lhs.origin == rhs.origin &&
+ lhs.flags == rhs.flags &&
+ lhs.cost == rhs.cost &&
+ lhs.expires == rhs.expires &&
+ lhs.announcement == rhs.announcement;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Route& route)
+{
+ os << "Route("
+ << "faceid: " << route.faceId
+ << ", origin: " << route.origin
+ << ", cost: " << route.cost
+ << ", flags: " << ndn::AsHex{route.flags};
+ if (route.expires) {
+ os << ", expires in: " << time::duration_cast<time::milliseconds>(*route.expires - time::steady_clock::now());
+ }
+ else {
+ os << ", never expires";
+ }
+ if (route.announcement) {
+ os << ", announcement: (" << *route.announcement << ')';
+ }
+ os << ')';
+
+ return os;
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/daemon/rib/route.hpp b/daemon/rib/route.hpp
new file mode 100644
index 0000000..f38fbab
--- /dev/null
+++ b/daemon/rib/route.hpp
@@ -0,0 +1,134 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_RIB_ROUTE_HPP
+#define NFD_DAEMON_RIB_ROUTE_HPP
+
+#include "core/common.hpp"
+
+#include <ndn-cxx/encoding/nfd-constants.hpp>
+#include <ndn-cxx/mgmt/nfd/route-flags-traits.hpp>
+#include <ndn-cxx/prefix-announcement.hpp>
+#include <ndn-cxx/util/scheduler.hpp>
+
+#include <type_traits>
+
+namespace nfd {
+namespace rib {
+
+/** \brief represents a route for a name prefix
+ */
+class Route : public ndn::nfd::RouteFlagsTraits<Route>
+{
+public:
+ /** \brief default constructor
+ */
+ Route() = default;
+
+ /** \brief construct from a prefix announcement
+ * \param ann a prefix announcement that has passed verification
+ * \param faceId the face on which \p ann arrived
+ */
+ Route(const ndn::PrefixAnnouncement& ann, uint64_t faceId);
+
+ const ndn::util::scheduler::EventId&
+ getExpirationEvent() const
+ {
+ return m_expirationEvent;
+ }
+
+ void
+ setExpirationEvent(const ndn::util::scheduler::EventId& eid)
+ {
+ m_expirationEvent = eid;
+ }
+
+ void
+ cancelExpirationEvent()
+ {
+ m_expirationEvent.cancel();
+ }
+
+ std::underlying_type_t<ndn::nfd::RouteFlags>
+ getFlags() const
+ {
+ return flags;
+ }
+
+public:
+ uint64_t faceId = 0;
+ ndn::nfd::RouteOrigin origin = ndn::nfd::ROUTE_ORIGIN_APP;
+ uint64_t cost = 0;
+ std::underlying_type_t<ndn::nfd::RouteFlags> flags = ndn::nfd::ROUTE_FLAGS_NONE;
+ optional<time::steady_clock::TimePoint> expires;
+
+ /** \brief The prefix announcement that caused the creation of this route.
+ *
+ * This is nullopt if this route is not created by a prefix announcement.
+ */
+ optional<ndn::PrefixAnnouncement> announcement;
+
+ /** \brief Expiration time of the prefix announcement.
+ *
+ * Valid only if announcement is not nullopt.
+ *
+ * If this field is before or equal the current time, it indicates the prefix announcement is
+ * not yet valid or has expired. In this case, the exact value of this field does not matter.
+ * If this field is after the current time, it indicates when the prefix announcement expires.
+ */
+ time::steady_clock::TimePoint annExpires;
+
+private:
+ ndn::util::scheduler::EventId m_expirationEvent;
+};
+
+bool
+operator==(const Route& lhs, const Route& rhs);
+
+inline bool
+operator!=(const Route& lhs, const Route& rhs)
+{
+ return !(lhs == rhs);
+}
+
+inline bool
+compareFaceIdAndOrigin(const Route& lhs, const Route& rhs)
+{
+ return (lhs.faceId == rhs.faceId && lhs.origin == rhs.origin);
+}
+
+inline bool
+compareFaceId(const Route& route, const uint64_t faceId)
+{
+ return (route.faceId == faceId);
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Route& route);
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_DAEMON_RIB_ROUTE_HPP
diff --git a/daemon/rib/service.cpp b/daemon/rib/service.cpp
new file mode 100644
index 0000000..35c092d
--- /dev/null
+++ b/daemon/rib/service.cpp
@@ -0,0 +1,256 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "service.hpp"
+
+#include "fib-updater.hpp"
+#include "readvertise/client-to-nlsr-readvertise-policy.hpp"
+#include "readvertise/host-to-gateway-readvertise-policy.hpp"
+#include "readvertise/nfd-rib-readvertise-destination.hpp"
+#include "readvertise/readvertise.hpp"
+
+#include "core/global-io.hpp"
+#include "core/logger.hpp"
+
+#include <boost/property_tree/info_parser.hpp>
+#include <ndn-cxx/transport/tcp-transport.hpp>
+#include <ndn-cxx/transport/unix-transport.hpp>
+
+namespace nfd {
+namespace rib {
+
+NFD_LOG_INIT(RibService);
+
+Service* Service::s_instance = nullptr;
+
+static const std::string CFG_SECTION = "rib";
+static const std::string CFG_LOCALHOST_SECURITY = "localhost_security";
+static const std::string CFG_LOCALHOP_SECURITY = "localhop_security";
+static const std::string CFG_PREFIX_PROPAGATE = "auto_prefix_propagate";
+static const std::string CFG_READVERTISE_NLSR = "readvertise_nlsr";
+static const Name READVERTISE_NLSR_PREFIX = "/localhost/nlsr";
+static const uint64_t PROPAGATE_DEFAULT_COST = 15;
+static const time::milliseconds PROPAGATE_DEFAULT_TIMEOUT = 10_s;
+
+static ConfigSection
+loadConfigSectionFromFile(const std::string& filename)
+{
+ ConfigSection config;
+ // Any format errors should have been caught already
+ boost::property_tree::read_info(filename, config);
+ return config;
+}
+
+/**
+ * \brief Look into the config file and construct appropriate transport to communicate with NFD
+ * If NFD-RIB instance was initialized with config file, INFO format is assumed
+ */
+static shared_ptr<ndn::Transport>
+makeLocalNfdTransport(const ConfigSection& config)
+{
+ if (config.get_child_optional("face_system.unix")) {
+ // default socket path should be the same as in UnixStreamFactory::processConfig
+ auto path = config.get<std::string>("face_system.unix.path", "/var/run/nfd.sock");
+ return make_shared<ndn::UnixTransport>(path);
+ }
+ else if (config.get_child_optional("face_system.tcp") &&
+ config.get<std::string>("face_system.tcp.listen", "yes") == "yes") {
+ // default port should be the same as in TcpFactory::processConfig
+ auto port = config.get<std::string>("face_system.tcp.port", "6363");
+ return make_shared<ndn::TcpTransport>("localhost", port);
+ }
+ else {
+ NDN_THROW(ConfigFile::Error("No transport is available to communicate with NFD"));
+ }
+}
+
+Service::Service(const std::string& configFile, ndn::KeyChain& keyChain)
+ : Service(keyChain, makeLocalNfdTransport(loadConfigSectionFromFile(configFile)),
+ [&configFile] (ConfigFile& config, bool isDryRun) {
+ config.parse(configFile, isDryRun);
+ })
+{
+}
+
+Service::Service(const ConfigSection& configSection, ndn::KeyChain& keyChain)
+ : Service(keyChain, makeLocalNfdTransport(configSection),
+ [&configSection] (ConfigFile& config, bool isDryRun) {
+ config.parse(configSection, isDryRun, "internal://nfd.conf");
+ })
+{
+}
+
+template<typename ConfigParseFunc>
+Service::Service(ndn::KeyChain& keyChain, shared_ptr<ndn::Transport> localNfdTransport,
+ const ConfigParseFunc& configParse)
+ : m_keyChain(keyChain)
+ , m_face(std::move(localNfdTransport), getGlobalIoService(), m_keyChain)
+ , m_scheduler(m_face.getIoService())
+ , m_nfdController(m_face, m_keyChain)
+ , m_fibUpdater(m_rib, m_nfdController)
+ , m_dispatcher(m_face, m_keyChain)
+ , m_ribManager(m_rib, m_face, m_keyChain, m_nfdController, m_dispatcher, m_scheduler)
+{
+ if (s_instance != nullptr) {
+ NDN_THROW(std::logic_error("RIB service cannot be instantiated more than once"));
+ }
+ if (&getGlobalIoService() != &getRibIoService()) {
+ NDN_THROW(std::logic_error("RIB service must run on RIB thread"));
+ }
+ s_instance = this;
+
+ ConfigFile config(ConfigFile::ignoreUnknownSection);
+ config.addSectionHandler(CFG_SECTION, bind(&Service::processConfig, this, _1, _2, _3));
+ configParse(config, true);
+ configParse(config, false);
+
+ m_ribManager.registerWithNfd();
+ m_ribManager.enableLocalFields();
+}
+
+Service::~Service()
+{
+ s_instance = nullptr;
+}
+
+Service&
+Service::get()
+{
+ if (s_instance == nullptr) {
+ NDN_THROW(std::logic_error("RIB service is not instantiated"));
+ }
+ if (&getGlobalIoService() != &getRibIoService()) {
+ NDN_THROW(std::logic_error("Must get RIB service on RIB thread"));
+ }
+ return *s_instance;
+}
+
+void
+Service::processConfig(const ConfigSection& section, bool isDryRun, const std::string& filename)
+{
+ if (isDryRun) {
+ checkConfig(section, filename);
+ }
+ else {
+ applyConfig(section, filename);
+ }
+}
+
+void
+Service::checkConfig(const ConfigSection& section, const std::string& filename)
+{
+ for (const auto& item : section) {
+ const std::string& key = item.first;
+ const ConfigSection& value = item.second;
+ if (key == CFG_LOCALHOST_SECURITY || key == CFG_LOCALHOP_SECURITY) {
+ ndn::ValidatorConfig testValidator(m_face);
+ testValidator.load(value, filename);
+ }
+ else if (key == CFG_PREFIX_PROPAGATE) {
+ // AutoPrefixPropagator does not support config dry-run
+ }
+ else if (key == CFG_READVERTISE_NLSR) {
+ ConfigFile::parseYesNo(item, CFG_SECTION + "." + CFG_READVERTISE_NLSR);
+ }
+ else {
+ NDN_THROW(ConfigFile::Error("Unrecognized option " + CFG_SECTION + "." + key));
+ }
+ }
+}
+
+void
+Service::applyConfig(const ConfigSection& section, const std::string& filename)
+{
+ bool wantPrefixPropagate = false;
+ bool wantReadvertiseNlsr = false;
+
+ for (const auto& item : section) {
+ const std::string& key = item.first;
+ const ConfigSection& value = item.second;
+ if (key == CFG_LOCALHOST_SECURITY) {
+ m_ribManager.applyLocalhostConfig(value, filename);
+ }
+ else if (key == CFG_LOCALHOP_SECURITY) {
+ m_ribManager.enableLocalhop(value, filename);
+ }
+ else if (key == CFG_PREFIX_PROPAGATE) {
+ wantPrefixPropagate = true;
+
+ if (!m_readvertisePropagation) {
+ NFD_LOG_DEBUG("Enabling automatic prefix propagation");
+
+ auto parameters = ndn::nfd::ControlParameters()
+ .setCost(PROPAGATE_DEFAULT_COST)
+ .setOrigin(ndn::nfd::ROUTE_ORIGIN_CLIENT);
+ auto cost = item.second.get_optional<uint64_t>("cost");
+ if (cost) {
+ parameters.setCost(*cost);
+ }
+
+ auto options = ndn::nfd::CommandOptions()
+ .setPrefix(RibManager::LOCALHOP_TOP_PREFIX)
+ .setTimeout(PROPAGATE_DEFAULT_TIMEOUT);
+ auto timeout = item.second.get_optional<uint64_t>("timeout");
+ if (timeout) {
+ options.setTimeout(time::milliseconds(*timeout));
+ }
+
+ m_readvertisePropagation = make_unique<Readvertise>(
+ m_rib,
+ m_scheduler,
+ make_unique<HostToGatewayReadvertisePolicy>(m_keyChain, item.second),
+ make_unique<NfdRibReadvertiseDestination>(m_nfdController, m_rib, options, parameters));
+ }
+ }
+ else if (key == CFG_READVERTISE_NLSR) {
+ wantReadvertiseNlsr = ConfigFile::parseYesNo(item, CFG_SECTION + "." + CFG_READVERTISE_NLSR);
+ }
+ else {
+ NDN_THROW(ConfigFile::Error("Unrecognized option " + CFG_SECTION + "." + key));
+ }
+ }
+
+ if (!wantPrefixPropagate && m_readvertisePropagation != nullptr) {
+ NFD_LOG_DEBUG("Disabling automatic prefix propagation");
+ m_readvertisePropagation.reset();
+ }
+
+ if (wantReadvertiseNlsr && m_readvertiseNlsr == nullptr) {
+ NFD_LOG_DEBUG("Enabling readvertise-to-nlsr");
+ auto options = ndn::nfd::CommandOptions().setPrefix(READVERTISE_NLSR_PREFIX);
+ m_readvertiseNlsr = make_unique<Readvertise>(
+ m_rib,
+ m_scheduler,
+ make_unique<ClientToNlsrReadvertisePolicy>(),
+ make_unique<NfdRibReadvertiseDestination>(m_nfdController, m_rib, options));
+ }
+ else if (!wantReadvertiseNlsr && m_readvertiseNlsr != nullptr) {
+ NFD_LOG_DEBUG("Disabling readvertise-to-nlsr");
+ m_readvertiseNlsr.reset();
+ }
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/daemon/rib/service.hpp b/daemon/rib/service.hpp
new file mode 100644
index 0000000..0d6a71a
--- /dev/null
+++ b/daemon/rib/service.hpp
@@ -0,0 +1,127 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_RIB_SERVICE_HPP
+#define NFD_DAEMON_RIB_SERVICE_HPP
+
+#include "rib-manager.hpp"
+
+#include "core/config-file.hpp"
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/mgmt/dispatcher.hpp>
+#include <ndn-cxx/mgmt/nfd/controller.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/transport/transport.hpp>
+#include <ndn-cxx/util/scheduler.hpp>
+
+namespace nfd {
+namespace rib {
+
+class Readvertise;
+
+/**
+ * \brief initializes and executes NFD-RIB service thread
+ *
+ * Only one instance of this class can be created at any time.
+ * After initialization, NFD-RIB instance can be started by running the global io_service.
+ */
+class Service : noncopyable
+{
+public:
+ /**
+ * \brief create NFD-RIB service
+ * \param configFile absolute or relative path of configuration file
+ * \param keyChain the KeyChain
+ * \throw std::logic_error Instance of rib::Service has been already constructed
+ * \throw std::logic_error Instance of rib::Service is not constructed on RIB thread
+ */
+ Service(const std::string& configFile, ndn::KeyChain& keyChain);
+
+ /**
+ * \brief create NFD-RIB service
+ * \param configSection parsed configuration section
+ * \param keyChain the KeyChain
+ * \note This constructor overload is more appropriate for integrated environments,
+ * such as NS-3 or android. Error messages related to configuration file
+ * will use "internal://nfd.conf" as configuration filename.
+ * \throw std::logic_error Instance of rib::Service has been already constructed
+ * \throw std::logic_error Instance of rib::Service is not constructed on RIB thread
+ */
+ Service(const ConfigSection& configSection, ndn::KeyChain& keyChain);
+
+ /**
+ * \brief Destructor
+ */
+ ~Service();
+
+ /**
+ * \brief Get a reference to the only instance of this class
+ * \throw std::logic_error No instance has been constructed
+ * \throw std::logic_error This function is invoked on a thread other than the RIB thread
+ */
+ static Service&
+ get();
+
+ RibManager&
+ getRibManager()
+ {
+ return m_ribManager;
+ }
+
+private:
+ template<typename ConfigParseFunc>
+ Service(ndn::KeyChain& keyChain, shared_ptr<ndn::Transport> localNfdTransport,
+ const ConfigParseFunc& configParse);
+
+ void
+ processConfig(const ConfigSection& section, bool isDryRun, const std::string& filename);
+
+ void
+ checkConfig(const ConfigSection& section, const std::string& filename);
+
+ void
+ applyConfig(const ConfigSection& section, const std::string& filename);
+
+private:
+ static Service* s_instance;
+
+ ndn::KeyChain& m_keyChain;
+ ndn::Face m_face;
+ ndn::util::Scheduler m_scheduler;
+ ndn::nfd::Controller m_nfdController;
+
+ Rib m_rib;
+ FibUpdater m_fibUpdater;
+ unique_ptr<Readvertise> m_readvertiseNlsr;
+ unique_ptr<Readvertise> m_readvertisePropagation;
+ ndn::mgmt::Dispatcher m_dispatcher;
+ RibManager m_ribManager;
+};
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_DAEMON_RIB_SERVICE_HPP