/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* * Copyright (c) 2014-2023, 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 "config.hpp" #include "version.hpp" #include "src/publisher/dataset-interest-handler.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace nlsrc { const ndn::Name LOCALHOST_PREFIX("/localhost"); const ndn::PartialName LSDB_SUFFIX("nlsr/lsdb"); const ndn::PartialName NAME_UPDATE_SUFFIX("nlsr/prefix-update"); const ndn::PartialName RT_SUFFIX("nlsr/routing-table"); const uint32_t ERROR_CODE_TIMEOUT = 10060; const uint32_t RESPONSE_CODE_SUCCESS = 200; const uint32_t RESPONSE_CODE_NO_EFFECT = 204; const uint32_t RESPONSE_CODE_SAVE_OR_DELETE = 205; Nlsrc::Nlsrc(std::string programName, ndn::Face& face) : m_programName(std::move(programName)) , m_routerPrefix(LOCALHOST_PREFIX) , m_face(face) { disableValidator(); } void Nlsrc::printUsage() const { const std::string help(R"EOT(Usage: @NLSRC@ [-h | -V] @NLSRC@ [-R [-c | -k]] COMMAND [] -h print usage and exit -V print version and exit -R target a remote NLSR instance -c verify response with nlsr.conf security.validator policy -k do not verify response (insecure) COMMAND can be one of the following: lsdb display NLSR lsdb status routing display routing table status status display all NLSR status (lsdb & routingtable) advertise advertise a name prefix through NLSR advertise save advertise and save the name prefix to the conf file withdraw remove a name prefix advertised through NLSR withdraw delete withdraw and delete the name prefix from the conf file )EOT"); boost::algorithm::replace_all_copy(std::ostream_iterator(std::cout), help, "@NLSRC@", m_programName); } void Nlsrc::setRouterPrefix(ndn::Name prefix) { m_routerPrefix = std::move(prefix); } void Nlsrc::disableValidator() { m_validator.reset(new ndn::security::ValidatorNull()); } bool Nlsrc::enableValidator(const std::string& filename) { using namespace boost::property_tree; ptree validatorConfig; try { ptree config; read_info(filename, config); validatorConfig = config.get_child("security.validator"); } catch (const ptree_error& e) { std::cerr << "Failed to parse configuration file '" << filename << "': " << e.what() << std::endl; return false; } auto validator = std::make_unique(m_face); try { validator->load(validatorConfig, filename); } catch (const ndn::security::validator_config::Error& e) { std::cerr << "Failed to load validator config from '" << filename << "' security.validator section: " << e.what() << std::endl; return false; } m_validator = std::move(validator); return true; } void Nlsrc::getStatus(const std::string& command) { if (command == "lsdb") { 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)); } else if (command == "routing") { m_fetchSteps.push_back(std::bind(&Nlsrc::fetchRtables, this)); m_fetchSteps.push_back(std::bind(&Nlsrc::printRT, this)); } else if (command == "status") { 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::fetchRtables, this)); m_fetchSteps.push_back(std::bind(&Nlsrc::printAll, this)); } runNextStep(); } bool Nlsrc::dispatch(ndn::span subcommand) { if (subcommand.size() == 0) { return false; } if (subcommand[0] == "advertise") { switch (subcommand.size()) { case 2: advertiseName(subcommand[1], false); return true; case 3: if (subcommand[2] != "save") { return false; } advertiseName(subcommand[1], true); return true; } return false; } if (subcommand[0] == "withdraw") { switch (subcommand.size()) { case 2: withdrawName(subcommand[1], false); return true; case 3: if (subcommand[2] != "delete") { return false; } withdrawName(subcommand[1], true); return true; } return false; } if (subcommand[0] == "lsdb" || subcommand[0] == "routing" || subcommand[0] == "status") { if (subcommand.size() != 1) { return false; } getStatus(subcommand[0]); 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, bool wantSave) { std::string info = (wantSave ? "(Save: " : "(Advertise: ") + name.toUri() + ")"; ndn::Name::Component verb("advertise"); sendNamePrefixUpdate(name, verb, info, wantSave); } void Nlsrc::withdrawName(ndn::Name name, bool wantDelete) { std::string info = (wantDelete ? "(Delete: " : "(Withdraw: ") + name.toUri() + ")"; ndn::Name::Component verb("withdraw"); sendNamePrefixUpdate(name, verb, info, wantDelete); } void Nlsrc::sendNamePrefixUpdate(const ndn::Name& name, const ndn::Name::Component& verb, const std::string& info, bool flag) { ndn::nfd::ControlParameters parameters; parameters.setName(name); if (flag) { parameters.setFlags(1); } auto paramWire = parameters.wireEncode(); ndn::Name commandName = m_routerPrefix; commandName.append(NAME_UPDATE_SUFFIX); commandName.append(verb); commandName.append(paramWire.begin(), paramWire.end()); ndn::security::InterestSigner signer(m_keyChain); auto commandInterest = signer.makeCommandInterest(commandName, ndn::security::signingByIdentity(m_keyChain.getPib().getDefaultIdentity())); commandInterest.setMustBeFresh(true); m_face.expressInterest(commandInterest, std::bind(&Nlsrc::onControlResponse, this, info, _2), std::bind(&Nlsrc::onTimeout, this, ERROR_CODE_TIMEOUT, "Nack"), std::bind(&Nlsrc::onTimeout, this, ERROR_CODE_TIMEOUT, "Timeout")); } void Nlsrc::onControlResponse(const std::string& info, const ndn::Data& data) { if (data.getMetaInfo().getType() == ndn::tlv::ContentType_Nack) { std::cerr << "ERROR: Run-time advertise/withdraw disabled" << std::endl; return; } ndn::nfd::ControlResponse response; try { response.wireDecode(data.getContent().blockFromValue()); } catch (const std::exception& e) { std::cerr << "ERROR: Control response decoding error" << std::endl; m_exitCode = 1; return; } uint32_t code = response.getCode(); if (code != RESPONSE_CODE_SUCCESS && code != RESPONSE_CODE_SAVE_OR_DELETE) { std::cerr << response.getText() << std::endl; std::cerr << "Name prefix update error (code: " << code << ")" << std::endl; m_exitCode = code == RESPONSE_CODE_NO_EFFECT ? 0 : 1; return; } std::cout << "Applied Name prefix update successfully: " << info << std::endl; m_exitCode = 0; } void Nlsrc::fetchAdjacencyLsas() { fetchFromLsdb(nlsr::dataset::ADJACENCY_COMPONENT, std::bind(&Nlsrc::recordLsa, this, _1)); } void Nlsrc::fetchCoordinateLsas() { fetchFromLsdb(nlsr::dataset::COORDINATE_COMPONENT, std::bind(&Nlsrc::recordLsa, this, _1)); } void Nlsrc::fetchNameLsas() { fetchFromLsdb(nlsr::dataset::NAME_COMPONENT, std::bind(&Nlsrc::recordLsa, this, _1)); } void Nlsrc::fetchRtables() { fetchFromRt([this] (const auto& rts) { this->recordRtable(rts); }); } template void Nlsrc::fetchFromLsdb(const ndn::Name::Component& datasetType, const std::function& recordLsa) { auto name = m_routerPrefix; name.append(LSDB_SUFFIX); name.append(datasetType); ndn::Interest interest(name); auto fetcher = ndn::SegmentFetcher::start(m_face, interest, *m_validator); fetcher->onComplete.connect(std::bind(&Nlsrc::onFetchSuccess, this, _1, recordLsa)); fetcher->onError.connect(std::bind(&Nlsrc::onTimeout, this, _1, _2)); } void Nlsrc::recordLsa(const nlsr::Lsa& lsa) { Router& router = m_routers.emplace(lsa.getOriginRouter(), Router()).first->second; if (lsa.getType() == nlsr::Lsa::Type::ADJACENCY) { router.adjacencyLsaString = lsa.toString(); } else if (lsa.getType() == nlsr::Lsa::Type::COORDINATE) { router.coordinateLsaString = lsa.toString(); } else if (lsa.getType() == nlsr::Lsa::Type::NAME) { router.nameLsaString = lsa.toString(); } } template void Nlsrc::fetchFromRt(const std::function& recordDataset) { auto name = m_routerPrefix; name.append(RT_SUFFIX); ndn::Interest interest(name); auto fetcher = ndn::SegmentFetcher::start(m_face, interest, *m_validator); fetcher->onComplete.connect(std::bind(&Nlsrc::onFetchSuccess, this, _1, recordDataset)); fetcher->onError.connect(std::bind(&Nlsrc::onTimeout, this, _1, _2)); } template void Nlsrc::onFetchSuccess(const ndn::ConstBufferPtr& buf, const std::function& recordDataset) { size_t offset = 0; while (offset < buf->size()) { auto [isOk, block] = ndn::Block::fromBuffer(buf, offset); if (!isOk) { std::cerr << "ERROR: cannot decode LSA TLV" << std::endl; break; } offset += block.size(); T dataset(block); recordDataset(dataset); } runNextStep(); } void Nlsrc::onTimeout(uint32_t errorCode, const std::string& error) { std::cerr << "Request timed out (code: " << errorCode << ", error: " << error << ")" << std::endl; m_exitCode = 1; } void Nlsrc::recordRtable(const nlsr::RoutingTableStatus& rts) { std::ostringstream os; os << rts; m_rtString = os.str(); } void Nlsrc::printLsdb() { 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; } } } void Nlsrc::printRT() { if (!m_rtString.empty()) { std::cout << m_rtString; } else { std::cout << "Routing Table is not calculated yet" << std::endl; } } void Nlsrc::printAll() { std::cout << "NLSR Status" << std::endl; printLsdb(); printRT(); } } // namespace nlsrc //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// int main(int argc, char** argv) { ndn::Face face; nlsrc::Nlsrc nlsrc(argv[0], face); if (argc < 2) { nlsrc.printUsage(); return 2; } int opt; const char* confFile = DEFAULT_CONFIG_FILE; bool disableValidator = false; while ((opt = ::getopt(argc, argv, "hVR:c:k")) != -1) { switch (opt) { case 'h': nlsrc.printUsage(); return 0; case 'V': std::cout << NLSR_VERSION_BUILD_STRING << std::endl; return 0; case 'R': nlsrc.setRouterPrefix(::optarg); break; case 'c': confFile = ::optarg; break; case 'k': disableValidator = true; break; default: nlsrc.printUsage(); return 2; } } if (argc == ::optind) { nlsrc.printUsage(); return 2; } if (nlsrc.getRouterPrefix() != nlsrc::LOCALHOST_PREFIX && !disableValidator) { if (!nlsrc.enableValidator(confFile)) { return 1; } } std::vector subcommand(&argv[::optind], &argv[argc]); try { bool isValidSyntax = nlsrc.dispatch(subcommand); if (!isValidSyntax) { nlsrc.printUsage(); return 2; } face.processEvents(); } catch (const std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; return 1; } return nlsrc.getExitCode(); }