36 #include <ndn-cxx/lp/pit-token.hpp>
37 #include <ndn-cxx/lp/tags.hpp>
52 : m_faceTable(faceTable)
56 , m_measurements(m_nameTree)
57 , m_strategyChoice(*this)
61 [
this, &face] (
const Interest& interest,
const EndpointId& endpointId) {
62 this->onIncomingInterest(interest,
FaceEndpoint(
const_cast<Face&
>(face), endpointId));
65 [
this, &face] (
const Data& data,
const EndpointId& endpointId) {
66 this->onIncomingData(data,
FaceEndpoint(
const_cast<Face&
>(face), endpointId));
69 [
this, &face] (
const lp::Nack& nack,
const EndpointId& endpointId) {
70 this->onIncomingNack(nack,
FaceEndpoint(
const_cast<Face&
>(face), endpointId));
73 [
this, &face] (
const Interest& interest) {
74 this->onDroppedInterest(interest,
const_cast<Face&
>(face));
83 this->onNewNextHop(prefix, nextHop);
90 Forwarder::onIncomingInterest(
const Interest& interest,
const FaceEndpoint& ingress)
92 interest.setTag(make_shared<lp::IncomingFaceIdTag>(ingress.
face.
getId()));
96 auto nonce = interest.getNonce();
97 auto hopLimit = interest.getHopLimit();
101 NFD_LOG_DEBUG(
"onIncomingInterest in=" << ingress <<
" interest=" << interest.getName()
102 <<
" nonce=" << nonce <<
" hop-limit=" <<
static_cast<unsigned>(*hopLimit));
103 if (*hopLimit == 0) {
108 const_cast<Interest&
>(interest).setHopLimit(*hopLimit - 1);
111 NFD_LOG_DEBUG(
"onIncomingInterest in=" << ingress <<
" interest=" << interest.getName()
112 <<
" nonce=" << nonce);
116 bool isViolatingLocalhost = ingress.
face.
getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL &&
118 if (isViolatingLocalhost) {
119 NFD_LOG_DEBUG(
"onIncomingInterest in=" << ingress <<
" interest=" << interest.getName()
120 <<
" nonce=" << nonce <<
" violates /localhost");
126 bool hasDuplicateNonceInDnl = m_deadNonceList.
has(interest.getName(), nonce);
127 if (hasDuplicateNonceInDnl) {
129 this->onInterestLoop(interest, ingress);
134 if (!interest.getForwardingHint().empty() &&
136 NFD_LOG_DEBUG(
"onIncomingInterest in=" << ingress <<
" interest=" << interest.getName()
137 <<
" nonce=" << nonce <<
" reaching-producer-region");
138 const_cast<Interest&
>(interest).setForwardingHint({});
142 shared_ptr<pit::Entry> pitEntry = m_pit.
insert(interest).first;
147 if (ingress.
face.
getLinkType() == ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
151 if (hasDuplicateNonceInPit) {
153 this->onInterestLoop(interest, ingress);
158 if (!pitEntry->hasInRecords()) {
160 [=] (
const Interest& i,
const Data& d) { onContentStoreHit(i, ingress, pitEntry, d); },
161 [=] (
const Interest& i) { onContentStoreMiss(i, ingress, pitEntry); });
164 this->onContentStoreMiss(interest, ingress, pitEntry);
169 Forwarder::onInterestLoop(
const Interest& interest,
const FaceEndpoint& ingress)
172 if (ingress.face.getLinkType() != ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
173 NFD_LOG_DEBUG(
"onInterestLoop in=" << ingress <<
" interest=" << interest.getName()
174 <<
" nonce=" << interest.getNonce() <<
" drop");
178 NFD_LOG_DEBUG(
"onInterestLoop in=" << ingress <<
" interest=" << interest.getName()
179 <<
" nonce=" << interest.getNonce());
186 Forwarder::onContentStoreMiss(
const Interest& interest,
const FaceEndpoint& ingress,
187 const shared_ptr<pit::Entry>& pitEntry)
189 NFD_LOG_DEBUG(
"onContentStoreMiss interest=" << interest.getName() <<
" nonce=" << interest.getNonce());
193 if (m_config.defaultHopLimit > 0 && !interest.getHopLimit()) {
194 const_cast<Interest&
>(interest).setHopLimit(m_config.defaultHopLimit);
198 pitEntry->insertOrUpdateInRecord(ingress.face, interest);
201 auto lastExpiring = std::max_element(pitEntry->in_begin(), pitEntry->in_end(),
202 [] (
const auto& a,
const auto& b) {
203 return a.getExpiry() < b.getExpiry();
205 auto lastExpiryFromNow = lastExpiring->getExpiry() - time::steady_clock::now();
206 this->setExpiryTimer(pitEntry, time::duration_cast<time::milliseconds>(lastExpiryFromNow));
209 auto nextHopTag = interest.getTag<lp::NextHopFaceIdTag>();
210 if (nextHopTag !=
nullptr) {
212 Face* nextHopFace = m_faceTable.
get(*nextHopTag);
213 if (nextHopFace !=
nullptr) {
214 NFD_LOG_DEBUG(
"onContentStoreMiss interest=" << interest.getName()
215 <<
" nonce=" << interest.getNonce() <<
" nexthop-faceid=" << nextHopFace->getId());
218 this->onOutgoingInterest(interest, *nextHopFace, pitEntry);
229 Forwarder::onContentStoreHit(
const Interest& interest,
const FaceEndpoint& ingress,
230 const shared_ptr<pit::Entry>& pitEntry,
const Data& data)
232 NFD_LOG_DEBUG(
"onContentStoreHit interest=" << interest.getName() <<
" nonce=" << interest.getNonce());
236 data.setTag(interest.getTag<lp::PitToken>());
239 pitEntry->isSatisfied =
true;
240 pitEntry->dataFreshnessPeriod = data.getFreshnessPeriod();
243 this->setExpiryTimer(pitEntry, 0_ms);
250 Forwarder::onOutgoingInterest(
const Interest& interest, Face& egress,
251 const shared_ptr<pit::Entry>& pitEntry)
254 if (interest.getHopLimit() == 0 && egress.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL) {
255 NFD_LOG_DEBUG(
"onOutgoingInterest out=" << egress.getId() <<
" interest=" << interest.getName()
256 <<
" nonce=" << interest.getNonce() <<
" non-local hop-limit=0");
257 ++egress.getCounters().nOutHopLimitZero;
261 NFD_LOG_DEBUG(
"onOutgoingInterest out=" << egress.getId() <<
" interest=" << interest.getName()
262 <<
" nonce=" << interest.getNonce());
265 auto it = pitEntry->insertOrUpdateOutRecord(egress, interest);
266 BOOST_ASSERT(it != pitEntry->out_end());
269 egress.sendInterest(interest);
276 Forwarder::onInterestFinalize(
const shared_ptr<pit::Entry>& pitEntry)
278 NFD_LOG_DEBUG(
"onInterestFinalize interest=" << pitEntry->getName()
279 << (pitEntry->isSatisfied ?
" satisfied" :
" unsatisfied"));
282 this->insertDeadNonceList(*pitEntry,
nullptr);
285 if (pitEntry->isSatisfied) {
293 pitEntry->expiryTimer.cancel();
294 m_pit.
erase(pitEntry.get());
298 Forwarder::onIncomingData(
const Data& data,
const FaceEndpoint& ingress)
300 data.setTag(make_shared<lp::IncomingFaceIdTag>(ingress.face.getId()));
302 NFD_LOG_DEBUG(
"onIncomingData in=" << ingress <<
" data=" << data.getName());
305 bool isViolatingLocalhost = ingress.face.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL &&
307 if (isViolatingLocalhost) {
308 NFD_LOG_DEBUG(
"onIncomingData in=" << ingress <<
" data=" << data.getName() <<
" violates /localhost");
315 if (pitMatches.size() == 0) {
317 this->onDataUnsolicited(data, ingress);
325 if (pitMatches.size() == 1) {
326 auto& pitEntry = pitMatches.front();
328 NFD_LOG_DEBUG(
"onIncomingData matching=" << pitEntry->getName());
331 this->setExpiryTimer(pitEntry, 0_ms);
337 pitEntry->isSatisfied =
true;
338 pitEntry->dataFreshnessPeriod = data.getFreshnessPeriod();
341 this->insertDeadNonceList(*pitEntry, &ingress.face);
344 pitEntry->deleteOutRecord(ingress.face);
349 std::set<Face*> pendingDownstreams;
350 auto now = time::steady_clock::now();
352 for (
const auto& pitEntry : pitMatches) {
353 NFD_LOG_DEBUG(
"onIncomingData matching=" << pitEntry->getName());
356 for (
const pit::InRecord& inRecord : pitEntry->getInRecords()) {
357 if (inRecord.getExpiry() > now) {
358 pendingDownstreams.insert(&inRecord.getFace());
363 this->setExpiryTimer(pitEntry, 0_ms);
369 pitEntry->isSatisfied =
true;
370 pitEntry->dataFreshnessPeriod = data.getFreshnessPeriod();
373 this->insertDeadNonceList(*pitEntry, &ingress.face);
376 pitEntry->clearInRecords();
377 pitEntry->deleteOutRecord(ingress.face);
380 for (Face* pendingDownstream : pendingDownstreams) {
381 if (pendingDownstream->getId() == ingress.face.getId() &&
382 pendingDownstream->getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) {
386 this->onOutgoingData(data, *pendingDownstream);
392 Forwarder::onDataUnsolicited(
const Data& data,
const FaceEndpoint& ingress)
397 auto decision = m_unsolicitedDataPolicy->decide(ingress.face, data);
398 NFD_LOG_DEBUG(
"onDataUnsolicited in=" << ingress <<
" data=" << data.getName()
399 <<
" decision=" << decision);
407 Forwarder::onOutgoingData(
const Data& data, Face& egress)
410 NFD_LOG_WARN(
"onOutgoingData out=(invalid) data=" << data.getName());
415 bool isViolatingLocalhost = egress.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL &&
417 if (isViolatingLocalhost) {
418 NFD_LOG_DEBUG(
"onOutgoingData out=" << egress.getId() <<
" data=" << data.getName()
419 <<
" violates /localhost");
424 NFD_LOG_DEBUG(
"onOutgoingData out=" << egress.getId() <<
" data=" << data.getName());
427 egress.sendData(data);
434 Forwarder::onIncomingNack(
const lp::Nack& nack,
const FaceEndpoint& ingress)
436 nack.setTag(make_shared<lp::IncomingFaceIdTag>(ingress.face.getId()));
440 if (ingress.face.getLinkType() != ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
441 NFD_LOG_DEBUG(
"onIncomingNack in=" << ingress <<
" nack=" << nack.getInterest().getName()
442 <<
"~" << nack.getReason() <<
" link-type=" << ingress.face.getLinkType());
447 shared_ptr<pit::Entry> pitEntry = m_pit.
find(nack.getInterest());
449 if (pitEntry ==
nullptr) {
450 NFD_LOG_DEBUG(
"onIncomingNack in=" << ingress <<
" nack=" << nack.getInterest().getName()
451 <<
"~" << nack.getReason() <<
" no-pit-entry");
456 auto outRecord = pitEntry->findOutRecord(ingress.face);
458 if (outRecord == pitEntry->out_end()) {
459 NFD_LOG_DEBUG(
"onIncomingNack in=" << ingress <<
" nack=" << nack.getInterest().getName()
460 <<
"~" << nack.getReason() <<
" no-out-record");
465 if (nack.getInterest().getNonce() != outRecord->getLastNonce()) {
466 NFD_LOG_DEBUG(
"onIncomingNack in=" << ingress <<
" nack=" << nack.getInterest().getName()
467 <<
"~" << nack.getReason() <<
" nonce-mismatch " << nack.getInterest().getNonce()
468 <<
"!=" << outRecord->getLastNonce());
472 NFD_LOG_DEBUG(
"onIncomingNack in=" << ingress <<
" nack=" << nack.getInterest().getName()
473 <<
"~" << nack.getReason());
476 outRecord->setIncomingNack(nack);
480 this->setExpiryTimer(pitEntry, 0_ms);
488 Forwarder::onOutgoingNack(
const lp::NackHeader& nack, Face& egress,
489 const shared_ptr<pit::Entry>& pitEntry)
492 NFD_LOG_WARN(
"onOutgoingNack out=(invalid)" <<
" nack=" << pitEntry->getName()
493 <<
"~" << nack.getReason());
498 auto inRecord = pitEntry->findInRecord(egress);
501 if (inRecord == pitEntry->in_end()) {
502 NFD_LOG_DEBUG(
"onOutgoingNack out=" << egress.getId() <<
" nack=" << pitEntry->getName()
503 <<
"~" << nack.getReason() <<
" no-in-record");
508 if (egress.getLinkType() != ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
509 NFD_LOG_DEBUG(
"onOutgoingNack out=" << egress.getId() <<
" nack=" << pitEntry->getName()
510 <<
"~" << nack.getReason() <<
" link-type=" << egress.getLinkType());
514 NFD_LOG_DEBUG(
"onOutgoingNack out=" << egress.getId() <<
" nack=" << pitEntry->getName()
515 <<
"~" << nack.getReason());
518 lp::Nack nackPkt(inRecord->getInterest());
519 nackPkt.setHeader(nack);
522 pitEntry->deleteInRecord(inRecord);
525 egress.sendNack(nackPkt);
532 Forwarder::onDroppedInterest(
const Interest& interest, Face& egress)
538 Forwarder::onNewNextHop(
const Name& prefix,
const fib::NextHop& nextHop)
541 [&] (
const name_tree::Entry& nte) -> std::pair<bool, bool> {
547 if (nte.getFibEntry() !=
nullptr && nte.getName().size() > prefix.size()) {
548 return {false, false};
550 return {nte.hasPitEntries(),
true};
553 for (
const auto& nte : affectedEntries) {
554 for (
const auto& pitEntry : nte.getPitEntries()) {
555 m_strategyChoice.findEffectiveStrategy(*pitEntry).afterNewNextHop(nextHop, pitEntry);
561 Forwarder::setExpiryTimer(
const shared_ptr<pit::Entry>& pitEntry, time::milliseconds duration)
563 BOOST_ASSERT(pitEntry);
564 duration = std::max(duration, 0_ms);
566 pitEntry->expiryTimer.cancel();
567 pitEntry->expiryTimer =
getScheduler().schedule(duration, [=] { onInterestFinalize(pitEntry); });
571 Forwarder::insertDeadNonceList(pit::Entry& pitEntry,
const Face* upstream)
575 if (pitEntry.isSatisfied) {
576 BOOST_ASSERT(pitEntry.dataFreshnessPeriod >= 0_ms);
577 needDnl = pitEntry.getInterest().getMustBeFresh() &&
578 pitEntry.dataFreshnessPeriod < m_deadNonceList.getLifetime();
586 if (upstream ==
nullptr) {
588 std::for_each(pitEntry.out_begin(), pitEntry.out_end(), [&] (
const auto& outRecord) {
589 m_deadNonceList.add(pitEntry.getName(), outRecord.getLastNonce());
594 auto outRecord = pitEntry.findOutRecord(*upstream);
595 if (outRecord != pitEntry.out_end()) {
596 m_deadNonceList.add(pitEntry.getName(), outRecord->getLastNonce());
605 processConfig(std::forward<decltype(args)>(args)...);
610 Forwarder::processConfig(
const ConfigSection& configSection,
bool isDryRun,
const std::string&)
614 for (
const auto& pair : configSection) {
615 const std::string& key = pair.first;
616 if (key ==
"default_hop_limit") {
617 config.defaultHopLimit = ConfigFile::parseNumber<uint8_t>(pair,
CFG_FORWARDER);
620 NDN_THROW(ConfigFile::Error(
"Unrecognized option " +
CFG_FORWARDER +
"." + key));
This file contains common algorithms used by forwarding strategies.
Configuration file parsing utility.
void addSectionHandler(const std::string §ionName, ConfigSectionHandler subscriber)
Setup notification of configuration file sections.
bool has(const Name &name, Interest::Nonce nonce) const
Determines if name+nonce is in the list.
Represents a face-endpoint pair in the forwarder.
signal::Signal< FaceTable, Face > beforeRemove
Fires immediately before a face is removed.
signal::Signal< FaceTable, Face > afterAdd
Fires immediately after a face is added.
Face * get(FaceId id) const noexcept
Get face by FaceId.
PacketCounter nSatisfiedInterests
PacketCounter nUnsatisfiedInterests
PacketCounter nOutInterests
PacketCounter nInInterests
PacketCounter nUnsolicitedData
NameTree & getNameTree() noexcept
Forwarder(FaceTable &faceTable)
bool isInProducerRegion(span< const Name > forwardingHint) const
Determines whether an Interest has reached a producer region.
void insert(const Data &data, bool isUnsolicited=false)
Inserts a Data packet.
void find(const Interest &interest, HitCallback &&hit, MissCallback &&miss) const
Finds the best matching Data packet.
PacketCounter nInHopLimitZero
Count of incoming Interests dropped due to HopLimit == 0.
Generalization of a network interface.
signal::Signal< LinkService, Data, EndpointId > & afterReceiveData
Called when a Data packet is received.
ndn::nfd::FaceScope getScope() const noexcept
Returns whether the face is local or non-local for scope control purposes.
signal::Signal< LinkService, Interest > & onDroppedInterest
Called when an Interest is dropped by the reliability system for exceeding the allowed number of retr...
signal::Signal< LinkService, lp::Nack, EndpointId > & afterReceiveNack
Called when a Nack packet is received.
ndn::nfd::LinkType getLinkType() const noexcept
Returns the link type of the face (point-to-point, multi-access, ...).
FaceId getId() const noexcept
Returns the face ID.
signal::Signal< LinkService, Interest, EndpointId > & afterReceiveInterest
Called when an Interest packet is received.
const FaceCounters & getCounters() const noexcept
signal::Signal< Fib, Name, NextHop > afterNewNextHop
Signals on Fib entry nexthop creation.
Represents a nexthop record in a FIB entry.
static const Name & getStrategyName()
virtual void onDroppedInterest(const Interest &interest, Face &egress)
Trigger after an Interest is dropped (e.g., for exceeding allowed retransmissions).
virtual void afterContentStoreHit(const Data &data, const FaceEndpoint &ingress, const shared_ptr< pit::Entry > &pitEntry)
Trigger after a matching Data is found in the Content Store.
virtual void afterReceiveNack(const lp::Nack &nack, const FaceEndpoint &ingress, const shared_ptr< pit::Entry > &pitEntry)
Trigger after a Nack is received.
virtual void onInterestLoop(const Interest &interest, const FaceEndpoint &ingress)
Trigger after an Interest loop is detected.
virtual void afterReceiveInterest(const Interest &interest, const FaceEndpoint &ingress, const shared_ptr< pit::Entry > &pitEntry)=0
Trigger after an Interest is received.
virtual void afterReceiveData(const Data &data, const FaceEndpoint &ingress, const shared_ptr< pit::Entry > &pitEntry)
Trigger after Data is received.
virtual void beforeSatisfyInterest(const Data &data, const FaceEndpoint &ingress, const shared_ptr< pit::Entry > &pitEntry)
Trigger before a PIT entry is satisfied.
Range partialEnumerate(const Name &prefix, const EntrySubTreeSelector &entrySubTreeSelector=AnyEntrySubTree()) const
Enumerate all entries under a prefix.
std::pair< shared_ptr< Entry >, bool > insert(const Interest &interest)
Inserts a PIT entry for interest.
shared_ptr< Entry > find(const Interest &interest) const
Finds a PIT entry for interest.
DataMatchResult findAllDataMatches(const Data &data) const
Performs a Data match.
void erase(Entry *entry)
Deletes an entry.
void setDefaultStrategy(const Name &strategyName)
Set the default strategy.
fw::Strategy & findEffectiveStrategy(const Name &prefix) const
Get effective strategy for prefix.
#define NFD_LOG_INIT(name)
constexpr FaceId INVALID_FACEID
Indicates an invalid FaceId.
constexpr FaceId FACEID_CONTENT_STORE
Identifies a packet comes from the ContentStore.
std::variant< std::monostate, ethernet::Address, udp::Endpoint > EndpointId
Identifies a remote endpoint on the link.
DropAllUnsolicitedDataPolicy DefaultUnsolicitedDataPolicy
The default UnsolicitedDataPolicy.
@ CACHE
the Data should be cached in the ContentStore
@ DUPLICATE_NONCE_NONE
no duplicate Nonce is found
@ DUPLICATE_NONCE_IN_SAME
in-record of same face
int findDuplicateNonce(const pit::Entry &pitEntry, Interest::Nonce nonce, const Face &face)
Determine whether pitEntry has duplicate Nonce nonce.
bool hasPendingOutRecords(const pit::Entry &pitEntry)
Determine whether pitEntry has any pending out-records.
void setConfigFile(ConfigFile &config)
std::vector< shared_ptr< Entry > > DataMatchResult
An unordered iterable of all PIT entries matching a Data packet.
const Name LOCALHOST
The localhost scope ndn:/localhost.
const std::string CFG_FORWARDER
ndn::Scheduler & getScheduler()
Returns the global Scheduler instance for the calling thread.
boost::property_tree::ptree ConfigSection
A configuration file section.
void cleanupOnFaceRemoval(NameTree &nt, Fib &fib, Pit &pit, const Face &face)
Cleanup tables when a face is destroyed.
static Name getDefaultStrategyName()