diff --git a/AUTHORS.md b/AUTHORS.md index cad7c8b2..f71d42d7 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -56,3 +56,8 @@ volunteers. * Haowei Yuan * Hila Ben Abraham * Patrick Crowley + +* The University of Memphis + + * Syed Obaid Amin + * Lan Wang diff --git a/README-dev.md b/README-dev.md index b4cba899..d9cae4af 100644 --- a/README-dev.md +++ b/README-dev.md @@ -10,7 +10,8 @@ Include the following header into all `.hpp` and `.cpp` files: * Colorado State University, * University Pierre & Marie Curie, Sorbonne University, * Washington University in St. Louis, - * Beijing Institute of Technology + * Beijing Institute of Technology, + * The University of Memphis * * This file is part of NFD (Named Data Networking Forwarding Daemon). * See AUTHORS.md for complete list of NFD authors and contributors. diff --git a/rib/src/face-monitor.cpp b/core/face-monitor.cpp similarity index 67% rename from rib/src/face-monitor.cpp rename to core/face-monitor.cpp index f4345238..658d4b9c 100644 --- a/rib/src/face-monitor.cpp +++ b/core/face-monitor.cpp @@ -1,19 +1,38 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /** - * Copyright (C) 2014 Regents of the University of California. - * See COPYING for copyright and distribution information. - */ + * Copyright (c) 2014 Regents of the University of California, + * Arizona Board of Regents, + * Colorado State University, + * University Pierre & Marie Curie, Sorbonne University, + * Washington University in St. Louis, + * Beijing Institute of Technology, + * The University of Memphis + * + * This file is part of NFD (Named Data Networking Forwarding Daemon). + * See AUTHORS.md for complete list of NFD authors and contributors. + * + * NFD is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * NFD, e.g., in COPYING.md file. If not, see . + **/ -#include "common.hpp" #include "face-monitor.hpp" -#include +#include "core/logger.hpp" +#include -namespace ndn { +namespace nfd { -using namespace nfd; +NFD_LOG_INIT("FaceMonitor"); -FaceMonitor::FaceMonitor(Face& face) +FaceMonitor::FaceMonitor(ndn::Face& face) : m_face(face) , m_isStopped(true) { @@ -53,8 +72,7 @@ FaceMonitor::startNotifications() .setInterestLifetime(time::seconds(60)) ; - //todo: add logging support. - std::cout << "startNotification: Interest Sent: " << interest << std::endl; + NFD_LOG_DEBUG("startNotification: Interest Sent: " << interest); m_lastInterestId = m_face.expressInterest(interest, bind(&FaceMonitor::onNotification, this, _2), @@ -103,8 +121,7 @@ FaceMonitor::onTimeout() .setInterestLifetime(time::seconds(60)) ; - //todo: add logging support. - std::cout << "In onTimeout, sending interest: " << newInterest << std::endl; + NFD_LOG_DEBUG("In onTimeout, sending interest: " << newInterest); m_lastInterestId = m_face.expressInterest(newInterest, bind(&FaceMonitor::onNotification, this, _2), @@ -138,12 +155,11 @@ FaceMonitor::onNotification(const Data& data) Interest interest(nextNotification); interest.setInterestLifetime(time::seconds(60)); - //todo: add logging support. - std::cout << "onNotification: Interest sent: " << interest << std::endl; + NFD_LOG_DEBUG("onNotification: Interest sent: " << interest); m_lastInterestId = m_face.expressInterest(interest, bind(&FaceMonitor::onNotification, this, _2), bind(&FaceMonitor::onTimeout, this)); } -}//namespace ndn +} // namespace nfd diff --git a/core/face-monitor.hpp b/core/face-monitor.hpp new file mode 100644 index 00000000..6ce65955 --- /dev/null +++ b/core/face-monitor.hpp @@ -0,0 +1,105 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/** + * Copyright (c) 2014 Regents of the University of California, + * Arizona Board of Regents, + * Colorado State University, + * University Pierre & Marie Curie, Sorbonne University, + * Washington University in St. Louis, + * Beijing Institute of Technology, + * The University of Memphis + * + * This file is part of NFD (Named Data Networking Forwarding Daemon). + * See AUTHORS.md for complete list of NFD authors and contributors. + * + * NFD is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * NFD, e.g., in COPYING.md file. If not, see . + **/ + +#ifndef NFD_CORE_FACE_MONITOR_HPP +#define NFD_CORE_FACE_MONITOR_HPP + +#include "common.hpp" + +#include + +namespace ndn { +class Face; +struct PendingInterestId; +} + +namespace nfd { + +using ndn::nfd::FaceEventNotification; + +/** + * \brief Helper class to subscribe to face notification events + * + * \todo Write test cases + */ +class FaceMonitor : noncopyable +{ +public: + typedef function NotificationCallback; + typedef function TimeoutCallback; + + typedef std::vector NotificationCallbacks; + typedef std::vector TimeoutCallbacks; + + explicit + FaceMonitor(ndn::Face& face); + + ~FaceMonitor(); + + /** \brief Stops all notifications. This method doesn't remove registered callbacks. + */ + void + stopNotifications(); + + /** \brief Resumes notifications for added subscribers. + */ + void + startNotifications(); + + /** \brief Removes all notification subscribers. + */ + void + removeAllSubscribers(); + + /** \brief Adds a notification subscriber. This method doesn't return on timeouts. + */ + void + addSubscriber(const NotificationCallback& notificationCallback); + + /** \brief Adds a notification subscriber. + */ + void + addSubscriber(const NotificationCallback& notificationCallback, + const TimeoutCallback& timeoutCallback); + +private: + void + onTimeout(); + + void + onNotification(const Data& data); + +private: + ndn::Face& m_face; + uint64_t m_lastSequence; + bool m_isStopped; + NotificationCallbacks m_notificationCallbacks; + TimeoutCallbacks m_timeoutCallbacks; + const ndn::PendingInterestId* m_lastInterestId; +}; + +} // namespace nfd + +#endif // NFD_CORE_FACE_MONITOR_HPP diff --git a/docs/conf.py b/docs/conf.py index 11d8a52d..647cca40 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -230,6 +230,7 @@ latex_documents = [ # (source start file, name, description, authors, manual section). man_pages = [ ('manpages/nfd', 'nfd', u'Named Data Networking Forwarding Daemon', None, 1), + ('manpages/nrd', 'nrd', u'NFD RIB Daemon', None, 1), ('manpages/ndn-autoconfig-server', 'ndn-autoconfig-server', u'NFD Auto-configuration Server', None, 1), ('manpages/ndn-autoconfig', 'ndn-autoconfig', diff --git a/docs/manpages.rst b/docs/manpages.rst index da28853c..ff5630ad 100644 --- a/docs/manpages.rst +++ b/docs/manpages.rst @@ -3,5 +3,6 @@ NFD Manpages .. toctree:: manpages/nfd + manpages/nrd manpages/ndn-autoconfig manpages/ndn-autoconfig-server diff --git a/docs/manpages/nrd.rst b/docs/manpages/nrd.rst new file mode 100644 index 00000000..a8255759 --- /dev/null +++ b/docs/manpages/nrd.rst @@ -0,0 +1,67 @@ +NRD (NFD RIB Daemon) +======================== + +NRD is a complementary daemon designed to work in parallel with NFD +and to provide services for Routing Information Base management and +constructing NFD's FIB: + +* prefix registration from applications; +* manual prefix registration; +* prefix registrations from multiple dynamic routing protocols. + +Running and Configuring NRD +--------------------------- + +NRD's runtime settings may be modified via configuration file. After +installation, a working sample configuration is provided at +``SYSCONFDIR/ndn/nrd.conf.sample``. At startup, NRD will attempt to read +the default configuration file from following location: +``SYSCONFDIR/ndn/nrd.conf``. + +You may also specify an alternative configuration file location by +running NRD with: + +:: + + nrd --config + +Currently, nrd.conf contains only a security section, which is well +documented in the sample configuration file. + +Starting experiments +-------------------- + +1. Build, install and setup NFD by following the directions provided in + README.md in NFD root folder. + +2. Create certificates by following the instructions in NFD's README.md + +3. Build and install NRD by following the directions in INSTALL.md + +4. Create nrd.conf file. You can simply copy the provided sample file in + ``SYSCONFDIR/ndn/nrd.conf``. + +5. Setup the trust-anchors in nrd.conf. Ideally, the prefix registration + Interest should be signed by application/user key, and this + application/user key should be used as trust-anchor. However, + currently the default certificate is used to sign the registration + Interests. Therefore, for initial testing the default certificate + should be set as the trust-anchor. For doing so, the default + certificate should be copied in the same folder where the nrd.conf + is. Secondly, the trust-anchor should point to the copied certificate + in nrd.conf. To know the id of default certificate you can use: + + ~$ ndnsec-get-default + +6. Start NFD (assuming the NFD's conf file exist in + ``SYSCONFDIR/ndn/nfd.conf``): + + ~$ nfd + +7. Start NRD (assuming the conf file exist in + ``SYSCONFDIR/ndn/nrd.conf``): + + ~$ nrd + +8. Try to connect any application to NFD. For example, producer + application that is provided ndn-cpp-dev examples. diff --git a/rib/nrd.conf.sample b/nrd.conf.sample similarity index 100% rename from rib/nrd.conf.sample rename to nrd.conf.sample diff --git a/rib/.waf-tools/flags.py b/rib/.waf-tools/flags.py deleted file mode 100644 index dea80b8e..00000000 --- a/rib/.waf-tools/flags.py +++ /dev/null @@ -1,52 +0,0 @@ -# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- - -from waflib import Logs, Configure - -def configure(conf): - areCustomCxxflagsPresent = (len(conf.env.CXXFLAGS) > 0) - if conf.options.debug: - conf.define('_DEBUG', 1) - defaultFlags = ['-O0', '-g3', - '-Werror', - '-Wall', - '-fcolor-diagnostics', # only clang supports - - # # to disable known warnings - # '-Wno-unused-variable', # cryptopp - # '-Wno-unused-function', - # '-Wno-deprecated-declarations', - ] - - if areCustomCxxflagsPresent: - missingFlags = [x for x in defaultFlags if x not in conf.env.CXXFLAGS] - if len(missingFlags) > 0: - Logs.warn("Selected debug mode, but CXXFLAGS is set to a custom value '%s'" - % " ".join(conf.env.CXXFLAGS)) - Logs.warn("Default flags '%s' are not activated" % " ".join(missingFlags)) - else: - conf.add_supported_cxxflags(cxxflags=defaultFlags) - else: - defaultFlags = ['-O2', '-g', '-Wall', - - # # to disable known warnings - # '-Wno-unused-variable', # cryptopp - # '-Wno-unused-function', - # '-Wno-deprecated-declarations', - ] - if not areCustomCxxflagsPresent: - conf.add_supported_cxxflags(cxxflags=defaultFlags) - -@Configure.conf -def add_supported_cxxflags(self, cxxflags): - """ - Check which cxxflags are supported by compiler and add them to env.CXXFLAGS variable - """ - self.start_msg('Checking allowed flags for c++ compiler') - - supportedFlags = [] - for flag in cxxflags: - if self.check_cxx (cxxflags=[flag], mandatory=False): - supportedFlags += [flag] - - self.end_msg (' '.join (supportedFlags)) - self.env.CXXFLAGS += supportedFlags diff --git a/rib/INSTALL.md b/rib/INSTALL.md deleted file mode 100644 index 868a8d10..00000000 --- a/rib/INSTALL.md +++ /dev/null @@ -1,16 +0,0 @@ -## Prerequisites - -* Boost libraries >= 1.48 -* [NDN-CPP-dev library](https://github.com/named-data/ndn-cpp-dev) -* `pkg-config` - -## Build - -The following commands should be used to build NRD: - - ./waf configure - ./waf - -If NRD needs to be installed - - sudo ./waf install diff --git a/rib/README.md b/rib/README.md deleted file mode 100644 index fbbe11ad..00000000 --- a/rib/README.md +++ /dev/null @@ -1,67 +0,0 @@ -NRD (NDN Routing Daemon) -======================== -NRD works in parallel with NFD and provides services for prefix registration. - -For installation details please see INSTALL.md. - -## Default Paths - -This README uses `SYSCONFDIR` when referring to the default locations of -various configuration files. By default, `SYSCONFDIR` is set to -`/usr/local/etc`. If you override just `PREFIX`, then `SYSCONFDIR` will -default to `PREFIX/etc`. - -You may override `SYSCONFDIR` and/or `PREFIX` by specifying their -corresponding options during configuration: - - ./waf configure --prefix --sysconfdir - -## Running and Configuring NRD - -NRD's runtime settings may be modified via configuration file. After -installation, a working sample configuration is provided at -`SYSCONFDIR/ndn/nrd.conf.sample`. At startup, NRD will attempt to -read the default configuration file from following location: -`SYSCONFDIR/ndn/nrd.conf`. - -You may also specify an alternative configuration file location -by running NRD with: - - nrd --config - -Currently, nrd.conf contains only a security section, which is well documented -in the sample configuration file. - -## Starting experiments - -1. Build, install and setup NFD by following the directions provided in -README.md in NFD root folder. - -2. Create certificates by following the instructions in NFD's README.md - -3. Build and install NRD by following the directions in INSTALL.md - -4. Create nrd.conf file. You can simply copy the provided sample file in -`SYSCONFDIR/ndn/nrd.conf`. - -5. Setup the trust-anchors in nrd.conf. Ideally, the prefix registration -Interest should be signed by application/user key, and this application/user -key should be used as trust-anchor. However, currently the default certificate -is used to sign the registration Interests. Therefore, for initial testing the -default certificate should be set as the trust-anchor. For doing so, the default -certificate should be copied in the same folder where the nrd.conf is. -Secondly, the trust-anchor should point to the copied certificate in nrd.conf. -To know the id of default certificate you can use: - - ~$ ndnsec-get-default - -6. Start NFD (assuming the NFD's conf file exist in `SYSCONFDIR/ndn/nfd.conf`): - - ~$ nfd - -7. Start NRD (assuming the conf file exist in `SYSCONFDIR/ndn/nrd.conf`): - - ~$ nrd - -8. Try to connect any application to NFD. For example, producer application that is provided -ndn-cpp-dev examples. diff --git a/rib/main.cpp b/rib/main.cpp new file mode 100644 index 00000000..7eddfdda --- /dev/null +++ b/rib/main.cpp @@ -0,0 +1,257 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/** + * Copyright (c) 2014 Regents of the University of California, + * Arizona Board of Regents, + * Colorado State University, + * University Pierre & Marie Curie, Sorbonne University, + * Washington University in St. Louis, + * Beijing Institute of Technology, + * The University of Memphis + * + * This file is part of NFD (Named Data Networking Forwarding Daemon). + * See AUTHORS.md for complete list of NFD authors and contributors. + * + * NFD is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * NFD, e.g., in COPYING.md file. If not, see . + **/ + +#include + +#include "common.hpp" +#include "rib-manager.hpp" +#include "core/config-file.hpp" +#include "core/logger.hpp" + +namespace nfd { +namespace rib { + +NFD_LOG_INIT("nrd.Main"); + +struct ProgramOptions +{ + bool showUsage; + bool showModules; + std::string config; +}; + +class Nrd : noncopyable +{ +public: + void + initialize(const std::string& configFile) + { + initializeLogging(configFile); + + m_ribManager = make_shared(); + + ConfigFile config; + m_ribManager->setConfigFile(config); + config.addSectionHandler("log", bind(std::plus(), 0, 0)); // no-op + + // parse config file + config.parse(configFile, true); + config.parse(configFile, false); + + m_ribManager->registerWithNfd(); + m_ribManager->enableLocalControlHeader(); + } + + void + initializeLogging(const std::string& configFile) + { + ConfigFile config; + LoggerFactory::getInstance().setConfigFile(config); + + for (size_t i = 0; i < N_SUPPORTED_CONFIG_SECTIONS; ++i) + { + if (SUPPORTED_CONFIG_SECTIONS[i] != "log") + { + config.addSectionHandler(SUPPORTED_CONFIG_SECTIONS[i], + bind(std::plus(), 0, 0)); // no-op. + } + } + + config.parse(configFile, true); + config.parse(configFile, false); + } + + boost::asio::io_service& + getIoService() + { + return m_ribManager->getIoService(); + } + + static void + printUsage(std::ostream& os, const std::string& programName) + { + os << "Usage: \n" + << " " << programName << " [options]\n" + << "\n" + << "Run NRD daemon\n" + << "\n" + << "Options:\n" + << " [--help] - print this help message\n" + << " [--config /path/to/nrd.conf] - path to configuration file " + << "(default: " << DEFAULT_CONFIG_FILE << ")\n" + ; + } + + static void + printModules(std::ostream& os) + { + using namespace std; + + os << "Available logging modules: \n"; + + list modules(LoggerFactory::getInstance().getModules()); + for (list::const_iterator i = modules.begin(); i != modules.end(); ++i) + { + os << *i << "\n"; + } + } + + static bool + parseCommandLine(int argc, char** argv, ProgramOptions& options) + { + options.showUsage = false; + options.config = DEFAULT_CONFIG_FILE; + + while (true) { + int optionIndex = 0; + static ::option longOptions[] = { + { "help" , no_argument , 0, 0 }, + { "config" , required_argument, 0, 0 }, + { 0 , 0 , 0, 0 } + }; + int c = getopt_long_only(argc, argv, "", longOptions, &optionIndex); + if (c == -1) + break; + + switch (c) { + case 0: + switch (optionIndex) { + case 0: // help + options.showUsage = true; + break; + case 1: // config + options.config = ::optarg; + break; + default: + return false; + } + break; + } + } + return true; + } + + + void + terminate(const boost::system::error_code& error, + int signalNo, + boost::asio::signal_set& signalSet) + { + if (error) + return; + + if (signalNo == SIGINT || + signalNo == SIGTERM) + { + getIoService().stop(); + std::cout << "Caught signal '" << strsignal(signalNo) << "', exiting..." << std::endl; + } + else + { + /// \todo May be try to reload config file (at least security section) + signalSet.async_wait(bind(&Nrd::terminate, this, _1, _2, + boost::ref(signalSet))); + } + } + +private: + shared_ptr m_ribManager; + + static const std::string SUPPORTED_CONFIG_SECTIONS[]; + static const size_t N_SUPPORTED_CONFIG_SECTIONS; +}; + +const std::string Nrd::SUPPORTED_CONFIG_SECTIONS[] = + { + "log", + "security", + }; + +const size_t Nrd::N_SUPPORTED_CONFIG_SECTIONS = + sizeof(SUPPORTED_CONFIG_SECTIONS) / sizeof(std::string); + +} // namespace rib +} // namespace nfd + +int +main(int argc, char** argv) +{ + using namespace nfd::rib; + + ProgramOptions options; + bool isCommandLineValid = Nrd::parseCommandLine(argc, argv, options); + if (!isCommandLineValid) { + Nrd::printUsage(std::cerr, argv[0]); + return 1; + } + if (options.showUsage) { + Nrd::printUsage(std::cout, argv[0]); + return 0; + } + + if (options.showModules) { + Nrd::printModules(std::cout); + return 0; + } + + Nrd nrdInstance; + + try { + nrdInstance.initialize(options.config); + } + catch (boost::filesystem::filesystem_error& e) { + if (e.code() == boost::system::errc::permission_denied) { + NFD_LOG_FATAL("Permissions denied for " << e.path1() << ". " << + argv[0] << " should be run as superuser"); + } + else { + NFD_LOG_FATAL(e.what()); + } + return 1; + } + catch (const std::exception& e) { + NFD_LOG_FATAL(e.what()); + return 2; + } + + boost::asio::signal_set signalSet(nrdInstance.getIoService()); + signalSet.add(SIGINT); + signalSet.add(SIGTERM); + signalSet.add(SIGHUP); + signalSet.add(SIGUSR1); + signalSet.add(SIGUSR2); + signalSet.async_wait(bind(&Nrd::terminate, &nrdInstance, _1, _2, + boost::ref(signalSet))); + + try { + nrdInstance.getIoService().run(); + } + catch (std::exception& e) { + NFD_LOG_FATAL(e.what()); + return 3; + } + + return 0; +} diff --git a/rib/rib-manager.cpp b/rib/rib-manager.cpp new file mode 100644 index 00000000..1a3e41f1 --- /dev/null +++ b/rib/rib-manager.cpp @@ -0,0 +1,341 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/** + * Copyright (c) 2014 Regents of the University of California, + * Arizona Board of Regents, + * Colorado State University, + * University Pierre & Marie Curie, Sorbonne University, + * Washington University in St. Louis, + * Beijing Institute of Technology, + * The University of Memphis + * + * This file is part of NFD (Named Data Networking Forwarding Daemon). + * See AUTHORS.md for complete list of NFD authors and contributors. + * + * NFD is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * NFD, e.g., in COPYING.md file. If not, see . + **/ + +#include "rib-manager.hpp" + +namespace nfd { +namespace rib { + +const Name RibManager::COMMAND_PREFIX = "/localhost/nrd"; +const Name RibManager::REMOTE_COMMAND_PREFIX = "/localhop/nrd"; + +const size_t RibManager::COMMAND_UNSIGNED_NCOMPS = + RibManager::COMMAND_PREFIX.size() + + 1 + // verb + 1; // verb options + +const size_t RibManager::COMMAND_SIGNED_NCOMPS = + RibManager::COMMAND_UNSIGNED_NCOMPS + + 4; // (timestamp, nonce, signed info tlv, signature tlv) + +const RibManager::VerbAndProcessor RibManager::COMMAND_VERBS[] = + { + VerbAndProcessor( + Name::Component("register"), + &RibManager::insertEntry + ), + + VerbAndProcessor( + Name::Component("unregister"), + &RibManager::deleteEntry + ), + }; + +RibManager::RibManager() + : m_face(new ndn::Face()) + , m_nfdController(new ndn::nfd::Controller(*m_face)) + , m_validator(m_face) + , m_faceMonitor(*m_face) + , m_verbDispatch(COMMAND_VERBS, + COMMAND_VERBS + (sizeof(COMMAND_VERBS) / sizeof(VerbAndProcessor))) +{ +} + +void +RibManager::registerWithNfd() +{ + //check whether the components of localhop and localhost prefixes are same + BOOST_ASSERT(COMMAND_PREFIX.size() == REMOTE_COMMAND_PREFIX.size()); + + std::cerr << "Setting interest filter on: " << COMMAND_PREFIX.toUri() << std::endl; + m_face->setController(m_nfdController); + m_face->setInterestFilter(COMMAND_PREFIX.toUri(), + bind(&RibManager::onRibRequest, this, _2), + bind(&RibManager::setInterestFilterFailed, this, _1, _2)); + + std::cerr << "Setting interest filter on: " << REMOTE_COMMAND_PREFIX.toUri() << std::endl; + m_face->setInterestFilter(REMOTE_COMMAND_PREFIX.toUri(), + bind(&RibManager::onRibRequest, this, _2), + bind(&RibManager::setInterestFilterFailed, this, _1, _2)); + + std::cerr << "Monitoring faces" << std::endl; + m_faceMonitor.addSubscriber(boost::bind(&RibManager::onNotification, this, _1)); + m_faceMonitor.startNotifications(); +} + +void +RibManager::setConfigFile(ConfigFile& configFile) +{ + configFile.addSectionHandler("security", + bind(&RibManager::onConfig, this, _1, _2, _3)); +} + +void +RibManager::onConfig(const ConfigSection& configSection, + bool isDryRun, + const std::string& filename) +{ + /// \todo remove check after validator-conf replaces settings on each load + if (!isDryRun) + m_validator.load(configSection, filename); +} + +void +RibManager::setInterestFilterFailed(const Name& name, const std::string& msg) +{ + std::cerr << "Error in setting interest filter (" << name << "): " << msg << std::endl; + m_face->shutdown(); +} + +void +RibManager::sendResponse(const Name& name, + const ControlResponse& response) +{ + const Block& encodedControl = response.wireEncode(); + + Data responseData(name); + responseData.setContent(encodedControl); + + m_keyChain.sign(responseData); + m_face->put(responseData); +} + +void +RibManager::sendResponse(const Name& name, + uint32_t code, + const std::string& text) +{ + ControlResponse response(code, text); + sendResponse(name, response); +} + +void +RibManager::onRibRequest(const Interest& request) +{ + m_validator.validate(request, + bind(&RibManager::onRibRequestValidated, this, _1), + bind(&RibManager::onRibRequestValidationFailed, this, _1, _2)); +} + +void +RibManager::onRibRequestValidated(const shared_ptr& request) +{ + const Name& command = request->getName(); + + //REMOTE_COMMAND_PREFIX number of componenets are same as + // NRD_COMMAND_PREFIX's so no extra checks are required. + const Name::Component& verb = command.get(COMMAND_PREFIX.size()); + VerbDispatchTable::const_iterator verbProcessor = m_verbDispatch.find(verb); + + if (verbProcessor != m_verbDispatch.end()) + { + PrefixRegOptions options; + if (!extractOptions(*request, options)) + { + sendResponse(command, 400, "Malformed command"); + return; + } + + /// \todo authorize command + if (false) + { + sendResponse(request->getName(), 403, "Unauthorized command"); + return; + } + + // \todo add proper log support + std::cout << "Received options (name, faceid, cost): " << options.getName() << + ", " << options.getFaceId() << ", " << options.getCost() << std::endl; + + ControlResponse response; + (verbProcessor->second)(this, *request, options); + } + else + { + sendResponse(request->getName(), 501, "Unsupported command"); + } +} + +void +RibManager::onRibRequestValidationFailed(const shared_ptr& request, + const std::string& failureInfo) +{ + sendResponse(request->getName(), 403, failureInfo); +} + +bool +RibManager::extractOptions(const Interest& request, + PrefixRegOptions& extractedOptions) +{ + // const Name& command = request.getName(); + //REMOTE_COMMAND_PREFIX is same in size of NRD_COMMAND_PREFIX + //so no extra processing is required. + const size_t optionCompIndex = COMMAND_PREFIX.size() + 1; + + try + { + Block rawOptions = request.getName()[optionCompIndex].blockFromValue(); + extractedOptions.wireDecode(rawOptions); + } + catch (const ndn::Tlv::Error& e) + { + return false; + } + + if (extractedOptions.getFaceId() == 0) + { + std::cout <<"IncomingFaceId: " << request.getIncomingFaceId() << std::endl; + extractedOptions.setFaceId(request.getIncomingFaceId()); + } + return true; +} + +void +RibManager::onCommandError(uint32_t code, const std::string& error, + const Interest& request, + const PrefixRegOptions& options) +{ + std::cout << "NFD Error: " << error << " (code: " << code << ")" << std::endl; + + ControlResponse response; + + if (code == 404) + { + response.setCode(code); + response.setText(error); + } + else + { + response.setCode(533); + std::ostringstream os; + os << "Failure to update NFD " << "(NFD Error: " << code << " " << error << ")"; + response.setText(os.str()); + } + + sendResponse(request.getName(), response); + m_managedRib.erase(options); +} + +void +RibManager::onUnRegSuccess(const Interest& request, const PrefixRegOptions& options) +{ + ControlResponse response; + + response.setCode(200); + response.setText("Success"); + response.setBody(options.wireEncode()); + + std::cout << "Success: Name unregistered (" << + options.getName() << ", " << + options.getFaceId() << ")" << std::endl; + sendResponse(request.getName(), response); + m_managedRib.erase(options); +} + +void +RibManager::onRegSuccess(const Interest& request, const PrefixRegOptions& options) +{ + ControlResponse response; + + response.setCode(200); + response.setText("Success"); + response.setBody(options.wireEncode()); + + std::cout << "Success: Name registered (" << options.getName() << ", " << + options.getFaceId() << ")" << std::endl; + sendResponse(request.getName(), response); +} + +void +RibManager::insertEntry(const Interest& request, const PrefixRegOptions& options) +{ + // For right now, just pass the options to fib as it is, + // without processing flags. Later options will be first added to + // Rib tree, then nrd will generate fib updates based on flags and then + // will add next hops one by one.. + m_managedRib.insert(options); + m_nfdController->start( + ControlParameters() + .setName(options.getName()) + .setFaceId(options.getFaceId()) + .setCost(options.getCost()), + bind(&RibManager::onRegSuccess, this, request, options), + bind(&RibManager::onCommandError, this, _1, _2, request, options)); +} + +void +RibManager::deleteEntry(const Interest& request, const PrefixRegOptions& options) +{ + m_nfdController->start( + ControlParameters() + .setName(options.getName()) + .setFaceId(options.getFaceId()), + bind(&RibManager::onUnRegSuccess, this, request, options), + bind(&RibManager::onCommandError, this, _1, _2, request, options)); +} + +boost::asio::io_service& +RibManager::getIoService() +{ + /// \todo Switch face to use global io service (needs library update) + return *m_face->ioService(); +} + +void +RibManager::onControlHeaderSuccess() +{ + std::cout << "Local control header enabled" << std::endl; +} + +void +RibManager::onControlHeaderError(uint32_t code, const std::string& reason) +{ + std::cout << "Error: couldn't enable local control header " + << "(code: " << code << ", info: " << reason << ")" << std::endl; + m_face->shutdown(); +} + +void +RibManager::enableLocalControlHeader() +{ + m_nfdController->start( + ControlParameters() + .setLocalControlFeature(ndn::nfd::LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID), + bind(&RibManager::onControlHeaderSuccess, this), + bind(&RibManager::onControlHeaderError, this, _1, _2)); +} + +void +RibManager::onNotification(const FaceEventNotification& notification) +{ + /// \todo A notification can be missed, in this case check Facelist + std::cerr << "Notification Rcvd: " << notification << std::endl; + if (notification.getKind() == ndn::nfd::FACE_EVENT_DESTROYED) { //face destroyed + m_managedRib.erase(notification.getFaceId()); + } +} + +} // namespace rib +} // namespace nfd diff --git a/rib/rib-manager.hpp b/rib/rib-manager.hpp new file mode 100644 index 00000000..94792919 --- /dev/null +++ b/rib/rib-manager.hpp @@ -0,0 +1,157 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/** + * Copyright (c) 2014 Regents of the University of California, + * Arizona Board of Regents, + * Colorado State University, + * University Pierre & Marie Curie, Sorbonne University, + * Washington University in St. Louis, + * Beijing Institute of Technology, + * The University of Memphis + * + * This file is part of NFD (Named Data Networking Forwarding Daemon). + * See AUTHORS.md for complete list of NFD authors and contributors. + * + * NFD is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * NFD, e.g., in COPYING.md file. If not, see . + **/ + +#ifndef NFD_RIB_RIB_MANAGER_HPP +#define NFD_RIB_RIB_MANAGER_HPP + +#include "rib.hpp" +#include "face-monitor.hpp" +#include "core/config-file.hpp" + +#include +#include +#include +#include +#include + +namespace nfd { +namespace rib { + +using ndn::nfd::ControlCommand; +using ndn::nfd::ControlResponse; +using ndn::nfd::ControlParameters; + +class RibManager : noncopyable +{ +public: + RibManager(); + + void + onRibRequest(const Interest& request); + + void + registerWithNfd(); + + void + enableLocalControlHeader(); + + boost::asio::io_service& + getIoService(); + + void + setConfigFile(ConfigFile& configFile); + +private: + void + onConfig(const ConfigSection& configSection, + bool isDryRun, + const std::string& filename); + + void + sendResponse(const Name& name, + const ControlResponse& response); + + void + sendResponse(const Name& name, + uint32_t code, + const std::string& text); + + void + onRibRequestValidated(const shared_ptr& request); + + void + onRibRequestValidationFailed(const shared_ptr& request, + const std::string& failureInfo); + + void + onCommandError(uint32_t code, const std::string& error, + const ndn::Interest& interest, + const PrefixRegOptions& options); + + void + onRegSuccess(const ndn::Interest& interest, const PrefixRegOptions& options); + + void + onUnRegSuccess(const ndn::Interest& interest, const PrefixRegOptions& options); + + void + onControlHeaderSuccess(); + + void + onControlHeaderError(uint32_t code, const std::string& reason); + + void + setInterestFilterFailed(const Name& name, const std::string& msg); + + void + insertEntry(const Interest& request, const PrefixRegOptions& options); + + void + deleteEntry(const Interest& request, const PrefixRegOptions& options); + + bool + extractOptions(const Interest& request, + PrefixRegOptions& extractedOptions); + + void + onNotification(const FaceEventNotification& notification); + +private: + Rib m_managedRib; + ndn::shared_ptr m_face; + ndn::shared_ptr m_nfdController; + ndn::KeyChain m_keyChain; + ndn::ValidatorConfig m_validator; + FaceMonitor m_faceMonitor; + + typedef boost::function VerbProcessor; + + typedef std::map VerbDispatchTable; + + typedef std::pair VerbAndProcessor; + + + const VerbDispatchTable m_verbDispatch; + + static const Name COMMAND_PREFIX; // /localhost/nrd + static const Name REMOTE_COMMAND_PREFIX; // /localhop/nrd + + // number of components in an invalid, but not malformed, unsigned command. + // (/localhost/nrd + verb + options) = 4 + static const size_t COMMAND_UNSIGNED_NCOMPS; + + // number of components in a valid signed Interest. + // 8 with signed Interest support. + static const size_t COMMAND_SIGNED_NCOMPS; + + static const VerbAndProcessor COMMAND_VERBS[]; +}; + +} // namespace rib +} // namespace nfd + +#endif // NFD_RIB_RIB_MANAGER_HPP diff --git a/rib/src/rib.cpp b/rib/rib.cpp similarity index 58% rename from rib/src/rib.cpp rename to rib/rib.cpp index 4f1387b9..8ad5ec6c 100644 --- a/rib/src/rib.cpp +++ b/rib/rib.cpp @@ -1,13 +1,32 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /** - * Copyright (C) 2014 Named Data Networking Project - * See COPYING for copyright and distribution information. - */ + * Copyright (c) 2014 Regents of the University of California, + * Arizona Board of Regents, + * Colorado State University, + * University Pierre & Marie Curie, Sorbonne University, + * Washington University in St. Louis, + * Beijing Institute of Technology, + * The University of Memphis + * + * This file is part of NFD (Named Data Networking Forwarding Daemon). + * See AUTHORS.md for complete list of NFD authors and contributors. + * + * NFD is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * NFD, e.g., in COPYING.md file. If not, see . + **/ #include "rib.hpp" -namespace ndn { -namespace nrd { +namespace nfd { +namespace rib { Rib::Rib() { @@ -86,5 +105,5 @@ Rib::erase(uint64_t faceId) } } -} // namespace nrd -} // namespace ndn +} // namespace rib +} // namespace nfd diff --git a/rib/rib.hpp b/rib/rib.hpp new file mode 100644 index 00000000..745a7609 --- /dev/null +++ b/rib/rib.hpp @@ -0,0 +1,108 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/** + * Copyright (c) 2014 Regents of the University of California, + * Arizona Board of Regents, + * Colorado State University, + * University Pierre & Marie Curie, Sorbonne University, + * Washington University in St. Louis, + * Beijing Institute of Technology, + * The University of Memphis + * + * This file is part of NFD (Named Data Networking Forwarding Daemon). + * See AUTHORS.md for complete list of NFD authors and contributors. + * + * NFD is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * NFD, e.g., in COPYING.md file. If not, see . + **/ + +#ifndef NFD_RIB_RIB_HPP +#define NFD_RIB_RIB_HPP + +#include "common.hpp" + +#include + +namespace nfd { +namespace rib { + +using ndn::nrd::PrefixRegOptions; +namespace tlv = ndn::tlv; + +/** \brief represents the RIB + */ +class Rib +{ +public: + typedef std::list RibTable; + typedef RibTable::const_iterator const_iterator; + + Rib(); + + ~Rib(); + + const_iterator + find(const PrefixRegOptions& options) const; + + void + insert(const PrefixRegOptions& options); + + void + erase(const PrefixRegOptions& options); + + void + erase(uint64_t faceId); + + const_iterator + begin() const; + + const_iterator + end() const; + + size_t + size() const; + + size_t + empty() const; + +private: + // Note: Taking a list right now. A Trie will be used later to store + // prefixes + RibTable m_rib; +}; + +inline Rib::const_iterator +Rib::begin() const +{ + return m_rib.begin(); +} + +inline Rib::const_iterator +Rib::end() const +{ + return m_rib.end(); +} + +inline size_t +Rib::size() const +{ + return m_rib.size(); +} + +inline size_t +Rib::empty() const +{ + return m_rib.empty(); +} + +} // namespace rib +} // namespace nfd + +#endif // NFD_RIB_RIB_HPP diff --git a/rib/src/common.hpp b/rib/src/common.hpp deleted file mode 100644 index f5208285..00000000 --- a/rib/src/common.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/** - * Copyright (C) 2014 Named Data Networking Project - * See COPYING for copyright and distribution information. - */ - -#ifndef NRD_COMMON_HPP -#define NRD_COMMON_HPP - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../build/src/config.hpp" - -namespace ndn { -namespace nrd { - -class Error : public std::runtime_error -{ -public: - explicit - Error(const std::string& what) - : std::runtime_error(what) - { - } -}; - -} //namespace nrd -} //namespace ndn -#endif // NRD_COMMON_HPP diff --git a/rib/src/face-monitor.hpp b/rib/src/face-monitor.hpp deleted file mode 100644 index e0eeaa60..00000000 --- a/rib/src/face-monitor.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */ -/** - * Copyright (C) 2014 Regents of the University of California. - * See COPYING for copyright and distribution information. - */ -#ifndef FACE_MONITOR_HPP -#define FACE_MONITOR_HPP - -#include "common.hpp" -#include -#include - -namespace ndn { - -class FaceMonitor -{ -public: - typedef function NotificationCallback; - typedef function TimeoutCallback; - - typedef std::vector NotificationCallbacks; - typedef std::vector TimeoutCallbacks; - - explicit - FaceMonitor(Face& face); - - ~FaceMonitor(); - - /** \brief Stops all notifications. This method doesn't remove registered callbacks. - */ - void - stopNotifications(); - - /** \brief Resumes notifications for added subscribers. - */ - void - startNotifications(); - - /** \brief Removes all notification subscribers. - */ - void - removeAllSubscribers(); - - /** \brief Adds a notification subscriber. This method doesn't return on timeouts. - */ - void - addSubscriber(const NotificationCallback& notificationCallback); - - /** \brief Adds a notification subscriber. - */ - void - addSubscriber(const NotificationCallback& notificationCallback, - const TimeoutCallback& timeoutCallback); - -private: - void - onTimeout(); - - void - onNotification(const Data& data); - -private: - Face& m_face; - uint64_t m_lastSequence; - bool m_isStopped; - NotificationCallbacks m_notificationCallbacks; - TimeoutCallbacks m_timeoutCallbacks; - const PendingInterestId* m_lastInterestId; -}; - -}//namespace ndn -#endif //FACE_MONITOR_HPP diff --git a/rib/src/main.cpp b/rib/src/main.cpp deleted file mode 100644 index 5d0e5ed3..00000000 --- a/rib/src/main.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/** - * Copyright (C) 2014 Named Data Networking Project - * See COPYING for copyright and distribution information. - */ - -#include "common.hpp" -#include "nrd.hpp" -#include "nrd-config.hpp" -#include - -struct ProgramOptions -{ - bool showUsage; - std::string config; -}; - -static void -printUsage(std::ostream& os, const std::string& programName) -{ - os << "Usage: \n" - << " " << programName << " [options]\n" - << "\n" - << "Run NRD daemon\n" - << "\n" - << "Options:\n" - << " [--help] - print this help message\n" - << " [--config /path/to/nrd.conf] - path to configuration file " - << "(default: " << DEFAULT_CONFIG_FILE << ")\n" - ; -} - -static bool -parseCommandLine(int argc, char** argv, ProgramOptions& options) -{ - options.showUsage = false; - options.config = DEFAULT_CONFIG_FILE; - - while (true) { - int optionIndex = 0; - static ::option longOptions[] = { - { "help" , no_argument , 0, 0 }, - { "config" , required_argument, 0, 0 }, - { 0 , 0 , 0, 0 } - }; - int c = getopt_long_only(argc, argv, "", longOptions, &optionIndex); - if (c == -1) - break; - - switch (c) { - case 0: - switch (optionIndex) { - case 0: // help - options.showUsage = true; - break; - case 1: // config - options.config = ::optarg; - break; - default: - return false; - } - break; - } - } - return true; -} - -int -main(int argc, char** argv) -{ - //processing command line arguments, if available - ProgramOptions options; - bool isCommandLineValid = parseCommandLine(argc, argv, options); - if (!isCommandLineValid) { - printUsage(std::cerr, argv[0]); - return 1; - } - if (options.showUsage) { - printUsage(std::cout, argv[0]); - return 0; - } - - //Now read the config file and pass the security section to validator - try { - std::string configFilename(options.config); - - ndn::nrd::NrdConfig config; - config.load(configFilename); - ndn::nrd::ConfigSection securitySection = config.getSecuritySection(); - - ndn::nrd::Nrd nrd(securitySection, configFilename); - nrd.enableLocalControlHeader(); - nrd.listen(); - } - catch (std::exception& e) { - std::cerr << "ERROR: " << e.what() << std::endl; - } - - return 0; -} diff --git a/rib/src/nrd-config.cpp b/rib/src/nrd-config.cpp deleted file mode 100644 index c6181f9c..00000000 --- a/rib/src/nrd-config.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "nrd-config.hpp" - -namespace ndn { -namespace nrd { - -NrdConfig::NrdConfig() - : m_isSecuritySectionDefined(false) -{ -} - -void -NrdConfig::load(const std::string& filename) -{ - std::ifstream inputFile; - inputFile.open(filename.c_str()); - if (!inputFile.good() || !inputFile.is_open()) - { - std::string msg = "Failed to read configuration file: "; - msg += filename; - std::cerr << filename << std::endl; - throw Error(msg); - } - load(inputFile, filename); - inputFile.close(); -} - -void -NrdConfig::load(const std::string& input, const std::string& filename) -{ - std::istringstream inputStream(input); - load(inputStream, filename); -} - -void -NrdConfig::load(std::istream& input, const std::string& filename) -{ - BOOST_ASSERT(!filename.empty()); - - ConfigSection ptree; - try - { - boost::property_tree::read_info(input, ptree); - } - catch (const boost::property_tree::info_parser_error& error) - { - std::stringstream msg; - msg << "Failed to parse configuration file"; - msg << " " << filename; - msg << " " << error.message() << " line " << error.line(); - throw Error(msg.str()); - } - process(ptree, filename); -} - -void -NrdConfig::process(const ConfigSection& configSection, - const std::string& filename) -{ - BOOST_ASSERT(!filename.empty()); - - if (configSection.begin() == configSection.end()) - { - std::string msg = "Error processing configuration file"; - msg += ": "; - msg += filename; - msg += " no data"; - throw Error(msg); - } - - for (ConfigSection::const_iterator i = configSection.begin(); - i != configSection.end(); ++i) - { - const std::string& sectionName = i->first; - const ConfigSection& section = i->second; - - if (boost::iequals(sectionName, "security")) - { - onSectionSecurity(section, filename); - } - //Add more sections here as needed - //else if (boost::iequals(sectionName, "another-section")) - //{ - //onSectionAnotherSection(section, filename); - //} - else - { - std::string msg = "Error processing configuration file"; - msg += " "; - msg += filename; - msg += " unrecognized section: " + sectionName; - throw Error(msg); - } - } -} - -void -NrdConfig::onSectionSecurity(const ConfigSection& section, - const std::string& filename) -{ - if (!m_isSecuritySectionDefined) { - //setSecturitySection(section); - m_securitySection = section; - m_filename = filename; - m_isSecuritySectionDefined = true; - } - else { - std::string msg = "Error processing configuration file"; - msg += " "; - msg += filename; - msg += " security section can appear only once"; - throw Error(msg); - } -} - -} //namespace nrd -} //namespace ndn diff --git a/rib/src/nrd-config.hpp b/rib/src/nrd-config.hpp deleted file mode 100644 index ee119f68..00000000 --- a/rib/src/nrd-config.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef NRD_CONFIG_HPP -#define NRD_CONFIG_HPP - -#include "common.hpp" - -namespace ndn { -namespace nrd { - -typedef boost::property_tree::ptree ConfigSection; - -class NrdConfig -{ - -public: - NrdConfig(); - - virtual - ~NrdConfig() - { - } - - void - load(const std::string& filename); - - void - load(const std::string& input, const std::string& filename); - - void - load(std::istream& input, const std::string& filename); - - const ConfigSection& - getSecuritySection() const - { - return m_securitySection; - } - -private: - void - process(const ConfigSection& configSection, - const std::string& filename); - - void - onSectionSecurity(const ConfigSection& section, - const std::string& filename); - - const void - setSecturitySection(const ConfigSection& securitySection) - { - m_securitySection = securitySection; - } - -private: - bool m_isSecuritySectionDefined; - ConfigSection m_securitySection; - std::string m_filename; -}; - -}//namespace nrd -}//namespace ndn - -#endif // NRD_CONFIG_HPP diff --git a/rib/src/nrd.cpp b/rib/src/nrd.cpp deleted file mode 100644 index 492f9c2c..00000000 --- a/rib/src/nrd.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/** - * Copyright (C) 2014 Named Data Networking Project - * See COPYING for copyright and distribution information. - */ - -#include "nrd.hpp" - -namespace ndn { -namespace nrd { - -const Name Nrd::COMMAND_PREFIX = "/localhost/nrd"; -const Name Nrd::REMOTE_COMMAND_PREFIX = "/localhop/nrd"; - -const size_t Nrd::COMMAND_UNSIGNED_NCOMPS = - Nrd::COMMAND_PREFIX.size() + - 1 + // verb - 1; // verb options - -const size_t Nrd::COMMAND_SIGNED_NCOMPS = - Nrd::COMMAND_UNSIGNED_NCOMPS + - 4; // (timestamp, nonce, signed info tlv, signature tlv) - -const Nrd::VerbAndProcessor Nrd::COMMAND_VERBS[] = - { - VerbAndProcessor( - Name::Component("register"), - &Nrd::insertEntry - ), - - VerbAndProcessor( - Name::Component("unregister"), - &Nrd::deleteEntry - ), - }; - -Nrd::Nrd(const ndn::nrd::ConfigSection& securitySection, - const std::string& validatorConfig) - : m_face(new Face()) - , m_nfdController(new nfd::Controller(*m_face)) - , m_validator(m_face) - , m_faceMonitor(*m_face) - , m_verbDispatch(COMMAND_VERBS, - COMMAND_VERBS + (sizeof(COMMAND_VERBS) / sizeof(VerbAndProcessor))) -{ - //check whether the components of localhop and localhost prefixes are same - BOOST_ASSERT(COMMAND_PREFIX.size() == REMOTE_COMMAND_PREFIX.size()); - - //m_validator.load(validatorConfig); - m_validator.load(securitySection, validatorConfig); - - std::cerr << "Setting interest filter on: " << COMMAND_PREFIX.toUri() << std::endl; - m_face->setController(m_nfdController); - m_face->setInterestFilter(COMMAND_PREFIX.toUri(), - bind(&Nrd::onRibRequest, this, _2), - bind(&Nrd::setInterestFilterFailed, this, _1, _2)); - - std::cerr << "Setting interest filter on: " << REMOTE_COMMAND_PREFIX.toUri() << std::endl; - m_face->setInterestFilter(REMOTE_COMMAND_PREFIX.toUri(), - bind(&Nrd::onRibRequest, this, _2), - bind(&Nrd::setInterestFilterFailed, this, _1, _2)); - - std::cerr << "Monitoring faces" << std::endl; - m_faceMonitor.addSubscriber(boost::bind(&Nrd::onNotification, this, _1)); - m_faceMonitor.startNotifications(); -} - -void -Nrd::setInterestFilterFailed(const Name& name, const std::string& msg) -{ - std::cerr << "Error in setting interest filter (" << name << "): " << msg << std::endl; - m_face->shutdown(); -} - -void -Nrd::sendResponse(const Name& name, - const nfd::ControlResponse& response) -{ - const Block& encodedControl = response.wireEncode(); - - Data responseData(name); - responseData.setContent(encodedControl); - - m_keyChain.sign(responseData); - m_face->put(responseData); -} - -void -Nrd::sendResponse(const Name& name, - uint32_t code, - const std::string& text) -{ - nfd::ControlResponse response(code, text); - sendResponse(name, response); -} - -void -Nrd::onRibRequest(const Interest& request) -{ - m_validator.validate(request, - bind(&Nrd::onRibRequestValidated, this, _1), - bind(&Nrd::onRibRequestValidationFailed, this, _1, _2)); -} - -void -Nrd::onRibRequestValidated(const shared_ptr& request) -{ - const Name& command = request->getName(); - - //REMOTE_COMMAND_PREFIX number of componenets are same as - // NRD_COMMAND_PREFIX's so no extra checks are required. - const Name::Component& verb = command.get(COMMAND_PREFIX.size()); - VerbDispatchTable::const_iterator verbProcessor = m_verbDispatch.find(verb); - - if (verbProcessor != m_verbDispatch.end()) - { - PrefixRegOptions options; - if (!extractOptions(*request, options)) - { - sendResponse(command, 400, "Malformed command"); - return; - } - - /// \todo authorize command - if (false) - { - sendResponse(request->getName(), 403, "Unauthorized command"); - return; - } - - // \todo add proper log support - std::cout << "Received options (name, faceid, cost): " << options.getName() << - ", " << options.getFaceId() << ", " << options.getCost() << std::endl; - - nfd::ControlResponse response; - (verbProcessor->second)(this, *request, options); - } - else - { - sendResponse(request->getName(), 501, "Unsupported command"); - } -} - -void -Nrd::onRibRequestValidationFailed(const shared_ptr& request, - const std::string& failureInfo) -{ - sendResponse(request->getName(), 403, failureInfo); -} - -bool -Nrd::extractOptions(const Interest& request, - PrefixRegOptions& extractedOptions) -{ - // const Name& command = request.getName(); - //REMOTE_COMMAND_PREFIX is same in size of NRD_COMMAND_PREFIX - //so no extra processing is required. - const size_t optionCompIndex = COMMAND_PREFIX.size() + 1; - - try - { - Block rawOptions = request.getName()[optionCompIndex].blockFromValue(); - extractedOptions.wireDecode(rawOptions); - } - catch (const ndn::Tlv::Error& e) - { - return false; - } - - if (extractedOptions.getFaceId() == 0) - { - std::cout <<"IncomingFaceId: " << request.getIncomingFaceId() << std::endl; - extractedOptions.setFaceId(request.getIncomingFaceId()); - } - return true; -} - -void -Nrd::onCommandError(uint32_t code, const std::string& error, - const Interest& request, - const PrefixRegOptions& options) -{ - std::cout << "NFD Error: " << error << " (code: " << code << ")" << std::endl; - - nfd::ControlResponse response; - - if (code == 404) - { - response.setCode(code); - response.setText(error); - } - else - { - response.setCode(533); - std::ostringstream os; - os << "Failure to update NFD " << "(NFD Error: " << code << " " << error << ")"; - response.setText(os.str()); - } - - sendResponse(request.getName(), response); - m_managedRib.erase(options); -} - -void -Nrd::onUnRegSuccess(const Interest& request, const PrefixRegOptions& options) -{ - nfd::ControlResponse response; - - response.setCode(200); - response.setText("Success"); - response.setBody(options.wireEncode()); - - std::cout << "Success: Name unregistered (" << - options.getName() << ", " << - options.getFaceId() << ")" << std::endl; - sendResponse(request.getName(), response); - m_managedRib.erase(options); -} - -void -Nrd::onRegSuccess(const Interest& request, const PrefixRegOptions& options) -{ - nfd::ControlResponse response; - - response.setCode(200); - response.setText("Success"); - response.setBody(options.wireEncode()); - - std::cout << "Success: Name registered (" << options.getName() << ", " << - options.getFaceId() << ")" << std::endl; - sendResponse(request.getName(), response); -} - -void -Nrd::insertEntry(const Interest& request, const PrefixRegOptions& options) -{ - // For right now, just pass the options to fib as it is, - // without processing flags. Later options will be first added to - // Rib tree, then nrd will generate fib updates based on flags and then - // will add next hops one by one.. - m_managedRib.insert(options); - m_nfdController->start( - nfd::ControlParameters() - .setName(options.getName()) - .setFaceId(options.getFaceId()) - .setCost(options.getCost()), - bind(&Nrd::onRegSuccess, this, request, options), - bind(&Nrd::onCommandError, this, _1, _2, request, options)); -} - -void -Nrd::deleteEntry(const Interest& request, const PrefixRegOptions& options) -{ - m_nfdController->start( - nfd::ControlParameters() - .setName(options.getName()) - .setFaceId(options.getFaceId()), - bind(&Nrd::onUnRegSuccess, this, request, options), - bind(&Nrd::onCommandError, this, _1, _2, request, options)); -} - -void -Nrd::listen() -{ - std::cout << "NRD started: listening for incoming interests" << std::endl; - m_face->processEvents(); -} - -void -Nrd::onControlHeaderSuccess() -{ - std::cout << "Local control header enabled" << std::endl; -} - -void -Nrd::onControlHeaderError(uint32_t code, const std::string& reason) -{ - std::cout << "Error: couldn't enable local control header " - << "(code: " << code << ", info: " << reason << ")" << std::endl; - m_face->shutdown(); -} - -void -Nrd::enableLocalControlHeader() -{ - m_nfdController->start( - nfd::ControlParameters() - .setLocalControlFeature(nfd::LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID), - bind(&Nrd::onControlHeaderSuccess, this), - bind(&Nrd::onControlHeaderError, this, _1, _2)); -} - -void -Nrd::onNotification(const nfd::FaceEventNotification& notification) -{ - /// \todo A notification can be missed, in this case check Facelist - std::cerr << "Notification Rcvd: " << notification << std::endl; - if (notification.getKind() == ndn::nfd::FACE_EVENT_DESTROYED) { //face destroyed - m_managedRib.erase(notification.getFaceId()); - } -} - -} // namespace nrd -} // namespace ndn diff --git a/rib/src/nrd.hpp b/rib/src/nrd.hpp deleted file mode 100644 index d6007d78..00000000 --- a/rib/src/nrd.hpp +++ /dev/null @@ -1,118 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/** - * Copyright (C) 2014 Named Data Networking Project - * See COPYING for copyright and distribution information. - */ - -#ifndef NRD_HPP -#define NRD_HPP - -#include "rib.hpp" -#include "face-monitor.hpp" -#include "nrd-config.hpp" - -namespace ndn { -namespace nrd { - -class Nrd : noncopyable -{ -public: - Nrd(const ndn::nrd::ConfigSection& securitySection, - const std::string& validatorConfig); - - void - onRibRequest(const Interest& request); - - void - enableLocalControlHeader(); - - void - listen(); - -private: - void - sendResponse(const Name& name, - const nfd::ControlResponse& response); - - void - sendResponse(const Name& name, - uint32_t code, - const std::string& text); - - void - onRibRequestValidated(const shared_ptr& request); - - void - onRibRequestValidationFailed(const shared_ptr& request, - const std::string& failureInfo); - - void - onCommandError(uint32_t code, const std::string& error, - const ndn::Interest& interest, - const PrefixRegOptions& options); - - void - onRegSuccess(const ndn::Interest& interest, const PrefixRegOptions& options); - - void - onUnRegSuccess(const ndn::Interest& interest, const PrefixRegOptions& options); - - void - onControlHeaderSuccess(); - - void - onControlHeaderError(uint32_t code, const std::string& reason); - - void - setInterestFilterFailed(const Name& name, const std::string& msg); - - void - insertEntry(const Interest& request, const PrefixRegOptions& options); - - void - deleteEntry(const Interest& request, const PrefixRegOptions& options); - - bool - extractOptions(const Interest& request, - PrefixRegOptions& extractedOptions); - - void - onNotification(const nfd::FaceEventNotification& notification); - -private: - Rib m_managedRib; - ndn::shared_ptr m_face; - ndn::shared_ptr m_nfdController; - ndn::KeyChain m_keyChain; - ndn::ValidatorConfig m_validator; - ndn::FaceMonitor m_faceMonitor; - - typedef boost::function VerbProcessor; - - typedef std::map VerbDispatchTable; - - typedef std::pair VerbAndProcessor; - - - const VerbDispatchTable m_verbDispatch; - - static const Name COMMAND_PREFIX; // /localhost/nrd - static const Name REMOTE_COMMAND_PREFIX; // /localhop/nrd - - // number of components in an invalid, but not malformed, unsigned command. - // (/localhost/nrd + verb + options) = 4 - static const size_t COMMAND_UNSIGNED_NCOMPS; - - // number of components in a valid signed Interest. - // 5 in mock (see UNSIGNED_NCOMPS), 8 with signed Interest support. - static const size_t COMMAND_SIGNED_NCOMPS; - - static const VerbAndProcessor COMMAND_VERBS[]; -}; - -} // namespace nrd -} // namespace ndn - -#endif // NRD_HPP diff --git a/rib/src/rib.hpp b/rib/src/rib.hpp deleted file mode 100644 index 3196817f..00000000 --- a/rib/src/rib.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/** - * Copyright (C) 2014 Named Data Networking Project - * See COPYING for copyright and distribution information. - */ - -#ifndef NRD_RIB_HPP -#define NRD_RIB_HPP - -#include "common.hpp" - -namespace ndn { -namespace nrd { - -/** \brief represents the RIB - */ -class Rib -{ -public: - typedef std::list RibTable; - typedef RibTable::const_iterator const_iterator; - - Rib(); - - ~Rib(); - - const_iterator - find(const PrefixRegOptions& options) const; - - void - insert(const PrefixRegOptions& options); - - void - erase(const PrefixRegOptions& options); - - void - erase(uint64_t faceId); - - const_iterator - begin() const; - - const_iterator - end() const; - - size_t - size() const; - - size_t - empty() const; - -private: - // Note: Taking a list right now. A Trie will be used later to store - // prefixes - RibTable m_rib; -}; - -inline Rib::const_iterator -Rib::begin() const -{ - return m_rib.begin(); -} - -inline Rib::const_iterator -Rib::end() const -{ - return m_rib.end(); -} - -inline size_t -Rib::size() const -{ - return m_rib.size(); -} - -inline size_t -Rib::empty() const -{ - return m_rib.empty(); -} - -} // namespace nrd -} // namespace ndn - -#endif // NRD_RIB_HPP diff --git a/rib/tests/main.cpp b/rib/tests/main.cpp deleted file mode 100644 index a7e827d3..00000000 --- a/rib/tests/main.cpp +++ /dev/null @@ -1,10 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/** - * Copyright (C) 2014 Named Data Networking Project - * See COPYING for copyright and distribution information. - */ - -#define BOOST_TEST_MAIN 1 -#define BOOST_TEST_DYN_LINK 1 - -#include diff --git a/rib/tests/test-rib.cpp b/rib/tests/test-rib.cpp deleted file mode 100644 index 920c606e..00000000 --- a/rib/tests/test-rib.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/** - * Copyright (C) 2014 Named Data Networking Project - * See COPYING for copyright and distribution information. - */ - -#include "rib.hpp" - -#include - -namespace ndn { -namespace nrd { -namespace tests { - -BOOST_AUTO_TEST_SUITE(TestRib) - -BOOST_AUTO_TEST_CASE(Basic) -{ - Rib rib; - - PrefixRegOptions options1; - options1.setName("/hello/world"); - options1.setFlags(tlv::nrd::NDN_FORW_CHILD_INHERIT | tlv::nrd::NDN_FORW_CAPTURE); - options1.setCost(10); - options1.setExpirationPeriod(time::milliseconds(1500)); - options1.setFaceId(1); - - rib.insert(options1); - BOOST_CHECK_EQUAL(rib.size(), 1); - - PrefixRegOptions options2; - options2.setName("/hello/world"); - options2.setFlags(tlv::nrd::NDN_FORW_CHILD_INHERIT); - options2.setExpirationPeriod(time::seconds(0)); - options2.setFaceId(1); - options2.setCost(100); - - rib.insert(options2); - BOOST_CHECK_EQUAL(rib.size(), 1); - - options2.setFaceId(2); - rib.insert(options2); - BOOST_CHECK_EQUAL(rib.size(), 2); - - options2.setName("/foo/bar"); - rib.insert(options2); - BOOST_CHECK_EQUAL(rib.size(), 3); - - rib.erase(options2); - BOOST_CHECK_EQUAL(rib.size(), 2); - - options2.setName("/hello/world"); - rib.erase(options2); - BOOST_CHECK_EQUAL(rib.size(), 1); - - BOOST_CHECK(rib.find(options2) == rib.end()); - BOOST_CHECK(rib.find(options1) != rib.end()); - - rib.erase(options1); - BOOST_CHECK(rib.empty()); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // namespace tests -} // namespace nrd -} // namespace ndn diff --git a/rib/waf b/rib/waf deleted file mode 100755 index 78a44f37..00000000 Binary files a/rib/waf and /dev/null differ diff --git a/rib/wscript b/rib/wscript deleted file mode 100644 index 1a7f82a3..00000000 --- a/rib/wscript +++ /dev/null @@ -1,71 +0,0 @@ -# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- - -from waflib import Logs - -def options(opt): - opt.load('compiler_cxx gnu_dirs') - opt.load('flags boost doxygen coverage', tooldir=['.waf-tools']) - - nrdopt = opt.add_option_group('NRD Options') - nrdopt.add_option('--debug', action='store_true', default=False, - dest='debug', - help='''Compile library debugging mode without all optimizations (-O0)''') - nrdopt.add_option('--with-tests', action='store_true', - default=False, dest='with_tests', help='''Build unit tests''') - -def configure(conf): - conf.load("compiler_cxx gnu_dirs boost flags") - - conf.check_cfg(package='libndn-cpp-dev', args=['--cflags', '--libs'], - uselib_store='NDN_CPP', mandatory=True) - - boost_libs = 'system' - if conf.options.with_tests: - conf.env['WITH_TESTS'] = 1 - conf.define('WITH_TESTS', 1); - boost_libs += ' unit_test_framework' - - conf.check_boost(lib=boost_libs) - if conf.env.BOOST_VERSION_NUMBER < 104800: - Logs.error("Minimum required boost version is 1.48") - Logs.error("Please upgrade your distribution or install custom boost libraries" + - " (http://redmine.named-data.net/projects/nfd/wiki/Boost_FAQ)") - return - - # conf.load('coverage') - - # try: - # conf.load('doxygen') - # except: - # pass - - conf.define('DEFAULT_CONFIG_FILE', '%s/ndn/nrd.conf' % conf.env['SYSCONFDIR']) - conf.write_config_header('src/config.hpp') - -def build (bld): - bld( - features='cxx', - name='nrd-objects', - source=bld.path.ant_glob('src/*.cpp', - excl='src/main.cpp'), - use='NDN_CPP BOOST', - ) - - bld.program( - target='nrd', - source='src/main.cpp', - use='nrd-objects' - ) - - # Unit tests - if bld.env['WITH_TESTS']: - unit_tests = bld.program( - target='unit-tests', - features='cxx cxxprogram', - source=bld.path.ant_glob(['tests/**/*.cpp']), - use='nrd-objects', - includes=['.', 'src'], - install_prefix=None, - ) - - bld.install_files('${SYSCONFDIR}/ndn', 'nrd.conf.sample') diff --git a/tests/rib/rib.cpp b/tests/rib/rib.cpp new file mode 100644 index 00000000..fd5777b2 --- /dev/null +++ b/tests/rib/rib.cpp @@ -0,0 +1,86 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/** + * Copyright (c) 2014 Regents of the University of California, + * Arizona Board of Regents, + * Colorado State University, + * University Pierre & Marie Curie, Sorbonne University, + * Washington University in St. Louis, + * Beijing Institute of Technology, + * The University of Memphis + * + * This file is part of NFD (Named Data Networking Forwarding Daemon). + * See AUTHORS.md for complete list of NFD authors and contributors. + * + * NFD is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * NFD, e.g., in COPYING.md file. If not, see . + **/ + +#include "rib/rib.hpp" + +#include "tests/test-common.hpp" + +namespace nfd { +namespace rib { +namespace tests { + +BOOST_FIXTURE_TEST_SUITE(Rib, nfd::tests::BaseFixture) + +BOOST_AUTO_TEST_CASE(Basic) +{ + rib::Rib rib; + + PrefixRegOptions options1; + options1.setName("/hello/world"); + options1.setFlags(tlv::nrd::NDN_FORW_CHILD_INHERIT | tlv::nrd::NDN_FORW_CAPTURE); + options1.setCost(10); + options1.setExpirationPeriod(time::milliseconds(1500)); + options1.setFaceId(1); + + rib.insert(options1); + BOOST_CHECK_EQUAL(rib.size(), 1); + + PrefixRegOptions options2; + options2.setName("/hello/world"); + options2.setFlags(tlv::nrd::NDN_FORW_CHILD_INHERIT); + options2.setExpirationPeriod(time::seconds(0)); + options2.setFaceId(1); + options2.setCost(100); + + rib.insert(options2); + BOOST_CHECK_EQUAL(rib.size(), 1); + + options2.setFaceId(2); + rib.insert(options2); + BOOST_CHECK_EQUAL(rib.size(), 2); + + options2.setName("/foo/bar"); + rib.insert(options2); + BOOST_CHECK_EQUAL(rib.size(), 3); + + rib.erase(options2); + BOOST_CHECK_EQUAL(rib.size(), 2); + + options2.setName("/hello/world"); + rib.erase(options2); + BOOST_CHECK_EQUAL(rib.size(), 1); + + BOOST_CHECK(rib.find(options2) == rib.end()); + BOOST_CHECK(rib.find(options1) != rib.end()); + + rib.erase(options1); + BOOST_CHECK(rib.empty()); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace tests +} // namespace rib +} // namespace nfd diff --git a/tests/wscript b/tests/wscript index 423e5e3d..bf569134 100644 --- a/tests/wscript +++ b/tests/wscript @@ -64,6 +64,15 @@ def build(bld): if bld.env['HAVE_UNIX_SOCKETS']: unit_tests_nfd.source += bld.path.ant_glob('daemon/face/unix-*.cpp') + unit_tests_rib = bld.program( + target='../unit-tests-rib', + features='cxx cxxprogram', + source=bld.path.ant_glob(['rib/**/*.cpp']), + use='rib-objects unit-tests-main', + includes=['.'], + install_prefix=None, + ) + # Other tests (e.g., stress tests that can be enabled even if unit tests are disabled) if bld.env['WITH_TESTS'] or bld.env['WITH_OTHER_TESTS']: bld.recurse("other") diff --git a/wscript b/wscript index 39ea4b1a..a476ee4f 100644 --- a/wscript +++ b/wscript @@ -138,6 +138,21 @@ def build(bld): use='daemon-objects', ) + rib_objects = bld( + target='rib-objects', + name='rib-objects', + features='cxx', + source=bld.path.ant_glob(['rib/**/*.cpp'], + excl=['rib/main.cpp']), + use='core-objects', + ) + + bld(target='bin/nrd', + features='cxx cxxprogram', + source='rib/main.cpp', + use='rib-objects', + ) + for app in bld.path.ant_glob('tools/*.cpp'): bld(features=['cxx', 'cxxprogram'], target='bin/%s' % (str(app.change_ext(''))), @@ -153,6 +168,9 @@ def build(bld): install_path="${SYSCONFDIR}/ndn", IF_HAVE_LIBPCAP="" if bld.env['HAVE_LIBPCAP'] else "; ") + # @todo Merge with nfd.conf + bld.install_files('${SYSCONFDIR}/ndn', 'nrd.conf.sample') + bld(features='subst', source='tools/nfd-status-http-server.py', target='nfd-status-http-server',