face-manager.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
26 #include "face-manager.hpp"
27 
30 #include "face/tcp-factory.hpp"
31 #include "face/udp-factory.hpp"
32 #include "fw/face-table.hpp"
33 
34 #include <ndn-cxx/lp/tags.hpp>
35 #include <ndn-cxx/mgmt/nfd/channel-status.hpp>
36 #include <ndn-cxx/mgmt/nfd/face-status.hpp>
37 #include <ndn-cxx/mgmt/nfd/face-event-notification.hpp>
38 
39 #ifdef HAVE_UNIX_SOCKETS
41 #endif // HAVE_UNIX_SOCKETS
42 
43 #ifdef HAVE_LIBPCAP
46 #endif // HAVE_LIBPCAP
47 
48 #ifdef HAVE_WEBSOCKET
50 #endif // HAVE_WEBSOCKET
51 
52 namespace nfd {
53 
54 NFD_LOG_INIT("FaceManager");
55 
56 FaceManager::FaceManager(FaceTable& faceTable, Dispatcher& dispatcher, CommandAuthenticator& authenticator)
57  : NfdManagerBase(dispatcher, authenticator, "faces")
58  , m_faceTable(faceTable)
59 {
60  registerCommandHandler<ndn::nfd::FaceCreateCommand>("create",
61  bind(&FaceManager::createFace, this, _2, _3, _4, _5));
62 
63  registerCommandHandler<ndn::nfd::FaceUpdateCommand>("update",
64  bind(&FaceManager::updateFace, this, _2, _3, _4, _5));
65 
66  registerCommandHandler<ndn::nfd::FaceDestroyCommand>("destroy",
67  bind(&FaceManager::destroyFace, this, _2, _3, _4, _5));
68 
69  registerCommandHandler<ndn::nfd::FaceEnableLocalControlCommand>("enable-local-control",
70  bind(&FaceManager::enableLocalControl, this, _2, _3, _4, _5));
71 
72  registerCommandHandler<ndn::nfd::FaceDisableLocalControlCommand>("disable-local-control",
73  bind(&FaceManager::disableLocalControl, this, _2, _3, _4, _5));
74 
75  registerStatusDatasetHandler("list", bind(&FaceManager::listFaces, this, _1, _2, _3));
76  registerStatusDatasetHandler("channels", bind(&FaceManager::listChannels, this, _1, _2, _3));
77  registerStatusDatasetHandler("query", bind(&FaceManager::queryFaces, this, _1, _2, _3));
78 
79  m_postNotification = registerNotificationStream("events");
80  m_faceAddConn = m_faceTable.afterAdd.connect(bind(&FaceManager::notifyAddFace, this, _1));
81  m_faceRemoveConn = m_faceTable.beforeRemove.connect(bind(&FaceManager::notifyRemoveFace, this, _1));
82 }
83 
84 void
86 {
87  configFile.addSectionHandler("face_system", bind(&FaceManager::processConfig, this, _1, _2, _3));
88 }
89 
90 void
91 FaceManager::createFace(const Name& topPrefix, const Interest& interest,
92  const ControlParameters& parameters,
93  const ndn::mgmt::CommandContinuation& done)
94 {
95  FaceUri uri;
96  if (!uri.parse(parameters.getUri())) {
97  NFD_LOG_TRACE("failed to parse URI");
98  done(ControlResponse(400, "Malformed command"));
99  return;
100  }
101 
102  if (!uri.isCanonical()) {
103  NFD_LOG_TRACE("received non-canonical URI");
104  done(ControlResponse(400, "Non-canonical URI"));
105  return;
106  }
107 
108  auto factory = m_factories.find(uri.getScheme());
109  if (factory == m_factories.end()) {
110  NFD_LOG_TRACE("received create request for unsupported protocol");
111  done(ControlResponse(406, "Unsupported protocol"));
112  return;
113  }
114 
115  try {
116  factory->second->createFace(uri,
117  parameters.getFacePersistency(),
118  parameters.hasFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED) ?
119  parameters.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED) : false,
120  bind(&FaceManager::afterCreateFaceSuccess,
121  this, parameters, _1, done),
122  bind(&FaceManager::afterCreateFaceFailure,
123  this, _1, _2, done));
124  }
125  catch (const std::runtime_error& error) {
126  NFD_LOG_ERROR("Face creation failed: " << error.what());
127  done(ControlResponse(500, "Face creation failed due to internal error"));
128  return;
129  }
130  catch (const std::logic_error& error) {
131  NFD_LOG_ERROR("Face creation failed: " << error.what());
132  done(ControlResponse(500, "Face creation failed due to internal error"));
133  return;
134  }
135 }
136 
143 void
144 FaceManager::afterCreateFaceSuccess(const ControlParameters& parameters,
145  const shared_ptr<Face>& face,
146  const ndn::mgmt::CommandContinuation& done)
147 {
148  // TODO: Re-enable check in #3232
149  //if (face->getId() != face::INVALID_FACEID) {
151  //ControlParameters response;
152  //response.setFaceId(face->getId());
153  //response.setUri(face->getRemoteUri().toString());
154  //response.setFacePersistency(face->getPersistency());
155  //
156  //auto linkService = dynamic_cast<face::GenericLinkService*>(face->getLinkService());
157  //BOOST_ASSERT(linkService != nullptr);
158  //auto options = linkService->getOptions();
159  //response.setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED, options.allowLocalFields, false);
160  //
161  // NFD_LOG_TRACE("Attempted to create duplicate face of " << face->getId());
162  // done(ControlResponse(409, "Face with remote URI already exists").setBody(response.wireEncode()));
163  //}
164  //else {
165  // If scope non-local and flags set to enable local fields, request shouldn't
166  // have made it this far
167  BOOST_ASSERT(face->getScope() == ndn::nfd::FACE_SCOPE_LOCAL ||
168  !parameters.hasFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED) ||
169  (parameters.hasFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED) &&
170  !parameters.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED)));
171 
172  m_faceTable.add(face);
173 
174  ControlParameters response;
175  response.setFaceId(face->getId());
176  response.setFacePersistency(face->getPersistency());
177  response.setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED,
178  parameters.hasFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED) ?
179  parameters.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED) : false,
180  false);
181 
182  done(ControlResponse(200, "OK").setBody(response.wireEncode()));
183  //}
184 }
185 
186 void
187 FaceManager::afterCreateFaceFailure(uint32_t status,
188  const std::string& reason,
189  const ndn::mgmt::CommandContinuation& done)
190 {
191  NFD_LOG_DEBUG("Face creation failed: " << reason);
192 
193  done(ControlResponse(status, reason));
194 }
195 
196 void
197 FaceManager::updateFace(const Name& topPrefix, const Interest& interest,
198  const ControlParameters& parameters,
199  const ndn::mgmt::CommandContinuation& done)
200 {
201  FaceId faceId = parameters.getFaceId();
202  if (faceId == 0) {
203  // Self-updating
204  shared_ptr<lp::IncomingFaceIdTag> incomingFaceIdTag = interest.getTag<lp::IncomingFaceIdTag>();
205  if (incomingFaceIdTag == nullptr) {
206  NFD_LOG_TRACE("unable to determine face for self-update");
207  done(ControlResponse(404, "No FaceId specified and IncomingFaceId not available"));
208  return;
209  }
210  faceId = *incomingFaceIdTag;
211  }
212 
213  Face* face = m_faceTable.get(faceId);
214 
215  if (face == nullptr) {
216  NFD_LOG_TRACE("invalid face specified");
217  done(ControlResponse(404, "Specified face does not exist"));
218  return;
219  }
220 
221  // Verify validity of requested changes
222  ControlParameters response;
223  bool areParamsValid = true;
224 
225  if (parameters.hasFacePersistency()) {
226  // TODO #3232: Add FacePersistency updating
227  NFD_LOG_TRACE("received unsupported face persistency change");
228  areParamsValid = false;
229  response.setFacePersistency(parameters.getFacePersistency());
230  }
231 
232  if (parameters.hasFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED) &&
233  parameters.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED) &&
234  face->getScope() != ndn::nfd::FACE_SCOPE_LOCAL) {
235  NFD_LOG_TRACE("received request to enable local fields on non-local face");
236  areParamsValid = false;
237  response.setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED,
238  parameters.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED));
239  }
240 
241  if (!areParamsValid) {
242  done(ControlResponse(409, "Invalid properties specified").setBody(response.wireEncode()));
243  return;
244  }
245 
246  // All specified properties are valid, so make changes
247 
248  // TODO #3232: Add FacePersistency updating
249 
250  setLinkServiceOptions(*face, parameters, response);
251 
252  // Set ControlResponse fields
253  response.setFaceId(faceId);
254  response.setFacePersistency(face->getPersistency());
255  response.setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED,
256  parameters.hasFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED) ?
257  parameters.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED) : false,
258  false);
259 
260  done(ControlResponse(200, "OK").setBody(response.wireEncode()));
261 }
262 
263 void
264 FaceManager::destroyFace(const Name& topPrefix, const Interest& interest,
265  const ControlParameters& parameters,
266  const ndn::mgmt::CommandContinuation& done)
267 {
268  Face* face = m_faceTable.get(parameters.getFaceId());
269  if (face != nullptr) {
270  face->close();
271  }
272 
273  done(ControlResponse(200, "OK").setBody(parameters.wireEncode()));
274 }
275 
276 void
277 FaceManager::enableLocalControl(const Name& topPrefix, const Interest& interest,
278  const ControlParameters& parameters,
279  const ndn::mgmt::CommandContinuation& done)
280 {
281  Face* face = findFaceForLocalControl(interest, parameters, done);
282  if (!face) {
283  return;
284  }
285 
286  // enable-local-control will enable all local fields in GenericLinkService
287  auto service = dynamic_cast<face::GenericLinkService*>(face->getLinkService());
288  if (service == nullptr) {
289  return done(ControlResponse(503, "LinkService type not supported"));
290  }
291 
292  face::GenericLinkService::Options options = service->getOptions();
293  options.allowLocalFields = true;
294  service->setOptions(options);
295 
296  return done(ControlResponse(200, "OK: enable all local fields on GenericLinkService")
297  .setBody(parameters.wireEncode()));
298 }
299 
300 void
301 FaceManager::disableLocalControl(const Name& topPrefix, const Interest& interest,
302  const ControlParameters& parameters,
303  const ndn::mgmt::CommandContinuation& done)
304 {
305  Face* face = findFaceForLocalControl(interest, parameters, done);
306  if (!face) {
307  return;
308  }
309 
310  // disable-local-control will disable all local fields in GenericLinkService
311  auto service = dynamic_cast<face::GenericLinkService*>(face->getLinkService());
312  if (service == nullptr) {
313  return done(ControlResponse(503, "LinkService type not supported"));
314  }
315 
316  face::GenericLinkService::Options options = service->getOptions();
317  options.allowLocalFields = false;
318  service->setOptions(options);
319 
320  return done(ControlResponse(200, "OK: disable all local fields on GenericLinkService")
321  .setBody(parameters.wireEncode()));
322 }
323 
324 Face*
325 FaceManager::findFaceForLocalControl(const Interest& request,
326  const ControlParameters& parameters,
327  const ndn::mgmt::CommandContinuation& done)
328 {
329  shared_ptr<lp::IncomingFaceIdTag> incomingFaceIdTag = request.getTag<lp::IncomingFaceIdTag>();
330  // NDNLPv2 says "application MUST be prepared to receive a packet without IncomingFaceId field",
331  // but it's fine to assert IncomingFaceId is available, because InternalFace lives inside NFD
332  // and is initialized synchronously with IncomingFaceId field enabled.
333  BOOST_ASSERT(incomingFaceIdTag != nullptr);
334 
335  Face* face = m_faceTable.get(*incomingFaceIdTag);
336  if (face == nullptr) {
337  NFD_LOG_DEBUG("FaceId " << *incomingFaceIdTag << " not found");
338  done(ControlResponse(410, "Face not found"));
339  return nullptr;
340  }
341 
342  if (face->getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL) {
343  NFD_LOG_DEBUG("Cannot enable local control on non-local FaceId " << face->getId());
344  done(ControlResponse(412, "Face is non-local"));
345  return nullptr;
346  }
347 
348  return face;
349 }
350 
351 void
352 FaceManager::setLinkServiceOptions(Face& face,
353  const ControlParameters& parameters,
354  ControlParameters& response)
355 {
356  auto linkService = dynamic_cast<face::GenericLinkService*>(face.getLinkService());
357  BOOST_ASSERT(linkService != nullptr);
358 
359  auto options = linkService->getOptions();
360  if (parameters.hasFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED) &&
361  face.getScope() == ndn::nfd::FACE_SCOPE_LOCAL) {
362  options.allowLocalFields = parameters.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED);
363  }
364  linkService->setOptions(options);
365 
366  // Set Flags for ControlResponse
367  response.setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED, options.allowLocalFields, false);
368 }
369 
370 void
371 FaceManager::listFaces(const Name& topPrefix, const Interest& interest,
372  ndn::mgmt::StatusDatasetContext& context)
373 {
374  auto now = time::steady_clock::now();
375  for (const Face& face : m_faceTable) {
376  ndn::nfd::FaceStatus status = collectFaceStatus(face, now);
377  context.append(status.wireEncode());
378  }
379  context.end();
380 }
381 
382 void
383 FaceManager::listChannels(const Name& topPrefix, const Interest& interest,
384  ndn::mgmt::StatusDatasetContext& context)
385 {
386  std::set<const ProtocolFactory*> seenFactories;
387 
388  for (const auto& kv : m_factories) {
389  const ProtocolFactory* factory = kv.second.get();
390  bool inserted;
391  std::tie(std::ignore, inserted) = seenFactories.insert(factory);
392 
393  if (inserted) {
394  for (const auto& channel : factory->getChannels()) {
395  ndn::nfd::ChannelStatus entry;
396  entry.setLocalUri(channel->getUri().toString());
397  context.append(entry.wireEncode());
398  }
399  }
400  }
401 
402  context.end();
403 }
404 
405 void
406 FaceManager::queryFaces(const Name& topPrefix, const Interest& interest,
407  ndn::mgmt::StatusDatasetContext& context)
408 {
409  ndn::nfd::FaceQueryFilter faceFilter;
410  const Name& query = interest.getName();
411  try {
412  faceFilter.wireDecode(query[-1].blockFromValue());
413  }
414  catch (const tlv::Error& e) {
415  NFD_LOG_DEBUG("Malformed query filter: " << e.what());
416  return context.reject(ControlResponse(400, "Malformed filter"));
417  }
418 
419  auto now = time::steady_clock::now();
420  for (const Face& face : m_faceTable) {
421  if (!matchFilter(faceFilter, face)) {
422  continue;
423  }
424  ndn::nfd::FaceStatus status = collectFaceStatus(face, now);
425  context.append(status.wireEncode());
426  }
427 
428  context.end();
429 }
430 
431 bool
432 FaceManager::matchFilter(const ndn::nfd::FaceQueryFilter& filter, const Face& face)
433 {
434  if (filter.hasFaceId() &&
435  filter.getFaceId() != static_cast<uint64_t>(face.getId())) {
436  return false;
437  }
438 
439  if (filter.hasUriScheme() &&
440  filter.getUriScheme() != face.getRemoteUri().getScheme() &&
441  filter.getUriScheme() != face.getLocalUri().getScheme()) {
442  return false;
443  }
444 
445  if (filter.hasRemoteUri() &&
446  filter.getRemoteUri() != face.getRemoteUri().toString()) {
447  return false;
448  }
449 
450  if (filter.hasLocalUri() &&
451  filter.getLocalUri() != face.getLocalUri().toString()) {
452  return false;
453  }
454 
455  if (filter.hasFaceScope() &&
456  filter.getFaceScope() != face.getScope()) {
457  return false;
458  }
459 
460  if (filter.hasFacePersistency() &&
461  filter.getFacePersistency() != face.getPersistency()) {
462  return false;
463  }
464 
465  if (filter.hasLinkType() &&
466  filter.getLinkType() != face.getLinkType()) {
467  return false;
468  }
469 
470  return true;
471 }
472 
473 ndn::nfd::FaceStatus
474 FaceManager::collectFaceStatus(const Face& face, const time::steady_clock::TimePoint& now)
475 {
476  ndn::nfd::FaceStatus status;
477 
478  collectFaceProperties(face, status);
479 
480  time::steady_clock::TimePoint expirationTime = face.getExpirationTime();
481  if (expirationTime != time::steady_clock::TimePoint::max()) {
482  status.setExpirationPeriod(std::max(time::milliseconds(0),
483  time::duration_cast<time::milliseconds>(expirationTime - now)));
484  }
485 
486  const face::FaceCounters& counters = face.getCounters();
487  status.setNInInterests(counters.nInInterests)
488  .setNOutInterests(counters.nOutInterests)
489  .setNInDatas(counters.nInData)
490  .setNOutDatas(counters.nOutData)
491  .setNInNacks(counters.nInNacks)
492  .setNOutNacks(counters.nOutNacks)
493  .setNInBytes(counters.nInBytes)
494  .setNOutBytes(counters.nOutBytes);
495 
496  // Set Flag bits
497  auto linkService = dynamic_cast<face::GenericLinkService*>(face.getLinkService());
498  if (linkService != nullptr) {
499  auto linkServiceOptions = linkService->getOptions();
500  status.setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED, linkServiceOptions.allowLocalFields);
501  }
502 
503  return status;
504 }
505 
506 template<typename FaceTraits>
507 void
508 FaceManager::collectFaceProperties(const Face& face, FaceTraits& traits)
509 {
510  traits.setFaceId(face.getId())
511  .setRemoteUri(face.getRemoteUri().toString())
512  .setLocalUri(face.getLocalUri().toString())
513  .setFaceScope(face.getScope())
514  .setFacePersistency(face.getPersistency())
515  .setLinkType(face.getLinkType());
516 }
517 
518 void
519 FaceManager::notifyAddFace(const Face& face)
520 {
521  ndn::nfd::FaceEventNotification notification;
522  notification.setKind(ndn::nfd::FACE_EVENT_CREATED);
523  collectFaceProperties(face, notification);
524 
525  m_postNotification(notification.wireEncode());
526 }
527 
528 void
529 FaceManager::notifyRemoveFace(const Face& face)
530 {
531  ndn::nfd::FaceEventNotification notification;
532  notification.setKind(ndn::nfd::FACE_EVENT_DESTROYED);
533  collectFaceProperties(face, notification);
534 
535  m_postNotification(notification.wireEncode());
536 }
537 
538 void
539 FaceManager::processConfig(const ConfigSection& configSection,
540  bool isDryRun,
541  const std::string& filename)
542 {
543  bool hasSeenUnix = false;
544  bool hasSeenTcp = false;
545  bool hasSeenUdp = false;
546  bool hasSeenEther = false;
547  bool hasSeenWebSocket = false;
548  auto nicList = listNetworkInterfaces();
549 
550  for (const auto& item : configSection) {
551  if (item.first == "unix") {
552  if (hasSeenUnix) {
553  BOOST_THROW_EXCEPTION(Error("Duplicate \"unix\" section"));
554  }
555  hasSeenUnix = true;
556 
557  processSectionUnix(item.second, isDryRun);
558  }
559  else if (item.first == "tcp") {
560  if (hasSeenTcp) {
561  BOOST_THROW_EXCEPTION(Error("Duplicate \"tcp\" section"));
562  }
563  hasSeenTcp = true;
564 
565  processSectionTcp(item.second, isDryRun);
566  }
567  else if (item.first == "udp") {
568  if (hasSeenUdp) {
569  BOOST_THROW_EXCEPTION(Error("Duplicate \"udp\" section"));
570  }
571  hasSeenUdp = true;
572 
573  processSectionUdp(item.second, isDryRun, nicList);
574  }
575  else if (item.first == "ether") {
576  if (hasSeenEther) {
577  BOOST_THROW_EXCEPTION(Error("Duplicate \"ether\" section"));
578  }
579  hasSeenEther = true;
580 
581  processSectionEther(item.second, isDryRun, nicList);
582  }
583  else if (item.first == "websocket") {
584  if (hasSeenWebSocket) {
585  BOOST_THROW_EXCEPTION(Error("Duplicate \"websocket\" section"));
586  }
587  hasSeenWebSocket = true;
588 
589  processSectionWebSocket(item.second, isDryRun);
590  }
591  else {
592  BOOST_THROW_EXCEPTION(Error("Unrecognized option \"" + item.first + "\""));
593  }
594  }
595 }
596 
597 void
598 FaceManager::processSectionUnix(const ConfigSection& configSection, bool isDryRun)
599 {
600  // ; the unix section contains settings of Unix stream faces and channels
601  // unix
602  // {
603  // path /var/run/nfd.sock ; Unix stream listener path
604  // }
605 
606 #if defined(HAVE_UNIX_SOCKETS)
607  std::string path = "/var/run/nfd.sock";
608 
609  for (const auto& i : configSection) {
610  if (i.first == "path") {
611  path = i.second.get_value<std::string>();
612  }
613  else {
614  BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
615  i.first + "\" in \"unix\" section"));
616  }
617  }
618 
619  if (!isDryRun) {
620  if (m_factories.count("unix") > 0) {
621  return;
622  }
623 
624  auto factory = make_shared<UnixStreamFactory>();
625  m_factories.insert(std::make_pair("unix", factory));
626 
627  auto channel = factory->createChannel(path);
628  channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
629  }
630 #else
631  BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without Unix sockets support, "
632  "cannot process \"unix\" section"));
633 #endif // HAVE_UNIX_SOCKETS
634 }
635 
636 void
637 FaceManager::processSectionTcp(const ConfigSection& configSection, bool isDryRun)
638 {
639  // ; the tcp section contains settings of TCP faces and channels
640  // tcp
641  // {
642  // listen yes ; set to 'no' to disable TCP listener, default 'yes'
643  // port 6363 ; TCP listener port number
644  // }
645 
646  uint16_t port = 6363;
647  bool needToListen = true;
648  bool enableV4 = true;
649  bool enableV6 = true;
650 
651  for (const auto& i : configSection) {
652  if (i.first == "port") {
653  port = ConfigFile::parseNumber<uint16_t>(i, "tcp");
654  NFD_LOG_TRACE("TCP port set to " << port);
655  }
656  else if (i.first == "listen") {
657  needToListen = ConfigFile::parseYesNo(i, "tcp");
658  }
659  else if (i.first == "enable_v4") {
660  enableV4 = ConfigFile::parseYesNo(i, "tcp");
661  }
662  else if (i.first == "enable_v6") {
663  enableV6 = ConfigFile::parseYesNo(i, "tcp");
664  }
665  else {
666  BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
667  i.first + "\" in \"tcp\" section"));
668  }
669  }
670 
671  if (!enableV4 && !enableV6) {
672  BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 TCP channels have been disabled."
673  " Remove \"tcp\" section to disable TCP channels or"
674  " re-enable at least one channel type."));
675  }
676 
677  if (!isDryRun) {
678  if (m_factories.count("tcp") > 0) {
679  return;
680  }
681 
682  auto factory = make_shared<TcpFactory>();
683  m_factories.insert(std::make_pair("tcp", factory));
684 
685  if (enableV4) {
686  tcp::Endpoint endpoint(boost::asio::ip::tcp::v4(), port);
687  shared_ptr<TcpChannel> v4Channel = factory->createChannel(endpoint);
688  if (needToListen) {
689  v4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
690  }
691 
692  m_factories.insert(std::make_pair("tcp4", factory));
693  }
694 
695  if (enableV6) {
696  tcp::Endpoint endpoint(boost::asio::ip::tcp::v6(), port);
697  shared_ptr<TcpChannel> v6Channel = factory->createChannel(endpoint);
698  if (needToListen) {
699  v6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
700  }
701 
702  m_factories.insert(std::make_pair("tcp6", factory));
703  }
704  }
705 }
706 
707 void
708 FaceManager::processSectionUdp(const ConfigSection& configSection, bool isDryRun,
709  const std::vector<NetworkInterfaceInfo>& nicList)
710 {
711  // ; the udp section contains settings of UDP faces and channels
712  // udp
713  // {
714  // port 6363 ; UDP unicast port number
715  // idle_timeout 600 ; idle time (seconds) before closing a UDP unicast face
716  // keep_alive_interval 25 ; interval (seconds) between keep-alive refreshes
717 
718  // ; NFD creates one UDP multicast face per NIC
719  // mcast yes ; set to 'no' to disable UDP multicast, default 'yes'
720  // mcast_port 56363 ; UDP multicast port number
721  // mcast_group 224.0.23.170 ; UDP multicast group (IPv4 only)
722  // }
723 
724  uint16_t port = 6363;
725  bool enableV4 = true;
726  bool enableV6 = true;
727  size_t timeout = 600;
728  size_t keepAliveInterval = 25;
729  bool useMcast = true;
730  auto mcastGroup = boost::asio::ip::address_v4::from_string("224.0.23.170");
731  uint16_t mcastPort = 56363;
732 
733  for (const auto& i : configSection) {
734  if (i.first == "port") {
735  port = ConfigFile::parseNumber<uint16_t>(i, "udp");
736  NFD_LOG_TRACE("UDP unicast port set to " << port);
737  }
738  else if (i.first == "enable_v4") {
739  enableV4 = ConfigFile::parseYesNo(i, "udp");
740  }
741  else if (i.first == "enable_v6") {
742  enableV6 = ConfigFile::parseYesNo(i, "udp");
743  }
744  else if (i.first == "idle_timeout") {
745  try {
746  timeout = i.second.get_value<size_t>();
747  }
748  catch (const boost::property_tree::ptree_bad_data&) {
749  BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
750  i.first + "\" in \"udp\" section"));
751  }
752  }
753  else if (i.first == "keep_alive_interval") {
754  try {
755  keepAliveInterval = i.second.get_value<size_t>();
757  (void)(keepAliveInterval);
758  }
759  catch (const boost::property_tree::ptree_bad_data&) {
760  BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
761  i.first + "\" in \"udp\" section"));
762  }
763  }
764  else if (i.first == "mcast") {
765  useMcast = ConfigFile::parseYesNo(i, "udp");
766  }
767  else if (i.first == "mcast_port") {
768  mcastPort = ConfigFile::parseNumber<uint16_t>(i, "udp");
769  NFD_LOG_TRACE("UDP multicast port set to " << mcastPort);
770  }
771  else if (i.first == "mcast_group") {
772  boost::system::error_code ec;
773  mcastGroup = boost::asio::ip::address_v4::from_string(i.second.get_value<std::string>(), ec);
774  if (ec) {
775  BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
776  i.first + "\" in \"udp\" section"));
777  }
778  NFD_LOG_TRACE("UDP multicast group set to " << mcastGroup);
779  }
780  else {
781  BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
782  i.first + "\" in \"udp\" section"));
783  }
784  }
785 
786  if (!enableV4 && !enableV6) {
787  BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 UDP channels have been disabled."
788  " Remove \"udp\" section to disable UDP channels or"
789  " re-enable at least one channel type."));
790  }
791  else if (useMcast && !enableV4) {
792  BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 multicast requested, but IPv4 channels"
793  " have been disabled (conflicting configuration options set)"));
794  }
795 
796  if (!isDryRun) {
797  shared_ptr<UdpFactory> factory;
798  bool isReload = false;
799  if (m_factories.count("udp") > 0) {
800  isReload = true;
801  factory = static_pointer_cast<UdpFactory>(m_factories["udp"]);
802  }
803  else {
804  factory = make_shared<UdpFactory>();
805  m_factories.insert(std::make_pair("udp", factory));
806  }
807 
808  if (!isReload && enableV4) {
809  udp::Endpoint endpoint(boost::asio::ip::udp::v4(), port);
810  shared_ptr<UdpChannel> v4Channel = factory->createChannel(endpoint, time::seconds(timeout));
811  v4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
812 
813  m_factories.insert(std::make_pair("udp4", factory));
814  }
815 
816  if (!isReload && enableV6) {
817  udp::Endpoint endpoint(boost::asio::ip::udp::v6(), port);
818  shared_ptr<UdpChannel> v6Channel = factory->createChannel(endpoint, time::seconds(timeout));
819  v6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
820 
821  m_factories.insert(std::make_pair("udp6", factory));
822  }
823 
824  std::set<shared_ptr<Face>> multicastFacesToRemove;
825  for (const auto& i : factory->getMulticastFaces()) {
826  multicastFacesToRemove.insert(i.second);
827  }
828 
829  if (useMcast && enableV4) {
830  std::vector<NetworkInterfaceInfo> ipv4MulticastInterfaces;
831  for (const auto& nic : nicList) {
832  if (nic.isUp() && nic.isMulticastCapable() && !nic.ipv4Addresses.empty()) {
833  ipv4MulticastInterfaces.push_back(nic);
834  }
835  }
836 
837  bool isNicNameNecessary = false;
838 #if defined(__linux__)
839  if (ipv4MulticastInterfaces.size() > 1) {
840  // On Linux if we have more than one MulticastUdpFace
841  // we need to specify the name of the interface
842  isNicNameNecessary = true;
843  }
844 #endif
845 
846  udp::Endpoint mcastEndpoint(mcastGroup, mcastPort);
847  for (const auto& nic : ipv4MulticastInterfaces) {
848  udp::Endpoint localEndpoint(nic.ipv4Addresses[0], mcastPort);
849  auto newFace = factory->createMulticastFace(localEndpoint, mcastEndpoint,
850  isNicNameNecessary ? nic.name : "");
851  m_faceTable.add(newFace);
852  multicastFacesToRemove.erase(newFace);
853  }
854  }
855 
856  for (const auto& face : multicastFacesToRemove) {
857  face->close();
858  }
859  }
860 }
861 
862 void
863 FaceManager::processSectionEther(const ConfigSection& configSection, bool isDryRun,
864  const std::vector<NetworkInterfaceInfo>& nicList)
865 {
866  // ; the ether section contains settings of Ethernet faces and channels
867  // ether
868  // {
869  // ; NFD creates one Ethernet multicast face per NIC
870  // mcast yes ; set to 'no' to disable Ethernet multicast, default 'yes'
871  // mcast_group 01:00:5E:00:17:AA ; Ethernet multicast group
872  // }
873 
874 #if defined(HAVE_LIBPCAP)
875  bool useMcast = true;
876  ethernet::Address mcastGroup(ethernet::getDefaultMulticastAddress());
877 
878  for (const auto& i : configSection) {
879  if (i.first == "mcast") {
880  useMcast = ConfigFile::parseYesNo(i, "ether");
881  }
882  else if (i.first == "mcast_group") {
883  mcastGroup = ethernet::Address::fromString(i.second.get_value<std::string>());
884  if (mcastGroup.isNull()) {
885  BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
886  i.first + "\" in \"ether\" section"));
887  }
888  NFD_LOG_TRACE("Ethernet multicast group set to " << mcastGroup);
889  }
890  else {
891  BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
892  i.first + "\" in \"ether\" section"));
893  }
894  }
895 
896  if (!isDryRun) {
897  shared_ptr<EthernetFactory> factory;
898  if (m_factories.count("ether") > 0) {
899  factory = static_pointer_cast<EthernetFactory>(m_factories["ether"]);
900  }
901  else {
902  factory = make_shared<EthernetFactory>();
903  m_factories.insert(std::make_pair("ether", factory));
904  }
905 
906  std::set<shared_ptr<Face>> multicastFacesToRemove;
907  for (const auto& i : factory->getMulticastFaces()) {
908  multicastFacesToRemove.insert(i.second);
909  }
910 
911  if (useMcast) {
912  for (const auto& nic : nicList) {
913  if (nic.isUp() && nic.isMulticastCapable()) {
914  try {
915  auto newFace = factory->createMulticastFace(nic, mcastGroup);
916  m_faceTable.add(newFace);
917  multicastFacesToRemove.erase(newFace);
918  }
919  catch (const EthernetFactory::Error& factoryError) {
920  NFD_LOG_ERROR(factoryError.what() << ", continuing");
921  }
922  catch (const face::EthernetTransport::Error& faceError) {
923  NFD_LOG_ERROR(faceError.what() << ", continuing");
924  }
925  }
926  }
927  }
928 
929  for (const auto& face : multicastFacesToRemove) {
930  face->close();
931  }
932  }
933 #else
934  BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without libpcap, cannot process \"ether\" section"));
935 #endif // HAVE_LIBPCAP
936 }
937 
938 void
939 FaceManager::processSectionWebSocket(const ConfigSection& configSection, bool isDryRun)
940 {
941  // ; the websocket section contains settings of WebSocket faces and channels
942  // websocket
943  // {
944  // listen yes ; set to 'no' to disable WebSocket listener, default 'yes'
945  // port 9696 ; WebSocket listener port number
946  // enable_v4 yes ; set to 'no' to disable listening on IPv4 socket, default 'yes'
947  // enable_v6 yes ; set to 'no' to disable listening on IPv6 socket, default 'yes'
948  // }
949 
950 #if defined(HAVE_WEBSOCKET)
951  uint16_t port = 9696;
952  bool needToListen = true;
953  bool enableV4 = true;
954  bool enableV6 = true;
955 
956  for (const auto& i : configSection) {
957  if (i.first == "port") {
958  port = ConfigFile::parseNumber<uint16_t>(i, "websocket");
959  NFD_LOG_TRACE("WebSocket port set to " << port);
960  }
961  else if (i.first == "listen") {
962  needToListen = ConfigFile::parseYesNo(i, "websocket");
963  }
964  else if (i.first == "enable_v4") {
965  enableV4 = ConfigFile::parseYesNo(i, "websocket");
966  }
967  else if (i.first == "enable_v6") {
968  enableV6 = ConfigFile::parseYesNo(i, "websocket");
969  }
970  else {
971  BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
972  i.first + "\" in \"websocket\" section"));
973  }
974  }
975 
976  if (!enableV4 && !enableV6) {
977  BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 WebSocket channels have been disabled."
978  " Remove \"websocket\" section to disable WebSocket channels or"
979  " re-enable at least one channel type."));
980  }
981 
982  if (!enableV4 && enableV6) {
983  BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD does not allow pure IPv6 WebSocket channel."));
984  }
985 
986  if (!isDryRun) {
987  if (m_factories.count("websocket") > 0) {
988  return;
989  }
990 
991  auto factory = make_shared<WebSocketFactory>();
992  m_factories.insert(std::make_pair("websocket", factory));
993 
994  shared_ptr<WebSocketChannel> channel;
995 
996  if (enableV6 && enableV4) {
997  websocket::Endpoint endpoint(boost::asio::ip::address_v6::any(), port);
998  channel = factory->createChannel(endpoint);
999 
1000  m_factories.insert(std::make_pair("websocket46", factory));
1001  }
1002  else if (enableV4) {
1003  websocket::Endpoint endpoint(boost::asio::ip::address_v4::any(), port);
1004  channel = factory->createChannel(endpoint);
1005 
1006  m_factories.insert(std::make_pair("websocket4", factory));
1007  }
1008 
1009  if (channel && needToListen) {
1010  channel->listen(bind(&FaceTable::add, &m_faceTable, _1));
1011  }
1012  }
1013 #else
1014  BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without WebSocket, "
1015  "cannot process \"websocket\" section"));
1016 #endif // HAVE_WEBSOCKET
1017 }
1018 
1019 } // namespace nfd
void registerStatusDatasetHandler(const std::string &verb, const ndn::mgmt::StatusDatasetHandler &handler)
void addSectionHandler(const std::string &sectionName, ConfigSectionHandler subscriber)
setup notification of configuration file sections
Definition: config-file.cpp:79
#define NFD_LOG_DEBUG(expression)
Definition: logger.hpp:161
configuration file parsing utility
Definition: config-file.hpp:50
container of all faces
Definition: face-table.hpp:37
Face * get(FaceId id) const
get face by FaceId
Definition: face-table.cpp:42
#define NFD_LOG_ERROR(expression)
Definition: logger.hpp:164
static bool parseYesNo(const ConfigSection &node, const std::string &key, const std::string &sectionName)
parse a config option that can be either "yes" or "no"
Definition: config-file.cpp:62
std::vector< NetworkInterfaceInfo > listNetworkInterfaces()
List configured network interfaces on the system and their info.
provides ControlCommand authorization according to NFD configuration file
void add(shared_ptr< Face > face)
add a face
Definition: face-table.cpp:58
Copyright (c) 2014-2015, Regents of the University of California, Arizona Board of Regents...
Definition: algorithm.hpp:32
boost::asio::ip::tcp::endpoint Endpoint
Definition: tcp-channel.hpp:35
signal::Signal< FaceTable, Face & > beforeRemove
fires before a face is removed
Definition: face-table.hpp:91
ndn::mgmt::PostNotification registerNotificationStream(const std::string &verb)
a collection of common functions shared by all NFD managers, such as communicating with the dispatche...
signal::Signal< FaceTable, Face & > afterAdd
fires after a face is added
Definition: face-table.hpp:85
boost::property_tree::ptree ConfigSection
Definition: config-file.hpp:35
void setConfigFile(ConfigFile &configFile)
Subscribe to face_system section for the config file.
boost::asio::ip::udp::endpoint Endpoint
Definition: udp-channel.hpp:34
#define NFD_LOG_INIT(name)
Definition: logger.hpp:122
#define NFD_LOG_TRACE(expression)
Definition: logger.hpp:160
boost::asio::ip::tcp::endpoint Endpoint
uint64_t FaceId
identifies a face
Definition: face.hpp:39
FaceManager(FaceTable &faceTable, Dispatcher &dispatcher, CommandAuthenticator &authenticator)