NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.0: NDN, CCN, CCNx, content centric networks
API Documentation
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
forwarder.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
26 #include "forwarder.hpp"
27 #include "core/logger.hpp"
28 #include "core/random.hpp"
29 #include "face/null-face.hpp"
30 #include "available-strategies.hpp"
31 
32 #include <boost/random/uniform_int_distribution.hpp>
33 
34 namespace nfd {
35 
36 NFD_LOG_INIT("Forwarder");
37 
38 using fw::Strategy;
39 
40 const Name Forwarder::LOCALHOST_NAME("ndn:/localhost");
41 
43  : m_faceTable(*this)
44  , m_fib(m_nameTree)
45  , m_pit(m_nameTree)
46  , m_measurements(m_nameTree)
47  , m_strategyChoice(m_nameTree, fw::makeDefaultStrategy(*this))
48  , m_csFace(make_shared<NullFace>(FaceUri("contentstore://")))
49 {
50  fw::installStrategies(*this);
52 }
53 
55 {
56 
57 }
58 
59 void
60 Forwarder::onIncomingInterest(Face& inFace, const Interest& interest)
61 {
62  // receive Interest
63  NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId() <<
64  " interest=" << interest.getName());
65  const_cast<Interest&>(interest).setIncomingFaceId(inFace.getId());
66  ++m_counters.getNInInterests();
67 
68  // /localhost scope control
69  bool isViolatingLocalhost = !inFace.isLocal() &&
70  LOCALHOST_NAME.isPrefixOf(interest.getName());
71  if (isViolatingLocalhost) {
72  NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId() <<
73  " interest=" << interest.getName() << " violates /localhost");
74  // (drop)
75  return;
76  }
77 
78  // PIT insert
79  shared_ptr<pit::Entry> pitEntry = m_pit.insert(interest).first;
80 
81  // detect duplicate Nonce
82  int dnw = pitEntry->findNonce(interest.getNonce(), inFace);
83  bool hasDuplicateNonce = (dnw != pit::DUPLICATE_NONCE_NONE) ||
84  m_deadNonceList.has(interest.getName(), interest.getNonce());
85  if (hasDuplicateNonce) {
86  // goto Interest loop pipeline
87  this->onInterestLoop(inFace, interest, pitEntry);
88  return;
89  }
90 
91  // cancel unsatisfy & straggler timer
92  this->cancelUnsatisfyAndStragglerTimer(pitEntry);
93 
94  // is pending?
95  const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
96  bool isPending = inRecords.begin() != inRecords.end();
97  if (!isPending) {
98  // CS lookup
99  const Data* csMatch;
100  shared_ptr<Data> match;
101  if (m_csFromNdnSim == nullptr)
102  csMatch = m_cs.find(interest);
103  else {
104  match = m_csFromNdnSim->Lookup(interest.shared_from_this());
105  csMatch = match.get();
106  }
107  if (csMatch != 0) {
108  const_cast<Data*>(csMatch)->setIncomingFaceId(FACEID_CONTENT_STORE);
109  // XXX should we lookup PIT for other Interests that also match csMatch?
110 
111  // invoke PIT satisfy callback
112  beforeSatisfyInterest(*pitEntry, *m_csFace, *csMatch);
113  this->dispatchToStrategy(pitEntry, bind(&Strategy::beforeSatisfyInterest, _1,
114  pitEntry, cref(*m_csFace), cref(*csMatch)));
115  // set PIT straggler timer
116  this->setStragglerTimer(pitEntry, true, csMatch->getFreshnessPeriod());
117 
118  // goto outgoing Data pipeline
119  this->onOutgoingData(*csMatch, inFace);
120  return;
121  }
122  }
123 
124  // insert InRecord
125  pitEntry->insertOrUpdateInRecord(inFace.shared_from_this(), interest);
126 
127  // set PIT unsatisfy timer
128  this->setUnsatisfyTimer(pitEntry);
129 
130  // FIB lookup
131  shared_ptr<fib::Entry> fibEntry = m_fib.findLongestPrefixMatch(*pitEntry);
132 
133  // dispatch to strategy
134  this->dispatchToStrategy(pitEntry, bind(&Strategy::afterReceiveInterest, _1,
135  cref(inFace), cref(interest), fibEntry, pitEntry));
136 }
137 
138 void
139 Forwarder::onInterestLoop(Face& inFace, const Interest& interest,
140  shared_ptr<pit::Entry> pitEntry)
141 {
142  NFD_LOG_DEBUG("onInterestLoop face=" << inFace.getId() <<
143  " interest=" << interest.getName());
144 
145  // (drop)
146 }
147 
158 static inline bool
159 compare_pickInterest(const pit::InRecord& a, const pit::InRecord& b, const Face* outFace)
160 {
161  bool isOutFaceA = a.getFace().get() == outFace;
162  bool isOutFaceB = b.getFace().get() == outFace;
163 
164  if (!isOutFaceA && isOutFaceB) {
165  return false;
166  }
167  if (isOutFaceA && !isOutFaceB) {
168  return true;
169  }
170 
171  return a.getLastRenewed() > b.getLastRenewed();
172 }
173 
174 void
175 Forwarder::onOutgoingInterest(shared_ptr<pit::Entry> pitEntry, Face& outFace,
176  bool wantNewNonce)
177 {
178  if (outFace.getId() == INVALID_FACEID) {
179  NFD_LOG_WARN("onOutgoingInterest face=invalid interest=" << pitEntry->getName());
180  return;
181  }
182  NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId() <<
183  " interest=" << pitEntry->getName());
184 
185  // scope control
186  if (pitEntry->violatesScope(outFace)) {
187  NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId() <<
188  " interest=" << pitEntry->getName() << " violates scope");
189  return;
190  }
191 
192  // pick Interest
193  const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
194  pit::InRecordCollection::const_iterator pickedInRecord = std::max_element(
195  inRecords.begin(), inRecords.end(), bind(&compare_pickInterest, _1, _2, &outFace));
196  BOOST_ASSERT(pickedInRecord != inRecords.end());
197  shared_ptr<Interest> interest = const_pointer_cast<Interest>(
198  pickedInRecord->getInterest().shared_from_this());
199 
200  if (wantNewNonce) {
201  interest = make_shared<Interest>(*interest);
202  static boost::random::uniform_int_distribution<uint32_t> dist;
203  interest->setNonce(dist(getGlobalRng()));
204  }
205 
206  // insert OutRecord
207  pitEntry->insertOrUpdateOutRecord(outFace.shared_from_this(), *interest);
208 
209  // send Interest
210  outFace.sendInterest(*interest);
211  ++m_counters.getNOutInterests();
212 }
213 
214 void
215 Forwarder::onInterestReject(shared_ptr<pit::Entry> pitEntry)
216 {
217  if (pitEntry->hasUnexpiredOutRecords()) {
218  NFD_LOG_ERROR("onInterestReject interest=" << pitEntry->getName() <<
219  " cannot reject forwarded Interest");
220  return;
221  }
222  NFD_LOG_DEBUG("onInterestReject interest=" << pitEntry->getName());
223 
224  // cancel unsatisfy & straggler timer
225  this->cancelUnsatisfyAndStragglerTimer(pitEntry);
226 
227  // set PIT straggler timer
228  this->setStragglerTimer(pitEntry, false);
229 }
230 
231 void
232 Forwarder::onInterestUnsatisfied(shared_ptr<pit::Entry> pitEntry)
233 {
234  NFD_LOG_DEBUG("onInterestUnsatisfied interest=" << pitEntry->getName());
235 
236  // invoke PIT unsatisfied callback
237  beforeExpirePendingInterest(*pitEntry);
238  this->dispatchToStrategy(pitEntry, bind(&Strategy::beforeExpirePendingInterest, _1,
239  pitEntry));
240 
241  // goto Interest Finalize pipeline
242  this->onInterestFinalize(pitEntry, false);
243 }
244 
245 void
246 Forwarder::onInterestFinalize(shared_ptr<pit::Entry> pitEntry, bool isSatisfied,
247  const time::milliseconds& dataFreshnessPeriod)
248 {
249  NFD_LOG_DEBUG("onInterestFinalize interest=" << pitEntry->getName() <<
250  (isSatisfied ? " satisfied" : " unsatisfied"));
251 
252  // Dead Nonce List insert if necessary
253  this->insertDeadNonceList(*pitEntry, isSatisfied, dataFreshnessPeriod, 0);
254 
255  // PIT delete
256  this->cancelUnsatisfyAndStragglerTimer(pitEntry);
257  m_pit.erase(pitEntry);
258 }
259 
260 void
261 Forwarder::onIncomingData(Face& inFace, const Data& data)
262 {
263  // receive Data
264  NFD_LOG_DEBUG("onIncomingData face=" << inFace.getId() << " data=" << data.getName());
265  const_cast<Data&>(data).setIncomingFaceId(inFace.getId());
266  ++m_counters.getNInDatas();
267 
268  // /localhost scope control
269  bool isViolatingLocalhost = !inFace.isLocal() &&
270  LOCALHOST_NAME.isPrefixOf(data.getName());
271  if (isViolatingLocalhost) {
272  NFD_LOG_DEBUG("onIncomingData face=" << inFace.getId() <<
273  " data=" << data.getName() << " violates /localhost");
274  // (drop)
275  return;
276  }
277 
278  // PIT match
279  pit::DataMatchResult pitMatches = m_pit.findAllDataMatches(data);
280  if (pitMatches.begin() == pitMatches.end()) {
281  // goto Data unsolicited pipeline
282  this->onDataUnsolicited(inFace, data);
283  return;
284  }
285 
286  // CS insert
287  if (m_csFromNdnSim == nullptr)
288  m_cs.insert(data);
289  else
290  m_csFromNdnSim->Add(data.shared_from_this());
291 
292  std::set<shared_ptr<Face> > pendingDownstreams;
293  // foreach PitEntry
294  for (const shared_ptr<pit::Entry>& pitEntry : pitMatches) {
295  NFD_LOG_DEBUG("onIncomingData matching=" << pitEntry->getName());
296 
297  // cancel unsatisfy & straggler timer
298  this->cancelUnsatisfyAndStragglerTimer(pitEntry);
299 
300  // remember pending downstreams
301  const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
302  for (pit::InRecordCollection::const_iterator it = inRecords.begin();
303  it != inRecords.end(); ++it) {
304  if (it->getExpiry() > time::steady_clock::now()) {
305  pendingDownstreams.insert(it->getFace());
306  }
307  }
308 
309  // invoke PIT satisfy callback
310  beforeSatisfyInterest(*pitEntry, inFace, data);
311  this->dispatchToStrategy(pitEntry, bind(&Strategy::beforeSatisfyInterest, _1,
312  pitEntry, cref(inFace), cref(data)));
313 
314  // Dead Nonce List insert if necessary (for OutRecord of inFace)
315  this->insertDeadNonceList(*pitEntry, true, data.getFreshnessPeriod(), &inFace);
316 
317  // mark PIT satisfied
318  pitEntry->deleteInRecords();
319  pitEntry->deleteOutRecord(inFace);
320 
321  // set PIT straggler timer
322  this->setStragglerTimer(pitEntry, true, data.getFreshnessPeriod());
323  }
324 
325  // foreach pending downstream
326  for (std::set<shared_ptr<Face> >::iterator it = pendingDownstreams.begin();
327  it != pendingDownstreams.end(); ++it) {
328  shared_ptr<Face> pendingDownstream = *it;
329  if (pendingDownstream.get() == &inFace) {
330  continue;
331  }
332  // goto outgoing Data pipeline
333  this->onOutgoingData(data, *pendingDownstream);
334  }
335 }
336 
337 void
338 Forwarder::onDataUnsolicited(Face& inFace, const Data& data)
339 {
340  // accept to cache?
341  bool acceptToCache = inFace.isLocal();
342  if (acceptToCache) {
343  // CS insert
344  if (m_csFromNdnSim == nullptr)
345  m_cs.insert(data, true);
346  else
347  m_csFromNdnSim->Add(data.shared_from_this());
348  }
349 
350  NFD_LOG_DEBUG("onDataUnsolicited face=" << inFace.getId() <<
351  " data=" << data.getName() <<
352  (acceptToCache ? " cached" : " not cached"));
353 }
354 
355 void
356 Forwarder::onOutgoingData(const Data& data, Face& outFace)
357 {
358  if (outFace.getId() == INVALID_FACEID) {
359  NFD_LOG_WARN("onOutgoingData face=invalid data=" << data.getName());
360  return;
361  }
362  NFD_LOG_DEBUG("onOutgoingData face=" << outFace.getId() << " data=" << data.getName());
363 
364  // /localhost scope control
365  bool isViolatingLocalhost = !outFace.isLocal() &&
366  LOCALHOST_NAME.isPrefixOf(data.getName());
367  if (isViolatingLocalhost) {
368  NFD_LOG_DEBUG("onOutgoingData face=" << outFace.getId() <<
369  " data=" << data.getName() << " violates /localhost");
370  // (drop)
371  return;
372  }
373 
374  // TODO traffic manager
375 
376  // send Data
377  outFace.sendData(data);
378  ++m_counters.getNOutDatas();
379 }
380 
381 static inline bool
383 {
384  return a.getExpiry() < b.getExpiry();
385 }
386 
387 void
388 Forwarder::setUnsatisfyTimer(shared_ptr<pit::Entry> pitEntry)
389 {
390  const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
391  pit::InRecordCollection::const_iterator lastExpiring =
392  std::max_element(inRecords.begin(), inRecords.end(),
394 
395  time::steady_clock::TimePoint lastExpiry = lastExpiring->getExpiry();
396  time::nanoseconds lastExpiryFromNow = lastExpiry - time::steady_clock::now();
397  if (lastExpiryFromNow <= time::seconds(0)) {
398  // TODO all InRecords are already expired; will this happen?
399  }
400 
401  scheduler::cancel(pitEntry->m_unsatisfyTimer);
402  pitEntry->m_unsatisfyTimer = scheduler::schedule(lastExpiryFromNow,
403  bind(&Forwarder::onInterestUnsatisfied, this, pitEntry));
404 }
405 
406 void
407 Forwarder::setStragglerTimer(shared_ptr<pit::Entry> pitEntry, bool isSatisfied,
408  const time::milliseconds& dataFreshnessPeriod)
409 {
410  time::nanoseconds stragglerTime = time::milliseconds(100);
411 
412  scheduler::cancel(pitEntry->m_stragglerTimer);
413  pitEntry->m_stragglerTimer = scheduler::schedule(stragglerTime,
414  bind(&Forwarder::onInterestFinalize, this, pitEntry, isSatisfied, dataFreshnessPeriod));
415 }
416 
417 void
418 Forwarder::cancelUnsatisfyAndStragglerTimer(shared_ptr<pit::Entry> pitEntry)
419 {
420  scheduler::cancel(pitEntry->m_unsatisfyTimer);
421  scheduler::cancel(pitEntry->m_stragglerTimer);
422 }
423 
424 static inline void
426  const pit::OutRecord& outRecord)
427 {
428  dnl.add(pitEntry.getName(), outRecord.getLastNonce());
429 }
430 
431 void
432 Forwarder::insertDeadNonceList(pit::Entry& pitEntry, bool isSatisfied,
433  const time::milliseconds& dataFreshnessPeriod,
434  Face* upstream)
435 {
436  // need Dead Nonce List insert?
437  bool needDnl = false;
438  if (isSatisfied) {
439  bool hasFreshnessPeriod = dataFreshnessPeriod >= time::milliseconds::zero();
440  // Data never becomes stale if it doesn't have FreshnessPeriod field
441  needDnl = static_cast<bool>(pitEntry.getInterest().getMustBeFresh()) &&
442  (hasFreshnessPeriod && dataFreshnessPeriod < m_deadNonceList.getLifetime());
443  }
444  else {
445  needDnl = true;
446  }
447 
448  if (!needDnl) {
449  return;
450  }
451 
452  // Dead Nonce List insert
453  if (upstream == 0) {
454  // insert all outgoing Nonces
455  const pit::OutRecordCollection& outRecords = pitEntry.getOutRecords();
456  std::for_each(outRecords.begin(), outRecords.end(),
457  bind(&insertNonceToDnl, ref(m_deadNonceList), cref(pitEntry), _1));
458  }
459  else {
460  // insert outgoing Nonce of a specific face
461  pit::OutRecordCollection::const_iterator outRecord = pitEntry.getOutRecord(*upstream);
462  if (outRecord != pitEntry.getOutRecords().end()) {
463  m_deadNonceList.add(pitEntry.getName(), outRecord->getLastNonce());
464  }
465  }
466 }
467 
468 } // namespace nfd
signal::Signal< Forwarder, pit::Entry, Face, Data > beforeSatisfyInterest
trigger before PIT entry is satisfied
Definition: forwarder.hpp:119
std::list< InRecord > InRecordCollection
represents an unordered collection of InRecords
Definition: pit-entry.hpp:45
void erase(shared_ptr< pit::Entry > pitEntry)
erases a PIT Entry
Definition: pit.cpp:108
represents the Dead Nonce list
#define NFD_LOG_DEBUG(expression)
Definition: logger.hpp:36
bool has(const Name &name, uint32_t nonce) const
determines if name+nonce exists
shared_ptr< fib::Entry > findLongestPrefixMatch(const Name &prefix) const
performs a longest prefix match
Definition: fib.cpp:66
time::steady_clock::TimePoint getLastRenewed() const
void cancel(const EventId &eventId)
cancel a scheduled event
Definition: scheduler.cpp:58
void installStrategies(Forwarder &forwarder)
FaceTable & getFaceTable()
Definition: forwarder.hpp:237
contains information about an Interest from an incoming face
uint32_t getLastNonce() const
#define NFD_LOG_ERROR(expression)
Definition: logger.hpp:38
pit::DataMatchResult findAllDataMatches(const Data &data) const
performs a Data match
Definition: pit.cpp:91
const PacketCounter & getNOutDatas() const
outgoing Data
static bool compare_InRecord_expiry(const pit::InRecord &a, const pit::InRecord &b)
Definition: forwarder.cpp:382
const PacketCounter & getNOutInterests() const
outgoing Interest
shared_ptr< Strategy > makeDefaultStrategy(Forwarder &forwarder)
const Data * find(const Interest &interest) const
finds the best match Data for an Interest
Definition: cs.cpp:407
void add(const Name &name, uint32_t nonce)
records name+nonce
represents a face
Definition: face.hpp:59
#define NFD_LOG_WARN(expression)
Definition: logger.hpp:39
bool insert(const Data &data, bool isUnsolicited=false)
inserts a Data packet This method does not consider the payload of the Data packet.
Definition: cs.cpp:265
static void insertNonceToDnl(DeadNonceList &dnl, const pit::Entry &pitEntry, const pit::OutRecord &outRecord)
Definition: forwarder.cpp:425
represents a PIT entry
Definition: pit-entry.hpp:67
const time::nanoseconds & getLifetime() const
signal::Signal< Forwarder, pit::Entry > beforeExpirePendingInterest
trigger before PIT entry expires
Definition: forwarder.hpp:124
const Name & getName() const
Definition: pit-entry.cpp:41
EventId schedule(const time::nanoseconds &after, const std::function< void()> &event)
schedule an event
Definition: scheduler.cpp:50
boost::random::mt19937 & getGlobalRng()
Definition: random.cpp:33
const PacketCounter & getNInDatas() const
incoming Data
std::pair< shared_ptr< pit::Entry >, bool > insert(const Interest &interest)
inserts a PIT entry for Interest
Definition: pit.cpp:65
a Face that has no underlying transport and drops every packet
Definition: null-face.hpp:36
std::list< OutRecord > OutRecordCollection
represents an unordered collection of OutRecords
Definition: pit-entry.hpp:49
static bool compare_pickInterest(const pit::InRecord &a, const pit::InRecord &b, const Face *outFace)
compare two InRecords for picking outgoing Interest
Definition: forwarder.cpp:159
shared_ptr< Face > getFace() const
#define NFD_LOG_INIT(name)
Definition: logger.hpp:33
const FaceId FACEID_CONTENT_STORE
identifies a packet comes from the ContentStore, in LocalControlHeader incomingFaceId ...
Definition: face.hpp:49
const FaceId INVALID_FACEID
indicates an invalid FaceId
Definition: face.hpp:44
void addReserved(shared_ptr< Face > face, FaceId faceId)
add a special Face with a reserved FaceId
Definition: face-table.cpp:72
contains information about an Interest toward an outgoing face
const PacketCounter & getNInInterests() const
incoming Interest
FaceId getId() const
Definition: face.cpp:51
time::steady_clock::TimePoint getExpiry() const
gives the time point this record expires
bool isLocal() const
Get whether face is connected to a local app.
Definition: face.hpp:216
std::vector< shared_ptr< pit::Entry > > DataMatchResult
Definition: pit.hpp:42