NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.5: NDN, CCN, CCNx, content centric networks
API Documentation
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-2021, 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 #include "face/null-face.hpp"
40 
41 namespace nfd {
42 
44 
45 const std::string CFG_FORWARDER = "forwarder";
46 
47 static Name
49 {
51 }
52 
54  : m_faceTable(faceTable)
55  , m_unsolicitedDataPolicy(make_unique<fw::DefaultUnsolicitedDataPolicy>())
56  , m_fib(m_nameTree)
57  , m_pit(m_nameTree)
58  , m_measurements(m_nameTree)
59  , m_strategyChoice(*this)
60  , m_csFace(face::makeNullFace(FaceUri("contentstore://")))
61 {
62  m_faceTable.addReserved(m_csFace, face::FACEID_CONTENT_STORE);
63 
64  m_faceTable.afterAdd.connect([this] (const Face& face) {
65  face.afterReceiveInterest.connect(
66  [this, &face] (const Interest& interest, const EndpointId& endpointId) {
67  this->onIncomingInterest(interest, FaceEndpoint(const_cast<Face&>(face), endpointId));
68  });
69  face.afterReceiveData.connect(
70  [this, &face] (const Data& data, const EndpointId& endpointId) {
71  this->onIncomingData(data, FaceEndpoint(const_cast<Face&>(face), endpointId));
72  });
73  face.afterReceiveNack.connect(
74  [this, &face] (const lp::Nack& nack, const EndpointId& endpointId) {
75  this->onIncomingNack(nack, FaceEndpoint(const_cast<Face&>(face), endpointId));
76  });
77  face.onDroppedInterest.connect(
78  [this, &face] (const Interest& interest) {
79  this->onDroppedInterest(interest, const_cast<Face&>(face));
80  });
81  });
82 
83  m_faceTable.beforeRemove.connect([this] (const Face& face) {
84  cleanupOnFaceRemoval(m_nameTree, m_fib, m_pit, face);
85  });
86 
87  m_fib.afterNewNextHop.connect([this] (const Name& prefix, const fib::NextHop& nextHop) {
88  this->onNewNextHop(prefix, nextHop);
89  });
90 
91  m_strategyChoice.setDefaultStrategy(getDefaultStrategyName());
92 }
93 
94 Forwarder::~Forwarder() = default;
95 
96 void
97 Forwarder::onIncomingInterest(const Interest& interest, const FaceEndpoint& ingress)
98 {
99  // receive Interest
100  NFD_LOG_DEBUG("onIncomingInterest in=" << ingress << " interest=" << interest.getName());
101  interest.setTag(make_shared<lp::IncomingFaceIdTag>(ingress.face.getId()));
102  ++m_counters.nInInterests;
103 
104  // drop if HopLimit zero, decrement otherwise (if present)
105  if (interest.getHopLimit()) {
106  if (*interest.getHopLimit() == 0) {
107  NFD_LOG_DEBUG("onIncomingInterest in=" << ingress << " interest=" << interest.getName()
108  << " hop-limit=0");
109  ++ingress.face.getCounters().nInHopLimitZero;
110  // drop
111  return;
112  }
113  const_cast<Interest&>(interest).setHopLimit(*interest.getHopLimit() - 1);
114  }
115 
116  // /localhost scope control
117  bool isViolatingLocalhost = ingress.face.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL &&
119  if (isViolatingLocalhost) {
120  NFD_LOG_DEBUG("onIncomingInterest in=" << ingress
121  << " interest=" << interest.getName() << " violates /localhost");
122  // drop
123  return;
124  }
125 
126  // detect duplicate Nonce with Dead Nonce List
127  bool hasDuplicateNonceInDnl = m_deadNonceList.has(interest.getName(), interest.getNonce());
128  if (hasDuplicateNonceInDnl) {
129  // goto Interest loop pipeline
130  this->onInterestLoop(interest, ingress);
131  return;
132  }
133 
134  // strip forwarding hint if Interest has reached producer region
135  if (!interest.getForwardingHint().empty() &&
136  m_networkRegionTable.isInProducerRegion(interest.getForwardingHint())) {
137  NFD_LOG_DEBUG("onIncomingInterest in=" << ingress
138  << " interest=" << interest.getName() << " reaching-producer-region");
139  const_cast<Interest&>(interest).setForwardingHint({});
140  }
141 
142  // PIT insert
143  shared_ptr<pit::Entry> pitEntry = m_pit.insert(interest).first;
144 
145  // detect duplicate Nonce in PIT entry
146  int dnw = fw::findDuplicateNonce(*pitEntry, interest.getNonce(), ingress.face);
147  bool hasDuplicateNonceInPit = dnw != fw::DUPLICATE_NONCE_NONE;
148  if (ingress.face.getLinkType() == ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
149  // for p2p face: duplicate Nonce from same incoming face is not loop
150  hasDuplicateNonceInPit = hasDuplicateNonceInPit && !(dnw & fw::DUPLICATE_NONCE_IN_SAME);
151  }
152  if (hasDuplicateNonceInPit) {
153  // goto Interest loop pipeline
154  this->onInterestLoop(interest, ingress);
155  m_strategyChoice.findEffectiveStrategy(*pitEntry).afterReceiveLoopedInterest(ingress, interest, *pitEntry);
156  return;
157  }
158 
159  // is pending?
160  if (!pitEntry->hasInRecords()) {
161  m_cs.find(interest,
162  [=] (const Interest& i, const Data& d) { onContentStoreHit(i, ingress, pitEntry, d); },
163  [=] (const Interest& i) { onContentStoreMiss(i, ingress, pitEntry); });
164  }
165  else {
166  this->onContentStoreMiss(interest, ingress, pitEntry);
167  }
168 }
169 
170 void
171 Forwarder::onInterestLoop(const Interest& interest, const FaceEndpoint& ingress)
172 {
173  // if multi-access or ad hoc face, drop
174  if (ingress.face.getLinkType() != ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
175  NFD_LOG_DEBUG("onInterestLoop in=" << ingress
176  << " interest=" << interest.getName() << " drop");
177  return;
178  }
179 
180  NFD_LOG_DEBUG("onInterestLoop in=" << ingress << " interest=" << interest.getName()
181  << " send-Nack-duplicate");
182 
183  // send Nack with reason=DUPLICATE
184  // note: Don't enter outgoing Nack pipeline because it needs an in-record.
185  lp::Nack nack(interest);
186  nack.setReason(lp::NackReason::DUPLICATE);
187  ingress.face.sendNack(nack);
188 }
189 
190 void
191 Forwarder::onContentStoreMiss(const Interest& interest, const FaceEndpoint& ingress,
192  const shared_ptr<pit::Entry>& pitEntry)
193 {
194  NFD_LOG_DEBUG("onContentStoreMiss interest=" << interest.getName());
195  ++m_counters.nCsMisses;
196  afterCsMiss(interest);
197 
198  // attach HopLimit if configured and not present in Interest
199  if (m_config.defaultHopLimit > 0 && !interest.getHopLimit()) {
200  const_cast<Interest&>(interest).setHopLimit(m_config.defaultHopLimit);
201  }
202 
203  // insert in-record
204  pitEntry->insertOrUpdateInRecord(ingress.face, interest);
205 
206  // set PIT expiry timer to the time that the last PIT in-record expires
207  auto lastExpiring = std::max_element(pitEntry->in_begin(), pitEntry->in_end(),
208  [] (const auto& a, const auto& b) {
209  return a.getExpiry() < b.getExpiry();
210  });
211  auto lastExpiryFromNow = lastExpiring->getExpiry() - time::steady_clock::now();
212  this->setExpiryTimer(pitEntry, time::duration_cast<time::milliseconds>(lastExpiryFromNow));
213 
214  // has NextHopFaceId?
215  auto nextHopTag = interest.getTag<lp::NextHopFaceIdTag>();
216  if (nextHopTag != nullptr) {
217  // chosen NextHop face exists?
218  Face* nextHopFace = m_faceTable.get(*nextHopTag);
219  if (nextHopFace != nullptr) {
220  NFD_LOG_DEBUG("onContentStoreMiss interest=" << interest.getName()
221  << " nexthop-faceid=" << nextHopFace->getId());
222  // go to outgoing Interest pipeline
223  // scope control is unnecessary, because privileged app explicitly wants to forward
224  this->onOutgoingInterest(interest, *nextHopFace, pitEntry);
225  }
226  return;
227  }
228 
229  // dispatch to strategy: after receive Interest
230  m_strategyChoice.findEffectiveStrategy(*pitEntry)
231  .afterReceiveInterest(interest, FaceEndpoint(ingress.face, 0), pitEntry);
232 }
233 
234 void
235 Forwarder::onContentStoreHit(const Interest& interest, const FaceEndpoint& ingress,
236  const shared_ptr<pit::Entry>& pitEntry, const Data& data)
237 {
238  NFD_LOG_DEBUG("onContentStoreHit interest=" << interest.getName());
239  ++m_counters.nCsHits;
240  afterCsHit(interest, data);
241 
242  data.setTag(make_shared<lp::IncomingFaceIdTag>(face::FACEID_CONTENT_STORE));
243  data.setTag(interest.getTag<lp::PitToken>());
244  // FIXME Should we lookup PIT for other Interests that also match the data?
245 
246  pitEntry->isSatisfied = true;
247  pitEntry->dataFreshnessPeriod = data.getFreshnessPeriod();
248 
249  // set PIT expiry timer to now
250  this->setExpiryTimer(pitEntry, 0_ms);
251 
252  beforeSatisfyInterest(*pitEntry, *m_csFace, data);
253  m_strategyChoice.findEffectiveStrategy(*pitEntry).beforeSatisfyInterest(data, FaceEndpoint(*m_csFace, 0), pitEntry);
254 
255  // dispatch to strategy: after Content Store hit
256  m_strategyChoice.findEffectiveStrategy(*pitEntry).afterContentStoreHit(data, ingress, pitEntry);
257 }
258 
260 Forwarder::onOutgoingInterest(const Interest& interest, Face& egress,
261  const shared_ptr<pit::Entry>& pitEntry)
262 {
263  // drop if HopLimit == 0 but sending on non-local face
264  if (interest.getHopLimit() == 0 && egress.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL) {
265  NFD_LOG_DEBUG("onOutgoingInterest out=" << egress.getId() << " interest=" << pitEntry->getName()
266  << " non-local hop-limit=0");
267  ++egress.getCounters().nOutHopLimitZero;
268  return nullptr;
269  }
270 
271  NFD_LOG_DEBUG("onOutgoingInterest out=" << egress.getId() << " interest=" << pitEntry->getName());
272 
273  // insert out-record
274  auto it = pitEntry->insertOrUpdateOutRecord(egress, interest);
275  BOOST_ASSERT(it != pitEntry->out_end());
276 
277  // send Interest
278  egress.sendInterest(interest);
279  ++m_counters.nOutInterests;
280  return &*it;
281 }
282 
283 void
284 Forwarder::onInterestFinalize(const shared_ptr<pit::Entry>& pitEntry)
285 {
286  NFD_LOG_DEBUG("onInterestFinalize interest=" << pitEntry->getName()
287  << (pitEntry->isSatisfied ? " satisfied" : " unsatisfied"));
288 
289  if (!pitEntry->isSatisfied) {
290  beforeExpirePendingInterest(*pitEntry);
291  }
292 
293  // Dead Nonce List insert if necessary
294  this->insertDeadNonceList(*pitEntry, nullptr);
295 
296  // Increment satisfied/unsatisfied Interests counter
297  if (pitEntry->isSatisfied) {
298  ++m_counters.nSatisfiedInterests;
299  }
300  else {
301  ++m_counters.nUnsatisfiedInterests;
302  }
303 
304  // PIT delete
305  pitEntry->expiryTimer.cancel();
306  m_pit.erase(pitEntry.get());
307 }
308 
309 void
310 Forwarder::onIncomingData(const Data& data, const FaceEndpoint& ingress)
311 {
312  // receive Data
313  NFD_LOG_DEBUG("onIncomingData in=" << ingress << " data=" << data.getName());
314  data.setTag(make_shared<lp::IncomingFaceIdTag>(ingress.face.getId()));
315  ++m_counters.nInData;
316 
317  // /localhost scope control
318  bool isViolatingLocalhost = ingress.face.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL &&
320  if (isViolatingLocalhost) {
321  NFD_LOG_DEBUG("onIncomingData in=" << ingress << " data=" << data.getName() << " violates /localhost");
322  // drop
323  return;
324  }
325 
326  // PIT match
327  pit::DataMatchResult pitMatches = m_pit.findAllDataMatches(data);
328  if (pitMatches.size() == 0) {
329  // goto Data unsolicited pipeline
330  this->onDataUnsolicited(data, ingress);
331  return;
332  }
333 
334  // CS insert
335  m_cs.insert(data);
336 
337  std::set<std::pair<Face*, EndpointId>> satisfiedDownstreams;
338  std::multimap<std::pair<Face*, EndpointId>, std::shared_ptr<pit::Entry>> unsatisfiedPitEntries;
339 
340  for (const auto& pitEntry : pitMatches) {
341  NFD_LOG_DEBUG("onIncomingData matching=" << pitEntry->getName());
342 
343  // invoke PIT satisfy callback
344  beforeSatisfyInterest(*pitEntry, ingress.face, data);
345 
346  std::set<std::pair<Face*, EndpointId>> unsatisfiedDownstreams;
347  m_strategyChoice.findEffectiveStrategy(*pitEntry).satisfyInterest(pitEntry, ingress, data,
348  satisfiedDownstreams, unsatisfiedDownstreams);
349  for (const auto& endpoint : unsatisfiedDownstreams) {
350  unsatisfiedPitEntries.emplace(endpoint, pitEntry);
351  }
352 
353  if (unsatisfiedDownstreams.empty()) {
354  // set PIT expiry timer to now
355  this->setExpiryTimer(pitEntry, 0_ms);
356 
357  // mark PIT satisfied
358  pitEntry->isSatisfied = true;
359  }
360 
361  // Dead Nonce List insert if necessary (for out-record of inFace)
362  this->insertDeadNonceList(*pitEntry, &ingress.face);
363 
364  pitEntry->dataFreshnessPeriod = data.getFreshnessPeriod();
365 
366  // clear PIT entry's in and out records
367  for (const auto& endpoint : satisfiedDownstreams) {
368  pitEntry->deleteInRecord(*endpoint.first);
369  }
370  pitEntry->deleteOutRecord(ingress.face);
371  }
372 
373  // now check all unsatisfied entries against to be satisfied downstreams, in case there is
374  // intersect, and those PIT entries will be actually satisfied regardless strategy's choice
375  for (const auto& unsatisfied : unsatisfiedPitEntries) {
376  auto downstreamIt = satisfiedDownstreams.find(unsatisfied.first);
377  if (downstreamIt != satisfiedDownstreams.end()) {
378  auto pitEntry = unsatisfied.second;
379  pitEntry->deleteInRecord(*unsatisfied.first.first);
380 
381  if (pitEntry->getInRecords().empty()) { // if nothing left, "closing down" the entry
382  // set PIT expiry timer to now
383  this->setExpiryTimer(pitEntry, 0_ms);
384 
385  // mark PIT satisfied
386  pitEntry->isSatisfied = true;
387  }
388  }
389  }
390 
391  // foreach pending downstream
392  for (const auto& downstream : satisfiedDownstreams) {
393  if (downstream.first->getId() == ingress.face.getId() &&
394  downstream.second == ingress.endpoint &&
395  downstream.first->getLinkType() != ndn::nfd::LINK_TYPE_AD_HOC) {
396  continue;
397  }
398 
399  this->onOutgoingData(data, *downstream.first);
400  }
401 }
402 
403 void
404 Forwarder::onDataUnsolicited(const Data& data, const FaceEndpoint& ingress)
405 {
406  // accept to cache?
407  auto decision = m_unsolicitedDataPolicy->decide(ingress.face, data);
408  if (decision == fw::UnsolicitedDataDecision::CACHE) {
409  // CS insert
410  m_cs.insert(data, true);
411  }
412 
413  NFD_LOG_DEBUG("onDataUnsolicited in=" << ingress << " data=" << data.getName()
414  << " decision=" << decision);
415  ++m_counters.nUnsolicitedData;
416 }
417 
418 bool
419 Forwarder::onOutgoingData(const Data& data, Face& egress)
420 {
421  if (egress.getId() == face::INVALID_FACEID) {
422  NFD_LOG_WARN("onOutgoingData out=(invalid) data=" << data.getName());
423  return false;
424  }
425  NFD_LOG_DEBUG("onOutgoingData out=" << egress.getId() << " data=" << data.getName());
426 
427  // /localhost scope control
428  bool isViolatingLocalhost = egress.getScope() == ndn::nfd::FACE_SCOPE_NON_LOCAL &&
430  if (isViolatingLocalhost) {
431  NFD_LOG_DEBUG("onOutgoingData out=" << egress.getId() << " data=" << data.getName()
432  << " violates /localhost");
433  // drop
434  return false;
435  }
436 
437  // TODO traffic manager
438 
439  // send Data
440  egress.sendData(data);
441  ++m_counters.nOutData;
442 
443  return true;
444 }
445 
446 void
447 Forwarder::onIncomingNack(const lp::Nack& nack, const FaceEndpoint& ingress)
448 {
449  // receive Nack
450  nack.setTag(make_shared<lp::IncomingFaceIdTag>(ingress.face.getId()));
451  ++m_counters.nInNacks;
452 
453  // if multi-access or ad hoc face, drop
454  if (ingress.face.getLinkType() != ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
455  NFD_LOG_DEBUG("onIncomingNack in=" << ingress
456  << " nack=" << nack.getInterest().getName() << "~" << nack.getReason()
457  << " link-type=" << ingress.face.getLinkType());
458  return;
459  }
460 
461  // PIT match
462  shared_ptr<pit::Entry> pitEntry = m_pit.find(nack.getInterest());
463  // if no PIT entry found, drop
464  if (pitEntry == nullptr) {
465  NFD_LOG_DEBUG("onIncomingNack in=" << ingress << " nack=" << nack.getInterest().getName()
466  << "~" << nack.getReason() << " no-PIT-entry");
467  return;
468  }
469 
470  // has out-record?
471  auto outRecord = pitEntry->getOutRecord(ingress.face);
472  // if no out-record found, drop
473  if (outRecord == pitEntry->out_end()) {
474  NFD_LOG_DEBUG("onIncomingNack in=" << ingress << " nack=" << nack.getInterest().getName()
475  << "~" << nack.getReason() << " no-out-record");
476  return;
477  }
478 
479  // if out-record has different Nonce, drop
480  if (nack.getInterest().getNonce() != outRecord->getLastNonce()) {
481  NFD_LOG_DEBUG("onIncomingNack in=" << ingress << " nack=" << nack.getInterest().getName()
482  << "~" << nack.getReason() << " wrong-Nonce " << nack.getInterest().getNonce()
483  << "!=" << outRecord->getLastNonce());
484  return;
485  }
486 
487  NFD_LOG_DEBUG("onIncomingNack in=" << ingress << " nack=" << nack.getInterest().getName()
488  << "~" << nack.getReason() << " OK");
489 
490  // record Nack on out-record
491  outRecord->setIncomingNack(nack);
492 
493  // set PIT expiry timer to now when all out-record receive Nack
494  if (!fw::hasPendingOutRecords(*pitEntry)) {
495  this->setExpiryTimer(pitEntry, 0_ms);
496  }
497 
498  // trigger strategy: after receive NACK
499  m_strategyChoice.findEffectiveStrategy(*pitEntry).afterReceiveNack(nack, ingress, pitEntry);
500 }
501 
502 bool
504  const shared_ptr<pit::Entry>& pitEntry)
505 {
506  if (egress.getId() == face::INVALID_FACEID) {
507  NFD_LOG_WARN("onOutgoingNack out=(invalid)"
508  << " nack=" << pitEntry->getInterest().getName() << "~" << nack.getReason());
509  return false;
510  }
511 
512  // has in-record?
513  auto inRecord = pitEntry->getInRecord(egress);
514 
515  // if no in-record found, drop
516  if (inRecord == pitEntry->in_end()) {
517  NFD_LOG_DEBUG("onOutgoingNack out=" << egress.getId()
518  << " nack=" << pitEntry->getInterest().getName()
519  << "~" << nack.getReason() << " no-in-record");
520  return false;
521  }
522 
523  // if multi-access or ad hoc face, drop
524  if (egress.getLinkType() != ndn::nfd::LINK_TYPE_POINT_TO_POINT) {
525  NFD_LOG_DEBUG("onOutgoingNack out=" << egress.getId()
526  << " nack=" << pitEntry->getInterest().getName() << "~" << nack.getReason()
527  << " link-type=" << egress.getLinkType());
528  return false;
529  }
530 
531  NFD_LOG_DEBUG("onOutgoingNack out=" << egress.getId()
532  << " nack=" << pitEntry->getInterest().getName()
533  << "~" << nack.getReason() << " OK");
534 
535  // create Nack packet with the Interest from in-record
536  lp::Nack nackPkt(inRecord->getInterest());
537  nackPkt.setHeader(nack);
538 
539  // erase in-record
540  pitEntry->deleteInRecord(egress);
541 
542  // send Nack on face
543  egress.sendNack(nackPkt);
544  ++m_counters.nOutNacks;
545 
546  return true;
547 }
548 
549 void
550 Forwarder::onDroppedInterest(const Interest& interest, Face& egress)
551 {
552  m_strategyChoice.findEffectiveStrategy(interest.getName()).onDroppedInterest(interest, egress);
553 }
554 
555 void
556 Forwarder::onNewNextHop(const Name& prefix, const fib::NextHop& nextHop)
557 {
558  const auto affectedEntries = this->getNameTree().partialEnumerate(prefix,
559  [&] (const name_tree::Entry& nte) -> std::pair<bool, bool> {
560  // we ignore an NTE and skip visiting its descendants if that NTE has an
561  // associated FIB entry (1st condition), since in that case the new nexthop
562  // won't affect any PIT entries anywhere in that subtree, *unless* this is
563  // the initial NTE from which the enumeration started (2nd condition), which
564  // must always be considered
565  if (nte.getFibEntry() != nullptr && nte.getName().size() > prefix.size()) {
566  return {false, false};
567  }
568  return {nte.hasPitEntries(), true};
569  });
570 
571  for (const auto& nte : affectedEntries) {
572  for (const auto& pitEntry : nte.getPitEntries()) {
573  m_strategyChoice.findEffectiveStrategy(*pitEntry).afterNewNextHop(nextHop, pitEntry);
574  }
575  }
576 }
577 
578 void
579 Forwarder::setExpiryTimer(const shared_ptr<pit::Entry>& pitEntry, time::milliseconds duration)
580 {
581  BOOST_ASSERT(pitEntry);
582  duration = std::max(duration, 0_ms);
583 
584  pitEntry->expiryTimer.cancel();
585  pitEntry->expiryTimer = getScheduler().schedule(duration, [=] { onInterestFinalize(pitEntry); });
586 }
587 
588 void
589 Forwarder::insertDeadNonceList(pit::Entry& pitEntry, const Face* upstream)
590 {
591  // need Dead Nonce List insert?
592  bool needDnl = true;
593  if (pitEntry.isSatisfied) {
594  BOOST_ASSERT(pitEntry.dataFreshnessPeriod >= 0_ms);
595  needDnl = pitEntry.getInterest().getMustBeFresh() &&
596  pitEntry.dataFreshnessPeriod < m_deadNonceList.getLifetime();
597  }
598 
599  if (!needDnl) {
600  return;
601  }
602 
603  // Dead Nonce List insert
604  if (upstream == nullptr) {
605  // insert all outgoing Nonces
606  const auto& outRecords = pitEntry.getOutRecords();
607  std::for_each(outRecords.begin(), outRecords.end(), [&] (const auto& outRecord) {
608  m_deadNonceList.add(pitEntry.getName(), outRecord.getLastNonce());
609  });
610  }
611  else {
612  // insert outgoing Nonce of a specific face
613  auto outRecord = pitEntry.getOutRecord(*upstream);
614  if (outRecord != pitEntry.getOutRecords().end()) {
615  m_deadNonceList.add(pitEntry.getName(), outRecord->getLastNonce());
616  }
617  }
618 }
619 
620 void
622 {
623  configFile.addSectionHandler(CFG_FORWARDER, [this] (auto&&... args) {
624  processConfig(std::forward<decltype(args)>(args)...);
625  });
626 }
627 
628 void
629 Forwarder::processConfig(const ConfigSection& configSection, bool isDryRun, const std::string&)
630 {
631  Config config;
632 
633  for (const auto& pair : configSection) {
634  const std::string& key = pair.first;
635  if (key == "default_hop_limit") {
636  config.defaultHopLimit = ConfigFile::parseNumber<uint8_t>(pair, CFG_FORWARDER);
637  }
638  else {
639  NDN_THROW(ConfigFile::Error("Unrecognized option " + CFG_FORWARDER + "." + key));
640  }
641  }
642 
643  if (!isDryRun) {
644  m_config = config;
645  }
646 }
647 
648 } // namespace nfd
NFD_VIRTUAL_WITH_TESTS ~Forwarder()
virtual void satisfyInterest(const shared_ptr< pit::Entry > &pitEntry, const FaceEndpoint &ingress, const Data &data, std::set< std::pair< Face *, EndpointId >> &satisfiedDownstreams, std::set< std::pair< Face *, EndpointId >> &unsatisfiedDownstreams)
Definition: strategy.cpp:171
signal::Signal< Forwarder, pit::Entry, Face, Data > beforeSatisfyInterest
trigger before PIT entry is satisfied
Definition: forwarder.hpp:138
bool isSatisfied
Indicates whether this PIT entry is satisfied.
Definition: pit-entry.hpp:227
bool getMustBeFresh() const noexcept
Check whether the MustBeFresh element is present.
Definition: interest.hpp:205
void setTag(shared_ptr< T > tag) const
set a tag item
Definition: tag-host.hpp:79
NFD_VIRTUAL_WITH_TESTS void onContentStoreHit(const Interest &interest, const FaceEndpoint &ingress, const shared_ptr< pit::Entry > &pitEntry, const Data &data)
Content Store hit pipeline.
Definition: forwarder.cpp:235
virtual void afterNewNextHop(const fib::NextHop &nextHop, const shared_ptr< pit::Entry > &pitEntry)
Trigger after a new nexthop is added.
Definition: strategy.cpp:230
void erase(Entry *entry)
Deletes an entry.
Definition: pit.hpp:91
NFD_PUBLIC_WITH_TESTS_ELSE_PRIVATE const FaceEndpoint & ingress
Definition: forwarder.hpp:159
void cleanupOnFaceRemoval(NameTree &nt, Fib &fib, Pit &pit, const Face &face)
cleanup tables when a face is destroyed
Definition: cleanup.cpp:31
shared_ptr< Face > makeNullFace(const FaceUri &uri)
Definition: null-face.cpp:34
shared_ptr< T > getTag() const
get a tag item
Definition: tag-host.hpp:66
const std::string CFG_FORWARDER
Definition: forwarder.cpp:45
OutRecordCollection::iterator getOutRecord(const Face &face)
get the out-record for face
Definition: pit-entry.cpp:91
#define NFD_LOG_INIT(name)
Definition: logger.hpp:31
time::nanoseconds getLifetime() const
Returns the expected nonce lifetime.
const Name LOCALHOST("ndn:/localhost")
ndn:/localhost
span< const Name > getForwardingHint() const noexcept
Definition: interest.hpp:222
const std::vector< shared_ptr< pit::Entry > > & getPitEntries() const
Represents a face-endpoint pair in the forwarder.
static time_point now() noexcept
Definition: time.cpp:80
Nack & setHeader(const NackHeader &header)
Definition: nack.hpp:75
bool isPrefixOf(const Name &other) const
Check if this name is a prefix of another name.
Definition: name.cpp:300
configuration file parsing utility
Definition: config-file.hpp:57
fib::Entry * getFibEntry() const
NFD_VIRTUAL_WITH_TESTS void onDroppedInterest(const Interest &interest, Face &egress)
Definition: forwarder.cpp:550
NFD_VIRTUAL_WITH_TESTS void onIncomingData(const Data &data, const FaceEndpoint &ingress)
incoming Data pipeline
Definition: forwarder.cpp:310
NFD_VIRTUAL_WITH_TESTS void onNewNextHop(const Name &prefix, const fib::NextHop &nextHop)
Definition: forwarder.cpp:556
Main class of NFD&#39;s forwarding engine.
Definition: forwarder.hpp:53
boost::chrono::duration< Rep, Period > duration
Definition: time.hpp:34
Forwarder(FaceTable &faceTable)
Definition: forwarder.cpp:53
NFD_VIRTUAL_WITH_TESTS void onDataUnsolicited(const Data &data, const FaceEndpoint &ingress)
Data unsolicited pipeline.
Definition: forwarder.cpp:404
Represents an Interest packet.
Definition: interest.hpp:48
void add(const Name &name, Interest::Nonce nonce)
Adds name+nonce to the list.
DataMatchResult findAllDataMatches(const Data &data) const
Performs a Data match.
Definition: pit.cpp:86
NFD_VIRTUAL_WITH_TESTS pit::OutRecord * onOutgoingInterest(const Interest &interest, Face &egress, const shared_ptr< pit::Entry > &pitEntry)
outgoing Interest pipeline
Definition: forwarder.cpp:260
uint64_t EndpointId
Identifies a remote endpoint on the link.
Definition: face-common.hpp:71
virtual void afterContentStoreHit(const Data &data, const FaceEndpoint &ingress, const shared_ptr< pit::Entry > &pitEntry)
trigger after a Data is matched in CS
Definition: strategy.cpp:196
bool isInProducerRegion(span< const Name > forwardingHint) const
determines whether an Interest has reached a producer region
NFD_VIRTUAL_WITH_TESTS bool onOutgoingNack(const lp::NackHeader &nack, Face &egress, const shared_ptr< pit::Entry > &pitEntry)
outgoing Nack pipeline
Definition: forwarder.cpp:503
Face * get(FaceId id) const
get face by FaceId
Definition: face-table.cpp:45
NackReason getReason() const
represents a Network Nack
Definition: nack.hpp:38
#define NDN_THROW(e)
Definition: exception.hpp:61
signal::Signal< FaceTable, Face > beforeRemove
Fires immediately before a face is removed.
Definition: face-table.hpp:91
NackReason getReason() const
Definition: nack.hpp:90
int findDuplicateNonce(const pit::Entry &pitEntry, Interest::Nonce nonce, const Face &face)
determine whether pitEntry has duplicate Nonce nonce
Definition: algorithm.cpp:55
virtual void afterReceiveLoopedInterest(const FaceEndpoint &ingress, const Interest &interest, pit::Entry &pitEntry)
trigger after a looped Interest is received
Definition: strategy.cpp:155
Scheduler & getScheduler()
Returns the global Scheduler instance for the calling thread.
Definition: global.cpp:70
Range partialEnumerate(const Name &prefix, const EntrySubTreeSelector &entrySubTreeSelector=AnyEntrySubTree()) const
Enumerate all entries under a prefix.
Definition: name-tree.cpp:232
provides a tag type for simple types
Definition: tag.hpp:58
in-record of same face
Definition: algorithm.hpp:49
static const Name & getStrategyName()
ndn Face
Definition: face-impl.hpp:42
DropAllUnsolicitedDataPolicy DefaultUnsolicitedDataPolicy
The default UnsolicitedDataPolicy.
NFD_VIRTUAL_WITH_TESTS bool onOutgoingData(const Data &data, Face &egress)
outgoing Data pipeline
Definition: forwarder.cpp:419
virtual void afterReceiveInterest(const Interest &interest, const FaceEndpoint &ingress, const shared_ptr< pit::Entry > &pitEntry)=0
Trigger after an Interest is received.
container of all faces
Definition: face-table.hpp:38
void insert(const Data &data, bool isUnsolicited=false)
inserts a Data packet
Definition: cs.cpp:51
Copyright (c) 2011-2015 Regents of the University of California.
Definition: ndn-common.hpp:39
void addSectionHandler(const std::string &sectionName, ConfigSectionHandler subscriber)
setup notification of configuration file sections
Definition: config-file.cpp:77
An Interest table entry.
Definition: pit-entry.hpp:58
NFD_VIRTUAL_WITH_TESTS void onIncomingNack(const lp::Nack &nack, const FaceEndpoint &ingress)
incoming Nack pipeline
Definition: forwarder.cpp:447
signal::Signal< Forwarder, pit::Entry > beforeExpirePendingInterest
trigger before PIT entry expires
Definition: forwarder.hpp:143
#define NFD_LOG_DEBUG
Definition: logger.hpp:38
NFD_VIRTUAL_WITH_TESTS void onInterestLoop(const Interest &interest, const FaceEndpoint &ingress)
Interest loop pipeline.
Definition: forwarder.cpp:171
std::pair< shared_ptr< Entry >, bool > insert(const Interest &interest)
Inserts a PIT entry for interest.
Definition: pit.hpp:77
bool has(const Name &name, Interest::Nonce nonce) const
Determines if name+nonce is in the list.
fw::Strategy & findEffectiveStrategy(const Name &prefix) const
Get effective strategy for prefix.
signal::Signal< Fib, Name, NextHop > afterNewNextHop
signals on Fib entry nexthop creation
Definition: fib.hpp:151
Nonce getNonce() const
Get nonce value.
Definition: interest.cpp:402
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:217
const Name & getName() const noexcept
Get name.
Definition: data.hpp:127
signal::Signal< Forwarder, Interest > afterCsMiss
Signals when the incoming interest pipeline gets a miss from the content store.
Definition: forwarder.hpp:151
signal::Signal< Forwarder, Interest, Data > afterCsHit
Signals when the incoming interest pipeline gets a hit from the content store.
Definition: forwarder.hpp:147
boost::property_tree::ptree ConfigSection
a config file section
optional< uint8_t > getHopLimit() const noexcept
Definition: interest.hpp:273
Represents an absolute name.
Definition: name.hpp:41
void setDefaultStrategy(const Name &strategyName)
Set the default strategy.
represents the underlying protocol and address used by a Face
Definition: face-uri.hpp:44
bool hasPendingOutRecords(const pit::Entry &pitEntry)
determine whether pitEntry has any pending out-records
Definition: algorithm.cpp:85
const Interest & getInterest() const
Definition: pit-entry.hpp:70
no duplicate Nonce is found
Definition: algorithm.hpp:48
size_t size() const
Returns the number of components.
Definition: name.hpp:151
static Name getDefaultStrategyName()
Definition: forwarder.cpp:48
#define NFD_LOG_WARN
Definition: logger.hpp:40
This file contains common algorithms used by forwarding strategies.
const Name & getName() const noexcept
Definition: interest.hpp:172
An unordered iterable of all PIT entries matching Data.
const Name & getName() const
Definition: pit-entry.hpp:78
void addReserved(shared_ptr< Face > face, FaceId faceId)
add a special face with a reserved FaceId
Definition: face-table.cpp:71
Contains information about an Interest toward an outgoing face.
the Data should be cached in the ContentStore
time::milliseconds dataFreshnessPeriod
Data freshness period.
Definition: pit-entry.hpp:232
NFD_VIRTUAL_WITH_TESTS void onContentStoreMiss(const Interest &interest, const FaceEndpoint &ingress, const shared_ptr< pit::Entry > &pitEntry)
Content Store miss pipeline.
Definition: forwarder.cpp:191
shared_ptr< Entry > find(const Interest &interest) const
Finds a PIT entry for interest.
Definition: pit.hpp:66
const EndpointId endpoint
time::milliseconds getFreshnessPeriod() const
Definition: data.hpp:284
Represents a Data packet.
Definition: data.hpp:37
const FaceId FACEID_CONTENT_STORE
identifies a packet comes from the ContentStore
Definition: face-common.hpp:51
represents a Network NACK header
Definition: nack-header.hpp:57
NameTree & getNameTree()
Definition: forwarder.hpp:82
void find(const Interest &interest, HitCallback &&hit, MissCallback &&miss) const
finds the best matching Data packet
Definition: cs.hpp:81
const FaceId INVALID_FACEID
indicates an invalid FaceId
Definition: face-common.hpp:47
const Name & getName() const
PacketCounter nUnsatisfiedInterests
void setConfigFile(ConfigFile &configFile)
register handler for forwarder section of NFD configuration file
Definition: forwarder.cpp:621
const Interest & getInterest() const
Definition: nack.hpp:51
const OutRecordCollection & getOutRecords() const
Definition: pit-entry.hpp:160
virtual void beforeSatisfyInterest(const Data &data, const FaceEndpoint &ingress, const shared_ptr< pit::Entry > &pitEntry)
trigger before PIT entry is satisfied
Definition: strategy.cpp:163
NFD_VIRTUAL_WITH_TESTS void onInterestFinalize(const shared_ptr< pit::Entry > &pitEntry)
Interest finalize pipeline.
Definition: forwarder.cpp:284
An entry in the name tree.
represent a PIT token field
Definition: pit-token.hpp:34
signal::Signal< FaceTable, Face > afterAdd
Fires immediately after a face is added.
Definition: face-table.hpp:85
Represents a nexthop record in a FIB entry.
Definition: fib-nexthop.hpp:37
boost::chrono::milliseconds milliseconds
Definition: time.hpp:48