blob: 684009d09bfbcade275920e8e03ba1229635828b [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2017, The University of Memphis,
* Regents of the University of California
*
* 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/>.
*
* \author A K M Mahmudul Hoque <ahoque1@memphis.edu>
*
**/
#include "nlsr.hpp"
#include "lsdb.hpp"
#include "hello-protocol.hpp"
#include "utility/name-helper.hpp"
#include "logger.hpp"
namespace nlsr {
INIT_LOGGER("HelloProtocol");
const std::string HelloProtocol::INFO_COMPONENT = "INFO";
const std::string HelloProtocol::NLSR_COMPONENT = "NLSR";
void
HelloProtocol::expressInterest(const ndn::Name& interestName, uint32_t seconds)
{
_LOG_DEBUG("Expressing Interest :" << interestName);
ndn::Interest i(interestName);
i.setInterestLifetime(ndn::time::seconds(seconds));
i.setMustBeFresh(true);
m_nlsr.getNlsrFace().expressInterest(i,
std::bind(&HelloProtocol::onContent,
this,
_1, _2),
std::bind(&HelloProtocol::processInterestTimedOut, // Nack
this, _1),
std::bind(&HelloProtocol::processInterestTimedOut,
this, _1));
}
void
HelloProtocol::sendScheduledInterest(uint32_t seconds)
{
std::list<Adjacent> adjList = m_nlsr.getAdjacencyList().getAdjList();
for (std::list<Adjacent>::iterator it = adjList.begin(); it != adjList.end();
++it) {
// If this adjacency has a Face, just proceed as usual.
if((*it).getFaceId() != 0) {
// interest name: /<neighbor>/NLSR/INFO/<router>
ndn::Name interestName = (*it).getName() ;
interestName.append(NLSR_COMPONENT);
interestName.append(INFO_COMPONENT);
interestName.append(m_nlsr.getConfParameter().getRouterPrefix().wireEncode());
expressInterest(interestName,
m_nlsr.getConfParameter().getInterestResendTime());
_LOG_DEBUG("Sending scheduled interest: " << interestName);
}
// If it does not have a Face, we need to give it one. A
// successful registration prompts a callback that sends the hello
// Interest to the new Face.
else {
registerPrefixes((*it).getName(), (*it).getConnectingFaceUri(),
(*it).getLinkCost(), ndn::time::milliseconds::max());
}
}
scheduleInterest(m_nlsr.getConfParameter().getInfoInterestInterval());
}
void
HelloProtocol::scheduleInterest(uint32_t seconds)
{
_LOG_DEBUG("Scheduling HELLO Interests in " << ndn::time::seconds(seconds));
m_scheduler.scheduleEvent(ndn::time::seconds(seconds),
std::bind(&HelloProtocol::sendScheduledInterest, this, seconds));
}
void
HelloProtocol::processInterest(const ndn::Name& name,
const ndn::Interest& interest)
{
// interest name: /<neighbor>/NLSR/INFO/<router>
const ndn::Name interestName = interest.getName();
_LOG_DEBUG("Interest Received for Name: " << interestName);
if (interestName.get(-2).toUri() != INFO_COMPONENT) {
_LOG_DEBUG("INFO_COMPONENT not found or interestName: " << interestName
<< " does not match expression");
return;
}
ndn::Name neighbor;
neighbor.wireDecode(interestName.get(-1).blockFromValue());
_LOG_DEBUG("Neighbor: " << neighbor);
if (m_nlsr.getAdjacencyList().isNeighbor(neighbor)) {
std::shared_ptr<ndn::Data> data = std::make_shared<ndn::Data>();
data->setName(ndn::Name(interest.getName()).appendVersion());
data->setFreshnessPeriod(ndn::time::seconds(10)); // 10 sec
data->setContent(reinterpret_cast<const uint8_t*>(INFO_COMPONENT.c_str()),
INFO_COMPONENT.size());
m_nlsr.getKeyChain().sign(*data, m_nlsr.getDefaultCertName());
_LOG_DEBUG("Sending out data for name: " << interest.getName());
m_nlsr.getNlsrFace().put(*data);
Adjacent* adjacent = m_nlsr.getAdjacencyList().findAdjacent(neighbor);
// If this neighbor was previously inactive, send our own hello interest, too
if (adjacent->getStatus() == Adjacent::STATUS_INACTIVE) {
// We can only do that if the neighbor currently has a face.
if(adjacent->getFaceId() != 0){
// interest name: /<neighbor>/NLSR/INFO/<router>
ndn::Name interestName(neighbor);
interestName.append(NLSR_COMPONENT);
interestName.append(INFO_COMPONENT);
interestName.append(m_nlsr.getConfParameter().getRouterPrefix().wireEncode());
expressInterest(interestName,
m_nlsr.getConfParameter().getInterestResendTime());
}
// If the originator of the Interest currently lacks a Face, we
// need to give it one.
else {
registerPrefixes(adjacent->getName(), adjacent->getConnectingFaceUri(),
adjacent->getLinkCost(), ndn::time::milliseconds::max());
}
}
}
}
void
HelloProtocol::processInterestTimedOut(const ndn::Interest& interest)
{
// interest name: /<neighbor>/NLSR/INFO/<router>
const ndn::Name interestName(interest.getName());
_LOG_DEBUG("Interest timed out for Name: " << interestName);
if (interestName.get(-2).toUri() != INFO_COMPONENT) {
return;
}
ndn::Name neighbor = interestName.getPrefix(-3);
_LOG_DEBUG("Neighbor: " << neighbor);
m_nlsr.getAdjacencyList().incrementTimedOutInterestCount(neighbor);
Adjacent::Status status = m_nlsr.getAdjacencyList().getStatusOfNeighbor(neighbor);
uint32_t infoIntTimedOutCount =
m_nlsr.getAdjacencyList().getTimedOutInterestCount(neighbor);
_LOG_DEBUG("Status: " << status);
_LOG_DEBUG("Info Interest Timed out: " << infoIntTimedOutCount);
if ((infoIntTimedOutCount < m_nlsr.getConfParameter().getInterestRetryNumber())) {
// interest name: /<neighbor>/NLSR/INFO/<router>
ndn::Name interestName(neighbor);
interestName.append(NLSR_COMPONENT);
interestName.append(INFO_COMPONENT);
interestName.append(m_nlsr.getConfParameter().getRouterPrefix().wireEncode());
_LOG_DEBUG("Resending interest: " << interestName);
expressInterest(interestName,
m_nlsr.getConfParameter().getInterestResendTime());
}
else if ((status == Adjacent::STATUS_ACTIVE) &&
(infoIntTimedOutCount == m_nlsr.getConfParameter().getInterestRetryNumber())) {
m_nlsr.getAdjacencyList().setStatusOfNeighbor(neighbor, Adjacent::STATUS_INACTIVE);
_LOG_DEBUG("Neighbor: " << neighbor << " status changed to INACTIVE");
m_nlsr.getLsdb().scheduleAdjLsaBuild();
}
}
// This is the first function that incoming Hello data will
// see. This checks if the data appears to be signed, and passes it
// on to validate the content of the data.
void
HelloProtocol::onContent(const ndn::Interest& interest, const ndn::Data& data)
{
_LOG_DEBUG("Received data for INFO(name): " << data.getName());
if (data.getSignature().hasKeyLocator()) {
if (data.getSignature().getKeyLocator().getType() == ndn::KeyLocator::KeyLocator_Name) {
_LOG_DEBUG("Data signed with: " << data.getSignature().getKeyLocator().getName());
}
}
m_nlsr.getValidator().validate(data,
std::bind(&HelloProtocol::onContentValidated, this, _1),
std::bind(&HelloProtocol::onContentValidationFailed,
this, _1, _2));
}
// A validator is called on the incoming data, and if the data
// passes the validator's description/definitions, this function is
// called. Set the neighbor's status to active and refresh its
// LSA. If there was a change in status, we schedule an adjacency
// LSA build.
void
HelloProtocol::onContentValidated(const std::shared_ptr<const ndn::Data>& data)
{
// data name: /<neighbor>/NLSR/INFO/<router>/<version>
ndn::Name dataName = data->getName();
_LOG_DEBUG("Data validation successful for INFO(name): " << dataName);
if (dataName.get(-3).toUri() == INFO_COMPONENT) {
ndn::Name neighbor = dataName.getPrefix(-4);
Adjacent::Status oldStatus = m_nlsr.getAdjacencyList().getStatusOfNeighbor(neighbor);
m_nlsr.getAdjacencyList().setStatusOfNeighbor(neighbor, Adjacent::STATUS_ACTIVE);
m_nlsr.getAdjacencyList().setTimedOutInterestCount(neighbor, 0);
Adjacent::Status newStatus = m_nlsr.getAdjacencyList().getStatusOfNeighbor(neighbor);
_LOG_DEBUG("Neighbor : " << neighbor);
_LOG_DEBUG("Old Status: " << oldStatus << " New Status: " << newStatus);
// change in Adjacency list
if ((oldStatus - newStatus) != 0) {
if (m_nlsr.getConfParameter().getHyperbolicState() == HYPERBOLIC_STATE_ON) {
m_nlsr.getRoutingTable().scheduleRoutingTableCalculation(m_nlsr);
}
else {
m_nlsr.getLsdb().scheduleAdjLsaBuild();
}
}
}
}
// Simply logs a debug message that the content could not be
// validated (and is implicitly being discarded as a result).
void
HelloProtocol::onContentValidationFailed(const std::shared_ptr<const ndn::Data>& data,
const std::string& msg)
{
_LOG_DEBUG("Validation Error: " << msg);
}
// Asks the FIB to register the supplied adjacency (in other words,
// create a Face for it).
void
HelloProtocol::registerPrefixes(const ndn::Name& adjName, const std::string& faceUri,
double linkCost, const ndn::time::milliseconds& timeout)
{
m_nlsr.getFib().registerPrefix(adjName, faceUri, linkCost, timeout,
ndn::nfd::ROUTE_FLAG_CAPTURE, 0,
std::bind(&HelloProtocol::onRegistrationSuccess,
this, _1, adjName,timeout),
std::bind(&HelloProtocol::onRegistrationFailure,
this, _1, adjName));
}
// After we create a new Face, we need to set it up for use. This
// function sets the controlling strategy, registers prefixes in
// sync, broadcast, and LSA.
void
HelloProtocol::onRegistrationSuccess(const ndn::nfd::ControlParameters& commandSuccessResult,
const ndn::Name& neighbor,const ndn::time::milliseconds& timeout)
{
Adjacent* adjacent = m_nlsr.getAdjacencyList().findAdjacent(neighbor);
if (adjacent != 0) {
adjacent->setFaceId(commandSuccessResult.getFaceId());
ndn::Name broadcastKeyPrefix = DEFAULT_BROADCAST_PREFIX;
broadcastKeyPrefix.append("KEYS");
std::string faceUri = adjacent->getConnectingFaceUri();
double linkCost = adjacent->getLinkCost();
m_nlsr.getFib().registerPrefix(m_nlsr.getConfParameter().getChronosyncPrefix(),
faceUri, linkCost, timeout,
ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
m_nlsr.getFib().registerPrefix(m_nlsr.getConfParameter().getLsaPrefix(),
faceUri, linkCost, timeout,
ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
m_nlsr.getFib().registerPrefix(broadcastKeyPrefix,
faceUri, linkCost, timeout,
ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
// Sends a Hello Interest to determine status before the next scheduled.
// interest name: /<neighbor>/NLSR/INFO/<router>
ndn::Name interestName(neighbor);
interestName.append(NLSR_COMPONENT);
interestName.append(INFO_COMPONENT);
interestName.append(m_nlsr.getConfParameter().getRouterPrefix().wireEncode());
expressInterest(interestName,
m_nlsr.getConfParameter().getInterestResendTime());
}
}
void
HelloProtocol::onRegistrationFailure(const ndn::nfd::ControlResponse& response,
const ndn::Name& name)
{
_LOG_DEBUG(response.getText() << " (code: " << response.getCode() << ")");
/*
* If NLSR can not create face for given faceUri then it will treat this
* failure as one INFO interest timed out. So that NLSR can move on with
* building Adj Lsa and calculate routing table. NLSR does not build Adj
* Lsa unless all the neighbors are ACTIVE or DEAD. For considering the
* missconfigured(link) neighbour dead this is required.
*/
Adjacent* adjacent = m_nlsr.getAdjacencyList().findAdjacent(name);
if (adjacent != 0) {
adjacent->setInterestTimedOutNo(adjacent->getInterestTimedOutNo() + 1);
Adjacent::Status status = adjacent->getStatus();
uint32_t infoIntTimedOutCount = adjacent->getInterestTimedOutNo();
if (infoIntTimedOutCount == m_nlsr.getConfParameter().getInterestRetryNumber()) {
if (status == Adjacent::STATUS_ACTIVE) {
adjacent->setStatus(Adjacent::STATUS_INACTIVE);
}
m_nlsr.getLsdb().scheduleAdjLsaBuild();
}
}
}
} // namespace nlsr