NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.0: 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; -*- */
26 #include "forwarder.hpp"
27 #include "core/logger.hpp"
28 #include "core/random.hpp"
29 #include "strategy.hpp"
30 #include "face/null-face.hpp"
31 
33 
34 #include <boost/random/uniform_int_distribution.hpp>
35 
36 namespace nfd {
37 
38 NFD_LOG_INIT("Forwarder");
39 
40 using fw::Strategy;
41 
42 const Name Forwarder::LOCALHOST_NAME("ndn:/localhost");
43 
45  : m_faceTable(*this)
46  , m_fib(m_nameTree)
47  , m_pit(m_nameTree)
48  , m_measurements(m_nameTree)
49  , m_strategyChoice(m_nameTree, fw::makeDefaultStrategy(*this))
50  , m_csFace(make_shared<NullFace>(FaceUri("contentstore://")))
51 {
52  fw::installStrategies(*this);
54 }
55 
57 {
58 
59 }
60 
61 void
62 Forwarder::onIncomingInterest(Face& inFace, const Interest& interest)
63 {
64  // receive Interest
65  NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId() <<
66  " interest=" << interest.getName());
67  const_cast<Interest&>(interest).setIncomingFaceId(inFace.getId());
68  ++m_counters.getNInInterests();
69 
70  // /localhost scope control
71  bool isViolatingLocalhost = !inFace.isLocal() &&
72  LOCALHOST_NAME.isPrefixOf(interest.getName());
73  if (isViolatingLocalhost) {
74  NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId() <<
75  " interest=" << interest.getName() << " violates /localhost");
76  // (drop)
77  return;
78  }
79 
80  // PIT insert
81  shared_ptr<pit::Entry> pitEntry = m_pit.insert(interest).first;
82 
83  // detect duplicate Nonce
84  int dnw = pitEntry->findNonce(interest.getNonce(), inFace);
85  bool hasDuplicateNonce = (dnw != pit::DUPLICATE_NONCE_NONE) ||
86  m_deadNonceList.has(interest.getName(), interest.getNonce());
87  if (hasDuplicateNonce) {
88  // goto Interest loop pipeline
89  this->onInterestLoop(inFace, interest, pitEntry);
90  return;
91  }
92 
93  // cancel unsatisfy & straggler timer
94  this->cancelUnsatisfyAndStragglerTimer(pitEntry);
95 
96  // is pending?
97  const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
98  bool isPending = inRecords.begin() != inRecords.end();
99  if (!isPending) {
100  if (m_csFromNdnSim == nullptr) {
101  m_cs.find(interest,
102  bind(&Forwarder::onContentStoreHit, this, ref(inFace), pitEntry, _1, _2),
103  bind(&Forwarder::onContentStoreMiss, this, ref(inFace), pitEntry, _1));
104  }
105  else {
106  shared_ptr<Data> match = m_csFromNdnSim->Lookup(interest.shared_from_this());
107  if (match != nullptr) {
108  this->onContentStoreHit(inFace, pitEntry, interest, *match);
109  }
110  else {
111  this->onContentStoreMiss(inFace, pitEntry, interest);
112  }
113  }
114  }
115  else {
116  this->onContentStoreMiss(inFace, pitEntry, interest);
117  }
118 }
119 
120 void
121 Forwarder::onContentStoreMiss(const Face& inFace,
122  shared_ptr<pit::Entry> pitEntry,
123  const Interest& interest)
124 {
125  NFD_LOG_DEBUG("onContentStoreMiss interest=" << interest.getName());
126 
127  shared_ptr<Face> face = const_pointer_cast<Face>(inFace.shared_from_this());
128  // insert InRecord
129  pitEntry->insertOrUpdateInRecord(face, interest);
130 
131  // set PIT unsatisfy timer
132  this->setUnsatisfyTimer(pitEntry);
133 
134  // FIB lookup
135  shared_ptr<fib::Entry> fibEntry = m_fib.findLongestPrefixMatch(*pitEntry);
136 
137  // dispatch to strategy
138  this->dispatchToStrategy(pitEntry, bind(&Strategy::afterReceiveInterest, _1,
139  cref(inFace), cref(interest), fibEntry, pitEntry));
140 }
141 
142 void
143 Forwarder::onContentStoreHit(const Face& inFace,
144  shared_ptr<pit::Entry> pitEntry,
145  const Interest& interest,
146  const Data& data)
147 {
148  NFD_LOG_DEBUG("onContentStoreHit interest=" << interest.getName());
149 
150  beforeSatisfyInterest(*pitEntry, *m_csFace, data);
151  this->dispatchToStrategy(pitEntry, bind(&Strategy::beforeSatisfyInterest, _1,
152  pitEntry, cref(*m_csFace), cref(data)));
153 
154  const_pointer_cast<Data>(data.shared_from_this())->setIncomingFaceId(FACEID_CONTENT_STORE);
155  // XXX should we lookup PIT for other Interests that also match csMatch?
156 
157  // set PIT straggler timer
158  this->setStragglerTimer(pitEntry, true, data.getFreshnessPeriod());
159 
160  // goto outgoing Data pipeline
161  this->onOutgoingData(data, *const_pointer_cast<Face>(inFace.shared_from_this()));
162 }
163 
164 void
165 Forwarder::onInterestLoop(Face& inFace, const Interest& interest,
166  shared_ptr<pit::Entry> pitEntry)
167 {
168  NFD_LOG_DEBUG("onInterestLoop face=" << inFace.getId() <<
169  " interest=" << interest.getName());
170 
171  // (drop)
172 }
173 
184 static inline bool
185 compare_pickInterest(const pit::InRecord& a, const pit::InRecord& b, const Face* outFace)
186 {
187  bool isOutFaceA = a.getFace().get() == outFace;
188  bool isOutFaceB = b.getFace().get() == outFace;
189 
190  if (!isOutFaceA && isOutFaceB) {
191  return false;
192  }
193  if (isOutFaceA && !isOutFaceB) {
194  return true;
195  }
196 
197  return a.getLastRenewed() > b.getLastRenewed();
198 }
199 
200 void
201 Forwarder::onOutgoingInterest(shared_ptr<pit::Entry> pitEntry, Face& outFace,
202  bool wantNewNonce)
203 {
204  if (outFace.getId() == INVALID_FACEID) {
205  NFD_LOG_WARN("onOutgoingInterest face=invalid interest=" << pitEntry->getName());
206  return;
207  }
208  NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId() <<
209  " interest=" << pitEntry->getName());
210 
211  // scope control
212  if (pitEntry->violatesScope(outFace)) {
213  NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId() <<
214  " interest=" << pitEntry->getName() << " violates scope");
215  return;
216  }
217 
218  // pick Interest
219  const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
220  pit::InRecordCollection::const_iterator pickedInRecord = std::max_element(
221  inRecords.begin(), inRecords.end(), bind(&compare_pickInterest, _1, _2, &outFace));
222  BOOST_ASSERT(pickedInRecord != inRecords.end());
223  shared_ptr<Interest> interest = const_pointer_cast<Interest>(
224  pickedInRecord->getInterest().shared_from_this());
225 
226  if (wantNewNonce) {
227  interest = make_shared<Interest>(*interest);
228  static boost::random::uniform_int_distribution<uint32_t> dist;
229  interest->setNonce(dist(getGlobalRng()));
230  }
231 
232  // insert OutRecord
233  pitEntry->insertOrUpdateOutRecord(outFace.shared_from_this(), *interest);
234 
235  // send Interest
236  outFace.sendInterest(*interest);
237  ++m_counters.getNOutInterests();
238 }
239 
240 void
241 Forwarder::onInterestReject(shared_ptr<pit::Entry> pitEntry)
242 {
243  if (pitEntry->hasUnexpiredOutRecords()) {
244  NFD_LOG_ERROR("onInterestReject interest=" << pitEntry->getName() <<
245  " cannot reject forwarded Interest");
246  return;
247  }
248  NFD_LOG_DEBUG("onInterestReject interest=" << pitEntry->getName());
249 
250  // cancel unsatisfy & straggler timer
251  this->cancelUnsatisfyAndStragglerTimer(pitEntry);
252 
253  // set PIT straggler timer
254  this->setStragglerTimer(pitEntry, false);
255 }
256 
257 void
258 Forwarder::onInterestUnsatisfied(shared_ptr<pit::Entry> pitEntry)
259 {
260  NFD_LOG_DEBUG("onInterestUnsatisfied interest=" << pitEntry->getName());
261 
262  // invoke PIT unsatisfied callback
263  beforeExpirePendingInterest(*pitEntry);
264  this->dispatchToStrategy(pitEntry, bind(&Strategy::beforeExpirePendingInterest, _1,
265  pitEntry));
266 
267  // goto Interest Finalize pipeline
268  this->onInterestFinalize(pitEntry, false);
269 }
270 
271 void
272 Forwarder::onInterestFinalize(shared_ptr<pit::Entry> pitEntry, bool isSatisfied,
273  const time::milliseconds& dataFreshnessPeriod)
274 {
275  NFD_LOG_DEBUG("onInterestFinalize interest=" << pitEntry->getName() <<
276  (isSatisfied ? " satisfied" : " unsatisfied"));
277 
278  // Dead Nonce List insert if necessary
279  this->insertDeadNonceList(*pitEntry, isSatisfied, dataFreshnessPeriod, 0);
280 
281  // PIT delete
282  this->cancelUnsatisfyAndStragglerTimer(pitEntry);
283  m_pit.erase(pitEntry);
284 }
285 
286 void
287 Forwarder::onIncomingData(Face& inFace, const Data& data)
288 {
289  // receive Data
290  NFD_LOG_DEBUG("onIncomingData face=" << inFace.getId() << " data=" << data.getName());
291  const_cast<Data&>(data).setIncomingFaceId(inFace.getId());
292  ++m_counters.getNInDatas();
293 
294  // /localhost scope control
295  bool isViolatingLocalhost = !inFace.isLocal() &&
296  LOCALHOST_NAME.isPrefixOf(data.getName());
297  if (isViolatingLocalhost) {
298  NFD_LOG_DEBUG("onIncomingData face=" << inFace.getId() <<
299  " data=" << data.getName() << " violates /localhost");
300  // (drop)
301  return;
302  }
303 
304  // PIT match
305  pit::DataMatchResult pitMatches = m_pit.findAllDataMatches(data);
306  if (pitMatches.begin() == pitMatches.end()) {
307  // goto Data unsolicited pipeline
308  this->onDataUnsolicited(inFace, data);
309  return;
310  }
311 
312  // Remove Ptr<Packet> from the Data before inserting into cache, serving two purposes
313  // - reduce amount of memory used by cached entries
314  // - remove all tags that (e.g., hop count tag) that could have been associated with Ptr<Packet>
315  //
316  // Copying of Data is relatively cheap operation, as it copies (mostly) a collection of Blocks
317  // pointing to the same underlying memory buffer.
318  shared_ptr<Data> dataCopyWithoutPacket = make_shared<Data>(data);
319  dataCopyWithoutPacket->removeTag<ns3::ndn::Ns3PacketTag>();
320 
321  // CS insert
322  if (m_csFromNdnSim == nullptr)
323  m_cs.insert(*dataCopyWithoutPacket);
324  else
325  m_csFromNdnSim->Add(dataCopyWithoutPacket);
326 
327  std::set<shared_ptr<Face> > pendingDownstreams;
328  // foreach PitEntry
329  for (const shared_ptr<pit::Entry>& pitEntry : pitMatches) {
330  NFD_LOG_DEBUG("onIncomingData matching=" << pitEntry->getName());
331 
332  // cancel unsatisfy & straggler timer
333  this->cancelUnsatisfyAndStragglerTimer(pitEntry);
334 
335  // remember pending downstreams
336  const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
337  for (pit::InRecordCollection::const_iterator it = inRecords.begin();
338  it != inRecords.end(); ++it) {
339  if (it->getExpiry() > time::steady_clock::now()) {
340  pendingDownstreams.insert(it->getFace());
341  }
342  }
343 
344  // invoke PIT satisfy callback
345  beforeSatisfyInterest(*pitEntry, inFace, data);
346  this->dispatchToStrategy(pitEntry, bind(&Strategy::beforeSatisfyInterest, _1,
347  pitEntry, cref(inFace), cref(data)));
348 
349  // Dead Nonce List insert if necessary (for OutRecord of inFace)
350  this->insertDeadNonceList(*pitEntry, true, data.getFreshnessPeriod(), &inFace);
351 
352  // mark PIT satisfied
353  pitEntry->deleteInRecords();
354  pitEntry->deleteOutRecord(inFace);
355 
356  // set PIT straggler timer
357  this->setStragglerTimer(pitEntry, true, data.getFreshnessPeriod());
358  }
359 
360  // foreach pending downstream
361  for (std::set<shared_ptr<Face> >::iterator it = pendingDownstreams.begin();
362  it != pendingDownstreams.end(); ++it) {
363  shared_ptr<Face> pendingDownstream = *it;
364  if (pendingDownstream.get() == &inFace) {
365  continue;
366  }
367  // goto outgoing Data pipeline
368  this->onOutgoingData(data, *pendingDownstream);
369  }
370 }
371 
372 void
373 Forwarder::onDataUnsolicited(Face& inFace, const Data& data)
374 {
375  // accept to cache?
376  bool acceptToCache = inFace.isLocal();
377  if (acceptToCache) {
378  // CS insert
379  if (m_csFromNdnSim == nullptr)
380  m_cs.insert(data, true);
381  else
382  m_csFromNdnSim->Add(data.shared_from_this());
383  }
384 
385  NFD_LOG_DEBUG("onDataUnsolicited face=" << inFace.getId() <<
386  " data=" << data.getName() <<
387  (acceptToCache ? " cached" : " not cached"));
388 }
389 
390 void
391 Forwarder::onOutgoingData(const Data& data, Face& outFace)
392 {
393  if (outFace.getId() == INVALID_FACEID) {
394  NFD_LOG_WARN("onOutgoingData face=invalid data=" << data.getName());
395  return;
396  }
397  NFD_LOG_DEBUG("onOutgoingData face=" << outFace.getId() << " data=" << data.getName());
398 
399  // /localhost scope control
400  bool isViolatingLocalhost = !outFace.isLocal() &&
401  LOCALHOST_NAME.isPrefixOf(data.getName());
402  if (isViolatingLocalhost) {
403  NFD_LOG_DEBUG("onOutgoingData face=" << outFace.getId() <<
404  " data=" << data.getName() << " violates /localhost");
405  // (drop)
406  return;
407  }
408 
409  // TODO traffic manager
410 
411  // send Data
412  outFace.sendData(data);
413  ++m_counters.getNOutDatas();
414 }
415 
416 static inline bool
418 {
419  return a.getExpiry() < b.getExpiry();
420 }
421 
422 void
423 Forwarder::setUnsatisfyTimer(shared_ptr<pit::Entry> pitEntry)
424 {
425  const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
426  pit::InRecordCollection::const_iterator lastExpiring =
427  std::max_element(inRecords.begin(), inRecords.end(),
429 
430  time::steady_clock::TimePoint lastExpiry = lastExpiring->getExpiry();
431  time::nanoseconds lastExpiryFromNow = lastExpiry - time::steady_clock::now();
432  if (lastExpiryFromNow <= time::seconds(0)) {
433  // TODO all InRecords are already expired; will this happen?
434  }
435 
436  scheduler::cancel(pitEntry->m_unsatisfyTimer);
437  pitEntry->m_unsatisfyTimer = scheduler::schedule(lastExpiryFromNow,
438  bind(&Forwarder::onInterestUnsatisfied, this, pitEntry));
439 }
440 
441 void
442 Forwarder::setStragglerTimer(shared_ptr<pit::Entry> pitEntry, bool isSatisfied,
443  const time::milliseconds& dataFreshnessPeriod)
444 {
445  time::nanoseconds stragglerTime = time::milliseconds(100);
446 
447  scheduler::cancel(pitEntry->m_stragglerTimer);
448  pitEntry->m_stragglerTimer = scheduler::schedule(stragglerTime,
449  bind(&Forwarder::onInterestFinalize, this, pitEntry, isSatisfied, dataFreshnessPeriod));
450 }
451 
452 void
453 Forwarder::cancelUnsatisfyAndStragglerTimer(shared_ptr<pit::Entry> pitEntry)
454 {
455  scheduler::cancel(pitEntry->m_unsatisfyTimer);
456  scheduler::cancel(pitEntry->m_stragglerTimer);
457 }
458 
459 static inline void
461  const pit::OutRecord& outRecord)
462 {
463  dnl.add(pitEntry.getName(), outRecord.getLastNonce());
464 }
465 
466 void
467 Forwarder::insertDeadNonceList(pit::Entry& pitEntry, bool isSatisfied,
468  const time::milliseconds& dataFreshnessPeriod,
469  Face* upstream)
470 {
471  // need Dead Nonce List insert?
472  bool needDnl = false;
473  if (isSatisfied) {
474  bool hasFreshnessPeriod = dataFreshnessPeriod >= time::milliseconds::zero();
475  // Data never becomes stale if it doesn't have FreshnessPeriod field
476  needDnl = static_cast<bool>(pitEntry.getInterest().getMustBeFresh()) &&
477  (hasFreshnessPeriod && dataFreshnessPeriod < m_deadNonceList.getLifetime());
478  }
479  else {
480  needDnl = true;
481  }
482 
483  if (!needDnl) {
484  return;
485  }
486 
487  // Dead Nonce List insert
488  if (upstream == 0) {
489  // insert all outgoing Nonces
490  const pit::OutRecordCollection& outRecords = pitEntry.getOutRecords();
491  std::for_each(outRecords.begin(), outRecords.end(),
492  bind(&insertNonceToDnl, ref(m_deadNonceList), cref(pitEntry), _1));
493  }
494  else {
495  // insert outgoing Nonce of a specific face
496  pit::OutRecordCollection::const_iterator outRecord = pitEntry.getOutRecord(*upstream);
497  if (outRecord != pitEntry.getOutRecords().end()) {
498  m_deadNonceList.add(pitEntry.getName(), outRecord->getLastNonce());
499  }
500  }
501 }
502 
503 } // 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
time_point TimePoint
Definition: time.hpp:108
const Name & getName() const
Definition: interest.hpp:216
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
OutRecordCollection::const_iterator getOutRecord(const Face &face) const
get the OutRecord for face
Definition: pit-entry.cpp:172
void cancel(const EventId &eventId)
cancel a scheduled event
Definition: scheduler.cpp:58
void installStrategies(Forwarder &forwarder)
static time_point now() noexcept
Definition: time.cpp:79
represents the underlying protocol and address used by a Face
Definition: face-uri.hpp:44
FaceTable & getFaceTable()
Definition: forwarder.hpp:248
contains information about an Interest from an incoming face
uint32_t getLastNonce() const
represents an Interest packet
Definition: interest.hpp:45
#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:417
virtual void sendData(const Data &data)=0
send a Data
const PacketCounter & getNOutInterests() const
outgoing Interest
shared_ptr< Strategy > makeDefaultStrategy(Forwarder &forwarder)
void add(const Name &name, uint32_t nonce)
records name+nonce
represents a face
Definition: face.hpp:57
uint32_t getNonce() const
Get Interest&#39;s nonce.
Definition: interest.cpp:62
const Name & getName() const
Get name of the Data packet.
Definition: data.hpp:343
Table::const_iterator iterator
Definition: cs-internal.hpp:41
#define NFD_LOG_WARN(expression)
Definition: logger.hpp:39
static void insertNonceToDnl(DeadNonceList &dnl, const pit::Entry &pitEntry, const pit::OutRecord &outRecord)
Definition: forwarder.cpp:460
int getMustBeFresh() const
Definition: interest.hpp:412
const Interest & getInterest() const
Definition: pit-entry.hpp:185
Copyright (c) 2011-2015 Regents of the University of California.
Definition: ndn-common.hpp:38
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
Interest & setNonce(uint32_t nonce)
Set Interest&#39;s nonce.
Definition: interest.cpp:76
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
Name abstraction to represent an absolute name.
Definition: name.hpp:46
boost::random::mt19937 & getGlobalRng()
Definition: random.cpp:34
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
const OutRecordCollection & getOutRecords() const
Definition: pit-entry.hpp:197
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:185
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:48
const FaceId INVALID_FACEID
indicates an invalid FaceId
Definition: face.hpp:43
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
bool isPrefixOf(const Name &name) const
Check if the N components of this name are the same as the first N components of the given name...
Definition: name.cpp:320
FaceId getId() const
Definition: face.hpp:221
represents a Data packet
Definition: data.hpp:39
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:245
const time::milliseconds & getFreshnessPeriod() const
Definition: data.hpp:361
virtual void sendInterest(const Interest &interest)=0
send an Interest
std::vector< shared_ptr< pit::Entry > > DataMatchResult
Definition: pit.hpp:42