| /* -*- 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 <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "nlsrc.hpp" |
| |
| #include "config.hpp" |
| #include "version.hpp" |
| #include "src/publisher/dataset-interest-handler.hpp" |
| |
| #include <ndn-cxx/data.hpp> |
| #include <ndn-cxx/encoding/block.hpp> |
| #include <ndn-cxx/face.hpp> |
| #include <ndn-cxx/interest.hpp> |
| #include <ndn-cxx/mgmt/nfd/control-parameters.hpp> |
| #include <ndn-cxx/mgmt/nfd/control-response.hpp> |
| #include <ndn-cxx/security/interest-signer.hpp> |
| #include <ndn-cxx/security/key-chain.hpp> |
| #include <ndn-cxx/security/signing-helpers.hpp> |
| #include <ndn-cxx/security/validator-config.hpp> |
| #include <ndn-cxx/security/validator-null.hpp> |
| #include <ndn-cxx/util/segment-fetcher.hpp> |
| |
| #include <boost/algorithm/string/replace.hpp> |
| #include <boost/property_tree/info_parser.hpp> |
| |
| #include <iostream> |
| |
| 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 <router prefix> [-c <nlsr.conf path> | -k]] COMMAND [<Command Options>] |
| -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 <name> |
| advertise a name prefix through NLSR |
| advertise <name> save |
| advertise and save the name prefix to the conf file |
| withdraw <name> |
| remove a name prefix advertised through NLSR |
| withdraw <name> delete |
| withdraw and delete the name prefix from the conf file |
| )EOT"); |
| boost::algorithm::replace_all_copy(std::ostream_iterator<char>(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<ndn::security::ValidatorConfig>(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<std::string> 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<void()> 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::AdjLsa>(nlsr::dataset::ADJACENCY_COMPONENT, |
| std::bind(&Nlsrc::recordLsa, this, _1)); |
| } |
| |
| void |
| Nlsrc::fetchCoordinateLsas() |
| { |
| fetchFromLsdb<nlsr::CoordinateLsa>(nlsr::dataset::COORDINATE_COMPONENT, |
| std::bind(&Nlsrc::recordLsa, this, _1)); |
| } |
| |
| void |
| Nlsrc::fetchNameLsas() |
| { |
| fetchFromLsdb<nlsr::NameLsa>(nlsr::dataset::NAME_COMPONENT, |
| std::bind(&Nlsrc::recordLsa, this, _1)); |
| } |
| |
| void |
| Nlsrc::fetchRtables() |
| { |
| fetchFromRt<nlsr::RoutingTableStatus>([this] (const auto& rts) { this->recordRtable(rts); }); |
| } |
| |
| template<class T> |
| void |
| Nlsrc::fetchFromLsdb(const ndn::Name::Component& datasetType, |
| const std::function<void(const T&)>& recordLsa) |
| { |
| auto name = m_routerPrefix; |
| name.append(LSDB_SUFFIX); |
| name.append(datasetType); |
| ndn::Interest interest(name); |
| |
| auto fetcher = ndn::util::SegmentFetcher::start(m_face, interest, *m_validator); |
| fetcher->onComplete.connect(std::bind(&Nlsrc::onFetchSuccess<T>, 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<class T> |
| void |
| Nlsrc::fetchFromRt(const std::function<void(const T&)>& recordDataset) |
| { |
| auto name = m_routerPrefix; |
| name.append(RT_SUFFIX); |
| ndn::Interest interest(name); |
| |
| auto fetcher = ndn::util::SegmentFetcher::start(m_face, interest, *m_validator); |
| fetcher->onComplete.connect(std::bind(&Nlsrc::onFetchSuccess<T>, this, _1, recordDataset)); |
| fetcher->onError.connect(std::bind(&Nlsrc::onTimeout, this, _1, _2)); |
| } |
| |
| template<class T> |
| void |
| Nlsrc::onFetchSuccess(const ndn::ConstBufferPtr& buf, |
| const std::function<void(const T&)>& 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<std::string> 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(); |
| } |