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-2017 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_DETAIL_FACE_IMPL_HPP
23 #define NDN_DETAIL_FACE_IMPL_HPP
24 
25 #include "../face.hpp"
27 #include "lp-field-tag.hpp"
28 #include "pending-interest.hpp"
29 #include "registered-prefix.hpp"
30 #include "../lp/packet.hpp"
31 #include "../lp/tags.hpp"
32 #include "../mgmt/nfd/command-options.hpp"
33 #include "../mgmt/nfd/controller.hpp"
34 #include "../transport/transport.hpp"
35 // #include "../transport/tcp-transport.hpp"
36 // #include "../transport/unix-transport.hpp"
37 #include "../util/config-file.hpp"
38 #include "../util/logger.hpp"
39 #include "../util/scheduler.hpp"
40 #include "../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:
63  using PendingInterestTable = ContainerWithOnEmptySignal<shared_ptr<PendingInterest>>;
64  using InterestFilterTable = std::list<shared_ptr<InterestFilterRecord>>;
65  using RegisteredPrefixTable = ContainerWithOnEmptySignal<shared_ptr<RegisteredPrefix>>;
66 
67  explicit
69  : m_face(face)
70  , m_scheduler(m_face.getIoService())
71  , m_processEventsTimeoutEvent(m_scheduler)
72  {
73  auto postOnEmptyPitOrNoRegisteredPrefixes = [this] {
74  m_scheduler.scheduleEvent(time::seconds(0), bind(&Impl::onEmptyPitOrNoRegisteredPrefixes, this));
75  // without this extra "post", transport can get paused (-async_read) and then resumed
76  // (+async_read) from within onInterest/onData callback. After onInterest/onData
77  // finishes, there is another +async_read with the same memory block. A few of such
78  // async_read duplications can cause various effects and result in segfault.
79  };
80 
81  m_pendingInterestTable.onEmpty.connect(postOnEmptyPitOrNoRegisteredPrefixes);
82  m_registeredPrefixTable.onEmpty.connect(postOnEmptyPitOrNoRegisteredPrefixes);
83  }
84 
85 public: // consumer
86  void
87  asyncExpressInterest(shared_ptr<const Interest> interest,
88  const DataCallback& afterSatisfied,
89  const NackCallback& afterNacked,
90  const TimeoutCallback& afterTimeout)
91  {
92  NDN_LOG_DEBUG("<I " << *interest);
93  this->ensureConnected(true);
94 
95  const Interest& interest2 = *interest;
96  auto i = m_pendingInterestTable.insert(make_shared<PendingInterest>(
97  std::move(interest), afterSatisfied, afterNacked, afterTimeout, ref(m_scheduler))).first;
98  // In dispatchInterest, an InterestCallback may respond with Data right away and delete
99  // the PendingInterestTable entry. shared_ptr is retained to ensure PendingInterest instance
100  // remains valid in this case.
101  shared_ptr<PendingInterest> entry = *i;
102  entry->setDeleter([this, i] { m_pendingInterestTable.erase(i); });
103 
104  lp::Packet lpPacket;
105  addFieldFromTag<lp::NextHopFaceIdField, lp::NextHopFaceIdTag>(lpPacket, interest2);
106  addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, interest2);
107 
108  entry->recordForwarding();
109  m_face.m_transport->send(finishEncoding(std::move(lpPacket), interest2.wireEncode(),
110  'I', interest2.getName()));
111  dispatchInterest(*entry, interest2);
112  }
113 
114  void
115  asyncRemovePendingInterest(const PendingInterestId* pendingInterestId)
116  {
117  m_pendingInterestTable.remove_if(MatchPendingInterestId(pendingInterestId));
118  }
119 
120  void
122  {
123  m_pendingInterestTable.clear();
124  }
125 
128  bool
130  {
131  bool hasAppMatch = false, hasForwarderMatch = false;
132  for (auto i = m_pendingInterestTable.begin(); i != m_pendingInterestTable.end(); ) {
133  shared_ptr<PendingInterest> entry = *i;
134  if (!entry->getInterest()->matchesData(data)) {
135  ++i;
136  continue;
137  }
138 
139  NDN_LOG_DEBUG(" satisfying " << *entry->getInterest() << " from " << entry->getOrigin());
140  i = m_pendingInterestTable.erase(i);
141 
142  if (entry->getOrigin() == PendingInterestOrigin::APP) {
143  hasAppMatch = true;
144  entry->invokeDataCallback(data);
145  }
146  else {
147  hasForwarderMatch = true;
148  }
149  }
150  // if Data matches no pending Interest record, it is sent to the forwarder as unsolicited Data
151  return hasForwarderMatch || !hasAppMatch;
152  }
153 
156  optional<lp::Nack>
158  {
159  optional<lp::Nack> outNack;
160  for (auto i = m_pendingInterestTable.begin(); i != m_pendingInterestTable.end(); ) {
161  shared_ptr<PendingInterest> entry = *i;
162  if (!nack.getInterest().matchesInterest(*entry->getInterest())) {
163  ++i;
164  continue;
165  }
166 
167  NDN_LOG_DEBUG(" nacking " << *entry->getInterest() << " from " << entry->getOrigin());
168 
169  optional<lp::Nack> outNack1 = entry->recordNack(nack);
170  if (!outNack1) {
171  ++i;
172  continue;
173  }
174 
175  if (entry->getOrigin() == PendingInterestOrigin::APP) {
176  entry->invokeNackCallback(*outNack1);
177  }
178  else {
179  outNack = outNack1;
180  }
181  i = m_pendingInterestTable.erase(i);
182  }
183  // send "least severe" Nack from any PendingInterest record originated from forwarder, because
184  // it is unimportant to consider Nack reason for the unlikely case when forwarder sends multiple
185  // Interests to an app in a short while
186  return outNack;
187  }
188 
189 public: // producer
190  void
191  asyncSetInterestFilter(shared_ptr<InterestFilterRecord> interestFilterRecord)
192  {
193  NDN_LOG_INFO("setting InterestFilter: " << interestFilterRecord->getFilter());
194  m_interestFilterTable.push_back(std::move(interestFilterRecord));
195  }
196 
197  void
198  asyncUnsetInterestFilter(const InterestFilterId* interestFilterId)
199  {
200  InterestFilterTable::iterator i = std::find_if(m_interestFilterTable.begin(),
201  m_interestFilterTable.end(),
202  MatchInterestFilterId(interestFilterId));
203  if (i != m_interestFilterTable.end()) {
204  NDN_LOG_INFO("unsetting InterestFilter: " << (*i)->getFilter());
205  m_interestFilterTable.erase(i);
206  }
207  }
208 
209  void
210  processIncomingInterest(shared_ptr<const Interest> interest)
211  {
212  const Interest& interest2 = *interest;
213  auto i = m_pendingInterestTable.insert(make_shared<PendingInterest>(
214  std::move(interest), ref(m_scheduler))).first;
215  // In dispatchInterest, an InterestCallback may respond with Data right away and delete
216  // the PendingInterestTable entry. shared_ptr is retained to ensure PendingInterest instance
217  // remains valid in this case.
218  shared_ptr<PendingInterest> entry = *i;
219  entry->setDeleter([this, i] { m_pendingInterestTable.erase(i); });
220 
221  this->dispatchInterest(*entry, interest2);
222  }
223 
224  void
225  dispatchInterest(PendingInterest& entry, const Interest& interest)
226  {
227  for (const auto& filter : m_interestFilterTable) {
228  if (filter->doesMatch(entry)) {
229  NDN_LOG_DEBUG(" matches " << filter->getFilter());
230  entry.recordForwarding();
231  filter->invokeInterestCallback(interest);
232  }
233  }
234  }
235 
236  void
237  asyncPutData(const Data& data)
238  {
239  NDN_LOG_DEBUG("<D " << data.getName());
240  bool shouldSendToForwarder = satisfyPendingInterests(data);
241  if (!shouldSendToForwarder) {
242  return;
243  }
244 
245  this->ensureConnected(true);
246 
247  lp::Packet lpPacket;
248  addFieldFromTag<lp::CachePolicyField, lp::CachePolicyTag>(lpPacket, data);
249  addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, data);
250 
251  m_face.m_transport->send(finishEncoding(std::move(lpPacket), data.wireEncode(),
252  'D', data.getName()));
253  }
254 
255  void
256  asyncPutNack(const lp::Nack& nack)
257  {
258  NDN_LOG_DEBUG("<N " << nack.getInterest() << '~' << nack.getHeader().getReason());
259  optional<lp::Nack> outNack = nackPendingInterests(nack);
260  if (!outNack) {
261  return;
262  }
263 
264  this->ensureConnected(true);
265 
266  lp::Packet lpPacket;
267  lpPacket.add<lp::NackField>(outNack->getHeader());
268  addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, *outNack);
269 
270  const Interest& interest = outNack->getInterest();
271  m_face.m_transport->send(finishEncoding(std::move(lpPacket), interest.wireEncode(),
272  'N', interest.getName()));
273  }
274 
275 public: // prefix registration
276  const RegisteredPrefixId*
277  registerPrefix(const Name& prefix,
278  shared_ptr<InterestFilterRecord> filter,
279  const RegisterPrefixSuccessCallback& onSuccess,
280  const RegisterPrefixFailureCallback& onFailure,
281  uint64_t flags,
282  const nfd::CommandOptions& options)
283  {
284  NDN_LOG_INFO("registering prefix: " << prefix);
285  auto record = make_shared<RegisteredPrefix>(prefix, filter, options);
286 
287  nfd::ControlParameters params;
288  params.setName(prefix);
289  params.setFlags(flags);
290  m_face.m_nfdController->start<nfd::RibRegisterCommand>(
291  params,
292  [=] (const nfd::ControlParameters&) { this->afterPrefixRegistered(record, onSuccess); },
293  [=] (const nfd::ControlResponse& resp) {
294  NDN_LOG_INFO("register prefix failed: " << record->getPrefix());
295  onFailure(record->getPrefix(), resp.getText());
296  },
297  options);
298 
299  return reinterpret_cast<const RegisteredPrefixId*>(record.get());
300  }
301 
302  void
303  afterPrefixRegistered(shared_ptr<RegisteredPrefix> registeredPrefix,
304  const RegisterPrefixSuccessCallback& onSuccess)
305  {
306  NDN_LOG_INFO("registered prefix: " << registeredPrefix->getPrefix());
307  m_registeredPrefixTable.insert(registeredPrefix);
308 
309  if (registeredPrefix->getFilter() != nullptr) {
310  // it was a combined operation
311  m_interestFilterTable.push_back(registeredPrefix->getFilter());
312  }
313 
314  if (onSuccess != nullptr) {
315  onSuccess(registeredPrefix->getPrefix());
316  }
317  }
318 
319  void
320  asyncUnregisterPrefix(const RegisteredPrefixId* registeredPrefixId,
321  const UnregisterPrefixSuccessCallback& onSuccess,
322  const UnregisterPrefixFailureCallback& onFailure)
323  {
324  auto i = std::find_if(m_registeredPrefixTable.begin(),
325  m_registeredPrefixTable.end(),
326  MatchRegisteredPrefixId(registeredPrefixId));
327  if (i != m_registeredPrefixTable.end()) {
328  RegisteredPrefix& record = **i;
329  const shared_ptr<InterestFilterRecord>& filter = record.getFilter();
330 
331  if (filter != nullptr) {
332  // it was a combined operation
333  m_interestFilterTable.remove(filter);
334  }
335 
336  NDN_LOG_INFO("unregistering prefix: " << record.getPrefix());
337 
338  nfd::ControlParameters params;
339  params.setName(record.getPrefix());
340  m_face.m_nfdController->start<nfd::RibUnregisterCommand>(
341  params,
342  [=] (const nfd::ControlParameters&) { this->finalizeUnregisterPrefix(i, onSuccess); },
343  [=] (const nfd::ControlResponse& resp) {
344  NDN_LOG_INFO("unregister prefix failed: " << params.getName());
345  onFailure(resp.getText());
346  },
347  record.getCommandOptions());
348  }
349  else {
350  if (onFailure != nullptr) {
351  onFailure("Unrecognized PrefixId");
352  }
353  }
354 
355  // there cannot be two registered prefixes with the same id
356  }
357 
358  void
360  const UnregisterPrefixSuccessCallback& onSuccess)
361  {
362  NDN_LOG_INFO("unregistered prefix: " << (*item)->getPrefix());
363  m_registeredPrefixTable.erase(item);
364 
365  if (onSuccess != nullptr) {
366  onSuccess();
367  }
368  }
369 
370 public: // IO routine
371  void
372  ensureConnected(bool wantResume)
373  {
374  if (!m_face.m_transport->isConnected())
375  m_face.m_transport->connect([=] (const Block& wire) { m_face.onReceiveElement(wire); });
376 
377  if (wantResume && !m_face.m_transport->isReceiving()) {
378  m_face.m_transport->resume();
379  }
380  }
381 
382  void
384  {
385  if (m_pendingInterestTable.empty() && m_registeredPrefixTable.empty()) {
386  m_face.m_transport->pause();
387  }
388  }
389 
390 private:
399  Block
400  finishEncoding(lp::Packet&& lpPacket, Block wire, char pktType, const Name& name)
401  {
402  if (!lpPacket.empty()) {
403  lpPacket.add<lp::FragmentField>(std::make_pair(wire.begin(), wire.end()));
404  wire = lpPacket.wireEncode();
405  }
406 
407  if (wire.size() > MAX_NDN_PACKET_SIZE) {
408  BOOST_THROW_EXCEPTION(Face::OversizedPacketError(pktType, name, wire.size()));
409  }
410 
411  return wire;
412  }
413 
414 private:
415  Face& m_face;
416  util::Scheduler m_scheduler;
417  util::scheduler::ScopedEventId m_processEventsTimeoutEvent;
418 
419  PendingInterestTable m_pendingInterestTable;
420  InterestFilterTable m_interestFilterTable;
421  RegisteredPrefixTable m_registeredPrefixTable;
422 
423  friend class Face;
424 };
425 
426 } // namespace ndn
427 
428 #endif // NDN_DETAIL_FACE_IMPL_HPP
#define NDN_LOG_INFO(expression)
Definition: logger.hpp:36
Copyright (c) 2011-2015 Regents of the University of California.
void ensureConnected(bool wantResume)
Definition: face-impl.hpp:372
function< void(const std::string &)> UnregisterPrefixFailureCallback
Callback invoked when unregisterPrefix or unsetInterestFilter command fails.
Definition: face.hpp:85
void asyncExpressInterest(shared_ptr< const Interest > interest, const DataCallback &afterSatisfied, const NackCallback &afterNacked, const TimeoutCallback &afterTimeout)
Definition: face-impl.hpp:87
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:153
Impl(Face &face)
Definition: face-impl.hpp:68
DummyIoService & getIoService()
Return nullptr (cannot use IoService in simulations), preserved for API compatibility.
Definition: face.hpp:432
bool matchesInterest(const Interest &other) const
Check if Interest matches other interest.
Definition: interest.cpp:435
ControlParameters & setFlags(uint64_t flags)
void asyncUnsetInterestFilter(const InterestFilterId *interestFilterId)
Definition: face-impl.hpp:198
const NackHeader & getHeader() const
Definition: nack.hpp:65
void asyncSetInterestFilter(shared_ptr< InterestFilterRecord > interestFilterRecord)
Definition: face-impl.hpp:191
void asyncUnregisterPrefix(const RegisteredPrefixId *registeredPrefixId, const UnregisterPrefixSuccessCallback &onSuccess, const UnregisterPrefixFailureCallback &onFailure)
Definition: face-impl.hpp:320
void asyncPutNack(const lp::Nack &nack)
Definition: face-impl.hpp:256
NackReason getReason() const
represents a Network Nack
Definition: nack.hpp:40
Declare a field.
Definition: field-decl.hpp:178
void asyncPutData(const Data &data)
Definition: face-impl.hpp:237
Table::const_iterator iterator
Definition: cs-internal.hpp:41
#define NDN_LOG_INIT(name)
declare a log module
Definition: logger.hpp:32
#define NDN_LOG_DEBUG(expression)
Definition: logger.hpp:35
friend class Face
Definition: face-impl.hpp:423
void finalizeUnregisterPrefix(RegisteredPrefixTable::iterator item, const UnregisterPrefixSuccessCallback &onSuccess)
Definition: face-impl.hpp:359
Provide a communication channel with local or remote NDN forwarder.
Definition: face.hpp:90
void asyncRemoveAllPendingInterests()
Definition: face-impl.hpp:121
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:277
function< void(const Name &, const std::string &)> RegisterPrefixFailureCallback
Callback invoked when registerPrefix or setInterestFilter command fails.
Definition: face.hpp:75
void processIncomingInterest(shared_ptr< const Interest > interest)
Definition: face-impl.hpp:210
function< void(const Name &)> RegisterPrefixSuccessCallback
Callback invoked when registerPrefix or setInterestFilter command succeeds.
Definition: face.hpp:70
bool satisfyPendingInterests(const Data &data)
Definition: face-impl.hpp:129
std::list< shared_ptr< InterestFilterRecord > > InterestFilterTable
Definition: face-impl.hpp:64
ControlParameters & setName(const Name &name)
function< void()> UnregisterPrefixSuccessCallback
Callback invoked when unregisterPrefix or unsetInterestFilter command succeeds.
Definition: face.hpp:80
void asyncRemovePendingInterest(const PendingInterestId *pendingInterestId)
Definition: face-impl.hpp:115
ControlCommand response.
Interest was received from this app via Face::expressInterest API.
ContainerWithOnEmptySignal< shared_ptr< PendingInterest > > PendingInterestTable
Definition: face-impl.hpp:63
function< void(const Interest &)> TimeoutCallback
Callback invoked when expressed Interest times out.
Definition: face.hpp:60
function< void(const Interest &, const lp::Nack &)> NackCallback
Callback invoked when Nack is sent in response to expressed Interest.
Definition: face.hpp:55
ContainerWithOnEmptySignal< shared_ptr< RegisteredPrefix > > RegisteredPrefixTable
Definition: face-impl.hpp:65
const Interest & getInterest() const
Definition: nack.hpp:53
function< void(const Interest &, const Data &)> DataCallback
Callback invoked when expressed Interest gets satisfied with a Data packet.
Definition: face.hpp:50
void dispatchInterest(PendingInterest &entry, const Interest &interest)
Definition: face-impl.hpp:225
void onEmptyPitOrNoRegisteredPrefixes()
Definition: face-impl.hpp:383
const size_t MAX_NDN_PACKET_SIZE
practical limit of network layer packet size
Definition: tlv.hpp:39
optional< lp::Nack > nackPendingInterests(const lp::Nack &nack)
Definition: face-impl.hpp:157
void afterPrefixRegistered(shared_ptr< RegisteredPrefix > registeredPrefix, const RegisterPrefixSuccessCallback &onSuccess)
Definition: face-impl.hpp:303