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