FaceCounters for LpFace consist of LinkService::Counters and Transport::Counters. Old FaceCounters class from 2014 face architecture is renamed as OldFaceCounters, and can be wrapped with new FaceCounters class. OldFaceCounters is only used in old Face subclasses. This commit also updates existing face test suites to use new FaceCounters, updates ForwarderStatus so that it does not rely on FaceCounters, and moves the logic of copying face properties and counters into management structs from face system into FaceManager. refs #3177 Change-Id: I587e8a32fa72bac3af9d750b2c88342740aa08d6
668 lines
22 KiB
C++
668 lines
22 KiB
C++
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
/**
|
|
* Copyright (c) 2014-2015, Regents of the University of California,
|
|
* Arizona Board of Regents,
|
|
* Colorado State University,
|
|
* University Pierre & Marie Curie, Sorbonne University,
|
|
* Washington University in St. Louis,
|
|
* Beijing Institute of Technology,
|
|
* The University of Memphis.
|
|
*
|
|
* This file is part of NFD (Named Data Networking Forwarding Daemon).
|
|
* See AUTHORS.md for complete list of NFD authors and contributors.
|
|
*
|
|
* NFD 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.
|
|
*
|
|
* NFD 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
|
|
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "forwarder.hpp"
|
|
#include "core/logger.hpp"
|
|
#include "core/random.hpp"
|
|
#include "strategy.hpp"
|
|
#include <boost/random/uniform_int_distribution.hpp>
|
|
|
|
namespace nfd {
|
|
|
|
NFD_LOG_INIT("Forwarder");
|
|
|
|
using fw::Strategy;
|
|
|
|
const Name Forwarder::LOCALHOST_NAME("ndn:/localhost");
|
|
|
|
Forwarder::Forwarder()
|
|
: m_faceTable(*this)
|
|
, m_fib(m_nameTree)
|
|
, m_pit(m_nameTree)
|
|
, m_measurements(m_nameTree)
|
|
, m_strategyChoice(m_nameTree, fw::makeDefaultStrategy(*this))
|
|
{
|
|
fw::installStrategies(*this);
|
|
}
|
|
|
|
Forwarder::~Forwarder()
|
|
{
|
|
}
|
|
|
|
void
|
|
Forwarder::startProcessInterest(Face& face, const Interest& interest)
|
|
{
|
|
// check fields used by forwarding are well-formed
|
|
try {
|
|
if (interest.hasLink()) {
|
|
interest.getLink();
|
|
}
|
|
}
|
|
catch (const tlv::Error&) {
|
|
NFD_LOG_DEBUG("startProcessInterest face=" << face.getId() <<
|
|
" interest=" << interest.getName() << " malformed");
|
|
// It's safe to call interest.getName() because Name has been fully parsed
|
|
return;
|
|
}
|
|
|
|
this->onIncomingInterest(face, interest);
|
|
}
|
|
|
|
void
|
|
Forwarder::startProcessData(Face& face, const Data& data)
|
|
{
|
|
// check fields used by forwarding are well-formed
|
|
// (none needed)
|
|
|
|
this->onIncomingData(face, data);
|
|
}
|
|
|
|
void
|
|
Forwarder::startProcessNack(Face& face, const lp::Nack& nack)
|
|
{
|
|
// check fields used by forwarding are well-formed
|
|
try {
|
|
if (nack.getInterest().hasLink()) {
|
|
nack.getInterest().getLink();
|
|
}
|
|
}
|
|
catch (const tlv::Error&) {
|
|
NFD_LOG_DEBUG("startProcessNack face=" << face.getId() <<
|
|
" nack=" << nack.getInterest().getName() <<
|
|
"~" << nack.getReason() << " malformed");
|
|
return;
|
|
}
|
|
|
|
this->onIncomingNack(face, nack);
|
|
}
|
|
|
|
void
|
|
Forwarder::onIncomingInterest(Face& inFace, const Interest& interest)
|
|
{
|
|
// receive Interest
|
|
NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId() <<
|
|
" interest=" << interest.getName());
|
|
const_cast<Interest&>(interest).setIncomingFaceId(inFace.getId());
|
|
++m_counters.nInInterests;
|
|
|
|
// /localhost scope control
|
|
bool isViolatingLocalhost = !inFace.isLocal() &&
|
|
LOCALHOST_NAME.isPrefixOf(interest.getName());
|
|
if (isViolatingLocalhost) {
|
|
NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId() <<
|
|
" interest=" << interest.getName() << " violates /localhost");
|
|
// (drop)
|
|
return;
|
|
}
|
|
|
|
// PIT insert
|
|
shared_ptr<pit::Entry> pitEntry = m_pit.insert(interest).first;
|
|
|
|
// detect duplicate Nonce
|
|
int dnw = pitEntry->findNonce(interest.getNonce(), inFace);
|
|
bool hasDuplicateNonce = (dnw != pit::DUPLICATE_NONCE_NONE) ||
|
|
m_deadNonceList.has(interest.getName(), interest.getNonce());
|
|
if (hasDuplicateNonce) {
|
|
// goto Interest loop pipeline
|
|
this->onInterestLoop(inFace, interest, pitEntry);
|
|
return;
|
|
}
|
|
|
|
// cancel unsatisfy & straggler timer
|
|
this->cancelUnsatisfyAndStragglerTimer(pitEntry);
|
|
|
|
// is pending?
|
|
const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
|
|
bool isPending = inRecords.begin() != inRecords.end();
|
|
if (!isPending) {
|
|
m_cs.find(interest,
|
|
bind(&Forwarder::onContentStoreHit, this, ref(inFace), pitEntry, _1, _2),
|
|
bind(&Forwarder::onContentStoreMiss, this, ref(inFace), pitEntry, _1));
|
|
}
|
|
else {
|
|
this->onContentStoreMiss(inFace, pitEntry, interest);
|
|
}
|
|
}
|
|
|
|
void
|
|
Forwarder::onInterestLoop(Face& inFace, const Interest& interest,
|
|
shared_ptr<pit::Entry> pitEntry)
|
|
{
|
|
// if multi-access face, drop
|
|
if (inFace.isMultiAccess()) {
|
|
NFD_LOG_DEBUG("onInterestLoop face=" << inFace.getId() <<
|
|
" interest=" << interest.getName() <<
|
|
" drop");
|
|
return;
|
|
}
|
|
|
|
NFD_LOG_DEBUG("onInterestLoop face=" << inFace.getId() <<
|
|
" interest=" << interest.getName() <<
|
|
" send-Nack-duplicate");
|
|
|
|
// send Nack with reason=DUPLICATE
|
|
// note: Don't enter outgoing Nack pipeline because it needs an in-record.
|
|
lp::Nack nack(interest);
|
|
nack.setReason(lp::NackReason::DUPLICATE);
|
|
inFace.sendNack(nack);
|
|
}
|
|
|
|
void
|
|
Forwarder::onContentStoreMiss(const Face& inFace,
|
|
shared_ptr<pit::Entry> pitEntry,
|
|
const Interest& interest)
|
|
{
|
|
NFD_LOG_DEBUG("onContentStoreMiss interest=" << interest.getName());
|
|
|
|
shared_ptr<Face> face = const_pointer_cast<Face>(inFace.shared_from_this());
|
|
// insert InRecord
|
|
pitEntry->insertOrUpdateInRecord(face, interest);
|
|
|
|
// set PIT unsatisfy timer
|
|
this->setUnsatisfyTimer(pitEntry);
|
|
|
|
shared_ptr<fib::Entry> fibEntry;
|
|
// has Link object?
|
|
if (!interest.hasLink()) {
|
|
// FIB lookup with Interest name
|
|
fibEntry = m_fib.findLongestPrefixMatch(*pitEntry);
|
|
NFD_LOG_TRACE("onContentStoreMiss noLinkObject");
|
|
}
|
|
else {
|
|
const Link& link = interest.getLink();
|
|
|
|
// in producer region?
|
|
if (m_networkRegionTable.isInProducerRegion(link)) {
|
|
// FIB lookup with Interest name
|
|
fibEntry = m_fib.findLongestPrefixMatch(*pitEntry);
|
|
NFD_LOG_TRACE("onContentStoreMiss inProducerRegion");
|
|
}
|
|
// has SelectedDelegation?
|
|
else if (interest.hasSelectedDelegation()) {
|
|
// FIB lookup with SelectedDelegation
|
|
fibEntry = m_fib.findLongestPrefixMatch(interest.getSelectedDelegation());
|
|
NFD_LOG_TRACE("onContentStoreMiss hasSelectedDelegation=" << interest.getSelectedDelegation());
|
|
}
|
|
else {
|
|
// FIB lookup with first delegation Name
|
|
fibEntry = m_fib.findLongestPrefixMatch(link.getDelegations().begin()->second);
|
|
|
|
// in default-free zone?
|
|
bool isDefaultFreeZone = !(fibEntry->getPrefix().size() == 0 && fibEntry->hasNextHops());
|
|
if (isDefaultFreeZone) {
|
|
// choose and set SelectedDelegation
|
|
for (const std::pair<uint32_t, Name>& delegation : link.getDelegations()) {
|
|
const Name& delegationName = delegation.second;
|
|
fibEntry = m_fib.findLongestPrefixMatch(delegationName);
|
|
if (fibEntry->hasNextHops()) {
|
|
const_cast<Interest&>(interest).setSelectedDelegation(delegationName);
|
|
NFD_LOG_TRACE("onContentStoreMiss enterDefaultFreeZone"
|
|
<< " setSelectedDelegation=" << delegationName);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
NFD_LOG_TRACE("onContentStoreMiss inConsumerRegion");
|
|
}
|
|
}
|
|
}
|
|
|
|
// dispatch to strategy
|
|
BOOST_ASSERT(fibEntry != nullptr);
|
|
this->dispatchToStrategy(pitEntry, bind(&Strategy::afterReceiveInterest, _1,
|
|
cref(inFace), cref(interest), fibEntry, pitEntry));
|
|
}
|
|
|
|
void
|
|
Forwarder::onContentStoreHit(const Face& inFace,
|
|
shared_ptr<pit::Entry> pitEntry,
|
|
const Interest& interest,
|
|
const Data& data)
|
|
{
|
|
NFD_LOG_DEBUG("onContentStoreHit interest=" << interest.getName());
|
|
|
|
const_pointer_cast<Data>(data.shared_from_this())->setIncomingFaceId(FACEID_CONTENT_STORE);
|
|
// XXX should we lookup PIT for other Interests that also match csMatch?
|
|
|
|
// set PIT straggler timer
|
|
this->setStragglerTimer(pitEntry, true, data.getFreshnessPeriod());
|
|
|
|
// goto outgoing Data pipeline
|
|
this->onOutgoingData(data, *const_pointer_cast<Face>(inFace.shared_from_this()));
|
|
}
|
|
|
|
/** \brief compare two InRecords for picking outgoing Interest
|
|
* \return true if b is preferred over a
|
|
*
|
|
* This function should be passed to std::max_element over InRecordCollection.
|
|
* The outgoing Interest picked is the last incoming Interest
|
|
* that does not come from outFace.
|
|
* If all InRecords come from outFace, it's fine to pick that. This happens when
|
|
* there's only one InRecord that comes from outFace. The legit use is for
|
|
* vehicular network; otherwise, strategy shouldn't send to the sole inFace.
|
|
*/
|
|
static inline bool
|
|
compare_pickInterest(const pit::InRecord& a, const pit::InRecord& b, const Face* outFace)
|
|
{
|
|
bool isOutFaceA = a.getFace().get() == outFace;
|
|
bool isOutFaceB = b.getFace().get() == outFace;
|
|
|
|
if (!isOutFaceA && isOutFaceB) {
|
|
return false;
|
|
}
|
|
if (isOutFaceA && !isOutFaceB) {
|
|
return true;
|
|
}
|
|
|
|
return a.getLastRenewed() > b.getLastRenewed();
|
|
}
|
|
|
|
void
|
|
Forwarder::onOutgoingInterest(shared_ptr<pit::Entry> pitEntry, Face& outFace,
|
|
bool wantNewNonce)
|
|
{
|
|
if (outFace.getId() == INVALID_FACEID) {
|
|
NFD_LOG_WARN("onOutgoingInterest face=invalid interest=" << pitEntry->getName());
|
|
return;
|
|
}
|
|
NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId() <<
|
|
" interest=" << pitEntry->getName());
|
|
|
|
// scope control
|
|
if (pitEntry->violatesScope(outFace)) {
|
|
NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId() <<
|
|
" interest=" << pitEntry->getName() << " violates scope");
|
|
return;
|
|
}
|
|
|
|
// pick Interest
|
|
const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
|
|
pit::InRecordCollection::const_iterator pickedInRecord = std::max_element(
|
|
inRecords.begin(), inRecords.end(), bind(&compare_pickInterest, _1, _2, &outFace));
|
|
BOOST_ASSERT(pickedInRecord != inRecords.end());
|
|
shared_ptr<Interest> interest = const_pointer_cast<Interest>(
|
|
pickedInRecord->getInterest().shared_from_this());
|
|
|
|
if (wantNewNonce) {
|
|
interest = make_shared<Interest>(*interest);
|
|
static boost::random::uniform_int_distribution<uint32_t> dist;
|
|
interest->setNonce(dist(getGlobalRng()));
|
|
}
|
|
|
|
// insert OutRecord
|
|
pitEntry->insertOrUpdateOutRecord(outFace.shared_from_this(), *interest);
|
|
|
|
// send Interest
|
|
outFace.sendInterest(*interest);
|
|
++m_counters.nOutInterests;
|
|
}
|
|
|
|
void
|
|
Forwarder::onInterestReject(shared_ptr<pit::Entry> pitEntry)
|
|
{
|
|
if (pitEntry->hasUnexpiredOutRecords()) {
|
|
NFD_LOG_ERROR("onInterestReject interest=" << pitEntry->getName() <<
|
|
" cannot reject forwarded Interest");
|
|
return;
|
|
}
|
|
NFD_LOG_DEBUG("onInterestReject interest=" << pitEntry->getName());
|
|
|
|
// cancel unsatisfy & straggler timer
|
|
this->cancelUnsatisfyAndStragglerTimer(pitEntry);
|
|
|
|
// set PIT straggler timer
|
|
this->setStragglerTimer(pitEntry, false);
|
|
}
|
|
|
|
void
|
|
Forwarder::onInterestUnsatisfied(shared_ptr<pit::Entry> pitEntry)
|
|
{
|
|
NFD_LOG_DEBUG("onInterestUnsatisfied interest=" << pitEntry->getName());
|
|
|
|
// invoke PIT unsatisfied callback
|
|
this->dispatchToStrategy(pitEntry, bind(&Strategy::beforeExpirePendingInterest, _1,
|
|
pitEntry));
|
|
|
|
// goto Interest Finalize pipeline
|
|
this->onInterestFinalize(pitEntry, false);
|
|
}
|
|
|
|
void
|
|
Forwarder::onInterestFinalize(shared_ptr<pit::Entry> pitEntry, bool isSatisfied,
|
|
const time::milliseconds& dataFreshnessPeriod)
|
|
{
|
|
NFD_LOG_DEBUG("onInterestFinalize interest=" << pitEntry->getName() <<
|
|
(isSatisfied ? " satisfied" : " unsatisfied"));
|
|
|
|
// Dead Nonce List insert if necessary
|
|
this->insertDeadNonceList(*pitEntry, isSatisfied, dataFreshnessPeriod, 0);
|
|
|
|
// PIT delete
|
|
this->cancelUnsatisfyAndStragglerTimer(pitEntry);
|
|
m_pit.erase(pitEntry);
|
|
}
|
|
|
|
void
|
|
Forwarder::onIncomingData(Face& inFace, const Data& data)
|
|
{
|
|
// receive Data
|
|
NFD_LOG_DEBUG("onIncomingData face=" << inFace.getId() << " data=" << data.getName());
|
|
const_cast<Data&>(data).setIncomingFaceId(inFace.getId());
|
|
++m_counters.nInData;
|
|
|
|
// /localhost scope control
|
|
bool isViolatingLocalhost = !inFace.isLocal() &&
|
|
LOCALHOST_NAME.isPrefixOf(data.getName());
|
|
if (isViolatingLocalhost) {
|
|
NFD_LOG_DEBUG("onIncomingData face=" << inFace.getId() <<
|
|
" data=" << data.getName() << " violates /localhost");
|
|
// (drop)
|
|
return;
|
|
}
|
|
|
|
// PIT match
|
|
pit::DataMatchResult pitMatches = m_pit.findAllDataMatches(data);
|
|
if (pitMatches.begin() == pitMatches.end()) {
|
|
// goto Data unsolicited pipeline
|
|
this->onDataUnsolicited(inFace, data);
|
|
return;
|
|
}
|
|
|
|
// CS insert
|
|
m_cs.insert(data);
|
|
|
|
std::set<Face*> pendingDownstreams;
|
|
// foreach PitEntry
|
|
for (const shared_ptr<pit::Entry>& pitEntry : pitMatches) {
|
|
NFD_LOG_DEBUG("onIncomingData matching=" << pitEntry->getName());
|
|
|
|
// cancel unsatisfy & straggler timer
|
|
this->cancelUnsatisfyAndStragglerTimer(pitEntry);
|
|
|
|
// remember pending downstreams
|
|
const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
|
|
for (const pit::InRecord& inRecord : inRecords) {
|
|
if (inRecord.getExpiry() > time::steady_clock::now()) {
|
|
pendingDownstreams.insert(inRecord.getFace().get());
|
|
}
|
|
}
|
|
|
|
// invoke PIT satisfy callback
|
|
this->dispatchToStrategy(pitEntry, bind(&Strategy::beforeSatisfyInterest, _1,
|
|
pitEntry, cref(inFace), cref(data)));
|
|
|
|
// Dead Nonce List insert if necessary (for OutRecord of inFace)
|
|
this->insertDeadNonceList(*pitEntry, true, data.getFreshnessPeriod(), &inFace);
|
|
|
|
// mark PIT satisfied
|
|
pitEntry->deleteInRecords();
|
|
pitEntry->deleteOutRecord(inFace);
|
|
|
|
// set PIT straggler timer
|
|
this->setStragglerTimer(pitEntry, true, data.getFreshnessPeriod());
|
|
}
|
|
|
|
// foreach pending downstream
|
|
for (Face* pendingDownstream : pendingDownstreams) {
|
|
if (pendingDownstream == &inFace) {
|
|
continue;
|
|
}
|
|
// goto outgoing Data pipeline
|
|
this->onOutgoingData(data, *pendingDownstream);
|
|
}
|
|
}
|
|
|
|
void
|
|
Forwarder::onDataUnsolicited(Face& inFace, const Data& data)
|
|
{
|
|
// accept to cache?
|
|
bool acceptToCache = inFace.isLocal();
|
|
if (acceptToCache) {
|
|
// CS insert
|
|
m_cs.insert(data, true);
|
|
}
|
|
|
|
NFD_LOG_DEBUG("onDataUnsolicited face=" << inFace.getId() <<
|
|
" data=" << data.getName() <<
|
|
(acceptToCache ? " cached" : " not cached"));
|
|
}
|
|
|
|
void
|
|
Forwarder::onOutgoingData(const Data& data, Face& outFace)
|
|
{
|
|
if (outFace.getId() == INVALID_FACEID) {
|
|
NFD_LOG_WARN("onOutgoingData face=invalid data=" << data.getName());
|
|
return;
|
|
}
|
|
NFD_LOG_DEBUG("onOutgoingData face=" << outFace.getId() << " data=" << data.getName());
|
|
|
|
// /localhost scope control
|
|
bool isViolatingLocalhost = !outFace.isLocal() &&
|
|
LOCALHOST_NAME.isPrefixOf(data.getName());
|
|
if (isViolatingLocalhost) {
|
|
NFD_LOG_DEBUG("onOutgoingData face=" << outFace.getId() <<
|
|
" data=" << data.getName() << " violates /localhost");
|
|
// (drop)
|
|
return;
|
|
}
|
|
|
|
// TODO traffic manager
|
|
|
|
// send Data
|
|
outFace.sendData(data);
|
|
++m_counters.nOutData;
|
|
}
|
|
|
|
void
|
|
Forwarder::onIncomingNack(Face& inFace, const lp::Nack& nack)
|
|
{
|
|
++m_counters.nInNacks;
|
|
|
|
// if multi-access face, drop
|
|
if (inFace.isMultiAccess()) {
|
|
NFD_LOG_DEBUG("onIncomingNack face=" << inFace.getId() <<
|
|
" nack=" << nack.getInterest().getName() <<
|
|
"~" << nack.getReason() << " face-is-multi-access");
|
|
return;
|
|
}
|
|
|
|
// PIT match
|
|
shared_ptr<pit::Entry> pitEntry = m_pit.find(nack.getInterest());
|
|
// if no PIT entry found, drop
|
|
if (pitEntry == nullptr) {
|
|
NFD_LOG_DEBUG("onIncomingNack face=" << inFace.getId() <<
|
|
" nack=" << nack.getInterest().getName() <<
|
|
"~" << nack.getReason() << " no-PIT-entry");
|
|
return;
|
|
}
|
|
|
|
// has out-record?
|
|
pit::OutRecordCollection::iterator outRecord = pitEntry->getOutRecord(inFace);
|
|
// if no out-record found, drop
|
|
if (outRecord == pitEntry->getOutRecords().end()) {
|
|
NFD_LOG_DEBUG("onIncomingNack face=" << inFace.getId() <<
|
|
" nack=" << nack.getInterest().getName() <<
|
|
"~" << nack.getReason() << " no-out-record");
|
|
return;
|
|
}
|
|
|
|
// if out-record has different Nonce, drop
|
|
if (nack.getInterest().getNonce() != outRecord->getLastNonce()) {
|
|
NFD_LOG_DEBUG("onIncomingNack face=" << inFace.getId() <<
|
|
" nack=" << nack.getInterest().getName() <<
|
|
"~" << nack.getReason() << " wrong-Nonce " <<
|
|
nack.getInterest().getNonce() << "!=" << outRecord->getLastNonce());
|
|
return;
|
|
}
|
|
|
|
NFD_LOG_DEBUG("onIncomingNack face=" << inFace.getId() <<
|
|
" nack=" << nack.getInterest().getName() <<
|
|
"~" << nack.getReason() << " OK");
|
|
|
|
// record Nack on out-record
|
|
outRecord->setIncomingNack(nack);
|
|
|
|
// trigger strategy: after receive NACK
|
|
shared_ptr<fib::Entry> fibEntry = m_fib.findLongestPrefixMatch(*pitEntry);
|
|
this->dispatchToStrategy(pitEntry, bind(&Strategy::afterReceiveNack, _1,
|
|
cref(inFace), cref(nack), fibEntry, pitEntry));
|
|
}
|
|
|
|
void
|
|
Forwarder::onOutgoingNack(shared_ptr<pit::Entry> pitEntry, const Face& outFace,
|
|
const lp::NackHeader& nack)
|
|
{
|
|
if (outFace.getId() == INVALID_FACEID) {
|
|
NFD_LOG_WARN("onOutgoingNack face=invalid" <<
|
|
" nack=" << pitEntry->getInterest().getName() <<
|
|
"~" << nack.getReason() << " no-in-record");
|
|
return;
|
|
}
|
|
|
|
// has in-record?
|
|
pit::InRecordCollection::const_iterator inRecord = pitEntry->getInRecord(outFace);
|
|
|
|
// if no in-record found, drop
|
|
if (inRecord == pitEntry->getInRecords().end()) {
|
|
NFD_LOG_DEBUG("onOutgoingNack face=" << outFace.getId() <<
|
|
" nack=" << pitEntry->getInterest().getName() <<
|
|
"~" << nack.getReason() << " no-in-record");
|
|
return;
|
|
}
|
|
|
|
// if multi-access face, drop
|
|
if (outFace.isMultiAccess()) {
|
|
NFD_LOG_DEBUG("onOutgoingNack face=" << outFace.getId() <<
|
|
" nack=" << pitEntry->getInterest().getName() <<
|
|
"~" << nack.getReason() << " face-is-multi-access");
|
|
return;
|
|
}
|
|
|
|
NFD_LOG_DEBUG("onOutgoingNack face=" << outFace.getId() <<
|
|
" nack=" << pitEntry->getInterest().getName() <<
|
|
"~" << nack.getReason() << " OK");
|
|
|
|
// create Nack packet with the Interest from in-record
|
|
lp::Nack nackPkt(inRecord->getInterest());
|
|
nackPkt.setHeader(nack);
|
|
|
|
// erase in-record
|
|
pitEntry->deleteInRecord(outFace);
|
|
|
|
// send Nack on face
|
|
const_cast<Face&>(outFace).sendNack(nackPkt);
|
|
++m_counters.nOutNacks;
|
|
}
|
|
|
|
static inline bool
|
|
compare_InRecord_expiry(const pit::InRecord& a, const pit::InRecord& b)
|
|
{
|
|
return a.getExpiry() < b.getExpiry();
|
|
}
|
|
|
|
void
|
|
Forwarder::setUnsatisfyTimer(shared_ptr<pit::Entry> pitEntry)
|
|
{
|
|
const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
|
|
pit::InRecordCollection::const_iterator lastExpiring =
|
|
std::max_element(inRecords.begin(), inRecords.end(),
|
|
&compare_InRecord_expiry);
|
|
|
|
time::steady_clock::TimePoint lastExpiry = lastExpiring->getExpiry();
|
|
time::nanoseconds lastExpiryFromNow = lastExpiry - time::steady_clock::now();
|
|
if (lastExpiryFromNow <= time::seconds(0)) {
|
|
// TODO all InRecords are already expired; will this happen?
|
|
}
|
|
|
|
scheduler::cancel(pitEntry->m_unsatisfyTimer);
|
|
pitEntry->m_unsatisfyTimer = scheduler::schedule(lastExpiryFromNow,
|
|
bind(&Forwarder::onInterestUnsatisfied, this, pitEntry));
|
|
}
|
|
|
|
void
|
|
Forwarder::setStragglerTimer(shared_ptr<pit::Entry> pitEntry, bool isSatisfied,
|
|
const time::milliseconds& dataFreshnessPeriod)
|
|
{
|
|
time::nanoseconds stragglerTime = time::milliseconds(100);
|
|
|
|
scheduler::cancel(pitEntry->m_stragglerTimer);
|
|
pitEntry->m_stragglerTimer = scheduler::schedule(stragglerTime,
|
|
bind(&Forwarder::onInterestFinalize, this, pitEntry, isSatisfied, dataFreshnessPeriod));
|
|
}
|
|
|
|
void
|
|
Forwarder::cancelUnsatisfyAndStragglerTimer(shared_ptr<pit::Entry> pitEntry)
|
|
{
|
|
scheduler::cancel(pitEntry->m_unsatisfyTimer);
|
|
scheduler::cancel(pitEntry->m_stragglerTimer);
|
|
}
|
|
|
|
static inline void
|
|
insertNonceToDnl(DeadNonceList& dnl, const pit::Entry& pitEntry,
|
|
const pit::OutRecord& outRecord)
|
|
{
|
|
dnl.add(pitEntry.getName(), outRecord.getLastNonce());
|
|
}
|
|
|
|
void
|
|
Forwarder::insertDeadNonceList(pit::Entry& pitEntry, bool isSatisfied,
|
|
const time::milliseconds& dataFreshnessPeriod,
|
|
Face* upstream)
|
|
{
|
|
// need Dead Nonce List insert?
|
|
bool needDnl = false;
|
|
if (isSatisfied) {
|
|
bool hasFreshnessPeriod = dataFreshnessPeriod >= time::milliseconds::zero();
|
|
// Data never becomes stale if it doesn't have FreshnessPeriod field
|
|
needDnl = static_cast<bool>(pitEntry.getInterest().getMustBeFresh()) &&
|
|
(hasFreshnessPeriod && dataFreshnessPeriod < m_deadNonceList.getLifetime());
|
|
}
|
|
else {
|
|
needDnl = true;
|
|
}
|
|
|
|
if (!needDnl) {
|
|
return;
|
|
}
|
|
|
|
// Dead Nonce List insert
|
|
if (upstream == 0) {
|
|
// insert all outgoing Nonces
|
|
const pit::OutRecordCollection& outRecords = pitEntry.getOutRecords();
|
|
std::for_each(outRecords.begin(), outRecords.end(),
|
|
bind(&insertNonceToDnl, ref(m_deadNonceList), cref(pitEntry), _1));
|
|
}
|
|
else {
|
|
// insert outgoing Nonce of a specific face
|
|
pit::OutRecordCollection::const_iterator outRecord = pitEntry.getOutRecord(*upstream);
|
|
if (outRecord != pitEntry.getOutRecords().end()) {
|
|
m_deadNonceList.add(pitEntry.getName(), outRecord->getLastNonce());
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace nfd
|