forwarder.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2014-2024, Regents of the University of California,
4  * Arizona Board of Regents,
5  * Colorado State University,
6  * University Pierre & Marie Curie, Sorbonne University,
7  * Washington University in St. Louis,
8  * Beijing Institute of Technology,
9  * The University of Memphis.
10  *
11  * This file is part of NFD (Named Data Networking Forwarding Daemon).
12  * See AUTHORS.md for complete list of NFD authors and contributors.
13  *
14  * NFD is free software: you can redistribute it and/or modify it under the terms
15  * of the GNU General Public License as published by the Free Software Foundation,
16  * either version 3 of the License, or (at your option) any later version.
17  *
18  * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20  * PURPOSE. See the GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License along with
23  * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 #include "forwarder.hpp"
27 
28 #include "algorithm.hpp"
29 #include "best-route-strategy.hpp"
30 #include "scope-prefix.hpp"
31 #include "strategy.hpp"
32 #include "common/global.hpp"
33 #include "common/logger.hpp"
34 #include "table/cleanup.hpp"
35 
36 #include <ndn-cxx/lp/pit-token.hpp>
37 #include <ndn-cxx/lp/tags.hpp>
38 
39 namespace nfd {
40 
41 NFD_LOG_INIT(Forwarder);
42 
43 const std::string CFG_FORWARDER = "forwarder";
44 
45 static Name
47 {
49 }
50 
52  : m_faceTable(faceTable)
53  , m_unsolicitedDataPolicy(make_unique<fw::DefaultUnsolicitedDataPolicy>())
54  , m_fib(m_nameTree)
55  , m_pit(m_nameTree)
56  , m_measurements(m_nameTree)
57  , m_strategyChoice(*this)
58 {
59  m_faceTable.afterAdd.connect([this] (const Face& face) {
60  face.afterReceiveInterest.connect(
61  [this, &face] (const Interest& interest, const EndpointId& endpointId) {
62  this->onIncomingInterest(interest, FaceEndpoint(const_cast<Face&>(face), endpointId));
63  });
64  face.afterReceiveData.connect(
65  [this, &face] (const Data& data, const EndpointId& endpointId) {
66  this->onIncomingData(data, FaceEndpoint(const_cast<Face&>(face), endpointId));
67  });
68  face.afterReceiveNack.connect(
69  [this, &face] (const lp::Nack& nack, const EndpointId& endpointId) {
70  this->onIncomingNack(nack, FaceEndpoint(const_cast<Face&>(face), endpointId));
71  });
72  face.onDroppedInterest.connect(
73  [this, &face] (const Interest& interest) {
74  this->onDroppedInterest(interest, const_cast<Face&>(face));
75  });
76  });
77 
78  m_faceTable.beforeRemove.connect([this] (const Face& face) {
79  cleanupOnFaceRemoval(m_nameTree, m_fib, m_pit, face);
80  });
81 
82  m_fib.afterNewNextHop.connect([this] (const Name& prefix, const fib::NextHop& nextHop) {
83  this->onNewNextHop(prefix, nextHop);
84  });
85 
86  m_strategyChoice.setDefaultStrategy(getDefaultStrategyName());
87 }
88 
89 void
90 Forwarder::onIncomingInterest(const Interest& interest, const FaceEndpoint& ingress)
91 {
92  interest.setTag(make_shared<lp::IncomingFaceIdTag>(ingress.face.getId()));
93  ++m_counters.nInInterests;
94 
95  // ensure the received Interest has a Nonce
96  auto nonce = interest.getNonce();
97  auto hopLimit = interest.getHopLimit();
98 
99  // drop if HopLimit zero, decrement otherwise (if present)
100  if (hopLimit) {
101  NFD_LOG_DEBUG("onIncomingInterest in=" << ingress << " interest=" << interest.getName()
102  << " nonce=" << nonce << " hop-limit=" << static_cast<unsigned>(*hopLimit));
103  if (*hopLimit == 0) {
104  ++ingress.face.getCounters().nInHopLimitZero;
105  // drop
106  return;
107  }
108  const_cast<Interest&>(interest).setHopLimit(*hopLimit - 1);
109  }
110  else {
111  NFD_LOG_DEBUG("onIncomingInterest in=" << ingress << " interest=" << interest.getName()
112  << " nonce=" << nonce);
113  }
114 
115  // /localhost scope control
116  bool isViolatingLocalhost = ingress.face.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL &&
117  scope_prefix::LOCALHOST.isPrefixOf(interest.getName());
118  if (isViolatingLocalhost) {
119  NFD_LOG_DEBUG("onIncomingInterest in=" << ingress << " interest=" << interest.getName()
120  << " nonce=" << nonce << " violates /localhost");
121  // drop
122  return;
123  }
124 
125  // detect duplicate Nonce with Dead Nonce List
126  bool hasDuplicateNonceInDnl = m_deadNonceList.has(interest.getName(), nonce);
127  if (hasDuplicateNonceInDnl) {
128  // go to Interest loop pipeline
129  this->onInterestLoop(interest, ingress);
130  return;
131  }
132 
133  // strip forwarding hint if Interest has reached producer region
134  if (!interest.getForwardingHint().empty() &&
135  m_networkRegionTable.isInProducerRegion(interest.getForwardingHint())) {
136  NFD_LOG_DEBUG("onIncomingInterest in=" << ingress << " interest=" << interest.getName()
137  << " nonce=" << nonce << " reaching-producer-region");
138  const_cast<Interest&>(interest).setForwardingHint({});
139  }
140 
141  // PIT insert
142  shared_ptr<pit::Entry> pitEntry = m_pit.insert(interest).first;
143 
144  // detect duplicate Nonce in PIT entry
145  int dnw = fw::findDuplicateNonce(*pitEntry, nonce, ingress.face);
146  bool hasDuplicateNonceInPit = dnw != fw::DUPLICATE_NONCE_NONE;
147  if (ingress.face.getLinkType() == ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
148  // for p2p face: duplicate Nonce from same incoming face is not loop
149  hasDuplicateNonceInPit = hasDuplicateNonceInPit && !(dnw & fw::DUPLICATE_NONCE_IN_SAME);
150  }
151  if (hasDuplicateNonceInPit) {
152  // go to Interest loop pipeline
153  this->onInterestLoop(interest, ingress);
154  return;
155  }
156 
157  // is pending?
158  if (!pitEntry->hasInRecords()) {
159  m_cs.find(interest,
160  [=] (const Interest& i, const Data& d) { onContentStoreHit(i, ingress, pitEntry, d); },
161  [=] (const Interest& i) { onContentStoreMiss(i, ingress, pitEntry); });
162  }
163  else {
164  this->onContentStoreMiss(interest, ingress, pitEntry);
165  }
166 }
167 
168 void
169 Forwarder::onInterestLoop(const Interest& interest, const FaceEndpoint& ingress)
170 {
171  // if multi-access or ad hoc face, drop
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");
175  return;
176  }
177 
178  NFD_LOG_DEBUG("onInterestLoop in=" << ingress << " interest=" << interest.getName()
179  << " nonce=" << interest.getNonce());
180 
181  // leave loop handling up to the strategy (e.g., whether to reply with a Nack)
182  m_strategyChoice.findEffectiveStrategy(interest.getName()).onInterestLoop(interest, ingress);
183 }
184 
185 void
186 Forwarder::onContentStoreMiss(const Interest& interest, const FaceEndpoint& ingress,
187  const shared_ptr<pit::Entry>& pitEntry)
188 {
189  NFD_LOG_DEBUG("onContentStoreMiss interest=" << interest.getName() << " nonce=" << interest.getNonce());
190  ++m_counters.nCsMisses;
191 
192  // attach HopLimit if configured and not present in Interest
193  if (m_config.defaultHopLimit > 0 && !interest.getHopLimit()) {
194  const_cast<Interest&>(interest).setHopLimit(m_config.defaultHopLimit);
195  }
196 
197  // insert in-record
198  pitEntry->insertOrUpdateInRecord(ingress.face, interest);
199 
200  // set PIT expiry timer to the time that the last PIT in-record expires
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();
204  });
205  auto lastExpiryFromNow = lastExpiring->getExpiry() - time::steady_clock::now();
206  this->setExpiryTimer(pitEntry, time::duration_cast<time::milliseconds>(lastExpiryFromNow));
207 
208  // has NextHopFaceId?
209  auto nextHopTag = interest.getTag<lp::NextHopFaceIdTag>();
210  if (nextHopTag != nullptr) {
211  // chosen NextHop face exists?
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());
216  // go to outgoing Interest pipeline
217  // scope control is unnecessary, because privileged app explicitly wants to forward
218  this->onOutgoingInterest(interest, *nextHopFace, pitEntry);
219  }
220  return;
221  }
222 
223  // dispatch to strategy: after receive Interest
224  m_strategyChoice.findEffectiveStrategy(*pitEntry)
225  .afterReceiveInterest(interest, FaceEndpoint(ingress.face), pitEntry);
226 }
227 
228 void
229 Forwarder::onContentStoreHit(const Interest& interest, const FaceEndpoint& ingress,
230  const shared_ptr<pit::Entry>& pitEntry, const Data& data)
231 {
232  NFD_LOG_DEBUG("onContentStoreHit interest=" << interest.getName() << " nonce=" << interest.getNonce());
233  ++m_counters.nCsHits;
234 
235  data.setTag(make_shared<lp::IncomingFaceIdTag>(face::FACEID_CONTENT_STORE));
236  data.setTag(interest.getTag<lp::PitToken>());
237  // FIXME Should we lookup PIT for other Interests that also match the data?
238 
239  pitEntry->isSatisfied = true;
240  pitEntry->dataFreshnessPeriod = data.getFreshnessPeriod();
241 
242  // set PIT expiry timer to now
243  this->setExpiryTimer(pitEntry, 0_ms);
244 
245  // dispatch to strategy: after Content Store hit
246  m_strategyChoice.findEffectiveStrategy(*pitEntry).afterContentStoreHit(data, ingress, pitEntry);
247 }
248 
249 pit::OutRecord*
250 Forwarder::onOutgoingInterest(const Interest& interest, Face& egress,
251  const shared_ptr<pit::Entry>& pitEntry)
252 {
253  // drop if HopLimit == 0 but sending on non-local face
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;
258  return nullptr;
259  }
260 
261  NFD_LOG_DEBUG("onOutgoingInterest out=" << egress.getId() << " interest=" << interest.getName()
262  << " nonce=" << interest.getNonce());
263 
264  // insert out-record
265  auto it = pitEntry->insertOrUpdateOutRecord(egress, interest);
266  BOOST_ASSERT(it != pitEntry->out_end());
267 
268  // send Interest
269  egress.sendInterest(interest);
270  ++m_counters.nOutInterests;
271 
272  return &*it;
273 }
274 
275 void
276 Forwarder::onInterestFinalize(const shared_ptr<pit::Entry>& pitEntry)
277 {
278  NFD_LOG_DEBUG("onInterestFinalize interest=" << pitEntry->getName()
279  << (pitEntry->isSatisfied ? " satisfied" : " unsatisfied"));
280 
281  // Dead Nonce List insert if necessary
282  this->insertDeadNonceList(*pitEntry, nullptr);
283 
284  // Increment satisfied/unsatisfied Interests counter
285  if (pitEntry->isSatisfied) {
286  ++m_counters.nSatisfiedInterests;
287  }
288  else {
289  ++m_counters.nUnsatisfiedInterests;
290  }
291 
292  // PIT delete
293  pitEntry->expiryTimer.cancel();
294  m_pit.erase(pitEntry.get());
295 }
296 
297 void
298 Forwarder::onIncomingData(const Data& data, const FaceEndpoint& ingress)
299 {
300  data.setTag(make_shared<lp::IncomingFaceIdTag>(ingress.face.getId()));
301  ++m_counters.nInData;
302  NFD_LOG_DEBUG("onIncomingData in=" << ingress << " data=" << data.getName());
303 
304  // /localhost scope control
305  bool isViolatingLocalhost = ingress.face.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL &&
306  scope_prefix::LOCALHOST.isPrefixOf(data.getName());
307  if (isViolatingLocalhost) {
308  NFD_LOG_DEBUG("onIncomingData in=" << ingress << " data=" << data.getName() << " violates /localhost");
309  // drop
310  return;
311  }
312 
313  // PIT match
314  pit::DataMatchResult pitMatches = m_pit.findAllDataMatches(data);
315  if (pitMatches.size() == 0) {
316  // go to Data unsolicited pipeline
317  this->onDataUnsolicited(data, ingress);
318  return;
319  }
320 
321  // CS insert
322  m_cs.insert(data);
323 
324  // when only one PIT entry is matched, trigger strategy: after receive Data
325  if (pitMatches.size() == 1) {
326  auto& pitEntry = pitMatches.front();
327 
328  NFD_LOG_DEBUG("onIncomingData matching=" << pitEntry->getName());
329 
330  // set PIT expiry timer to now
331  this->setExpiryTimer(pitEntry, 0_ms);
332 
333  // trigger strategy: after receive Data
334  m_strategyChoice.findEffectiveStrategy(*pitEntry).afterReceiveData(data, ingress, pitEntry);
335 
336  // mark PIT satisfied
337  pitEntry->isSatisfied = true;
338  pitEntry->dataFreshnessPeriod = data.getFreshnessPeriod();
339 
340  // Dead Nonce List insert if necessary (for out-record of ingress face)
341  this->insertDeadNonceList(*pitEntry, &ingress.face);
342 
343  // delete PIT entry's out-record
344  pitEntry->deleteOutRecord(ingress.face);
345  }
346  // when more than one PIT entry is matched, trigger strategy: before satisfy Interest,
347  // and send Data to all matched out faces
348  else {
349  std::set<Face*> pendingDownstreams;
350  auto now = time::steady_clock::now();
351 
352  for (const auto& pitEntry : pitMatches) {
353  NFD_LOG_DEBUG("onIncomingData matching=" << pitEntry->getName());
354 
355  // remember pending downstreams
356  for (const pit::InRecord& inRecord : pitEntry->getInRecords()) {
357  if (inRecord.getExpiry() > now) {
358  pendingDownstreams.insert(&inRecord.getFace());
359  }
360  }
361 
362  // set PIT expiry timer to now
363  this->setExpiryTimer(pitEntry, 0_ms);
364 
365  // invoke PIT satisfy callback
366  m_strategyChoice.findEffectiveStrategy(*pitEntry).beforeSatisfyInterest(data, ingress, pitEntry);
367 
368  // mark PIT satisfied
369  pitEntry->isSatisfied = true;
370  pitEntry->dataFreshnessPeriod = data.getFreshnessPeriod();
371 
372  // Dead Nonce List insert if necessary (for out-record of ingress face)
373  this->insertDeadNonceList(*pitEntry, &ingress.face);
374 
375  // clear PIT entry's in and out records
376  pitEntry->clearInRecords();
377  pitEntry->deleteOutRecord(ingress.face);
378  }
379 
380  for (Face* pendingDownstream : pendingDownstreams) {
381  if (pendingDownstream->getId() == ingress.face.getId() &&
382  pendingDownstream->getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) {
383  continue;
384  }
385  // go to outgoing Data pipeline
386  this->onOutgoingData(data, *pendingDownstream);
387  }
388  }
389 }
390 
391 void
392 Forwarder::onDataUnsolicited(const Data& data, const FaceEndpoint& ingress)
393 {
394  ++m_counters.nUnsolicitedData;
395 
396  // accept to cache?
397  auto decision = m_unsolicitedDataPolicy->decide(ingress.face, data);
398  NFD_LOG_DEBUG("onDataUnsolicited in=" << ingress << " data=" << data.getName()
399  << " decision=" << decision);
400  if (decision == fw::UnsolicitedDataDecision::CACHE) {
401  // CS insert
402  m_cs.insert(data, true);
403  }
404 }
405 
406 bool
407 Forwarder::onOutgoingData(const Data& data, Face& egress)
408 {
409  if (egress.getId() == face::INVALID_FACEID) {
410  NFD_LOG_WARN("onOutgoingData out=(invalid) data=" << data.getName());
411  return false;
412  }
413 
414  // /localhost scope control
415  bool isViolatingLocalhost = egress.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL &&
416  scope_prefix::LOCALHOST.isPrefixOf(data.getName());
417  if (isViolatingLocalhost) {
418  NFD_LOG_DEBUG("onOutgoingData out=" << egress.getId() << " data=" << data.getName()
419  << " violates /localhost");
420  // drop
421  return false;
422  }
423 
424  NFD_LOG_DEBUG("onOutgoingData out=" << egress.getId() << " data=" << data.getName());
425 
426  // send Data
427  egress.sendData(data);
428  ++m_counters.nOutData;
429 
430  return true;
431 }
432 
433 void
434 Forwarder::onIncomingNack(const lp::Nack& nack, const FaceEndpoint& ingress)
435 {
436  nack.setTag(make_shared<lp::IncomingFaceIdTag>(ingress.face.getId()));
437  ++m_counters.nInNacks;
438 
439  // if multi-access or ad hoc face, drop
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());
443  return;
444  }
445 
446  // PIT match
447  shared_ptr<pit::Entry> pitEntry = m_pit.find(nack.getInterest());
448  // if no PIT entry found, drop
449  if (pitEntry == nullptr) {
450  NFD_LOG_DEBUG("onIncomingNack in=" << ingress << " nack=" << nack.getInterest().getName()
451  << "~" << nack.getReason() << " no-pit-entry");
452  return;
453  }
454 
455  // has out-record?
456  auto outRecord = pitEntry->findOutRecord(ingress.face);
457  // if no out-record found, drop
458  if (outRecord == pitEntry->out_end()) {
459  NFD_LOG_DEBUG("onIncomingNack in=" << ingress << " nack=" << nack.getInterest().getName()
460  << "~" << nack.getReason() << " no-out-record");
461  return;
462  }
463 
464  // if out-record has different Nonce, drop
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());
469  return;
470  }
471 
472  NFD_LOG_DEBUG("onIncomingNack in=" << ingress << " nack=" << nack.getInterest().getName()
473  << "~" << nack.getReason());
474 
475  // record Nack on out-record
476  outRecord->setIncomingNack(nack);
477 
478  // set PIT expiry timer to now when all out-record receive Nack
479  if (!fw::hasPendingOutRecords(*pitEntry)) {
480  this->setExpiryTimer(pitEntry, 0_ms);
481  }
482 
483  // trigger strategy: after receive Nack
484  m_strategyChoice.findEffectiveStrategy(*pitEntry).afterReceiveNack(nack, ingress, pitEntry);
485 }
486 
487 bool
488 Forwarder::onOutgoingNack(const lp::NackHeader& nack, Face& egress,
489  const shared_ptr<pit::Entry>& pitEntry)
490 {
491  if (egress.getId() == face::INVALID_FACEID) {
492  NFD_LOG_WARN("onOutgoingNack out=(invalid)" << " nack=" << pitEntry->getName()
493  << "~" << nack.getReason());
494  return false;
495  }
496 
497  // has in-record?
498  auto inRecord = pitEntry->findInRecord(egress);
499 
500  // if no in-record found, drop
501  if (inRecord == pitEntry->in_end()) {
502  NFD_LOG_DEBUG("onOutgoingNack out=" << egress.getId() << " nack=" << pitEntry->getName()
503  << "~" << nack.getReason() << " no-in-record");
504  return false;
505  }
506 
507  // if multi-access or ad hoc face, drop
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());
511  return false;
512  }
513 
514  NFD_LOG_DEBUG("onOutgoingNack out=" << egress.getId() << " nack=" << pitEntry->getName()
515  << "~" << nack.getReason());
516 
517  // create Nack packet with the Interest from in-record
518  lp::Nack nackPkt(inRecord->getInterest());
519  nackPkt.setHeader(nack);
520 
521  // erase in-record
522  pitEntry->deleteInRecord(inRecord);
523 
524  // send Nack on face
525  egress.sendNack(nackPkt);
526  ++m_counters.nOutNacks;
527 
528  return true;
529 }
530 
531 void
532 Forwarder::onDroppedInterest(const Interest& interest, Face& egress)
533 {
534  m_strategyChoice.findEffectiveStrategy(interest.getName()).onDroppedInterest(interest, egress);
535 }
536 
537 void
538 Forwarder::onNewNextHop(const Name& prefix, const fib::NextHop& nextHop)
539 {
540  const auto affectedEntries = this->getNameTree().partialEnumerate(prefix,
541  [&] (const name_tree::Entry& nte) -> std::pair<bool, bool> {
542  // we ignore an NTE and skip visiting its descendants if that NTE has an
543  // associated FIB entry (1st condition), since in that case the new nexthop
544  // won't affect any PIT entries anywhere in that subtree, *unless* this is
545  // the initial NTE from which the enumeration started (2nd condition), which
546  // must always be considered
547  if (nte.getFibEntry() != nullptr && nte.getName().size() > prefix.size()) {
548  return {false, false};
549  }
550  return {nte.hasPitEntries(), true};
551  });
552 
553  for (const auto& nte : affectedEntries) {
554  for (const auto& pitEntry : nte.getPitEntries()) {
555  m_strategyChoice.findEffectiveStrategy(*pitEntry).afterNewNextHop(nextHop, pitEntry);
556  }
557  }
558 }
559 
560 void
561 Forwarder::setExpiryTimer(const shared_ptr<pit::Entry>& pitEntry, time::milliseconds duration)
562 {
563  BOOST_ASSERT(pitEntry);
564  duration = std::max(duration, 0_ms);
565 
566  pitEntry->expiryTimer.cancel();
567  pitEntry->expiryTimer = getScheduler().schedule(duration, [=] { onInterestFinalize(pitEntry); });
568 }
569 
570 void
571 Forwarder::insertDeadNonceList(pit::Entry& pitEntry, const Face* upstream)
572 {
573  // need Dead Nonce List insert?
574  bool needDnl = true;
575  if (pitEntry.isSatisfied) {
576  BOOST_ASSERT(pitEntry.dataFreshnessPeriod >= 0_ms);
577  needDnl = pitEntry.getInterest().getMustBeFresh() &&
578  pitEntry.dataFreshnessPeriod < m_deadNonceList.getLifetime();
579  }
580 
581  if (!needDnl) {
582  return;
583  }
584 
585  // Dead Nonce List insert
586  if (upstream == nullptr) {
587  // insert all outgoing Nonces
588  std::for_each(pitEntry.out_begin(), pitEntry.out_end(), [&] (const auto& outRecord) {
589  m_deadNonceList.add(pitEntry.getName(), outRecord.getLastNonce());
590  });
591  }
592  else {
593  // insert outgoing Nonce of a specific face
594  auto outRecord = pitEntry.findOutRecord(*upstream);
595  if (outRecord != pitEntry.out_end()) {
596  m_deadNonceList.add(pitEntry.getName(), outRecord->getLastNonce());
597  }
598  }
599 }
600 
601 void
603 {
604  configFile.addSectionHandler(CFG_FORWARDER, [this] (auto&&... args) {
605  processConfig(std::forward<decltype(args)>(args)...);
606  });
607 }
608 
609 void
610 Forwarder::processConfig(const ConfigSection& configSection, bool isDryRun, const std::string&)
611 {
612  Config config;
613 
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);
618  }
619  else {
620  NDN_THROW(ConfigFile::Error("Unrecognized option " + CFG_FORWARDER + "." + key));
621  }
622  }
623 
624  if (!isDryRun) {
625  m_config = config;
626  }
627 }
628 
629 } // namespace nfd
This file contains common algorithms used by forwarding strategies.
Configuration file parsing utility.
Definition: config-file.hpp:66
void addSectionHandler(const std::string &sectionName, ConfigSectionHandler subscriber)
Setup notification of configuration file sections.
Definition: config-file.cpp:77
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.
Container of all faces.
Definition: face-table.hpp:42
signal::Signal< FaceTable, Face > beforeRemove
Fires immediately before a face is removed.
Definition: face-table.hpp:89
signal::Signal< FaceTable, Face > afterAdd
Fires immediately after a face is added.
Definition: face-table.hpp:83
Face * get(FaceId id) const noexcept
Get face by FaceId.
Definition: face-table.cpp:38
PacketCounter nUnsatisfiedInterests
NameTree & getNameTree() noexcept
Definition: forwarder.hpp:84
Forwarder(FaceTable &faceTable)
Definition: forwarder.cpp:51
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.
Definition: cs.cpp:49
void find(const Interest &interest, HitCallback &&hit, MissCallback &&miss) const
Finds the best matching Data packet.
Definition: cs.hpp:81
PacketCounter nInHopLimitZero
Count of incoming Interests dropped due to HopLimit == 0.
Definition: face.hpp:96
Generalization of a network interface.
Definition: face.hpp:118
signal::Signal< LinkService, Data, EndpointId > & afterReceiveData
Called when a Data packet is received.
Definition: face.hpp:182
ndn::nfd::FaceScope getScope() const noexcept
Returns whether the face is local or non-local for scope control purposes.
Definition: face.hpp:232
signal::Signal< LinkService, Interest > & onDroppedInterest
Called when an Interest is dropped by the reliability system for exceeding the allowed number of retr...
Definition: face.hpp:188
signal::Signal< LinkService, lp::Nack, EndpointId > & afterReceiveNack
Called when a Nack packet is received.
Definition: face.hpp:185
ndn::nfd::LinkType getLinkType() const noexcept
Returns the link type of the face (point-to-point, multi-access, ...).
Definition: face.hpp:259
FaceId getId() const noexcept
Returns the face ID.
Definition: face.hpp:195
signal::Signal< LinkService, Interest, EndpointId > & afterReceiveInterest
Called when an Interest packet is received.
Definition: face.hpp:179
const FaceCounters & getCounters() const noexcept
Definition: face.hpp:298
signal::Signal< Fib, Name, NextHop > afterNewNextHop
Signals on Fib entry nexthop creation.
Definition: fib.hpp:154
Represents a nexthop record in a FIB entry.
Definition: fib-entry.hpp:50
static const Name & getStrategyName()
virtual void onDroppedInterest(const Interest &interest, Face &egress)
Trigger after an Interest is dropped (e.g., for exceeding allowed retransmissions).
Definition: strategy.cpp:223
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.
Definition: strategy.cpp:187
virtual void afterReceiveNack(const lp::Nack &nack, const FaceEndpoint &ingress, const shared_ptr< pit::Entry > &pitEntry)
Trigger after a Nack is received.
Definition: strategy.cpp:216
virtual void onInterestLoop(const Interest &interest, const FaceEndpoint &ingress)
Trigger after an Interest loop is detected.
Definition: strategy.cpp:177
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.
Definition: strategy.cpp:205
virtual void beforeSatisfyInterest(const Data &data, const FaceEndpoint &ingress, const shared_ptr< pit::Entry > &pitEntry)
Trigger before a PIT entry is satisfied.
Definition: strategy.cpp:197
Range partialEnumerate(const Name &prefix, const EntrySubTreeSelector &entrySubTreeSelector=AnyEntrySubTree()) const
Enumerate all entries under a prefix.
Definition: name-tree.cpp:231
std::pair< shared_ptr< Entry >, bool > insert(const Interest &interest)
Inserts a PIT entry for interest.
Definition: pit.hpp:115
shared_ptr< Entry > find(const Interest &interest) const
Finds a PIT entry for interest.
Definition: pit.hpp:104
DataMatchResult findAllDataMatches(const Data &data) const
Performs a Data match.
Definition: pit.cpp:99
void erase(Entry *entry)
Deletes an entry.
Definition: pit.hpp:129
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)
Definition: logger.hpp:31
#define NFD_LOG_WARN
Definition: logger.hpp:40
#define NFD_LOG_DEBUG
Definition: logger.hpp:38
constexpr FaceId INVALID_FACEID
Indicates an invalid FaceId.
Definition: face-common.hpp:51
constexpr FaceId FACEID_CONTENT_STORE
Identifies a packet comes from the ContentStore.
Definition: face-common.hpp:55
std::variant< std::monostate, ethernet::Address, udp::Endpoint > EndpointId
Identifies a remote endpoint on the link.
Definition: face-common.hpp:78
DropAllUnsolicitedDataPolicy DefaultUnsolicitedDataPolicy
The default UnsolicitedDataPolicy.
@ CACHE
the Data should be cached in the ContentStore
@ DUPLICATE_NONCE_NONE
no duplicate Nonce is found
Definition: algorithm.hpp:47
@ DUPLICATE_NONCE_IN_SAME
in-record of same face
Definition: algorithm.hpp:48
int findDuplicateNonce(const pit::Entry &pitEntry, Interest::Nonce nonce, const Face &face)
Determine whether pitEntry has duplicate Nonce nonce.
Definition: algorithm.cpp:55
bool hasPendingOutRecords(const pit::Entry &pitEntry)
Determine whether pitEntry has any pending out-records.
Definition: algorithm.cpp:85
void setConfigFile(ConfigFile &config)
std::vector< shared_ptr< Entry > > DataMatchResult
An unordered iterable of all PIT entries matching a Data packet.
Definition: pit.hpp:38
const Name LOCALHOST
The localhost scope ndn:/localhost.
-status-http-server
Definition: common.hpp:71
const std::string CFG_FORWARDER
Definition: forwarder.cpp:43
ndn::Scheduler & getScheduler()
Returns the global Scheduler instance for the calling thread.
Definition: global.cpp:45
boost::property_tree::ptree ConfigSection
A configuration file section.
Definition: config-file.hpp:41
void cleanupOnFaceRemoval(NameTree &nt, Fib &fib, Pit &pit, const Face &face)
Cleanup tables when a face is destroyed.
Definition: cleanup.cpp:33
static Name getDefaultStrategyName()
Definition: forwarder.cpp:46