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/tcp-transport.hpp"
35 #include "../transport/unix-transport.hpp"
36 #include "../util/config-file.hpp"
37 #include "../util/logger.hpp"
38 #include "../util/scheduler.hpp"
39 #include "../util/signal.hpp"
40 
42 // INFO level: prefix registration, etc.
43 //
44 // DEBUG level: packet logging.
45 // Each log entry starts with a direction symbol ('<' denotes an outgoing packet, '>' denotes an
46 // incoming packet) and a packet type symbol ('I' denotes an Interest, 'D' denotes a Data, 'N'
47 // denotes a Nack). Interest is printed as its string representation, Data is printed as name only,
48 // Nack is printed as the Interest followed by the Nack reason and delimited by a '~' symbol. A
49 // log line about an incoming packet may be followed by zero or more lines about Interest matching
50 // InterestFilter, Data satisfying Interest, or Nack rejecting Interest, which are also written at
51 // DEBUG level.
52 //
53 // TRACE level: more detailed unstructured messages.
54 
55 namespace ndn {
56 
59 class Face::Impl : noncopyable
60 {
61 public:
62  using PendingInterestTable = ContainerWithOnEmptySignal<shared_ptr<PendingInterest>>;
63  using InterestFilterTable = std::list<shared_ptr<InterestFilterRecord>>;
64  using RegisteredPrefixTable = ContainerWithOnEmptySignal<shared_ptr<RegisteredPrefix>>;
65 
66  explicit
68  : m_face(face)
69  , m_scheduler(m_face.getIoService())
70  , m_processEventsTimeoutEvent(m_scheduler)
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(m_face.getIoService(),
375  [=] (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:371
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:86
represents parameters in a ControlCommand request or response
implementation detail of Face
Definition: face-impl.hpp:59
Packet & add(const typename FIELD::ValueType &value)
add a FIELD with value
Definition: packet.hpp:153
Impl(Face &face)
Definition: face-impl.hpp:67
bool matchesInterest(const Interest &other) const
Check if Interest matches other interest.
Definition: interest.cpp:291
ControlParameters & setFlags(uint64_t flags)
void asyncUnsetInterestFilter(const InterestFilterId *interestFilterId)
Definition: face-impl.hpp:197
const NackHeader & getHeader() const
Definition: nack.hpp:65
void asyncSetInterestFilter(shared_ptr< InterestFilterRecord > interestFilterRecord)
Definition: face-impl.hpp:190
void asyncUnregisterPrefix(const RegisteredPrefixId *registeredPrefixId, const UnregisterPrefixSuccessCallback &onSuccess, const UnregisterPrefixFailureCallback &onFailure)
Definition: face-impl.hpp:319
void asyncPutNack(const lp::Nack &nack)
Definition: face-impl.hpp:255
NackReason getReason() const
represents a Network Nack
Definition: nack.hpp:40
void asyncPutData(const Data &data)
Definition: face-impl.hpp:236
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:358
Provide a communication channel with local or remote NDN forwarder.
Definition: face.hpp:90
void asyncRemoveAllPendingInterests()
Definition: face-impl.hpp:120
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
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:209
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:128
boost::asio::io_service & getIoService()
Return nullptr (cannot use IoService in simulations), preserved for API compatibility.
Definition: face.hpp:463
std::list< shared_ptr< InterestFilterRecord > > InterestFilterTable
Definition: face-impl.hpp:63
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:114
ControlCommand response.
Interest was received from this app via Face::expressInterest API.
ContainerWithOnEmptySignal< shared_ptr< PendingInterest > > PendingInterestTable
Definition: face-impl.hpp:62
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:64
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:224
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:156
void afterPrefixRegistered(shared_ptr< RegisteredPrefix > registeredPrefix, const RegisterPrefixSuccessCallback &onSuccess)
Definition: face-impl.hpp:302