NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.5: NDN, CCN, CCNx, content centric networks
API Documentation
face-impl.hpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2019 Regents of the University of California.
4  *
5  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6  *
7  * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU Lesser General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later version.
10  *
11  * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13  * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14  *
15  * You should have received copies of the GNU General Public License and GNU Lesser
16  * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17  * <http://www.gnu.org/licenses/>.
18  *
19  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20  */
21 
22 #ifndef NDN_IMPL_FACE_IMPL_HPP
23 #define NDN_IMPL_FACE_IMPL_HPP
24 
25 #include "ndn-cxx/face.hpp"
30 #include "ndn-cxx/lp/packet.hpp"
31 #include "ndn-cxx/lp/tags.hpp"
35 // #include "ndn-cxx/transport/tcp-transport.hpp"
36 // #include "ndn-cxx/transport/unix-transport.hpp"
38 #include "ndn-cxx/util/logger.hpp"
40 #include "ndn-cxx/util/signal.hpp"
41 
43 // INFO level: prefix registration, etc.
44 //
45 // DEBUG level: packet logging.
46 // Each log entry starts with a direction symbol ('<' denotes an outgoing packet, '>' denotes an
47 // incoming packet) and a packet type symbol ('I' denotes an Interest, 'D' denotes a Data, 'N'
48 // denotes a Nack). Interest is printed as its string representation, Data is printed as name only,
49 // Nack is printed as the Interest followed by the Nack reason and delimited by a '~' symbol. A
50 // log line about an incoming packet may be followed by zero or more lines about Interest matching
51 // InterestFilter, Data satisfying Interest, or Nack rejecting Interest, which are also written at
52 // DEBUG level.
53 //
54 // TRACE level: more detailed unstructured messages.
55 
56 namespace ndn {
57 
60 class Face::Impl : noncopyable
61 {
62 public:
64  using InterestFilterTable = std::list<shared_ptr<InterestFilterRecord>>;
66 
67  explicit
68  Impl(Face& face)
69  : m_face(face)
70  , m_scheduler(m_face.getIoService())
71  {
72  auto postOnEmptyPitOrNoRegisteredPrefixes = [this] {
73  m_scheduler.scheduleEvent(time::seconds(0), bind(&Impl::onEmptyPitOrNoRegisteredPrefixes, this));
74  // without this extra "post", transport can get paused (-async_read) and then resumed
75  // (+async_read) from within onInterest/onData callback. After onInterest/onData
76  // finishes, there is another +async_read with the same memory block. A few of such
77  // async_read duplications can cause various effects and result in segfault.
78  };
79 
80  m_pendingInterestTable.onEmpty.connect(postOnEmptyPitOrNoRegisteredPrefixes);
81  m_registeredPrefixTable.onEmpty.connect(postOnEmptyPitOrNoRegisteredPrefixes);
82  }
83 
84 public: // consumer
85  void
86  asyncExpressInterest(shared_ptr<const Interest> interest,
87  const DataCallback& afterSatisfied,
88  const NackCallback& afterNacked,
89  const TimeoutCallback& afterTimeout)
90  {
91  NDN_LOG_DEBUG("<I " << *interest);
92  this->ensureConnected(true);
93 
94  const Interest& interest2 = *interest;
95  auto i = m_pendingInterestTable.insert(make_shared<PendingInterest>(
96  std::move(interest), afterSatisfied, afterNacked, afterTimeout, ref(m_scheduler))).first;
97  // In dispatchInterest, an InterestCallback may respond with Data right away and delete
98  // the PendingInterestTable entry. shared_ptr is retained to ensure PendingInterest instance
99  // remains valid in this case.
100  shared_ptr<PendingInterest> entry = *i;
101  entry->setDeleter([this, i] { m_pendingInterestTable.erase(i); });
102 
103  lp::Packet lpPacket;
104  addFieldFromTag<lp::NextHopFaceIdField, lp::NextHopFaceIdTag>(lpPacket, interest2);
105  addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, interest2);
106 
107  entry->recordForwarding();
108  m_face.m_transport->send(finishEncoding(std::move(lpPacket), interest2.wireEncode(),
109  'I', interest2.getName()));
110  dispatchInterest(*entry, interest2);
111  }
112 
113  void
114  asyncRemovePendingInterest(const PendingInterestId* pendingInterestId)
115  {
116  m_pendingInterestTable.remove_if(MatchPendingInterestId(pendingInterestId));
117  }
118 
119  void
121  {
122  m_pendingInterestTable.clear();
123  }
124 
127  bool
129  {
130  bool hasAppMatch = false, hasForwarderMatch = false;
131  for (auto i = m_pendingInterestTable.begin(); i != m_pendingInterestTable.end(); ) {
132  shared_ptr<PendingInterest> entry = *i;
133  if (!entry->getInterest()->matchesData(data)) {
134  ++i;
135  continue;
136  }
137 
138  NDN_LOG_DEBUG(" satisfying " << *entry->getInterest() << " from " << entry->getOrigin());
139  i = m_pendingInterestTable.erase(i);
140 
141  if (entry->getOrigin() == PendingInterestOrigin::APP) {
142  hasAppMatch = true;
143  entry->invokeDataCallback(data);
144  }
145  else {
146  hasForwarderMatch = true;
147  }
148  }
149  // if Data matches no pending Interest record, it is sent to the forwarder as unsolicited Data
150  return hasForwarderMatch || !hasAppMatch;
151  }
152 
155  optional<lp::Nack>
157  {
158  optional<lp::Nack> outNack;
159  for (auto i = m_pendingInterestTable.begin(); i != m_pendingInterestTable.end(); ) {
160  shared_ptr<PendingInterest> entry = *i;
161  if (!nack.getInterest().matchesInterest(*entry->getInterest())) {
162  ++i;
163  continue;
164  }
165 
166  NDN_LOG_DEBUG(" nacking " << *entry->getInterest() << " from " << entry->getOrigin());
167 
168  optional<lp::Nack> outNack1 = entry->recordNack(nack);
169  if (!outNack1) {
170  ++i;
171  continue;
172  }
173 
174  if (entry->getOrigin() == PendingInterestOrigin::APP) {
175  entry->invokeNackCallback(*outNack1);
176  }
177  else {
178  outNack = outNack1;
179  }
180  i = m_pendingInterestTable.erase(i);
181  }
182  // send "least severe" Nack from any PendingInterest record originated from forwarder, because
183  // it is unimportant to consider Nack reason for the unlikely case when forwarder sends multiple
184  // Interests to an app in a short while
185  return outNack;
186  }
187 
188 public: // producer
189  void
190  asyncSetInterestFilter(shared_ptr<InterestFilterRecord> interestFilterRecord)
191  {
192  NDN_LOG_INFO("setting InterestFilter: " << interestFilterRecord->getFilter());
193  m_interestFilterTable.push_back(std::move(interestFilterRecord));
194  }
195 
196  void
197  asyncUnsetInterestFilter(const InterestFilterId* interestFilterId)
198  {
199  InterestFilterTable::iterator i = std::find_if(m_interestFilterTable.begin(),
200  m_interestFilterTable.end(),
201  MatchInterestFilterId(interestFilterId));
202  if (i != m_interestFilterTable.end()) {
203  NDN_LOG_INFO("unsetting InterestFilter: " << (*i)->getFilter());
204  m_interestFilterTable.erase(i);
205  }
206  }
207 
208  void
209  processIncomingInterest(shared_ptr<const Interest> interest)
210  {
211  const Interest& interest2 = *interest;
212  auto i = m_pendingInterestTable.insert(make_shared<PendingInterest>(
213  std::move(interest), ref(m_scheduler))).first;
214  // In dispatchInterest, an InterestCallback may respond with Data right away and delete
215  // the PendingInterestTable entry. shared_ptr is retained to ensure PendingInterest instance
216  // remains valid in this case.
217  shared_ptr<PendingInterest> entry = *i;
218  entry->setDeleter([this, i] { m_pendingInterestTable.erase(i); });
219 
220  this->dispatchInterest(*entry, interest2);
221  }
222 
223  void
224  dispatchInterest(PendingInterest& entry, const Interest& interest)
225  {
226  for (const auto& filter : m_interestFilterTable) {
227  if (filter->doesMatch(entry)) {
228  NDN_LOG_DEBUG(" matches " << filter->getFilter());
229  entry.recordForwarding();
230  filter->invokeInterestCallback(interest);
231  }
232  }
233  }
234 
235  void
236  asyncPutData(const Data& data)
237  {
238  NDN_LOG_DEBUG("<D " << data.getName());
239  bool shouldSendToForwarder = satisfyPendingInterests(data);
240  if (!shouldSendToForwarder) {
241  return;
242  }
243 
244  this->ensureConnected(true);
245 
246  lp::Packet lpPacket;
247  addFieldFromTag<lp::CachePolicyField, lp::CachePolicyTag>(lpPacket, data);
248  addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, data);
249 
250  m_face.m_transport->send(finishEncoding(std::move(lpPacket), data.wireEncode(),
251  'D', data.getName()));
252  }
253 
254  void
255  asyncPutNack(const lp::Nack& nack)
256  {
257  NDN_LOG_DEBUG("<N " << nack.getInterest() << '~' << nack.getHeader().getReason());
258  optional<lp::Nack> outNack = nackPendingInterests(nack);
259  if (!outNack) {
260  return;
261  }
262 
263  this->ensureConnected(true);
264 
265  lp::Packet lpPacket;
266  lpPacket.add<lp::NackField>(outNack->getHeader());
267  addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, *outNack);
268 
269  const Interest& interest = outNack->getInterest();
270  m_face.m_transport->send(finishEncoding(std::move(lpPacket), interest.wireEncode(),
271  'N', interest.getName()));
272  }
273 
274 public: // prefix registration
275  const RegisteredPrefixId*
276  registerPrefix(const Name& prefix,
277  shared_ptr<InterestFilterRecord> filter,
278  const RegisterPrefixSuccessCallback& onSuccess,
279  const RegisterPrefixFailureCallback& onFailure,
280  uint64_t flags,
281  const nfd::CommandOptions& options)
282  {
283  NDN_LOG_INFO("registering prefix: " << prefix);
284  auto record = make_shared<RegisteredPrefix>(prefix, filter, options);
285 
286  nfd::ControlParameters params;
287  params.setName(prefix);
288  params.setFlags(flags);
289  m_face.m_nfdController->start<nfd::RibRegisterCommand>(
290  params,
291  [=] (const nfd::ControlParameters&) { this->afterPrefixRegistered(record, onSuccess); },
292  [=] (const nfd::ControlResponse& resp) {
293  NDN_LOG_INFO("register prefix failed: " << record->getPrefix());
294  onFailure(record->getPrefix(), resp.getText());
295  },
296  options);
297 
298  return reinterpret_cast<const RegisteredPrefixId*>(record.get());
299  }
300 
301  void
302  afterPrefixRegistered(shared_ptr<RegisteredPrefix> registeredPrefix,
303  const RegisterPrefixSuccessCallback& onSuccess)
304  {
305  NDN_LOG_INFO("registered prefix: " << registeredPrefix->getPrefix());
306  m_registeredPrefixTable.insert(registeredPrefix);
307 
308  if (registeredPrefix->getFilter() != nullptr) {
309  // it was a combined operation
310  m_interestFilterTable.push_back(registeredPrefix->getFilter());
311  }
312 
313  if (onSuccess != nullptr) {
314  onSuccess(registeredPrefix->getPrefix());
315  }
316  }
317 
318  void
319  asyncUnregisterPrefix(const RegisteredPrefixId* registeredPrefixId,
320  const UnregisterPrefixSuccessCallback& onSuccess,
321  const UnregisterPrefixFailureCallback& onFailure)
322  {
323  auto i = std::find_if(m_registeredPrefixTable.begin(),
324  m_registeredPrefixTable.end(),
325  MatchRegisteredPrefixId(registeredPrefixId));
326  if (i != m_registeredPrefixTable.end()) {
327  RegisteredPrefix& record = **i;
328  const shared_ptr<InterestFilterRecord>& filter = record.getFilter();
329 
330  if (filter != nullptr) {
331  // it was a combined operation
332  m_interestFilterTable.remove(filter);
333  }
334 
335  NDN_LOG_INFO("unregistering prefix: " << record.getPrefix());
336 
337  nfd::ControlParameters params;
338  params.setName(record.getPrefix());
339  m_face.m_nfdController->start<nfd::RibUnregisterCommand>(
340  params,
341  [=] (const nfd::ControlParameters&) { this->finalizeUnregisterPrefix(i, onSuccess); },
342  [=] (const nfd::ControlResponse& resp) {
343  NDN_LOG_INFO("unregister prefix failed: " << params.getName());
344  onFailure(resp.getText());
345  },
346  record.getCommandOptions());
347  }
348  else {
349  if (onFailure != nullptr) {
350  onFailure("Unrecognized PrefixId");
351  }
352  }
353 
354  // there cannot be two registered prefixes with the same id
355  }
356 
357  void
359  const UnregisterPrefixSuccessCallback& onSuccess)
360  {
361  NDN_LOG_INFO("unregistered prefix: " << (*item)->getPrefix());
362  m_registeredPrefixTable.erase(item);
363 
364  if (onSuccess != nullptr) {
365  onSuccess();
366  }
367  }
368 
369 public: // IO routine
370  void
371  ensureConnected(bool wantResume)
372  {
373  if (!m_face.m_transport->isConnected())
374  m_face.m_transport->connect([=] (const Block& wire) { m_face.onReceiveElement(wire); });
375 
376  if (wantResume && !m_face.m_transport->isReceiving()) {
377  m_face.m_transport->resume();
378  }
379  }
380 
381  void
383  {
384  if (m_pendingInterestTable.empty() && m_registeredPrefixTable.empty()) {
385  m_face.m_transport->pause();
386  }
387  }
388 
389 private:
398  Block
399  finishEncoding(lp::Packet&& lpPacket, Block wire, char pktType, const Name& name)
400  {
401  if (!lpPacket.empty()) {
402  lpPacket.add<lp::FragmentField>(std::make_pair(wire.begin(), wire.end()));
403  wire = lpPacket.wireEncode();
404  }
405 
406  if (wire.size() > MAX_NDN_PACKET_SIZE) {
407  BOOST_THROW_EXCEPTION(Face::OversizedPacketError(pktType, name, wire.size()));
408  }
409 
410  return wire;
411  }
412 
413 private:
414  Face& m_face;
415  util::Scheduler m_scheduler;
416  util::scheduler::ScopedEventId m_processEventsTimeoutEvent;
417 
418  PendingInterestTable m_pendingInterestTable;
419  InterestFilterTable m_interestFilterTable;
420  RegisteredPrefixTable m_registeredPrefixTable;
421 
422  friend class Face;
423 };
424 
425 } // namespace ndn
426 
427 #endif // NDN_IMPL_FACE_IMPL_HPP
Copyright (c) 2011-2015 Regents of the University of California.
const Name & getName() const
Get name.
Definition: data.hpp:124
function< void(const std::string &)> UnregisterPrefixFailureCallback
Callback invoked when unregisterPrefix or unsetInterestFilter command fails.
Definition: face.hpp:88
void dispatchInterest(PendingInterest &entry, const Interest &interest)
Definition: face-impl.hpp:224
const shared_ptr< InterestFilterRecord > & getFilter() const
ContainerWithOnEmptySignal< shared_ptr< RegisteredPrefix > > RegisteredPrefixTable
Definition: face-impl.hpp:65
represents parameters in a ControlCommand request or response
implementation detail of Face
Definition: face-impl.hpp:60
Packet & add(const typename FIELD::ValueType &value)
add a FIELD with value
Definition: packet.hpp:149
void asyncPutNack(const lp::Nack &nack)
Definition: face-impl.hpp:255
void asyncExpressInterest(shared_ptr< const Interest > interest, const DataCallback &afterSatisfied, const NackCallback &afterNacked, const TimeoutCallback &afterTimeout)
Definition: face-impl.hpp:86
DummyIoService & getIoService()
Definition: face.hpp:439
#define NDN_LOG_DEBUG(expression)
Definition: logger.hpp:108
bool matchesInterest(const Interest &other) const
Check if Interest matches other interest.
Definition: interest.cpp:529
ContainerWithOnEmptySignal< shared_ptr< PendingInterest > > PendingInterestTable
Definition: face-impl.hpp:63
ControlParameters & setFlags(uint64_t flags)
Represents a TLV element of NDN packet format.
Definition: block.hpp:42
Represents an Interest packet.
Definition: interest.hpp:44
Functor to match PendingInterestId.
const NackHeader & getHeader() const
Definition: nack.hpp:63
void asyncRemovePendingInterest(const PendingInterestId *pendingInterestId)
Definition: face-impl.hpp:114
void asyncPutData(const Data &data)
Definition: face-impl.hpp:236
stores information about a prefix registered in NDN forwarder
void asyncRemoveAllPendingInterests()
Definition: face-impl.hpp:120
EventId scheduleEvent(time::nanoseconds after, const EventCallback &callback)
Schedule a one-time event after the specified delay.
Definition: scheduler.cpp:103
Impl(Face &face)
Definition: face-impl.hpp:68
void processIncomingInterest(shared_ptr< const Interest > interest)
Definition: face-impl.hpp:209
NackReason getReason() const
const nfd::CommandOptions & getCommandOptions() const
const Name & getPrefix() const
void asyncUnsetInterestFilter(const InterestFilterId *interestFilterId)
Definition: face-impl.hpp:197
represents a Network Nack
Definition: nack.hpp:38
Declare a field.
Definition: field-decl.hpp:181
Table::const_iterator iterator
Definition: cs-internal.hpp:41
void onEmptyPitOrNoRegisteredPrefixes()
Definition: face-impl.hpp:382
void recordForwarding()
Record that the Interest has been forwarded to one destination.
size_t size() const
Get size of encoded wire, including Type-Length-Value.
Definition: block.cpp:298
void finalizeUnregisterPrefix(RegisteredPrefixTable::iterator item, const UnregisterPrefixSuccessCallback &onSuccess)
Definition: face-impl.hpp:358
contains options for ControlCommand execution
friend class Face
Definition: face-impl.hpp:422
std::pair< iterator, bool > insert(const value_type &value)
Provide a communication channel with local or remote NDN forwarder.
Definition: face.hpp:93
Functor to match InterestFilterId.
#define NDN_LOG_INFO(expression)
Definition: logger.hpp:109
void ensureConnected(bool wantResume)
Definition: face-impl.hpp:371
Buffer::const_iterator begin() const
Get begin iterator of encoded wire.
Definition: block.cpp:271
function< void(const Name &, const std::string &)> RegisterPrefixFailureCallback
Callback invoked when registerPrefix or setInterestFilter command fails.
Definition: face.hpp:78
std::list< shared_ptr< InterestFilterRecord > > InterestFilterTable
Definition: face-impl.hpp:64
function< void(const Name &)> RegisterPrefixSuccessCallback
Callback invoked when registerPrefix or setInterestFilter command succeeds.
Definition: face.hpp:73
util::Signal< ContainerWithOnEmptySignal< T > > onEmpty
Signal to be fired when container becomes empty.
Represents an absolute name.
Definition: name.hpp:43
Buffer::const_iterator end() const
Get end iterator of encoded wire.
Definition: block.cpp:280
represents a rib/unregister command
represents a rib/register command
size_t wireEncode(EncodingImpl< TAG > &encoder) const
Prepend wire encoding to encoder.
Definition: interest.cpp:70
bool satisfyPendingInterests(const Data &data)
Definition: face-impl.hpp:128
void afterPrefixRegistered(shared_ptr< RegisteredPrefix > registeredPrefix, const RegisterPrefixSuccessCallback &onSuccess)
Definition: face-impl.hpp:302
ControlParameters & setName(const Name &name)
size_t wireEncode(EncodingImpl< TAG > &encoder, bool wantUnsignedPortionOnly=false) const
Prepend wire encoding to encoder in NDN Packet Format v0.2.
Definition: data.cpp:48
optional< lp::Nack > nackPendingInterests(const lp::Nack &nack)
Definition: face-impl.hpp:156
function< void()> UnregisterPrefixSuccessCallback
Callback invoked when unregisterPrefix or unsetInterestFilter command succeeds.
Definition: face.hpp:83
#define NDN_LOG_INIT(name)
declare a log module
Definition: logger.hpp:90
Functor to match RegisteredPrefixId.
ControlCommand response.
Interest was received from this app via Face::expressInterest API.
function< void(const Interest &)> TimeoutCallback
Callback invoked when expressed Interest times out.
Definition: face.hpp:63
function< void(const Interest &, const lp::Nack &)> NackCallback
Callback invoked when Nack is sent in response to expressed Interest.
Definition: face.hpp:58
Represents a Data packet.
Definition: data.hpp:35
Stores a pending Interest and associated callbacks.
void asyncUnregisterPrefix(const RegisteredPrefixId *registeredPrefixId, const UnregisterPrefixSuccessCallback &onSuccess, const UnregisterPrefixFailureCallback &onFailure)
Definition: face-impl.hpp:319
void asyncSetInterestFilter(shared_ptr< InterestFilterRecord > interestFilterRecord)
Definition: face-impl.hpp:190
const Interest & getInterest() const
Definition: nack.hpp:51
function< void(const Interest &, const Data &)> DataCallback
Callback invoked when expressed Interest gets satisfied with a Data packet.
Definition: face.hpp:53
Exception thrown when attempting to send a packet over size limit.
Definition: face.hpp:105
const RegisteredPrefixId * registerPrefix(const Name &prefix, shared_ptr< InterestFilterRecord > filter, const RegisterPrefixSuccessCallback &onSuccess, const RegisterPrefixFailureCallback &onFailure, uint64_t flags, const nfd::CommandOptions &options)
Definition: face-impl.hpp:276
const size_t MAX_NDN_PACKET_SIZE
practical limit of network layer packet size
Definition: tlv.hpp:41
const Name & getName() const
Definition: interest.hpp:134