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-2022, 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 <ndn-cxx/lp/tags.hpp>
34 #include <ndn-cxx/mgmt/nfd/face-status.hpp>
35 #include <ndn-cxx/mgmt/nfd/rib-entry.hpp>
36 #include <ndn-cxx/security/certificate-fetcher-direct-fetch.hpp>
37 
38 namespace nfd {
39 
40 using rib::RibUpdate;
41 using rib::Route;
42 
43 NFD_LOG_INIT(RibManager);
44 
45 const std::string MGMT_MODULE_NAME = "rib";
46 const Name LOCALHOST_TOP_PREFIX = "/localhost/nfd";
47 constexpr time::seconds ACTIVE_FACE_FETCH_INTERVAL = 5_min;
48 
49 RibManager::RibManager(rib::Rib& rib, ndn::Face& face, ndn::KeyChain& keyChain,
50  ndn::nfd::Controller& nfdController, Dispatcher& dispatcher)
51  : ManagerBase(MGMT_MODULE_NAME, dispatcher)
52  , m_rib(rib)
53  , m_keyChain(keyChain)
54  , m_nfdController(nfdController)
55  , m_dispatcher(dispatcher)
56  , m_faceMonitor(face)
57  , m_localhostValidator(face)
58  , m_localhopValidator(make_unique<ndn::security::CertificateFetcherDirectFetch>(face))
59  , m_paValidator(make_unique<ndn::security::CertificateFetcherDirectFetch>(face))
60  , m_isLocalhopEnabled(false)
61 {
62  registerCommandHandler<ndn::nfd::RibRegisterCommand>("register",
63  std::bind(&RibManager::registerEntry, this, _2, _3, _4, _5));
64  registerCommandHandler<ndn::nfd::RibUnregisterCommand>("unregister",
65  std::bind(&RibManager::unregisterEntry, this, _2, _3, _4, _5));
66 
67  registerStatusDatasetHandler("list", std::bind(&RibManager::listEntries, this, _1, _2, _3));
68 }
69 
70 void
71 RibManager::applyLocalhostConfig(const ConfigSection& section, const std::string& filename)
72 {
73  m_localhostValidator.load(section, filename);
74 }
75 
76 void
77 RibManager::enableLocalhop(const ConfigSection& section, const std::string& filename)
78 {
79  m_localhopValidator.load(section, filename);
80  m_isLocalhopEnabled = true;
81 }
82 
83 void
85 {
86  m_isLocalhopEnabled = false;
87 }
88 
89 void
90 RibManager::applyPaConfig(const ConfigSection& section, const std::string& filename)
91 {
92  m_paValidator.load(section, filename);
93 }
94 
95 void
97 {
98  registerTopPrefix(LOCALHOST_TOP_PREFIX);
99 
100  if (m_isLocalhopEnabled) {
101  registerTopPrefix(LOCALHOP_TOP_PREFIX);
102  }
103 
104  NFD_LOG_INFO("Start monitoring face create/destroy events");
105  m_faceMonitor.onNotification.connect([this] (const auto& notif) { onNotification(notif); });
106  m_faceMonitor.start();
107 
108  scheduleActiveFaceFetch(ACTIVE_FACE_FETCH_INTERVAL);
109 }
110 
111 void
113 {
114  m_nfdController.start<ndn::nfd::FaceUpdateCommand>(
115  ControlParameters().setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED, true),
116  [] (const ControlParameters&) {
117  NFD_LOG_DEBUG("Local fields enabled");
118  },
119  [] (const ControlResponse& res) {
120  NDN_THROW(Error("Couldn't enable local fields (" + to_string(res.getCode()) +
121  " " + res.getText() + ")"));
122  });
123 }
124 
125 void
126 RibManager::beginAddRoute(const Name& name, Route route, std::optional<time::nanoseconds> expires,
127  const std::function<void(RibUpdateResult)>& done)
128 {
129  if (expires) {
130  route.expires = time::steady_clock::now() + *expires;
131  }
132  else if (route.expires) {
133  expires = *route.expires - time::steady_clock::now();
134  }
135 
136  if (expires && *expires <= 0_s) {
137  m_rib.onRouteExpiration(name, route);
138  return done(RibUpdateResult::EXPIRED);
139  }
140 
141  NFD_LOG_INFO("Adding route " << name << " nexthop=" << route.faceId <<
142  " origin=" << route.origin << " cost=" << route.cost);
143 
144  if (expires) {
145  auto event = getScheduler().schedule(*expires, [=] { m_rib.onRouteExpiration(name, route); });
146  route.setExpirationEvent(event);
147  NFD_LOG_TRACE("Scheduled unregistration at: " << *route.expires);
148  }
149 
150  RibUpdate update;
151  update.setAction(RibUpdate::REGISTER)
152  .setName(name)
153  .setRoute(route);
154  beginRibUpdate(update, done);
155 }
156 
157 void
158 RibManager::beginRemoveRoute(const Name& name, const Route& route,
159  const std::function<void(RibUpdateResult)>& done)
160 {
161  NFD_LOG_INFO("Removing route " << name << " nexthop=" << route.faceId <<
162  " origin=" << route.origin);
163 
164  RibUpdate update;
165  update.setAction(RibUpdate::UNREGISTER)
166  .setName(name)
167  .setRoute(route);
168  beginRibUpdate(update, done);
169 }
170 
171 void
172 RibManager::beginRibUpdate(const RibUpdate& update,
173  const std::function<void(RibUpdateResult)>& done)
174 {
175  m_rib.beginApplyUpdate(update,
176  [=] {
177  NFD_LOG_DEBUG("RIB update succeeded for " << update);
178  done(RibUpdateResult::OK);
179  },
180  [=] (uint32_t code, const std::string& error) {
181  NFD_LOG_DEBUG("RIB update failed for " << update << " (" << code << " " << error << ")");
182 
183  // Since the FIB rejected the update, clean up invalid routes
184  scheduleActiveFaceFetch(1_s);
185 
186  done(RibUpdateResult::ERROR);
187  });
188 }
189 
190 void
191 RibManager::registerTopPrefix(const Name& topPrefix)
192 {
193  // add FIB nexthop
194  m_nfdController.start<ndn::nfd::FibAddNextHopCommand>(
195  ControlParameters().setName(Name(topPrefix).append(MGMT_MODULE_NAME))
196  .setFaceId(0),
197  [=] (const ControlParameters& res) {
198  NFD_LOG_DEBUG("Successfully registered " << topPrefix << " with NFD");
199 
200  // Routes must be inserted into the RIB so route flags can be applied
201  Route route;
202  route.faceId = res.getFaceId();
203  route.origin = ndn::nfd::ROUTE_ORIGIN_APP;
204  route.flags = ndn::nfd::ROUTE_FLAG_CHILD_INHERIT;
205 
206  m_rib.insert(topPrefix, route);
207  },
208  [=] (const ControlResponse& res) {
209  NDN_THROW(Error("Cannot add FIB entry " + topPrefix.toUri() + " (" +
210  to_string(res.getCode()) + " " + res.getText() + ")"));
211  });
212 
213  // add top prefix to the dispatcher without prefix registration
214  m_dispatcher.addTopPrefix(topPrefix, false);
215 }
216 
217 void
218 RibManager::registerEntry(const Name& topPrefix, const Interest& interest,
219  ControlParameters parameters,
220  const ndn::mgmt::CommandContinuation& done)
221 {
222  if (parameters.getName().size() > Fib::getMaxDepth()) {
223  done(ControlResponse(414, "Route prefix cannot exceed " + to_string(Fib::getMaxDepth()) +
224  " components"));
225  return;
226  }
227 
228  setFaceForSelfRegistration(interest, parameters);
229 
230  // Respond since command is valid and authorized
231  done(ControlResponse(200, "Success").setBody(parameters.wireEncode()));
232 
233  Route route;
234  route.faceId = parameters.getFaceId();
235  route.origin = parameters.getOrigin();
236  route.cost = parameters.getCost();
237  route.flags = parameters.getFlags();
238 
239  std::optional<time::nanoseconds> expires;
240  if (parameters.hasExpirationPeriod() &&
241  parameters.getExpirationPeriod() != time::milliseconds::max()) {
242  expires = time::duration_cast<time::nanoseconds>(parameters.getExpirationPeriod());
243  }
244 
245  beginAddRoute(parameters.getName(), std::move(route), expires, [] (RibUpdateResult) {});
246 }
247 
248 void
249 RibManager::unregisterEntry(const Name&, const Interest& interest,
250  ControlParameters parameters,
251  const ndn::mgmt::CommandContinuation& done)
252 {
253  setFaceForSelfRegistration(interest, parameters);
254 
255  // Respond since command is valid and authorized
256  done(ControlResponse(200, "Success").setBody(parameters.wireEncode()));
257 
258  Route route;
259  route.faceId = parameters.getFaceId();
260  route.origin = parameters.getOrigin();
261 
262  beginRemoveRoute(parameters.getName(), route, [] (auto&&...) {});
263 }
264 
265 void
266 RibManager::listEntries(const Name&, const Interest&, ndn::mgmt::StatusDatasetContext& context)
267 {
268  auto now = time::steady_clock::now();
269  for (const auto& kv : m_rib) {
270  const rib::RibEntry& entry = *kv.second;
271  ndn::nfd::RibEntry item;
272  item.setName(entry.getName());
273  for (const Route& route : entry.getRoutes()) {
274  ndn::nfd::Route r;
275  r.setFaceId(route.faceId);
276  r.setOrigin(route.origin);
277  r.setCost(route.cost);
278  r.setFlags(route.flags);
279  if (route.expires) {
280  r.setExpirationPeriod(time::duration_cast<time::milliseconds>(*route.expires - now));
281  }
282  item.addRoute(r);
283  }
284  context.append(item.wireEncode());
285  }
286  context.end();
287 }
288 
289 void
290 RibManager::setFaceForSelfRegistration(const Interest& request, ControlParameters& parameters)
291 {
292  bool isSelfRegistration = (parameters.getFaceId() == 0);
293  if (isSelfRegistration) {
294  shared_ptr<lp::IncomingFaceIdTag> incomingFaceIdTag = request.getTag<lp::IncomingFaceIdTag>();
295  // NDNLPv2 says "application MUST be prepared to receive a packet without IncomingFaceId field",
296  // but it's fine to assert IncomingFaceId is available, because InternalFace lives inside NFD
297  // and is initialized synchronously with IncomingFaceId field enabled.
298  BOOST_ASSERT(incomingFaceIdTag != nullptr);
299  parameters.setFaceId(*incomingFaceIdTag);
300  }
301 }
302 
303 ndn::mgmt::Authorization
304 RibManager::makeAuthorization(const std::string&)
305 {
306  return [this] (const Name& prefix, const Interest& interest,
307  const ndn::mgmt::ControlParameters* params,
308  const ndn::mgmt::AcceptContinuation& accept,
309  const ndn::mgmt::RejectContinuation& reject) {
310  BOOST_ASSERT(params != nullptr);
311  BOOST_ASSERT(typeid(*params) == typeid(ndn::nfd::ControlParameters));
312  BOOST_ASSERT(prefix == LOCALHOST_TOP_PREFIX || prefix == LOCALHOP_TOP_PREFIX);
313 
314  auto& validator = prefix == LOCALHOST_TOP_PREFIX ? m_localhostValidator : m_localhopValidator;
315  validator.validate(interest,
316  [&interest, accept] (auto&&...) { accept(extractSigner(interest)); },
317  [reject] (auto&&...) { reject(ndn::mgmt::RejectReply::STATUS403); });
318  };
319 }
320 
321 std::ostream&
322 operator<<(std::ostream& os, RibManager::SlAnnounceResult res)
323 {
324  switch (res) {
326  return os << "OK";
328  return os << "ERROR";
330  return os << "VALIDATION_FAILURE";
332  return os << "EXPIRED";
334  return os << "NOT_FOUND";
335  }
336  NDN_THROW(std::invalid_argument("Unknown SlAnnounceResult"));
337 }
338 
340 RibManager::getSlAnnounceResultFromRibUpdateResult(RibUpdateResult r)
341 {
342  switch (r) {
343  case RibUpdateResult::OK:
344  return SlAnnounceResult::OK;
345  case RibUpdateResult::ERROR:
347  case RibUpdateResult::EXPIRED:
349  }
350  NDN_CXX_UNREACHABLE;
351 }
352 
353 void
354 RibManager::slAnnounce(const ndn::PrefixAnnouncement& pa, uint64_t faceId,
355  time::milliseconds maxLifetime, const SlAnnounceCallback& cb)
356 {
357  BOOST_ASSERT(pa.getData());
358 
359  m_paValidator.validate(*pa.getData(),
360  [=] (const Data&) {
361  Route route(pa, faceId);
362  route.expires = std::min(route.annExpires, time::steady_clock::now() + maxLifetime);
363  beginAddRoute(pa.getAnnouncedName(), route, std::nullopt,
364  [=] (RibUpdateResult ribRes) {
365  auto res = getSlAnnounceResultFromRibUpdateResult(ribRes);
366  NFD_LOG_INFO("slAnnounce " << pa.getAnnouncedName() << " " << faceId << ": " << res);
367  cb(res);
368  });
369  },
370  [=] (const Data&, ndn::security::ValidationError err) {
371  NFD_LOG_INFO("slAnnounce " << pa.getAnnouncedName() << " " << faceId <<
372  " validation error: " << err);
373  cb(SlAnnounceResult::VALIDATION_FAILURE);
374  }
375  );
376 }
377 
378 void
379 RibManager::slRenew(const Name& name, uint64_t faceId, time::milliseconds maxLifetime,
380  const SlAnnounceCallback& cb)
381 {
382  Route routeQuery;
383  routeQuery.faceId = faceId;
384  routeQuery.origin = ndn::nfd::ROUTE_ORIGIN_PREFIXANN;
385  Route* oldRoute = m_rib.findLongestPrefix(name, routeQuery);
386 
387  if (oldRoute == nullptr || !oldRoute->announcement) {
388  NFD_LOG_DEBUG("slRenew " << name << " " << faceId << ": not found");
389  return cb(SlAnnounceResult::NOT_FOUND);
390  }
391  Name routeName = oldRoute->announcement->getAnnouncedName();
392 
393  Route route = *oldRoute;
394  route.expires = std::min(route.annExpires, time::steady_clock::now() + maxLifetime);
395  beginAddRoute(routeName, route, std::nullopt,
396  [=] (RibUpdateResult ribRes) {
397  auto res = getSlAnnounceResultFromRibUpdateResult(ribRes);
398  NFD_LOG_INFO("slRenew " << name << " " << faceId << ": " << res << " " << routeName);
399  cb(res);
400  });
401 }
402 
403 void
404 RibManager::slFindAnn(const Name& name, const SlFindAnnCallback& cb) const
405 {
406  shared_ptr<rib::RibEntry> entry;
407  auto exactMatch = m_rib.find(name);
408  if (exactMatch != m_rib.end()) {
409  entry = exactMatch->second;
410  }
411  else {
412  entry = m_rib.findParent(name);
413  }
414  if (entry == nullptr) {
415  return cb(std::nullopt);
416  }
417 
418  auto pa = entry->getPrefixAnnouncement();
419  pa.toData(m_keyChain);
420  cb(pa);
421 }
422 
423 void
424 RibManager::fetchActiveFaces()
425 {
426  NFD_LOG_DEBUG("Fetching active faces");
427 
428  m_nfdController.fetch<ndn::nfd::FaceDataset>(
429  std::bind(&RibManager::removeInvalidFaces, this, _1),
430  std::bind(&RibManager::onFetchActiveFacesFailure, this, _1, _2),
431  ndn::nfd::CommandOptions());
432 }
433 
434 void
435 RibManager::onFetchActiveFacesFailure(uint32_t code, const std::string& reason)
436 {
437  NFD_LOG_DEBUG("Face Status Dataset request failure " << code << " " << reason);
438  scheduleActiveFaceFetch(ACTIVE_FACE_FETCH_INTERVAL);
439 }
440 
441 void
442 RibManager::scheduleActiveFaceFetch(const time::seconds& timeToWait)
443 {
444  m_activeFaceFetchEvent = getScheduler().schedule(timeToWait, [this] { fetchActiveFaces(); });
445 }
446 
447 void
448 RibManager::removeInvalidFaces(const std::vector<ndn::nfd::FaceStatus>& activeFaces)
449 {
450  NFD_LOG_DEBUG("Checking for invalid face registrations");
451 
452  std::set<uint64_t> activeFaceIds;
453  for (const auto& faceStatus : activeFaces) {
454  activeFaceIds.insert(faceStatus.getFaceId());
455  }
456  getGlobalIoService().post([=] { m_rib.beginRemoveFailedFaces(activeFaceIds); });
457 
458  // Reschedule the check for future clean up
459  scheduleActiveFaceFetch(ACTIVE_FACE_FETCH_INTERVAL);
460 }
461 
462 void
463 RibManager::onNotification(const ndn::nfd::FaceEventNotification& notification)
464 {
465  NFD_LOG_TRACE("onNotification: " << notification);
466 
467  if (notification.getKind() == ndn::nfd::FACE_EVENT_DESTROYED) {
468  NFD_LOG_DEBUG("Received notification for destroyed FaceId " << notification.getFaceId());
469  getGlobalIoService().post([this, id = notification.getFaceId()] { m_rib.beginRemoveFace(id); });
470  }
471 }
472 
473 } // 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.
Definition: rib-manager.cpp:96
void disableLocalhop()
Disallow accepting commands on /localhop/nfd/rib prefix.
Definition: rib-manager.cpp:84
RibManager(rib::Rib &rib, ndn::Face &face, ndn::KeyChain &keyChain, ndn::nfd::Controller &nfdController, Dispatcher &dispatcher)
Definition: rib-manager.cpp:49
static const Name LOCALHOP_TOP_PREFIX
void applyLocalhostConfig(const ConfigSection &section, const std::string &filename)
Apply localhost_security configuration.
Definition: rib-manager.cpp:71
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:77
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:90
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:62
void insert(const Name &prefix, const Route &route)
Definition: rib.cpp:91
void onRouteExpiration(const Name &prefix, const Route &route)
Definition: rib.cpp:196
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:342
Represents a route for a name prefix.
Definition: route.hpp:43
ndn::nfd::RouteOrigin origin
Definition: route.hpp:81
uint64_t cost
Definition: route.hpp:82
time::steady_clock::time_point annExpires
Expiration time of the prefix announcement.
Definition: route.hpp:100
std::optional< time::steady_clock::time_point > expires
Definition: route.hpp:84
void setExpirationEvent(const scheduler::EventId &eid)
Definition: route.hpp:62
std::optional< ndn::PrefixAnnouncement > announcement
The prefix announcement that caused the creation of this route.
Definition: route.hpp:90
uint64_t faceId
Definition: route.hpp:80
#define NFD_LOG_INFO
Definition: logger.hpp:39
#define NFD_LOG_INIT(name)
Definition: logger.hpp:31
#define NFD_LOG_DEBUG
Definition: logger.hpp:38
#define NFD_LOG_TRACE
Definition: logger.hpp:37
Definition: dns-srv.cpp:44
Definition: common.hpp:77
boost::property_tree::ptree ConfigSection
A configuration file section.
Definition: config-file.hpp:38
const std::string MGMT_MODULE_NAME
Definition: rib-manager.cpp:45
constexpr time::seconds ACTIVE_FACE_FETCH_INTERVAL
Definition: rib-manager.cpp:47
std::ostream & operator<<(std::ostream &os, const Network &network)
Definition: network.cpp:83
boost::asio::io_service & getGlobalIoService()
Returns the global io_service instance for the calling thread.
Definition: global.cpp:36
Scheduler & getScheduler()
Returns the global Scheduler instance for the calling thread.
Definition: global.cpp:45
const Name LOCALHOST_TOP_PREFIX
Definition: rib-manager.cpp:46