All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
rib-manager.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2014-2025, Regents of the University of California,
4  * Arizona Board of Regents,
5  * Colorado State University,
6  * University Pierre & Marie Curie, Sorbonne University,
7  * Washington University in St. Louis,
8  * Beijing Institute of Technology,
9  * The University of Memphis.
10  *
11  * This file is part of NFD (Named Data Networking Forwarding Daemon).
12  * See AUTHORS.md for complete list of NFD authors and contributors.
13  *
14  * NFD is free software: you can redistribute it and/or modify it under the terms
15  * of the GNU General Public License as published by the Free Software Foundation,
16  * either version 3 of the License, or (at your option) any later version.
17  *
18  * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20  * PURPOSE. See the GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License along with
23  * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 #include "rib-manager.hpp"
27 
28 #include "common/global.hpp"
29 #include "common/logger.hpp"
30 #include "rib/rib.hpp"
31 #include "table/fib.hpp"
32 
33 #include <boost/asio/defer.hpp>
34 #include <boost/lexical_cast.hpp>
35 
36 #include <ndn-cxx/lp/tags.hpp>
37 #include <ndn-cxx/mgmt/nfd/rib-entry.hpp>
38 #include <ndn-cxx/mgmt/nfd/status-dataset.hpp>
39 #include <ndn-cxx/security/certificate-fetcher-direct-fetch.hpp>
40 
41 namespace nfd {
42 
43 using rib::Route;
44 
45 NFD_LOG_INIT(RibManager);
46 
47 const std::string MGMT_MODULE_NAME = "rib";
48 const Name LOCALHOST_TOP_PREFIX = "/localhost/nfd";
49 constexpr time::seconds ACTIVE_FACE_FETCH_INTERVAL = 5_min;
50 
51 RibManager::RibManager(rib::Rib& rib, ndn::Face& face, ndn::KeyChain& keyChain,
52  ndn::nfd::Controller& nfdController, Dispatcher& dispatcher)
53  : ManagerBase(MGMT_MODULE_NAME, dispatcher)
54  , m_rib(rib)
55  , m_keyChain(keyChain)
56  , m_nfdController(nfdController)
57  , m_dispatcher(dispatcher)
58  , m_faceMonitor(face)
59  , m_localhostValidator(face)
60  , m_localhopValidator(make_unique<ndn::security::CertificateFetcherDirectFetch>(face))
61  , m_paValidator(make_unique<ndn::security::CertificateFetcherDirectFetch>(face))
62  , m_isLocalhopEnabled(false)
63 {
64  registerCommandHandler<ndn::nfd::RibRegisterCommand>([this] (auto&&, auto&&... args) {
65  registerEntry(std::forward<decltype(args)>(args)...);
66  });
67  registerCommandHandler<ndn::nfd::RibUnregisterCommand>([this] (auto&&, auto&&... args) {
68  unregisterEntry(std::forward<decltype(args)>(args)...);
69  });
70  registerCommandHandler<ndn::nfd::RibAnnounceCommand>([this] (auto&&, auto&&... args) {
71  announceEntry(std::forward<decltype(args)>(args)...);
72  });
73  registerStatusDatasetHandler("list", [this] (auto&&, auto&&, auto&&... args) {
74  listEntries(std::forward<decltype(args)>(args)...);
75  });
76 }
77 
78 void
79 RibManager::applyLocalhostConfig(const ConfigSection& section, const std::string& filename)
80 {
81  m_localhostValidator.load(section, filename);
82 }
83 
84 void
85 RibManager::enableLocalhop(const ConfigSection& section, const std::string& filename)
86 {
87  m_localhopValidator.load(section, filename);
88  m_isLocalhopEnabled = true;
89 }
90 
91 void
93 {
94  m_isLocalhopEnabled = false;
95 }
96 
97 void
98 RibManager::applyPaConfig(const ConfigSection& section, const std::string& filename)
99 {
100  m_paValidator.load(section, filename);
101 }
102 
103 void
105 {
106  registerTopPrefix(LOCALHOST_TOP_PREFIX);
107 
108  if (m_isLocalhopEnabled) {
109  registerTopPrefix(LOCALHOP_TOP_PREFIX);
110  }
111 
112  NFD_LOG_INFO("Start monitoring face create/destroy events");
113  m_faceMonitor.onNotification.connect([this] (const auto& notif) { onNotification(notif); });
114  m_faceMonitor.start();
115 
116  scheduleActiveFaceFetch(ACTIVE_FACE_FETCH_INTERVAL);
117 }
118 
119 void
121 {
122  m_nfdController.start<ndn::nfd::FaceUpdateCommand>(
123  ControlParameters().setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED, true),
124  [] (const ControlParameters&) {
125  NFD_LOG_TRACE("Local fields enabled");
126  },
127  [] (const ControlResponse& res) {
128  NDN_THROW(Error("Could not enable local fields (error " +
129  std::to_string(res.getCode()) + ": " + res.getText() + ")"));
130  });
131 }
132 
133 void
134 RibManager::addRoute(const Name& name, Route route, const time::steady_clock::time_point& now,
135  const std::function<void(RibUpdateResult)>& done)
136 {
137  if (route.expires && *route.expires <= now) {
138  m_rib.onRouteExpiration(name, route);
139  if (done) {
140  done(RibUpdateResult::EXPIRED);
141  }
142  return;
143  }
144 
145  NFD_LOG_INFO("Adding route " << name << " nexthop=" << route.faceId <<
146  " origin=" << route.origin << " cost=" << route.cost);
147 
148  if (route.expires) {
149  auto delay = *route.expires - now;
150  auto event = getScheduler().schedule(delay, [=] { m_rib.onRouteExpiration(name, route); });
151  route.setExpirationEvent(std::move(event));
152  // cast to milliseconds to make the logs easier to read
153  NFD_LOG_TRACE("Route will expire in " << time::duration_cast<time::milliseconds>(delay));
154  }
155 
156  beginRibUpdate({rib::RibUpdate::REGISTER, name, std::move(route)}, done);
157 }
158 
159 void
160 RibManager::beginRibUpdate(const rib::RibUpdate& update,
161  const std::function<void(RibUpdateResult)>& done)
162 {
163  m_rib.beginApplyUpdate(update,
164  [=] {
165  NFD_LOG_DEBUG(update << " -> OK");
166  if (done) {
167  done(RibUpdateResult::OK);
168  }
169  },
170  [=] (uint32_t code, const std::string& error) {
171  NFD_LOG_DEBUG(update << " -> error " << code << ": " << error);
172 
173  // Since the FIB rejected the update, clean up invalid routes
174  scheduleActiveFaceFetch(1_s);
175 
176  if (done) {
177  done(RibUpdateResult::ERROR);
178  }
179  });
180 }
181 
182 void
183 RibManager::registerTopPrefix(const Name& topPrefix)
184 {
185  // add FIB nexthop
186  m_nfdController.start<ndn::nfd::FibAddNextHopCommand>(
187  ControlParameters().setName(Name(topPrefix).append(MGMT_MODULE_NAME)).setFaceId(0),
188  [=] (const ControlParameters& res) {
189  NFD_LOG_DEBUG("Successfully registered " << topPrefix << " with the forwarder");
190 
191  // Routes must be inserted into the RIB so route flags can be applied
192  Route route;
193  route.faceId = res.getFaceId();
194  route.origin = ndn::nfd::ROUTE_ORIGIN_APP;
195  route.flags = ndn::nfd::ROUTE_FLAG_CHILD_INHERIT;
196 
197  m_rib.insert(topPrefix, route);
198  },
199  [=] (const ControlResponse& res) {
200  NDN_THROW(Error("Could not add FIB entry " + topPrefix.toUri() + " (error " +
201  std::to_string(res.getCode()) + ": " + res.getText() + ")"));
202  });
203 
204  // add top prefix to the dispatcher without prefix registration
205  m_dispatcher.addTopPrefix(topPrefix, false);
206 }
207 
208 static uint64_t
209 getIncomingFaceId(const Interest& request)
210 {
211  auto incomingFaceIdTag = request.getTag<lp::IncomingFaceIdTag>();
212  // NDNLPv2 says "application MUST be prepared to receive a packet without IncomingFaceId field",
213  // but it's fine to assert IncomingFaceId is available, because InternalFace lives inside NFD
214  // and is initialized synchronously with IncomingFaceId field enabled.
215  BOOST_ASSERT(incomingFaceIdTag != nullptr);
216  return incomingFaceIdTag->get();
217 }
218 
219 void
220 RibManager::registerEntry(const Interest& interest, ControlParameters parameters,
221  const CommandContinuation& done)
222 {
223  if (parameters.getName().size() > Fib::getMaxDepth()) {
224  done(ControlResponse(414, "Route prefix cannot exceed " + std::to_string(Fib::getMaxDepth()) +
225  " components"));
226  return;
227  }
228 
229  if (parameters.getFaceId() == 0) { // self registration
230  parameters.setFaceId(getIncomingFaceId(interest));
231  }
232 
233  // Respond since command is valid and authorized
234  done(ControlResponse(200, "OK").setBody(parameters.wireEncode()));
235 
236  Route route;
237  route.faceId = parameters.getFaceId();
238  route.origin = parameters.getOrigin();
239  route.cost = parameters.getCost();
240  route.flags = parameters.getFlags();
241 
242  auto now = time::steady_clock::now();
243  if (parameters.hasExpirationPeriod() &&
244  parameters.getExpirationPeriod() != time::milliseconds::max()) {
245  route.expires = now + parameters.getExpirationPeriod();
246  }
247 
248  addRoute(parameters.getName(), std::move(route), now);
249 }
250 
251 void
252 RibManager::unregisterEntry(const Interest& interest, ControlParameters parameters,
253  const CommandContinuation& done)
254 {
255  if (parameters.getFaceId() == 0) { // self unregistration
256  parameters.setFaceId(getIncomingFaceId(interest));
257  }
258 
259  // Respond since command is valid and authorized
260  done(ControlResponse(200, "OK").setBody(parameters.wireEncode()));
261 
262  Route route;
263  route.faceId = parameters.getFaceId();
264  route.origin = parameters.getOrigin();
265 
266  NFD_LOG_INFO("Removing route " << parameters.getName() <<
267  " nexthop=" << route.faceId << " origin=" << route.origin);
268 
269  beginRibUpdate({rib::RibUpdate::UNREGISTER, parameters.getName(), route}, nullptr);
270 }
271 
272 void
273 RibManager::announceEntry(const Interest& interest, const ndn::nfd::RibAnnounceParameters& parameters,
274  const CommandContinuation& done)
275 {
276  const auto& announcement = parameters.getPrefixAnnouncement();
277  const auto& name = announcement.getAnnouncedName();
278  if (name.size() > Fib::getMaxDepth()) {
279  done(ControlResponse(414, "Route prefix cannot exceed " + std::to_string(Fib::getMaxDepth()) +
280  " components"));
281  return;
282  }
283 
284  auto inFaceId = getIncomingFaceId(interest);
285 
286  NDN_LOG_TRACE("Validating announcement " << announcement);
287  BOOST_ASSERT(announcement.getData());
288  m_paValidator.validate(*announcement.getData(),
289  [=] (const Data&) {
290  auto now = time::steady_clock::now();
291  Route route(announcement, inFaceId);
292 
293  // Prepare parameters for response
294  ControlParameters responseParams;
295  responseParams
296  .setName(announcement.getAnnouncedName())
297  .setFaceId(route.faceId)
298  .setOrigin(route.origin)
299  .setCost(route.cost)
300  .setFlags(route.flags)
301  .setExpirationPeriod(time::duration_cast<time::milliseconds>(route.annExpires - now));
302 
303  // Respond since command is valid and authorized
304  done(ControlResponse(200, "OK").setBody(responseParams.wireEncode()));
305  addRoute(announcement.getAnnouncedName(), std::move(route), now);
306  },
307  [=] (const Data&, const ndn::security::ValidationError& err) {
308  NDN_LOG_DEBUG("announce " << name << " -> " << err);
309  done(ControlResponse(403, "Prefix announcement rejected: " +
310  boost::lexical_cast<std::string>(err.getCode())));
311  });
312 }
313 
314 void
315 RibManager::listEntries(ndn::mgmt::StatusDatasetContext& context) const
316 {
317  auto now = time::steady_clock::now();
318  for (const auto& kv : m_rib) {
319  const rib::RibEntry& entry = *kv.second;
320  ndn::nfd::RibEntry item;
321  item.setName(entry.getName());
322  for (const Route& route : entry.getRoutes()) {
323  ndn::nfd::Route r;
324  r.setFaceId(route.faceId);
325  r.setOrigin(route.origin);
326  r.setCost(route.cost);
327  r.setFlags(route.flags);
328  if (route.expires) {
329  r.setExpirationPeriod(time::duration_cast<time::milliseconds>(*route.expires - now));
330  }
331  item.addRoute(r);
332  }
333  context.append(item.wireEncode());
334  }
335  context.end();
336 }
337 
338 ndn::mgmt::Authorization
339 RibManager::makeAuthorization(const std::string&)
340 {
341  return [this] (const Name& prefix, const Interest& interest,
342  const ndn::mgmt::ControlParametersBase* params,
343  const ndn::mgmt::AcceptContinuation& accept,
344  const ndn::mgmt::RejectContinuation& reject) {
345  BOOST_ASSERT(params != nullptr);
346  BOOST_ASSERT(typeid(*params) == typeid(ndn::nfd::ControlParameters) ||
347  typeid(*params) == typeid(ndn::nfd::RibAnnounceParameters));
348  BOOST_ASSERT(prefix == LOCALHOST_TOP_PREFIX || prefix == LOCALHOP_TOP_PREFIX);
349 
350  auto& validator = prefix == LOCALHOST_TOP_PREFIX ? m_localhostValidator : m_localhopValidator;
351  validator.validate(interest,
352  [&interest, accept] (auto&&...) { accept(extractSigner(interest)); },
353  [reject] (auto&&...) { reject(ndn::mgmt::RejectReply::STATUS403); });
354  };
355 }
356 
357 std::ostream&
358 operator<<(std::ostream& os, RibManager::SlAnnounceResult res)
359 {
360  switch (res) {
362  return os << "OK";
364  return os << "ERROR";
366  return os << "VALIDATION_FAILURE";
368  return os << "EXPIRED";
370  return os << "NOT_FOUND";
371  }
372  NDN_THROW(std::invalid_argument("Unknown SlAnnounceResult"));
373 }
374 
376 RibManager::getSlAnnounceResultFromRibUpdateResult(RibUpdateResult r)
377 {
378  switch (r) {
379  case RibUpdateResult::OK:
380  return SlAnnounceResult::OK;
381  case RibUpdateResult::ERROR:
383  case RibUpdateResult::EXPIRED:
385  }
386  NDN_CXX_UNREACHABLE;
387 }
388 
389 void
390 RibManager::slAnnounce(const ndn::PrefixAnnouncement& pa, uint64_t faceId,
391  time::milliseconds maxLifetime, const SlAnnounceCallback& cb)
392 {
393  BOOST_ASSERT(pa.getData());
394 
395  m_paValidator.validate(*pa.getData(),
396  [=] (const Data&) {
397  auto now = time::steady_clock::now();
398  Route route(pa, faceId);
399  route.expires = std::min(route.annExpires, now + maxLifetime);
400  addRoute(pa.getAnnouncedName(), std::move(route), now, [=] (RibUpdateResult ribRes) {
401  auto res = getSlAnnounceResultFromRibUpdateResult(ribRes);
402  NFD_LOG_INFO("slAnnounce " << pa.getAnnouncedName() << " " << faceId << " -> " << res);
403  cb(res);
404  });
405  },
406  [=] (const Data&, const ndn::security::ValidationError& err) {
407  NFD_LOG_INFO("slAnnounce " << pa.getAnnouncedName() << " " << faceId <<
408  " -> validation error: " << err);
409  cb(SlAnnounceResult::VALIDATION_FAILURE);
410  });
411 }
412 
413 void
414 RibManager::slRenew(const Name& name, uint64_t faceId, time::milliseconds maxLifetime,
415  const SlAnnounceCallback& cb)
416 {
417  Route routeQuery;
418  routeQuery.faceId = faceId;
419  routeQuery.origin = ndn::nfd::ROUTE_ORIGIN_PREFIXANN;
420  Route* oldRoute = m_rib.findLongestPrefix(name, routeQuery);
421 
422  if (oldRoute == nullptr || !oldRoute->announcement) {
423  NFD_LOG_DEBUG("slRenew " << name << " " << faceId << " -> not found");
424  return cb(SlAnnounceResult::NOT_FOUND);
425  }
426 
427  auto now = time::steady_clock::now();
428  Name routeName = oldRoute->announcement->getAnnouncedName();
429  Route route = *oldRoute;
430  route.expires = std::min(route.annExpires, now + maxLifetime);
431 
432  addRoute(routeName, std::move(route), now, [=] (RibUpdateResult ribRes) {
433  auto res = getSlAnnounceResultFromRibUpdateResult(ribRes);
434  NFD_LOG_INFO("slRenew " << name << " " << faceId << " -> " << res << " " << routeName);
435  cb(res);
436  });
437 }
438 
439 void
440 RibManager::slFindAnn(const Name& name, const SlFindAnnCallback& cb) const
441 {
442  shared_ptr<rib::RibEntry> entry;
443  auto exactMatch = m_rib.find(name);
444  if (exactMatch != m_rib.end()) {
445  entry = exactMatch->second;
446  }
447  else {
448  entry = m_rib.findParent(name);
449  }
450  if (entry == nullptr) {
451  return cb(std::nullopt);
452  }
453 
454  auto pa = entry->getPrefixAnnouncement();
455  pa.toData(m_keyChain);
456  cb(pa);
457 }
458 
459 void
460 RibManager::fetchActiveFaces()
461 {
462  NFD_LOG_DEBUG("Fetching active faces");
463 
464  m_nfdController.fetch<ndn::nfd::FaceDataset>(
465  [this] (auto&&... args) { removeInvalidFaces(std::forward<decltype(args)>(args)...); },
466  [this] (uint32_t code, const std::string& reason) {
467  NFD_LOG_WARN("Failed to fetch face dataset (error " << code << ": " << reason << ")");
468  scheduleActiveFaceFetch(ACTIVE_FACE_FETCH_INTERVAL);
469  });
470 }
471 
472 void
473 RibManager::scheduleActiveFaceFetch(time::seconds timeToWait)
474 {
475  m_activeFaceFetchEvent = getScheduler().schedule(timeToWait, [this] { fetchActiveFaces(); });
476 }
477 
478 void
479 RibManager::removeInvalidFaces(const std::vector<ndn::nfd::FaceStatus>& activeFaces)
480 {
481  NFD_LOG_DEBUG("Checking for invalid face registrations");
482 
483  std::set<uint64_t> activeIds;
484  for (const auto& faceStatus : activeFaces) {
485  activeIds.insert(faceStatus.getFaceId());
486  }
487  boost::asio::defer(getGlobalIoService(),
488  [this, active = std::move(activeIds)] { m_rib.beginRemoveFailedFaces(active); });
489 
490  // Reschedule the check for future clean up
491  scheduleActiveFaceFetch(ACTIVE_FACE_FETCH_INTERVAL);
492 }
493 
494 void
495 RibManager::onNotification(const ndn::nfd::FaceEventNotification& notification)
496 {
497  NFD_LOG_TRACE("Received notification " << notification);
498 
499  if (notification.getKind() == ndn::nfd::FACE_EVENT_DESTROYED) {
500  boost::asio::defer(getGlobalIoService(),
501  [this, id = notification.getFaceId()] { m_rib.beginRemoveFace(id); });
502  }
503 }
504 
505 } // namespace nfd
A collection of common functions shared by all NFD managers, such as communicating with the dispatche...
void registerStatusDatasetHandler(const std::string &verb, const ndn::mgmt::StatusDatasetHandler &handler)
static std::string extractSigner(const Interest &interest)
Extracts the name from the KeyLocator of a ControlCommand request.
std::function< void(SlAnnounceResult res)> SlAnnounceCallback
void registerWithNfd()
Start accepting commands and dataset requests.
void disableLocalhop()
Disallow accepting commands on /localhop/nfd/rib prefix.
Definition: rib-manager.cpp:92
RibManager(rib::Rib &rib, ndn::Face &face, ndn::KeyChain &keyChain, ndn::nfd::Controller &nfdController, Dispatcher &dispatcher)
Definition: rib-manager.cpp:51
static const Name LOCALHOP_TOP_PREFIX
void applyLocalhostConfig(const ConfigSection &section, const std::string &filename)
Apply localhost_security configuration.
Definition: rib-manager.cpp:79
void enableLocalhop(const ConfigSection &section, const std::string &filename)
Apply localhop_security configuration and allow accepting commands on /localhop/nfd/rib prefix.
Definition: rib-manager.cpp:85
void slAnnounce(const ndn::PrefixAnnouncement &pa, uint64_t faceId, time::milliseconds maxLifetime, const SlAnnounceCallback &cb)
Insert a route by prefix announcement from self-learning strategy.
std::function< void(std::optional< ndn::PrefixAnnouncement >)> SlFindAnnCallback
@ VALIDATION_FAILURE
the announcement cannot be verified against the trust schema
@ EXPIRED
the announcement has expired
@ NOT_FOUND
route does not exist (slRenew only)
@ OK
RIB and FIB have been updated.
void enableLocalFields()
Enable NDNLP IncomingFaceId field in order to support self-registration commands.
void applyPaConfig(const ConfigSection &section, const std::string &filename)
Apply prefix_announcement_validation configuration.
Definition: rib-manager.cpp:98
static constexpr size_t getMaxDepth()
Maximum number of components in a FIB entry prefix.
Definition: fib.hpp:91
Represents the Routing Information Base.
Definition: rib.hpp:70
void insert(const Name &prefix, const Route &route)
Definition: rib.cpp:84
void onRouteExpiration(const Name &prefix, const Route &route)
Definition: rib.cpp:189
void beginApplyUpdate(const RibUpdate &update, const UpdateSuccessCallback &onSuccess, const UpdateFailureCallback &onFailure)
Passes the provided RibUpdateBatch to FibUpdater to calculate and send FibUpdates.
Definition: rib.cpp:329
Represents a route for a name prefix.
Definition: route.hpp:44
ndn::nfd::RouteOrigin origin
Definition: route.hpp:97
uint64_t cost
Definition: route.hpp:98
time::steady_clock::time_point annExpires
Expiration time of the prefix announcement.
Definition: route.hpp:116
std::optional< time::steady_clock::time_point > expires
Definition: route.hpp:100
void setExpirationEvent(ndn::scheduler::EventId &&eid)
Definition: route.hpp:63
std::optional< ndn::PrefixAnnouncement > announcement
The prefix announcement that caused the creation of this route.
Definition: route.hpp:106
uint64_t faceId
Definition: route.hpp:96
#define NFD_LOG_INFO
Definition: logger.hpp:39
#define NFD_LOG_INIT(name)
Definition: logger.hpp:31
#define NFD_LOG_WARN
Definition: logger.hpp:40
#define NFD_LOG_DEBUG
Definition: logger.hpp:38
#define NFD_LOG_TRACE
Definition: logger.hpp:37
Definition: dns-srv.cpp:44
-status-http-server
Definition: common.hpp:71
ndn::Scheduler & getScheduler()
Returns the global Scheduler instance for the calling thread.
Definition: global.cpp:45
boost::property_tree::ptree ConfigSection
A configuration file section.
Definition: config-file.hpp:41
static uint64_t getIncomingFaceId(const Interest &request)
const std::string MGMT_MODULE_NAME
Definition: rib-manager.cpp:47
constexpr time::seconds ACTIVE_FACE_FETCH_INTERVAL
Definition: rib-manager.cpp:49
std::ostream & operator<<(std::ostream &os, const Network &network)
Definition: network.cpp:83
const Name LOCALHOST_TOP_PREFIX
Definition: rib-manager.cpp:48
boost::asio::io_context & getGlobalIoService()
Returns the global io_context instance for the calling thread.
Definition: global.cpp:36