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-2022 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_CXX_IMPL_FACE_IMPL_HPP
23 #define NDN_CXX_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 in its URI string representation, Data is printed as name
49 // only, Nack is printed as the Interest followed by the Nack reason separated 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 : public std::enable_shared_from_this<Face::Impl>
61 {
62 public:
63  Impl(Face& face, KeyChain& keyChain)
64  : m_face(face)
65  , m_scheduler(m_face.getIoService())
66  , m_nfdController(m_face, keyChain)
67  {
68  auto onEmptyPitOrNoRegisteredPrefixes = [this] {
69  // Without this extra "post", transport can get paused (-async_read) and then resumed
70  // (+async_read) from within onInterest/onData callback. After onInterest/onData
71  // finishes, there is another +async_read with the same memory block. A few of such
72  // async_read duplications can cause various effects and result in segfault.
73  m_scheduler.schedule(time::seconds(0), [this] {
74  if (m_pendingInterestTable.empty() && m_registeredPrefixTable.empty()) {
75  m_face.m_transport->pause();
76  m_processEventsTimeoutEvent.cancel();
77  }
78  });
79  };
80 
81  m_pendingInterestTable.onEmpty.connect(onEmptyPitOrNoRegisteredPrefixes);
82  m_registeredPrefixTable.onEmpty.connect(onEmptyPitOrNoRegisteredPrefixes);
83  }
84 
85 public: // consumer
86  void
87  expressInterest(detail::RecordId id, 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& entry = m_pendingInterestTable.put(id, std::move(interest), afterSatisfied,
97  afterNacked, afterTimeout, m_scheduler);
98 
99  lp::Packet lpPacket;
100  addFieldFromTag<lp::NextHopFaceIdField, lp::NextHopFaceIdTag>(lpPacket, interest2);
101  addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, interest2);
102 
103  entry.recordForwarding();
104  m_face.m_transport->send(finishEncoding(std::move(lpPacket), interest2.wireEncode(),
105  'I', interest2.getName()));
106  dispatchInterest(entry, interest2);
107  }
108 
109  void
111  {
112  m_scheduler.schedule(time::seconds(0), [id, w = weak_ptr<Impl>{shared_from_this()}] { // use weak_from_this() in C++17
113  auto impl = w.lock();
114  if (impl != nullptr) {
115  impl->m_pendingInterestTable.erase(id);
116  }
117  });
118  }
119 
120  void
122  {
123  m_pendingInterestTable.clear();
124  }
125 
128  bool
130  {
131  bool hasAppMatch = false, hasForwarderMatch = false;
132  m_pendingInterestTable.removeIf([&] (PendingInterest& entry) {
133  if (!entry.getInterest()->matchesData(data)) {
134  return false;
135  }
136  NDN_LOG_DEBUG(" satisfying " << *entry.getInterest() << " from " << entry.getOrigin());
137 
138  if (entry.getOrigin() == PendingInterestOrigin::APP) {
139  hasAppMatch = true;
140  entry.invokeDataCallback(data);
141  }
142  else {
143  hasForwarderMatch = true;
144  }
145 
146  return 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  m_pendingInterestTable.removeIf([&] (PendingInterest& entry) {
160  if (!nack.getInterest().matchesInterest(*entry.getInterest())) {
161  return false;
162  }
163  NDN_LOG_DEBUG(" nacking " << *entry.getInterest() << " from " << entry.getOrigin());
164 
165  optional<lp::Nack> outNack1 = entry.recordNack(nack);
166  if (!outNack1) {
167  return false;
168  }
169 
170  if (entry.getOrigin() == PendingInterestOrigin::APP) {
171  entry.invokeNackCallback(*outNack1);
172  }
173  else {
174  outNack = outNack1;
175  }
176  return true;
177  });
178 
179  // send "least severe" Nack from any PendingInterest record originated from forwarder, because
180  // it is unimportant to consider Nack reason for the unlikely case when forwarder sends multiple
181  // Interests to an app in a short while
182  return outNack;
183  }
184 
185 public: // producer
186  void
188  {
189  NDN_LOG_INFO("setting InterestFilter: " << filter);
190  m_interestFilterTable.put(id, filter, onInterest);
191  }
192 
193  void
195  {
196  m_scheduler.schedule(time::seconds(0), [id, w = weak_ptr<Impl>{shared_from_this()}] { // use weak_from_this() in C++17
197  auto impl = w.lock();
198  if (impl != nullptr) {
199  impl->unsetInterestFilter(id);
200  }
201  });
202  }
203 
204  void
205  processIncomingInterest(shared_ptr<const Interest> interest)
206  {
207  const Interest& interest2 = *interest;
208  auto& entry = m_pendingInterestTable.insert(std::move(interest), m_scheduler);
209  dispatchInterest(entry, interest2);
210  }
211 
212  void
213  putData(const Data& data)
214  {
215  NDN_LOG_DEBUG("<D " << data.getName());
216  bool shouldSendToForwarder = satisfyPendingInterests(data);
217  if (!shouldSendToForwarder) {
218  return;
219  }
220 
221  this->ensureConnected(true);
222 
223  lp::Packet lpPacket;
224  addFieldFromTag<lp::CachePolicyField, lp::CachePolicyTag>(lpPacket, data);
225  addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, data);
226 
227  m_face.m_transport->send(finishEncoding(std::move(lpPacket), data.wireEncode(),
228  'D', data.getName()));
229  }
230 
231  void
232  putNack(const lp::Nack& nack)
233  {
234  NDN_LOG_DEBUG("<N " << nack.getInterest() << '~' << nack.getHeader().getReason());
235  optional<lp::Nack> outNack = nackPendingInterests(nack);
236  if (!outNack) {
237  return;
238  }
239 
240  this->ensureConnected(true);
241 
242  lp::Packet lpPacket;
243  lpPacket.add<lp::NackField>(outNack->getHeader());
244  addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, *outNack);
245 
246  const Interest& interest = outNack->getInterest();
247  m_face.m_transport->send(finishEncoding(std::move(lpPacket), interest.wireEncode(),
248  'N', interest.getName()));
249  }
250 
251 public: // prefix registration
253  registerPrefix(const Name& prefix,
254  const RegisterPrefixSuccessCallback& onSuccess,
255  const RegisterPrefixFailureCallback& onFailure,
256  uint64_t flags, const nfd::CommandOptions& options,
257  const optional<InterestFilter>& filter, const InterestCallback& onInterest)
258  {
259  NDN_LOG_INFO("registering prefix: " << prefix);
260  auto id = m_registeredPrefixTable.allocateId();
261 
262  m_nfdController.start<nfd::RibRegisterCommand>(
263  nfd::ControlParameters().setName(prefix).setFlags(flags),
264  [=] (const nfd::ControlParameters&) {
265  NDN_LOG_INFO("registered prefix: " << prefix);
266 
267  detail::RecordId filterId = 0;
268  if (filter) {
269  NDN_LOG_INFO("setting InterestFilter: " << *filter);
270  auto& filterRecord = m_interestFilterTable.insert(*filter, onInterest);
271  filterId = filterRecord.getId();
272  }
273  m_registeredPrefixTable.put(id, prefix, options, filterId);
274 
275  if (onSuccess) {
276  onSuccess(prefix);
277  }
278  },
279  [=] (const nfd::ControlResponse& resp) {
280  NDN_LOG_INFO("register prefix failed: " << prefix);
281  onFailure(prefix, resp.getText());
282  },
283  options);
284 
285  return id;
286  }
287 
288  void
290  const UnregisterPrefixSuccessCallback& onSuccess,
291  const UnregisterPrefixFailureCallback& onFailure)
292  {
293  m_scheduler.schedule(time::seconds(0), [=, w = weak_ptr<Impl>{shared_from_this()}] { // use weak_from_this() in C++17
294  auto impl = w.lock();
295  if (impl != nullptr) {
296  impl->unregisterPrefix(id, onSuccess, onFailure);
297  }
298  });
299  }
300 
301 public: // IO routine
302  void
303  ensureConnected(bool wantResume)
304  {
305  if (!m_face.m_transport->isConnected()) {
306  m_face.m_transport->connect([this] (const Block& wire) { m_face.onReceiveElement(wire); });
307  }
308 
309  if (wantResume && !m_face.m_transport->isReceiving()) {
310  m_face.m_transport->resume();
311  }
312  }
313 
314  void
316  {
317  m_pendingInterestTable.clear();
318  m_registeredPrefixTable.clear();
319  }
320 
321 private:
330  Block
331  finishEncoding(lp::Packet&& lpPacket, Block wire, char pktType, const Name& name)
332  {
333  if (!lpPacket.empty()) {
334  lpPacket.add<lp::FragmentField>({wire.begin(), wire.end()});
335  wire = lpPacket.wireEncode();
336  }
337 
338  if (wire.size() > MAX_NDN_PACKET_SIZE) {
339  NDN_THROW(Face::OversizedPacketError(pktType, name, wire.size()));
340  }
341 
342  return wire;
343  }
344 
345  void
346  dispatchInterest(PendingInterest& entry, const Interest& interest)
347  {
348  m_interestFilterTable.forEach([&] (const InterestFilterRecord& filter) {
349  if (!filter.doesMatch(entry)) {
350  return;
351  }
352  NDN_LOG_DEBUG(" matches " << filter.getFilter());
353  entry.recordForwarding();
354  filter.invokeInterestCallback(interest);
355  });
356  }
357 
358  void
359  unsetInterestFilter(detail::RecordId id)
360  {
361  const auto* record = m_interestFilterTable.get(id);
362  if (record != nullptr) {
363  NDN_LOG_INFO("unsetting InterestFilter: " << record->getFilter());
364  m_interestFilterTable.erase(id);
365  }
366  }
367 
368  void
369  unregisterPrefix(detail::RecordId id,
370  const UnregisterPrefixSuccessCallback& onSuccess,
371  const UnregisterPrefixFailureCallback& onFailure)
372  {
373  const auto* record = m_registeredPrefixTable.get(id);
374  if (record == nullptr) {
375  if (onFailure) {
376  onFailure("Unrecognized RegisteredPrefixHandle");
377  }
378  return;
379  }
380 
381  if (record->getFilterId() != 0) {
382  unsetInterestFilter(record->getFilterId());
383  }
384 
385  NDN_LOG_INFO("unregistering prefix: " << record->getPrefix());
386 
387  m_nfdController.start<nfd::RibUnregisterCommand>(
388  nfd::ControlParameters().setName(record->getPrefix()),
389  [=] (const nfd::ControlParameters&) {
390  NDN_LOG_INFO("unregistered prefix: " << record->getPrefix());
391  m_registeredPrefixTable.erase(id);
392  if (onSuccess) {
393  onSuccess();
394  }
395  },
396  [=] (const nfd::ControlResponse& resp) {
397  NDN_LOG_INFO("unregister prefix failed: " << record->getPrefix());
398  if (onFailure) {
399  onFailure(resp.getText());
400  }
401  },
402  record->getCommandOptions());
403  }
404 
405 private:
406  Face& m_face;
407  Scheduler m_scheduler;
408  scheduler::ScopedEventId m_processEventsTimeoutEvent;
409  nfd::Controller m_nfdController;
410 
411  detail::RecordContainer<PendingInterest> m_pendingInterestTable;
412  detail::RecordContainer<InterestFilterRecord> m_interestFilterTable;
413  detail::RecordContainer<RegisteredPrefix> m_registeredPrefixTable;
414 
415  friend Face;
416 };
417 
418 } // namespace ndn
419 
420 #endif // NDN_CXX_IMPL_FACE_IMPL_HPP
boost::chrono::seconds seconds
Definition: time.hpp:47
void start(const ControlParameters &parameters, const CommandSucceedCallback &onSuccess, const CommandFailCallback &onFailure, const CommandOptions &options=CommandOptions())
start command execution
Definition: controller.hpp:78
void invokeInterestCallback(const Interest &interest) const
invokes the InterestCallback
Copyright (c) 2011-2015 Regents of the University of California.
void removeAllPendingInterests()
Definition: face-impl.hpp:121
void forEach(const Visitor &f)
Visit all records.
function< void(const std::string &)> UnregisterPrefixFailureCallback
Callback invoked when unregistering a prefix fails.
Definition: face.hpp:85
shared_ptr< const Interest > getInterest() const
represents parameters in a ControlCommand request or response
implementation detail of Face
Definition: face-impl.hpp:60
Record & insert(TArgs &&... args)
Insert a record with newly assigned ID.
NDN_CXX_NODISCARD bool empty() const noexcept
ndn security KeyChain
Definition: key-chain.cpp:70
void removeIf(const Visitor &f)
Visit all records with the option to erase.
util::Signal< RecordContainer< T > > onEmpty
Signals when container becomes empty.
Packet & add(const typename FIELD::ValueType &value)
add a FIELD with value
Definition: packet.hpp:148
void asyncUnregisterPrefix(detail::RecordId id, const UnregisterPrefixSuccessCallback &onSuccess, const UnregisterPrefixFailureCallback &onFailure)
Definition: face-impl.hpp:289
declares the set of Interests a producer can serve, which starts with a name prefix, plus an optional regular expression
const InterestFilter & getFilter() const
DummyIoService & getIoService()
Definition: face.hpp:383
#define NDN_LOG_DEBUG(expression)
Definition: logger.hpp:99
bool matchesInterest(const Interest &other) const
Check if this Interest matches other.
Definition: interest.cpp:357
optional< lp::Nack > recordNack(const lp::Nack &nack)
Record an incoming Nack against a forwarded Interest.
Associates an InterestFilter with an Interest callback.
const_iterator begin() const
Get begin iterator of encoded wire.
Definition: block.cpp:267
void asyncRemovePendingInterest(detail::RecordId id)
Definition: face-impl.hpp:110
bool doesMatch(const PendingInterest &entry) const
Check if Interest name matches the filter.
Represents a TLV element of the NDN packet format.
Definition: block.hpp:44
Represents an Interest packet.
Definition: interest.hpp:48
const NackHeader & getHeader() const
Definition: nack.hpp:63
void putNack(const lp::Nack &nack)
Definition: face-impl.hpp:232
EventId schedule(time::nanoseconds after, EventCallback callback)
Schedule a one-time event after the specified delay.
Definition: scheduler.cpp:96
Record * get(RecordId id)
Retrieve record by ID.
void processIncomingInterest(shared_ptr< const Interest > interest)
Definition: face-impl.hpp:205
NackReason getReason() const
represents a Network Nack
Definition: nack.hpp:38
#define NDN_THROW(e)
Definition: exception.hpp:61
Declare a field.
Definition: field-decl.hpp:176
void invokeNackCallback(const lp::Nack &nack)
Invoke the Nack callback.
void recordForwarding()
Record that the Interest has been forwarded to one destination.
size_t size() const
Return the size of the encoded wire, i.e., of the whole TLV.
Definition: block.cpp:294
contains options for ControlCommand execution
Record & put(RecordId id, TArgs &&... args)
Insert a record with given ID.
detail::RecordId registerPrefix(const Name &prefix, const RegisterPrefixSuccessCallback &onSuccess, const RegisterPrefixFailureCallback &onFailure, uint64_t flags, const nfd::CommandOptions &options, const optional< InterestFilter > &filter, const InterestCallback &onInterest)
Definition: face-impl.hpp:253
Provide a communication channel with local or remote NDN forwarder.
Definition: face.hpp:90
NFD Management protocol client.
Definition: controller.hpp:51
#define NDN_LOG_INFO(expression)
Definition: logger.hpp:100
void ensureConnected(bool wantResume)
Definition: face-impl.hpp:303
const Name & getName() const noexcept
Get name.
Definition: data.hpp:127
function< void(const Name &, const std::string &)> RegisterPrefixFailureCallback
Callback invoked when registerPrefix or setInterestFilter command fails.
Definition: face.hpp:75
function< void(const Name &)> RegisterPrefixSuccessCallback
Callback invoked when registerPrefix or setInterestFilter command succeeds.
Definition: face.hpp:70
Represents an absolute name.
Definition: name.hpp:41
void putData(const Data &data)
Definition: face-impl.hpp:213
represents a rib/unregister command
Generic time-based scheduler.
Definition: scheduler.hpp:132
represents a rib/register command
function< void(const InterestFilter &, const Interest &)> InterestCallback
Callback invoked when an incoming Interest matches the specified InterestFilter.
Definition: face.hpp:65
size_t wireEncode(EncodingImpl< TAG > &encoder) const
Prepend wire encoding to encoder.
Definition: interest.cpp:60
void setInterestFilter(detail::RecordId id, const InterestFilter &filter, const InterestCallback &onInterest)
Definition: face-impl.hpp:187
bool satisfyPendingInterests(const Data &data)
Definition: face-impl.hpp:129
Impl(Face &face, KeyChain &keyChain)
Definition: face-impl.hpp:63
void expressInterest(detail::RecordId id, shared_ptr< const Interest > interest, const DataCallback &afterSatisfied, const NackCallback &afterNacked, const TimeoutCallback &afterTimeout)
Definition: face-impl.hpp:87
const Name & getName() const noexcept
Definition: interest.hpp:172
void asyncUnsetInterestFilter(detail::RecordId id)
Definition: face-impl.hpp:194
void cancel()
Cancel the operation.
size_t wireEncode(EncodingImpl< TAG > &encoder, bool wantUnsignedPortionOnly=false) const
Prepend wire encoding to encoder.
Definition: data.cpp:46
optional< lp::Nack > nackPendingInterests(const lp::Nack &nack)
Definition: face-impl.hpp:156
uint64_t RecordId
Definition: face.hpp:44
function< void()> UnregisterPrefixSuccessCallback
Callback invoked when unregistering a prefix succeeds.
Definition: face.hpp:80
#define NDN_LOG_INIT(name)
declare a log module
Definition: logger.hpp:81
ControlCommand response.
Interest was received from this app via Face::expressInterest API.
function< void(const Interest &)> TimeoutCallback
Callback invoked when an expressed Interest times out.
Definition: face.hpp:60
function< void(const Interest &, const lp::Nack &)> NackCallback
Callback invoked when a Nack is received in response to an expressed Interest.
Definition: face.hpp:55
PendingInterestOrigin getOrigin() const
Represents a Data packet.
Definition: data.hpp:37
Stores a pending Interest and associated callbacks.
const_iterator end() const
Get end iterator of encoded wire.
Definition: block.cpp:276
const Interest & getInterest() const
Definition: nack.hpp:51
function< void(const Interest &, const Data &)> DataCallback
Callback invoked when an expressed Interest is satisfied by a Data packet.
Definition: face.hpp:50
Exception thrown when attempting to send a packet over size limit.
Definition: face.hpp:102
const size_t MAX_NDN_PACKET_SIZE
Practical size limit of a network-layer packet.
Definition: tlv.hpp:41
void invokeDataCallback(const Data &data)
Invoke the Data callback.