nlsrc: run command on remote router
refs #4544
Change-Id: I977ebfb94c84730bd2bcc73515f77cc4773ec2de
diff --git a/docs/manpages/nlsrc.rst b/docs/manpages/nlsrc.rst
index 2508fb3..f91706b 100644
--- a/docs/manpages/nlsrc.rst
+++ b/docs/manpages/nlsrc.rst
@@ -6,7 +6,8 @@
::
- nlsrc [-h] [-V] COMMAND [<Command Options>]
+ nlsrc [-h | -V]
+ nlsrc [-R <router prefix> [-c <nlsr.conf path> | -k]] COMMAND [<Command Options>]
Description
@@ -24,6 +25,17 @@
``-V``
Show NLSRC version information
+``-R <router prefix>``
+ Target a remote NLSR instance.
+ The default is the local NLSR instance ``/localhost``.
+
+``-c <nlsr.conf path>``
+ Verify remote status information with the trust schema loaded from ``security.validator`` section of specified nlsr.conf config file.
+ The default is loading from ``/etc/ndn/nlsr.conf``.
+
+``-k``
+ Insecure: do not verify signature on status information retrieved from remote router.
+
``COMMAND``
``lsdb``
diff --git a/nlsr.conf b/nlsr.conf
index 752dc44..fd2ad23 100644
--- a/nlsr.conf
+++ b/nlsr.conf
@@ -191,6 +191,34 @@
rule
{
+ id "NLSR datasets"
+ for data
+ filter
+ {
+ type name
+ regex ^[^<nlsr>]*<nlsr>[<lsdb><routing-table>]
+ }
+ checker
+ {
+ type customized
+ sig-type ecdsa-sha256
+ key-locator
+ {
+ type name
+ hyper-relation
+ {
+ k-regex ^([^<KEY>]*)<KEY><>{1,3}$ ; router key or certificate
+ k-expand \\1
+ h-relation equal
+ p-regex ^([^<nlsr>]*)<nlsr>[<lsdb><routing-table>]
+ p-expand \\1
+ }
+ }
+ }
+ }
+
+ rule
+ {
id "NLSR Hierarchy Exception Rule"
for data
filter
diff --git a/tools/nlsrc.cpp b/tools/nlsrc.cpp
index 12900f5..9eeed3a 100644
--- a/tools/nlsrc.cpp
+++ b/tools/nlsrc.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2014-2021, The University of Memphis,
+ * Copyright (c) 2014-2022, The University of Memphis,
* Regents of the University of California,
* Arizona Board of Regents.
*
@@ -21,6 +21,7 @@
#include "nlsrc.hpp"
+#include "config.hpp"
#include "version.hpp"
#include "src/publisher/dataset-interest-handler.hpp"
@@ -33,50 +34,106 @@
#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 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 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 ndn::Name Nlsrc::RT_PREFIX = ndn::Name(Nlsrc::LOCALHOST_PREFIX).append("routing-table");
+const uint32_t ERROR_CODE_TIMEOUT = 10060;
+const uint32_t RESPONSE_CODE_SUCCESS = 200;
+const uint32_t RESPONSE_CODE_SAVE_OR_DELETE = 205;
-const uint32_t Nlsrc::ERROR_CODE_TIMEOUT = 10060;
-const uint32_t Nlsrc::RESPONSE_CODE_SUCCESS = 200;
-const uint32_t Nlsrc::RESPONSE_CODE_SAVE_OR_DELETE = 205;
-
-Nlsrc::Nlsrc(ndn::Face& face)
- : m_face(face)
+Nlsrc::Nlsrc(std::string programName, ndn::Face& face)
+ : m_programName(std::move(programName))
+ , m_routerPrefix(LOCALHOST_PREFIX)
+ , m_face(face)
{
+ disableValidator();
}
void
-Nlsrc::printUsage()
+Nlsrc::printUsage() const
{
- std::cout << "Usage:\n" << programName << " [-h] [-V] COMMAND [<Command Options>]\n"
- " -h print usage and exit\n"
- " -V print version and exit\n"
- "\n"
- " COMMAND can be one of the following:\n"
- " lsdb\n"
- " display NLSR lsdb status\n"
- " routing\n"
- " display routing table status\n"
- " status\n"
- " display all NLSR status (lsdb & routingtable)\n"
- " advertise name\n"
- " advertise a name prefix through NLSR\n"
- " advertise name save\n"
- " advertise and save the name prefix to the conf file\n"
- " withdraw name\n"
- " remove a name prefix advertised through NLSR\n"
- " withdraw name delete\n"
- " withdraw and delete the name prefix from the conf file"
- << std::endl;
+ 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(help, "@NLSRC@", m_programName);
+ std::cout << help;
+}
+
+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
@@ -92,7 +149,7 @@
m_fetchSteps.push_back(std::bind(&Nlsrc::fetchRtables, this));
m_fetchSteps.push_back(std::bind(&Nlsrc::printRT, this));
}
- else if(command == "status") {
+ 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));
@@ -103,46 +160,49 @@
}
bool
-Nlsrc::dispatch(const std::string& command)
+Nlsrc::dispatch(ndn::span<std::string> subcommand)
{
- if (command == "advertise") {
- if (nOptions < 0) {
- return false;
- }
- else if (nOptions == 1) {
- std::string saveFlag = commandLineArguments[0];
- if (saveFlag != "save") {
- return false;
- }
- }
-
- advertiseName();
- return true;
- }
- else if (command == "withdraw") {
- if (nOptions < 0) {
- return false;
- }
- else if (nOptions == 1) {
- std::string saveFlag = commandLineArguments[0];
- if (saveFlag != "delete") {
- return false;
- }
- }
-
- withdrawName();
- return true;
- }
- else if ((command == "lsdb") || (command == "routing") || (command == "status")) {
- if (nOptions != -1) {
- return false;
- }
- commandString = command;
-
- getStatus(command);
- return true;
+ 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;
}
@@ -160,33 +220,19 @@
}
void
-Nlsrc::advertiseName()
+Nlsrc::advertiseName(ndn::Name name, bool wantSave)
{
- ndn::Name name = commandLineArguments[-1];
-
- bool saveFlag = false;
- std::string info = "(Advertise: " + name.toUri() + ")";
- if (commandLineArguments[0]) {
- saveFlag = true;
- info = "(Save: " + name.toUri() + ")";
- }
+ std::string info = (wantSave ? "(Save: " : "(Advertise: ") + name.toUri() + ")";
ndn::Name::Component verb("advertise");
- sendNamePrefixUpdate(name, verb, info, saveFlag);
+ sendNamePrefixUpdate(name, verb, info, wantSave);
}
void
-Nlsrc::withdrawName()
+Nlsrc::withdrawName(ndn::Name name, bool wantDelete)
{
- ndn::Name name = commandLineArguments[-1];
-
- bool deleteFlag = false;
- std::string info = "(Withdraw: " + name.toUri() + ")";
- if (commandLineArguments[0]) {
- deleteFlag = true;
- info = "(Delete: " + name.toUri() + ")";
- }
+ std::string info = (wantDelete ? "(Delete: " : "(Withdraw: ") + name.toUri() + ")";
ndn::Name::Component verb("withdraw");
- sendNamePrefixUpdate(name, verb, info, deleteFlag);
+ sendNamePrefixUpdate(name, verb, info, wantDelete);
}
void
@@ -201,9 +247,11 @@
parameters.setFlags(1);
}
- ndn::Name commandName = NAME_UPDATE_PREFIX;
+ auto paramWire = parameters.wireEncode();
+ ndn::Name commandName = m_routerPrefix;
+ commandName.append(NAME_UPDATE_SUFFIX);
commandName.append(verb);
- commandName.append(parameters.wireEncode());
+ commandName.append(paramWire.begin(), paramWire.end());
ndn::security::InterestSigner signer(m_keyChain);
auto commandInterest = signer.makeCommandInterest(commandName,
@@ -273,14 +321,17 @@
fetchFromRt<nlsr::RoutingTableStatus>([this] (const auto& rts) { this->recordRtable(rts); });
}
-template <class T>
+template<class T>
void
Nlsrc::fetchFromLsdb(const ndn::Name::Component& datasetType,
const std::function<void(const T&)>& recordLsa)
{
- ndn::Interest interest(ndn::Name(LSDB_PREFIX).append(datasetType));
+ 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);
+ 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));
}
@@ -301,18 +352,20 @@
}
}
-template <class T>
+template<class T>
void
Nlsrc::fetchFromRt(const std::function<void(const T&)>& recordDataset)
{
- ndn::Interest interest(RT_PREFIX);
+ auto name = m_routerPrefix;
+ name.append(RT_SUFFIX);
+ ndn::Interest interest(name);
- auto fetcher = ndn::util::SegmentFetcher::start(m_face, interest, m_validator);
+ 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>
+template<class T>
void
Nlsrc::onFetchSuccess(const ndn::ConstBufferPtr& data,
const std::function<void(const T&)>& recordDataset)
@@ -406,9 +459,7 @@
main(int argc, char** argv)
{
ndn::Face face;
- nlsrc::Nlsrc nlsrc(face);
-
- nlsrc.programName = argv[0];
+ nlsrc::Nlsrc nlsrc(argv[0], face);
if (argc < 2) {
nlsrc.printUsage();
@@ -416,7 +467,9 @@
}
int opt;
- while ((opt = ::getopt(argc, argv, "hV")) != -1) {
+ const char* confFile = DEFAULT_CONFIG_FILE;
+ bool disableValidator = false;
+ while ((opt = ::getopt(argc, argv, "hVR:c:k")) != -1) {
switch (opt) {
case 'h':
nlsrc.printUsage();
@@ -424,6 +477,15 @@
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 1;
@@ -435,14 +497,17 @@
return 1;
}
+ if (nlsrc.getRouterPrefix() != nlsrc::LOCALHOST_PREFIX && !disableValidator) {
+ if (!nlsrc.enableValidator(confFile)) {
+ return 1;
+ }
+ }
+
+ std::vector<std::string> subcommand;
+ std::copy(&argv[::optind], &argv[argc], std::back_inserter(subcommand));
+
try {
- ::optind = 3; // 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]);
+ bool isOk = nlsrc.dispatch(subcommand);
if (!isOk) {
nlsrc.printUsage();
return 1;
diff --git a/tools/nlsrc.hpp b/tools/nlsrc.hpp
index c3ee7f1..99a441c 100644
--- a/tools/nlsrc.hpp
+++ b/tools/nlsrc.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2014-2020, The University of Memphis,
+ * Copyright (c) 2014-2022, The University of Memphis,
* Regents of the University of California,
* Arizona Board of Regents.
*
@@ -27,7 +27,7 @@
#include <boost/noncopyable.hpp>
#include <ndn-cxx/face.hpp>
#include <ndn-cxx/security/key-chain.hpp>
-#include <ndn-cxx/security/validator-null.hpp>
+#include <ndn-cxx/security/validator.hpp>
#include <deque>
#include <map>
@@ -42,16 +42,31 @@
{
public:
explicit
- Nlsrc(ndn::Face& face);
+ Nlsrc(std::string programName, ndn::Face& face);
void
- printUsage();
+ printUsage() const;
+
+ const ndn::Name&
+ getRouterPrefix() const
+ {
+ return m_routerPrefix;
+ }
+
+ void
+ setRouterPrefix(ndn::Name prefix);
+
+ void
+ disableValidator();
+
+ bool
+ enableValidator(const std::string& filename);
void
getStatus(const std::string& command);
bool
- dispatch(const std::string& cmd);
+ dispatch(ndn::span<std::string> subcommand);
private:
void
@@ -65,7 +80,7 @@
*
*/
void
- advertiseName();
+ advertiseName(ndn::Name name, bool wantSave);
/**
* \brief Removes a name prefix from NLSR's Name LSA
@@ -75,7 +90,7 @@
*
*/
void
- withdrawName();
+ withdrawName(ndn::Name name, bool wantDelete);
void
sendNamePrefixUpdate(const ndn::Name& name,
@@ -96,7 +111,7 @@
void
fetchNameLsas();
- template <class T>
+ template<class T>
void
fetchFromLsdb(const ndn::Name::Component& datasetType,
const std::function<void(const T&)>& recordLsa);
@@ -107,11 +122,11 @@
void
fetchRtables();
- template <class T>
+ template<class T>
void
fetchFromRt(const std::function<void(const T&)>& recordLsa);
- template <class T>
+ template<class T>
void
onFetchSuccess(const ndn::ConstBufferPtr& data,
const std::function<void(const T&)>& recordLsa);
@@ -131,41 +146,22 @@
void
printAll();
-public:
- const char* programName;
-
- // command parameters without leading 'cmd' component
- const char* const* commandLineArguments;
- int nOptions;
-
private:
+ std::string m_programName;
+ ndn::Name m_routerPrefix;
+ std::unique_ptr<ndn::security::Validator> m_validator;
+ ndn::KeyChain m_keyChain;
+ ndn::Face& m_face;
+
struct Router
{
std::string adjacencyLsaString;
std::string coordinateLsaString;
std::string nameLsaString;
};
-
std::map<ndn::Name, Router> m_routers;
-
-private:
- ndn::KeyChain m_keyChain;
- ndn::Face& m_face;
- ndn::security::ValidatorNull m_validator;
- std::string commandString;
std::string m_rtString;
-
std::deque<std::function<void()>> m_fetchSteps;
-
- static const ndn::Name LOCALHOST_PREFIX;
- static const ndn::Name LSDB_PREFIX;
- static const ndn::Name NAME_UPDATE_PREFIX;
-
- static const ndn::Name RT_PREFIX;
-
- static const uint32_t ERROR_CODE_TIMEOUT;
- static const uint32_t RESPONSE_CODE_SUCCESS;
- static const uint32_t RESPONSE_CODE_SAVE_OR_DELETE;
};
} // namespace nlsrc
diff --git a/wscript b/wscript
index b18396a..aa570ef 100644
--- a/wscript
+++ b/wscript
@@ -82,6 +82,7 @@
conf.load('sanitizers')
conf.define_cond('WITH_TESTS', conf.env.WITH_TESTS)
+ conf.define('DEFAULT_CONFIG_FILE', '%s/ndn/nlsr.conf' % conf.env.SYSCONFDIR)
# The config header will contain all defines that were added using conf.define()
# or conf.define_cond(). Everything that was added directly to conf.env.DEFINES
# will not appear in the config header, but will instead be passed directly to the