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-2022, 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  // receive Interest
93  NFD_LOG_DEBUG("onIncomingInterest in=" << ingress << " interest=" << interest.getName());
94  interest.setTag(make_shared<lp::IncomingFaceIdTag>(ingress.face.getId()));
95  ++m_counters.nInInterests;
96 
97  // drop if HopLimit zero, decrement otherwise (if present)
98  if (interest.getHopLimit()) {
99  if (*interest.getHopLimit() == 0) {
100  NFD_LOG_DEBUG("onIncomingInterest in=" << ingress << " interest=" << interest.getName()
101  << " hop-limit=0");
102  ++ingress.face.getCounters().nInHopLimitZero;
103  // drop
104  return;
105  }
106  const_cast<Interest&>(interest).setHopLimit(*interest.getHopLimit() - 1);
107  }
108 
109  // /localhost scope control
110  bool isViolatingLocalhost = ingress.face.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL &&
111  scope_prefix::LOCALHOST.isPrefixOf(interest.getName());
112  if (isViolatingLocalhost) {
113  NFD_LOG_DEBUG("onIncomingInterest in=" << ingress
114  << " interest=" << interest.getName() << " violates /localhost");
115  // drop
116  return;
117  }
118 
119  // detect duplicate Nonce with Dead Nonce List
120  bool hasDuplicateNonceInDnl = m_deadNonceList.has(interest.getName(), interest.getNonce());
121  if (hasDuplicateNonceInDnl) {
122  // goto Interest loop pipeline
123  this->onInterestLoop(interest, ingress);
124  return;
125  }
126 
127  // strip forwarding hint if Interest has reached producer region
128  if (!interest.getForwardingHint().empty() &&
129  m_networkRegionTable.isInProducerRegion(interest.getForwardingHint())) {
130  NFD_LOG_DEBUG("onIncomingInterest in=" << ingress
131  << " interest=" << interest.getName() << " reaching-producer-region");
132  const_cast<Interest&>(interest).setForwardingHint({});
133  }
134 
135  // PIT insert
136  shared_ptr<pit::Entry> pitEntry = m_pit.insert(interest).first;
137 
138  // detect duplicate Nonce in PIT entry
139  int dnw = fw::findDuplicateNonce(*pitEntry, interest.getNonce(), ingress.face);
140  bool hasDuplicateNonceInPit = dnw != fw::DUPLICATE_NONCE_NONE;
141  if (ingress.face.getLinkType() == ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
142  // for p2p face: duplicate Nonce from same incoming face is not loop
143  hasDuplicateNonceInPit = hasDuplicateNonceInPit && !(dnw & fw::DUPLICATE_NONCE_IN_SAME);
144  }
145  if (hasDuplicateNonceInPit) {
146  // goto Interest loop pipeline
147  this->onInterestLoop(interest, ingress);
148  return;
149  }
150 
151  // is pending?
152  if (!pitEntry->hasInRecords()) {
153  m_cs.find(interest,
154  [=] (const Interest& i, const Data& d) { onContentStoreHit(i, ingress, pitEntry, d); },
155  [=] (const Interest& i) { onContentStoreMiss(i, ingress, pitEntry); });
156  }
157  else {
158  this->onContentStoreMiss(interest, ingress, pitEntry);
159  }
160 }
161 
162 void
163 Forwarder::onInterestLoop(const Interest& interest, const FaceEndpoint& ingress)
164 {
165  // if multi-access or ad hoc face, drop
166  if (ingress.face.getLinkType() != ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
167  NFD_LOG_DEBUG("onInterestLoop in=" << ingress
168  << " interest=" << interest.getName() << " drop");
169  return;
170  }
171 
172  NFD_LOG_DEBUG("onInterestLoop in=" << ingress << " interest=" << interest.getName()
173  << " send-Nack-duplicate");
174 
175  // send Nack with reason=DUPLICATE
176  // note: Don't enter outgoing Nack pipeline because it needs an in-record.
177  lp::Nack nack(interest);
178  nack.setReason(lp::NackReason::DUPLICATE);
179  ingress.face.sendNack(nack);
180 }
181 
182 void
183 Forwarder::onContentStoreMiss(const Interest& interest, const FaceEndpoint& ingress,
184  const shared_ptr<pit::Entry>& pitEntry)
185 {
186  NFD_LOG_DEBUG("onContentStoreMiss interest=" << interest.getName());
187  ++m_counters.nCsMisses;
188 
189  // attach HopLimit if configured and not present in Interest
190  if (m_config.defaultHopLimit > 0 && !interest.getHopLimit()) {
191  const_cast<Interest&>(interest).setHopLimit(m_config.defaultHopLimit);
192  }
193 
194  // insert in-record
195  pitEntry->insertOrUpdateInRecord(ingress.face, interest);
196 
197  // set PIT expiry timer to the time that the last PIT in-record expires
198  auto lastExpiring = std::max_element(pitEntry->in_begin(), pitEntry->in_end(),
199  [] (const auto& a, const auto& b) {
200  return a.getExpiry() < b.getExpiry();
201  });
202  auto lastExpiryFromNow = lastExpiring->getExpiry() - time::steady_clock::now();
203  this->setExpiryTimer(pitEntry, time::duration_cast<time::milliseconds>(lastExpiryFromNow));
204 
205  // has NextHopFaceId?
206  auto nextHopTag = interest.getTag<lp::NextHopFaceIdTag>();
207  if (nextHopTag != nullptr) {
208  // chosen NextHop face exists?
209  Face* nextHopFace = m_faceTable.get(*nextHopTag);
210  if (nextHopFace != nullptr) {
211  NFD_LOG_DEBUG("onContentStoreMiss interest=" << interest.getName()
212  << " nexthop-faceid=" << nextHopFace->getId());
213  // go to outgoing Interest pipeline
214  // scope control is unnecessary, because privileged app explicitly wants to forward
215  this->onOutgoingInterest(interest, *nextHopFace, pitEntry);
216  }
217  return;
218  }
219 
220  // dispatch to strategy: after receive Interest
221  m_strategyChoice.findEffectiveStrategy(*pitEntry)
222  .afterReceiveInterest(interest, FaceEndpoint(ingress.face), pitEntry);
223 }
224 
225 void
226 Forwarder::onContentStoreHit(const Interest& interest, const FaceEndpoint& ingress,
227  const shared_ptr<pit::Entry>& pitEntry, const Data& data)
228 {
229  NFD_LOG_DEBUG("onContentStoreHit interest=" << interest.getName());
230  ++m_counters.nCsHits;
231 
232  data.setTag(make_shared<lp::IncomingFaceIdTag>(face::FACEID_CONTENT_STORE));
233  data.setTag(interest.getTag<lp::PitToken>());
234  // FIXME Should we lookup PIT for other Interests that also match the data?
235 
236  pitEntry->isSatisfied = true;
237  pitEntry->dataFreshnessPeriod = data.getFreshnessPeriod();
238 
239  // set PIT expiry timer to now
240  this->setExpiryTimer(pitEntry, 0_ms);
241 
242  // dispatch to strategy: after Content Store hit
243  m_strategyChoice.findEffectiveStrategy(*pitEntry).afterContentStoreHit(data, ingress, pitEntry);
244 }
245 
246 pit::OutRecord*
247 Forwarder::onOutgoingInterest(const Interest& interest, Face& egress,
248  const shared_ptr<pit::Entry>& pitEntry)
249 {
250  // drop if HopLimit == 0 but sending on non-local face
251  if (interest.getHopLimit() == 0 && egress.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL) {
252  NFD_LOG_DEBUG("onOutgoingInterest out=" << egress.getId() << " interest=" << pitEntry->getName()
253  << " non-local hop-limit=0");
254  ++egress.getCounters().nOutHopLimitZero;
255  return nullptr;
256  }
257 
258  NFD_LOG_DEBUG("onOutgoingInterest out=" << egress.getId() << " interest=" << pitEntry->getName());
259 
260  // insert out-record
261  auto it = pitEntry->insertOrUpdateOutRecord(egress, interest);
262  BOOST_ASSERT(it != pitEntry->out_end());
263 
264  // send Interest
265  egress.sendInterest(interest);
266  ++m_counters.nOutInterests;
267  return &*it;
268 }
269 
270 void
271 Forwarder::onInterestFinalize(const shared_ptr<pit::Entry>& pitEntry)
272 {
273  NFD_LOG_DEBUG("onInterestFinalize interest=" << pitEntry->getName()
274  << (pitEntry->isSatisfied ? " satisfied" : " unsatisfied"));
275 
276  // Dead Nonce List insert if necessary
277  this->insertDeadNonceList(*pitEntry, nullptr);
278 
279  // Increment satisfied/unsatisfied Interests counter
280  if (pitEntry->isSatisfied) {
281  ++m_counters.nSatisfiedInterests;
282  }
283  else {
284  ++m_counters.nUnsatisfiedInterests;
285  }
286 
287  // PIT delete
288  pitEntry->expiryTimer.cancel();
289  m_pit.erase(pitEntry.get());
290 }
291 
292 void
293 Forwarder::onIncomingData(const Data& data, const FaceEndpoint& ingress)
294 {
295  // receive Data
296  NFD_LOG_DEBUG("onIncomingData in=" << ingress << " data=" << data.getName());
297  data.setTag(make_shared<lp::IncomingFaceIdTag>(ingress.face.getId()));
298  ++m_counters.nInData;
299 
300  // /localhost scope control
301  bool isViolatingLocalhost = ingress.face.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL &&
302  scope_prefix::LOCALHOST.isPrefixOf(data.getName());
303  if (isViolatingLocalhost) {
304  NFD_LOG_DEBUG("onIncomingData in=" << ingress << " data=" << data.getName() << " violates /localhost");
305  // drop
306  return;
307  }
308 
309  // PIT match
310  pit::DataMatchResult pitMatches = m_pit.findAllDataMatches(data);
311  if (pitMatches.size() == 0) {
312  // goto Data unsolicited pipeline
313  this->onDataUnsolicited(data, ingress);
314  return;
315  }
316 
317  // CS insert
318  m_cs.insert(data);
319 
320  // when only one PIT entry is matched, trigger strategy: after receive Data
321  if (pitMatches.size() == 1) {
322  auto& pitEntry = pitMatches.front();
323 
324  NFD_LOG_DEBUG("onIncomingData matching=" << pitEntry->getName());
325 
326  // set PIT expiry timer to now
327  this->setExpiryTimer(pitEntry, 0_ms);
328 
329  // trigger strategy: after receive Data
330  m_strategyChoice.findEffectiveStrategy(*pitEntry).afterReceiveData(data, ingress, pitEntry);
331 
332  // mark PIT satisfied
333  pitEntry->isSatisfied = true;
334  pitEntry->dataFreshnessPeriod = data.getFreshnessPeriod();
335 
336  // Dead Nonce List insert if necessary (for out-record of ingress face)
337  this->insertDeadNonceList(*pitEntry, &ingress.face);
338 
339  // delete PIT entry's out-record
340  pitEntry->deleteOutRecord(ingress.face);
341  }
342  // when more than one PIT entry is matched, trigger strategy: before satisfy Interest,
343  // and send Data to all matched out faces
344  else {
345  std::set<Face*> pendingDownstreams;
346  auto now = time::steady_clock::now();
347 
348  for (const auto& pitEntry : pitMatches) {
349  NFD_LOG_DEBUG("onIncomingData matching=" << pitEntry->getName());
350 
351  // remember pending downstreams
352  for (const pit::InRecord& inRecord : pitEntry->getInRecords()) {
353  if (inRecord.getExpiry() > now) {
354  pendingDownstreams.insert(&inRecord.getFace());
355  }
356  }
357 
358  // set PIT expiry timer to now
359  this->setExpiryTimer(pitEntry, 0_ms);
360 
361  // invoke PIT satisfy callback
362  m_strategyChoice.findEffectiveStrategy(*pitEntry).beforeSatisfyInterest(data, ingress, pitEntry);
363 
364  // mark PIT satisfied
365  pitEntry->isSatisfied = true;
366  pitEntry->dataFreshnessPeriod = data.getFreshnessPeriod();
367 
368  // Dead Nonce List insert if necessary (for out-record of ingress face)
369  this->insertDeadNonceList(*pitEntry, &ingress.face);
370 
371  // clear PIT entry's in and out records
372  pitEntry->clearInRecords();
373  pitEntry->deleteOutRecord(ingress.face);
374  }
375 
376  // foreach pending downstream
377  for (const auto& pendingDownstream : pendingDownstreams) {
378  if (pendingDownstream->getId() == ingress.face.getId() &&
379  pendingDownstream->getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) {
380  continue;
381  }
382  // goto outgoing Data pipeline
383  this->onOutgoingData(data, *pendingDownstream);
384  }
385  }
386 }
387 
388 void
389 Forwarder::onDataUnsolicited(const Data& data, const FaceEndpoint& ingress)
390 {
391  // accept to cache?
392  auto decision = m_unsolicitedDataPolicy->decide(ingress.face, data);
393  if (decision == fw::UnsolicitedDataDecision::CACHE) {
394  // CS insert
395  m_cs.insert(data, true);
396  }
397 
398  NFD_LOG_DEBUG("onDataUnsolicited in=" << ingress << " data=" << data.getName()
399  << " decision=" << decision);
400  ++m_counters.nUnsolicitedData;
401 }
402 
403 bool
404 Forwarder::onOutgoingData(const Data& data, Face& egress)
405 {
406  if (egress.getId() == face::INVALID_FACEID) {
407  NFD_LOG_WARN("onOutgoingData out=(invalid) data=" << data.getName());
408  return false;
409  }
410  NFD_LOG_DEBUG("onOutgoingData out=" << egress.getId() << " data=" << data.getName());
411 
412  // /localhost scope control
413  bool isViolatingLocalhost = egress.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL &&
414  scope_prefix::LOCALHOST.isPrefixOf(data.getName());
415  if (isViolatingLocalhost) {
416  NFD_LOG_DEBUG("onOutgoingData out=" << egress.getId() << " data=" << data.getName()
417  << " violates /localhost");
418  // drop
419  return false;
420  }
421 
422  // TODO traffic manager
423 
424  // send Data
425  egress.sendData(data);
426  ++m_counters.nOutData;
427 
428  return true;
429 }
430 
431 void
432 Forwarder::onIncomingNack(const lp::Nack& nack, const FaceEndpoint& ingress)
433 {
434  // receive Nack
435  nack.setTag(make_shared<lp::IncomingFaceIdTag>(ingress.face.getId()));
436  ++m_counters.nInNacks;
437 
438  // if multi-access or ad hoc face, drop
439  if (ingress.face.getLinkType() != ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
440  NFD_LOG_DEBUG("onIncomingNack in=" << ingress
441  << " nack=" << nack.getInterest().getName() << "~" << nack.getReason()
442  << " 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->getOutRecord(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() << " wrong-Nonce " << nack.getInterest().getNonce()
468  << "!=" << outRecord->getLastNonce());
469  return;
470  }
471 
472  NFD_LOG_DEBUG("onIncomingNack in=" << ingress << " nack=" << nack.getInterest().getName()
473  << "~" << nack.getReason() << " OK");
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)"
493  << " nack=" << pitEntry->getInterest().getName() << "~" << nack.getReason());
494  return false;
495  }
496 
497  // has in-record?
498  auto inRecord = pitEntry->getInRecord(egress);
499 
500  // if no in-record found, drop
501  if (inRecord == pitEntry->in_end()) {
502  NFD_LOG_DEBUG("onOutgoingNack out=" << egress.getId()
503  << " nack=" << pitEntry->getInterest().getName()
504  << "~" << nack.getReason() << " no-in-record");
505  return false;
506  }
507 
508  // if multi-access or ad hoc face, drop
509  if (egress.getLinkType() != ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
510  NFD_LOG_DEBUG("onOutgoingNack out=" << egress.getId()
511  << " nack=" << pitEntry->getInterest().getName() << "~" << nack.getReason()
512  << " link-type=" << egress.getLinkType());
513  return false;
514  }
515 
516  NFD_LOG_DEBUG("onOutgoingNack out=" << egress.getId()
517  << " nack=" << pitEntry->getInterest().getName()
518  << "~" << nack.getReason() << " OK");
519 
520  // create Nack packet with the Interest from in-record
521  lp::Nack nackPkt(inRecord->getInterest());
522  nackPkt.setHeader(nack);
523 
524  // erase in-record
525  pitEntry->deleteInRecord(egress);
526 
527  // send Nack on face
528  egress.sendNack(nackPkt);
529  ++m_counters.nOutNacks;
530 
531  return true;
532 }
533 
534 void
535 Forwarder::onDroppedInterest(const Interest& interest, Face& egress)
536 {
537  m_strategyChoice.findEffectiveStrategy(interest.getName()).onDroppedInterest(interest, egress);
538 }
539 
540 void
541 Forwarder::onNewNextHop(const Name& prefix, const fib::NextHop& nextHop)
542 {
543  const auto affectedEntries = this->getNameTree().partialEnumerate(prefix,
544  [&] (const name_tree::Entry& nte) -> std::pair<bool, bool> {
545  // we ignore an NTE and skip visiting its descendants if that NTE has an
546  // associated FIB entry (1st condition), since in that case the new nexthop
547  // won't affect any PIT entries anywhere in that subtree, *unless* this is
548  // the initial NTE from which the enumeration started (2nd condition), which
549  // must always be considered
550  if (nte.getFibEntry() != nullptr && nte.getName().size() > prefix.size()) {
551  return {false, false};
552  }
553  return {nte.hasPitEntries(), true};
554  });
555 
556  for (const auto& nte : affectedEntries) {
557  for (const auto& pitEntry : nte.getPitEntries()) {
558  m_strategyChoice.findEffectiveStrategy(*pitEntry).afterNewNextHop(nextHop, pitEntry);
559  }
560  }
561 }
562 
563 void
564 Forwarder::setExpiryTimer(const shared_ptr<pit::Entry>& pitEntry, time::milliseconds duration)
565 {
566  BOOST_ASSERT(pitEntry);
567  duration = std::max(duration, 0_ms);
568 
569  pitEntry->expiryTimer.cancel();
570  pitEntry->expiryTimer = getScheduler().schedule(duration, [=] { onInterestFinalize(pitEntry); });
571 }
572 
573 void
574 Forwarder::insertDeadNonceList(pit::Entry& pitEntry, const Face* upstream)
575 {
576  // need Dead Nonce List insert?
577  bool needDnl = true;
578  if (pitEntry.isSatisfied) {
579  BOOST_ASSERT(pitEntry.dataFreshnessPeriod >= 0_ms);
580  needDnl = pitEntry.getInterest().getMustBeFresh() &&
581  pitEntry.dataFreshnessPeriod < m_deadNonceList.getLifetime();
582  }
583 
584  if (!needDnl) {
585  return;
586  }
587 
588  // Dead Nonce List insert
589  if (upstream == nullptr) {
590  // insert all outgoing Nonces
591  const auto& outRecords = pitEntry.getOutRecords();
592  std::for_each(outRecords.begin(), outRecords.end(), [&] (const auto& outRecord) {
593  m_deadNonceList.add(pitEntry.getName(), outRecord.getLastNonce());
594  });
595  }
596  else {
597  // insert outgoing Nonce of a specific face
598  auto outRecord = pitEntry.getOutRecord(*upstream);
599  if (outRecord != pitEntry.getOutRecords().end()) {
600  m_deadNonceList.add(pitEntry.getName(), outRecord->getLastNonce());
601  }
602  }
603 }
604 
605 void
607 {
608  configFile.addSectionHandler(CFG_FORWARDER, [this] (auto&&... args) {
609  processConfig(std::forward<decltype(args)>(args)...);
610  });
611 }
612 
613 void
614 Forwarder::processConfig(const ConfigSection& configSection, bool isDryRun, const std::string&)
615 {
616  Config config;
617 
618  for (const auto& pair : configSection) {
619  const std::string& key = pair.first;
620  if (key == "default_hop_limit") {
621  config.defaultHopLimit = ConfigFile::parseNumber<uint8_t>(pair, CFG_FORWARDER);
622  }
623  else {
624  NDN_THROW(ConfigFile::Error("Unrecognized option " + CFG_FORWARDER + "." + key));
625  }
626  }
627 
628  if (!isDryRun) {
629  m_config = config;
630  }
631 }
632 
633 } // namespace nfd
This file contains common algorithms used by forwarding strategies.
Configuration file parsing utility.
Definition: config-file.hpp:63
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:40
signal::Signal< FaceTable, Face > beforeRemove
Fires immediately before a face is removed.
Definition: face-table.hpp:87
signal::Signal< FaceTable, Face > afterAdd
Fires immediately after a face is added.
Definition: face-table.hpp:81
Face * get(FaceId id) const noexcept
Get face by FaceId.
Definition: face-table.cpp:40
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.
Generalization of a network interface.
Definition: face.hpp:56
ndn::nfd::FaceScope getScope() const
Returns whether the face is local or non-local for scope control purposes.
Definition: face.hpp:273
signal::Signal< LinkService, Data, EndpointId > & afterReceiveData
Signals on Data received.
Definition: face.hpp:106
signal::Signal< LinkService, Interest > & onDroppedInterest
Signals on Interest dropped by reliability system for exceeding allowed number of retx.
Definition: face.hpp:114
signal::Signal< LinkService, lp::Nack, EndpointId > & afterReceiveNack
Signals on Nack received.
Definition: face.hpp:110
FaceId getId() const noexcept
Returns the face ID.
Definition: face.hpp:121
signal::Signal< LinkService, Interest, EndpointId > & afterReceiveInterest
Signals on Interest received.
Definition: face.hpp:102
const FaceCounters & getCounters() const noexcept
Definition: face.hpp:199
ndn::nfd::LinkType getLinkType() const
Returns the link type of the face (point-to-point, multi-access, ...).
Definition: face.hpp:291
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-nexthop.hpp:37
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:213
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:177
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:206
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:195
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:187
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:77
shared_ptr< Entry > find(const Interest &interest) const
Finds a PIT entry for interest.
Definition: pit.hpp:66
DataMatchResult findAllDataMatches(const Data &data) const
Performs a Data match.
Definition: pit.cpp:85
void erase(Entry *entry)
Deletes an entry.
Definition: pit.hpp:91
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:50
constexpr FaceId FACEID_CONTENT_STORE
Identifies a packet comes from the ContentStore.
Definition: face-common.hpp:54
std::variant< std::monostate, ethernet::Address, udp::Endpoint > EndpointId
Identifies a remote endpoint on the link.
Definition: face-common.hpp:77
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:54
bool hasPendingOutRecords(const pit::Entry &pitEntry)
Determine whether pitEntry has any pending out-records.
Definition: algorithm.cpp:84
void setConfigFile(ConfigFile &config)
std::vector< shared_ptr< Entry > > DataMatchResult
Definition: pit.hpp:43
const Name LOCALHOST
The localhost scope ndn:/localhost.
Definition: common.hpp:77
const std::string CFG_FORWARDER
Definition: forwarder.cpp:43
boost::property_tree::ptree ConfigSection
A configuration file section.
Definition: config-file.hpp:38
void cleanupOnFaceRemoval(NameTree &nt, Fib &fib, Pit &pit, const Face &face)
Cleanup tables when a face is destroyed.
Definition: cleanup.cpp:31
static Name getDefaultStrategyName()
Definition: forwarder.cpp:46
Scheduler & getScheduler()
Returns the global Scheduler instance for the calling thread.
Definition: global.cpp:45