blob: 12900f5d73506223657961c47898e2f9abe1668a [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2021, 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 "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/util/segment-fetcher.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 Nlsrc::RT_PREFIX = ndn::Name(Nlsrc::LOCALHOST_PREFIX).append("routing-table");
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)
{
}
void
Nlsrc::printUsage()
{
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;
}
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(const std::string& command)
{
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;
}
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 = commandLineArguments[-1];
bool saveFlag = false;
std::string info = "(Advertise: " + name.toUri() + ")";
if (commandLineArguments[0]) {
saveFlag = true;
info = "(Save: " + name.toUri() + ")";
}
ndn::Name::Component verb("advertise");
sendNamePrefixUpdate(name, verb, info, saveFlag);
}
void
Nlsrc::withdrawName()
{
ndn::Name name = commandLineArguments[-1];
bool deleteFlag = false;
std::string info = "(Withdraw: " + name.toUri() + ")";
if (commandLineArguments[0]) {
deleteFlag = true;
info = "(Delete: " + name.toUri() + ")";
}
ndn::Name::Component verb("withdraw");
sendNamePrefixUpdate(name, verb, info, deleteFlag);
}
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);
}
ndn::Name commandName = NAME_UPDATE_PREFIX;
commandName.append(verb);
commandName.append(parameters.wireEncode());
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;
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;
return;
}
std::cout << "Applied Name prefix update successfully: " << info << std::endl;
}
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)
{
ndn::Interest interest(ndn::Name(LSDB_PREFIX).append(datasetType));
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)
{
ndn::Interest interest(RT_PREFIX);
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& data,
const std::function<void(const T&)>& recordDataset)
{
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 data(block);
recordDataset(data);
}
runNextStep();
}
void
Nlsrc::onTimeout(uint32_t errorCode, const std::string& error)
{
std::cerr << "Request timed out (code: " << errorCode
<< ", error: " << error << ")" << std::endl;
}
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(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 = 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]);
if (!isOk) {
nlsrc.printUsage();
return 1;
}
face.processEvents();
}
catch (const std::exception& e) {
std::cerr << "ERROR: " << e.what() << std::endl;
return 2;
}
return 0;
}