diff --git a/docs/conf.py b/docs/conf.py index 83e251b..7314c6d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -59,7 +59,7 @@ master_doc = 'index' # General information about the project. project = u'NLSR - Named Data Link State Routing Protocol' -copyright = u'2014, Named Data Networking Project' +copyright = u'2014-2015, Named Data Networking Project' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -239,6 +239,7 @@ latex_documents = [ man_pages = [ ('manpages/nlsr', 'nlsr', u'Named Data Link State Routing Protocol Daemon', None, 1), ('manpages/nlsr.conf', 'nlsr.conf', u'Named Data Link State Routing Protocol Daemon config file', None, 5), + ('manpages/nlsrc', 'nlsrc', u'Command-line utility to interact with and collect statistics from NLSR', None, 1), ] diff --git a/docs/manpages.rst b/docs/manpages.rst index 40bd0de..e2e1582 100644 --- a/docs/manpages.rst +++ b/docs/manpages.rst @@ -6,4 +6,5 @@ Manpages .. toctree:: manpages/nlsr manpages/nlsr.conf + manpages/nlsrc :maxdepth: 1 diff --git a/docs/manpages/nlsrc.rst b/docs/manpages/nlsrc.rst new file mode 100644 index 0000000..1db6cf3 --- /dev/null +++ b/docs/manpages/nlsrc.rst @@ -0,0 +1,55 @@ +nlsrc +===== + +Usage +----- + +:: + + nlsrc [-h] [-V] COMMAND [] + + +Description +----------- + +``nlsrc`` is a tool to retrieve link-state database (LSDB) status information from NLSR and +announce/withdraw Name prefixes advertised by NLSR. + +Options +------- + +``-h`` + Print usage information + +``-V`` + Show NLSRC version information + +``COMMAND`` + + ``status`` + Retrieve LSDB status information + + ``advertise`` + Add a Name prefix to be advertised by NLSR + + ``advertise `` + + ``name`` + The Name prefix to be advertised + + ``withdraw`` + Remove a Name prefix advertised through NLSR + + ``withdraw `` + + ``name`` + The Name prefix to be withdrawn + +Exit Status +----------- + +nlsrc exits with one of the following values: +:: + + 0 nlsrc exited successfully + >0 An error occurred diff --git a/tools/nlsrc.cpp b/tools/nlsrc.cpp new file mode 100644 index 0000000..9142774 --- /dev/null +++ b/tools/nlsrc.cpp @@ -0,0 +1,412 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/** + * Copyright (c) 2014-2015, The University of Memphis, + * Regents of the University of California, + * Arizona Board of Regents. + * + * This file is part of NLSR (Named-data Link State Routing). + * See AUTHORS.md for complete list of NLSR authors and contributors. + * + * NLSR 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. + * + * NLSR 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 + * NLSR, e.g., in COPYING.md file. If not, see . + **/ + +#include "nlsrc.hpp" + +#include "version.hpp" +#include "src/publisher/lsa-publisher.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace nlsrc { + +const ndn::Name Nlsrc::LOCALHOST_PREFIX = ndn::Name("/localhost/nlsr"); +const ndn::Name Nlsrc::LSDB_PREFIX = ndn::Name(Nlsrc::LOCALHOST_PREFIX).append("lsdb"); +const ndn::Name Nlsrc::NAME_UPDATE_PREFIX = ndn::Name(Nlsrc::LOCALHOST_PREFIX).append("prefix-update"); + +const uint32_t Nlsrc::ERROR_CODE_TIMEOUT = 10060; +const uint32_t Nlsrc::RESPONSE_CODE_SUCCESS = 200; + +Nlsrc::Nlsrc(ndn::Face& face) + : m_face(face) +{ +} + +void +Nlsrc::printUsage() +{ + std::cout << "Usage:\n" << programName << " [-h] [-V] COMMAND []\n" + " -h print usage and exit\n" + " -V print version and exit\n" + "\n" + " COMMAND can be one of the following:\n" + " status\n" + " display NLSR status\n" + " advertise name\n" + " advertise a name prefix through NLSR\n" + " withdraw name\n" + " remove a name prefix advertised through NLSR" + << std::endl; +} + +void +Nlsrc::getStatus() +{ + m_fetchSteps.push_back(std::bind(&Nlsrc::fetchAdjacencyLsas, this)); + m_fetchSteps.push_back(std::bind(&Nlsrc::fetchCoordinateLsas, this)); + m_fetchSteps.push_back(std::bind(&Nlsrc::fetchNameLsas, this)); + m_fetchSteps.push_back(std::bind(&Nlsrc::printLsdb, this)); + + runNextStep(); +} + +bool +Nlsrc::dispatch(const std::string& command) +{ + if (command == "advertise") { + if (nOptions != 1) { + return false; + } + + advertiseName(); + return true; + } + else if (command == "withdraw") { + if (nOptions != 1) { + return false; + } + + withdrawName(); + return true; + } + else if (command == "status") { + if (nOptions != 0) { + return false; + } + + getStatus(); + return true; + } + + return false; +} + +void +Nlsrc::runNextStep() +{ + if (m_fetchSteps.empty()) { + return; + } + + std::function nextStep = m_fetchSteps.front(); + m_fetchSteps.pop_front(); + + nextStep(); +} + +void +Nlsrc::advertiseName() +{ + ndn::Name name = commandLineArguments[0]; + ndn::Name::Component verb("advertise"); + std::string info = "(Advertise: " + name.toUri() + ")"; + + sendNamePrefixUpdate(name, verb, info); +} + +void +Nlsrc::withdrawName() +{ + ndn::Name name = commandLineArguments[0]; + ndn::Name::Component verb("withdraw"); + std::string info = "(Withdraw: " + name.toUri() + ")"; + + sendNamePrefixUpdate(name, verb, info); +} + +void +Nlsrc::sendNamePrefixUpdate(const ndn::Name& name, + const ndn::Name::Component& verb, + const std::string& info) +{ + ndn::nfd::ControlParameters parameters; + parameters.setName(name); + + ndn::Name commandName = NAME_UPDATE_PREFIX; + commandName.append(verb); + + ndn::Interest interest(commandName.append(parameters.wireEncode())); + interest.setMustBeFresh(true); + m_keyChain.sign(interest); + + m_face.expressInterest(interest, + std::bind(&Nlsrc::onControlResponse, this, info, _2), + std::bind(&Nlsrc::onTimeout, this, ERROR_CODE_TIMEOUT, "Timeout")); +} + +void +Nlsrc::onControlResponse(const std::string& info, const ndn::Data& data) +{ + ndn::nfd::ControlResponse response; + + try { + response.wireDecode(data.getContent().blockFromValue()); + } + catch (const std::exception& e) { + std::cerr << "ERROR: Control response decoding error" << std::endl; + return; + } + + uint32_t code = response.getCode(); + + if (code != RESPONSE_CODE_SUCCESS) { + std::cerr << "Name prefix update error (code: " << code << ")" << std::endl; + return; + } + + std::cout << "Applied Name prefix update successfully: " << info << std::endl; +} + +void +Nlsrc::fetchAdjacencyLsas() +{ + fetchFromLsdb(nlsr::AdjacencyLsaPublisher::DATASET_COMPONENT, + std::bind(&Nlsrc::recordAdjacencyLsa, this, _1)); +} + +void +Nlsrc::fetchCoordinateLsas() +{ + fetchFromLsdb(nlsr::CoordinateLsaPublisher::DATASET_COMPONENT, + std::bind(&Nlsrc::recordCoordinateLsa, this, _1)); +} + +void +Nlsrc::fetchNameLsas() +{ + fetchFromLsdb(nlsr::NameLsaPublisher::DATASET_COMPONENT, + std::bind(&Nlsrc::recordNameLsa, this, _1)); +} + +template +void +Nlsrc::fetchFromLsdb(const ndn::Name::Component& datasetType, + const std::function& recordLsa) +{ + ndn::Name command = LSDB_PREFIX; + command.append(datasetType); + + ndn::Interest interest(command); + + ndn::util::SegmentFetcher::fetch(m_face, + interest, + ndn::util::DontVerifySegment(), + std::bind(&Nlsrc::onFetchSuccess, + this, _1, recordLsa), + std::bind(&Nlsrc::onTimeout, this, _1, _2)); +} + +template +void +Nlsrc::onFetchSuccess(const ndn::ConstBufferPtr& data, + const std::function& recordLsa) +{ + ndn::Block block; + size_t offset = 0; + + while (offset < data->size()) { + bool isOk = false; + std::tie(isOk, block) = ndn::Block::fromBuffer(data, offset); + + if (!isOk) { + std::cerr << "ERROR: cannot decode LSA TLV" << std::endl; + break; + } + + offset += block.size(); + + T lsa(block); + recordLsa(lsa); + } + + runNextStep(); +} + +void +Nlsrc::onTimeout(uint32_t errorCode, const std::string& error) +{ + std::cerr << "Request timed out (code: " << errorCode + << ", error: " << error << ")" << std::endl; +} + +std::string +Nlsrc::getLsaInfoString(const nlsr::tlv::LsaInfo& info) +{ + std::ostringstream os; + os << " info=" << info; + + return os.str(); +} + +void +Nlsrc::recordAdjacencyLsa(const nlsr::tlv::AdjacencyLsa& lsa) +{ + Router& router = getRouter(lsa.getLsaInfo()); + + std::ostringstream os; + os << " AdjacencyLsa:" << std::endl; + + os << getLsaInfoString(lsa.getLsaInfo()) << std::endl; + + for (const auto& adjacency : lsa.getAdjacencies()) { + os << " adjacency=" << adjacency << std::endl; + } + + router.adjacencyLsaString = os.str(); +} + +void +Nlsrc::recordCoordinateLsa(const nlsr::tlv::CoordinateLsa& lsa) +{ + Router& router = getRouter(lsa.getLsaInfo()); + + std::ostringstream os; + os << " Coordinate LSA:" << std::endl; + + os << getLsaInfoString(lsa.getLsaInfo()) << std::endl; + + os << " angle=" << lsa.getHyperbolicAngle() << std::endl; + os << " radius=" << lsa.getHyperbolicRadius() << std::endl; + + router.coordinateLsaString = os.str(); +} + +void +Nlsrc::recordNameLsa(const nlsr::tlv::NameLsa& lsa) +{ + Router& router = getRouter(lsa.getLsaInfo()); + + std::ostringstream os; + os << " Name LSA:" << std::endl; + + os << getLsaInfoString(lsa.getLsaInfo()) << std::endl; + + for (const auto& name : lsa.getNames()) { + os << " name=" << name << std::endl; + } + + router.nameLsaString = os.str(); +} + +void +Nlsrc::printLsdb() +{ + std::cout << "NLSR Status" << std::endl; + std::cout << "LSDB:" << std::endl; + + for (const auto& item : m_routers) { + std::cout << " OriginRouter: " << item.first << std::endl; + std::cout << std::endl; + + const Router& router = item.second; + + if (!router.adjacencyLsaString.empty()) { + std::cout << router.adjacencyLsaString << std::endl; + } + + if (!router.coordinateLsaString.empty()) { + std::cout << router.coordinateLsaString << std::endl; + } + + if (!router.nameLsaString.empty()) { + std::cout << router.nameLsaString << std::endl; + } + } +} + +Nlsrc::Router& +Nlsrc::getRouter(const nlsr::tlv::LsaInfo& info) +{ + const ndn::Name& originRouterName = info.getOriginRouter(); + + const auto& pair = + m_routers.insert(std::make_pair(originRouterName, std::move(Router()))); + + return pair.first->second; +} + +} // namespace nlsrc + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +int +main(int argc, char** argv) +{ + ndn::Face face; + nlsrc::Nlsrc nlsrc(face); + + nlsrc.programName = argv[0]; + + if (argc < 2) { + nlsrc.printUsage(); + return 0; + } + + int opt; + while ((opt = ::getopt(argc, argv, "hV")) != -1) { + switch (opt) { + case 'h': + nlsrc.printUsage(); + return 0; + case 'V': + std::cout << NLSR_VERSION_BUILD_STRING << std::endl; + return 0; + default: + nlsrc.printUsage(); + return 1; + } + } + + if (argc == ::optind) { + nlsrc.printUsage(); + return 1; + } + + try { + ::optind = 2; // Set ::optind to the command's index + + nlsrc.commandLineArguments = argv + ::optind; + nlsrc.nOptions = argc - ::optind; + + // argv[1] points to the command, so pass it to the dispatch + bool isOk = nlsrc.dispatch(argv[1]); + if (!isOk) { + nlsrc.printUsage(); + return 1; + } + + face.processEvents(); + } + catch (const std::exception& e) { + std::cerr << "ERROR: " << e.what() << std::endl; + return 2; + } + return 0; +} \ No newline at end of file diff --git a/tools/nlsrc.hpp b/tools/nlsrc.hpp new file mode 100644 index 0000000..b62cca0 --- /dev/null +++ b/tools/nlsrc.hpp @@ -0,0 +1,162 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/** + * Copyright (c) 2014-2015, The University of Memphis, + * Regents of the University of California, + * Arizona Board of Regents. + * + * This file is part of NLSR (Named-data Link State Routing). + * See AUTHORS.md for complete list of NLSR authors and contributors. + * + * NLSR 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. + * + * NLSR 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 + * NLSR, e.g., in COPYING.md file. If not, see . + **/ + +#include "tlv/adjacency-lsa.hpp" +#include "tlv/coordinate-lsa.hpp" +#include "tlv/name-lsa.hpp" + +#include +#include +#include + +#include +#include +#include + +#ifndef NLSR_TOOLS_NLSRC_HPP +#define NLSR_TOOLS_NLSRC_HPP + +namespace nlsrc { + +class Nlsrc : boost::noncopyable +{ +public: + explicit + Nlsrc(ndn::Face& face); + + void + printUsage(); + + void + getStatus(); + + bool + dispatch(const std::string& cmd); + +private: + void + runNextStep(); + + /** + * \brief Adds a name prefix to be advertised in NLSR's Name LSA + * + * cmd format: + * name + * + */ + void + advertiseName(); + + /** + * \brief Removes a name prefix from NLSR's Name LSA + * + * cmd format: + * name + * + */ + void + withdrawName(); + + void + sendNamePrefixUpdate(const ndn::Name& name, + const ndn::Name::Component& verb, + const std::string& info); + + void + onControlResponse(const std::string& info, const ndn::Data& data); + +private: + void + fetchAdjacencyLsas(); + + void + fetchCoordinateLsas(); + + void + fetchNameLsas(); + + template + void + fetchFromLsdb(const ndn::Name::Component& datasetType, + const std::function& recordLsa); + + template + void + onFetchSuccess(const ndn::ConstBufferPtr& data, + const std::function& recordLsa); + + void + onTimeout(uint32_t errorCode, const std::string& error); + +private: + std::string + getLsaInfoString(const nlsr::tlv::LsaInfo& info); + + void + recordAdjacencyLsa(const nlsr::tlv::AdjacencyLsa& lsa); + + void + recordCoordinateLsa(const nlsr::tlv::CoordinateLsa& lsa); + + void + recordNameLsa(const nlsr::tlv::NameLsa& lsa); + + void + printLsdb(); + +public: + const char* programName; + + // command parameters without leading 'cmd' component + const char* const* commandLineArguments; + int nOptions; + +private: + struct Router + { + std::string adjacencyLsaString; + std::string coordinateLsaString; + std::string nameLsaString; + }; + + Router& + getRouter(const nlsr::tlv::LsaInfo& info); + + typedef std::map RouterMap; + RouterMap m_routers; + +private: + ndn::KeyChain m_keyChain; + ndn::Face& m_face; + + std::deque> m_fetchSteps; + + static const ndn::Name LOCALHOST_PREFIX; + static const ndn::Name LSDB_PREFIX; + static const ndn::Name NAME_UPDATE_PREFIX; + + static const uint32_t ERROR_CODE_TIMEOUT; + static const uint32_t RESPONSE_CODE_SUCCESS; +}; + +} // namespace nlsrc + +#endif // NLSR_TOOLS_NLSRC_HPP diff --git a/wscript b/wscript index 946bb5a..0dbf533 100644 --- a/wscript +++ b/wscript @@ -128,10 +128,25 @@ def build(bld): use='nlsr-objects', ) + nlsrc = bld( + target='bin/nlsrc', + features='cxx cxxprogram', + source='tools/nlsrc.cpp', + use='nlsr-objects BOOST', + ) + if bld.env['WITH_TESTS']: bld.recurse('tests') bld.recurse('tests-integrated') + if bld.env['SPHINX_BUILD']: + bld(features="sphinx", + builder="man", + outdir="docs/manpages", + config="docs/conf.py", + source=bld.path.ant_glob('docs/manpages/**/*.rst'), + install_path="${MANDIR}/", + VERSION=VERSION) def docs(bld): from waflib import Options