esp8266ndn
NDN Arduino library for ESP8266 and more
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
lp.hpp
Go to the documentation of this file.
1 #ifndef NDNPH_PACKET_LP_HPP
2 #define NDNPH_PACKET_LP_HPP
3 
4 #include "data.hpp"
5 #include "interest.hpp"
6 #include "nack.hpp"
7 
8 namespace ndnph {
9 namespace lp {
10 
13 public:
15  using MaxSize = std::integral_constant<size_t, 1 + 1 + 8 + 1 + 1 + 1 + 1 + 1 + 1>;
16 
17  uint64_t getSeqNumBase() const {
18  return seqNum - fragIndex;
19  }
20 
21  void encodeTo(Encoder& encoder) const {
22  encoder.prepend(
23  [this](Encoder& encoder) { encoder.prependTlv(TT::LpSeqNum, tlv::NNI8(seqNum)); },
24  [this](Encoder& encoder) { encoder.prependTlv(TT::FragIndex, tlv::NNI(fragIndex)); },
25  [this](Encoder& encoder) { encoder.prependTlv(TT::FragCount, tlv::NNI(fragCount)); });
26  }
27 
28 public:
29  uint64_t seqNum = 0;
30  uint8_t fragIndex = 0;
31  uint8_t fragCount = 1;
32 };
33 
35 class PitToken {
36 public:
38  static PitToken from4(uint32_t n) {
39  uint8_t room[4];
40  tlv::NNI4::writeValue(room, n);
41  PitToken token;
42  token.set(4, room);
43  return token;
44  }
45 
47  explicit operator bool() const {
48  return m_length > 0;
49  }
50 
51  size_t length() const {
52  return m_length;
53  }
54 
55  const uint8_t* value() const {
56  return m_value.begin();
57  }
58 
60  uint32_t to4() const {
61  return m_length == 4 ? tlv::NNI4::readValue(m_value.begin()) : 0;
62  }
63 
65  bool set(size_t length, const uint8_t* value) {
66  if (length > m_value.size()) {
67  return false;
68  }
69  m_length = length;
70  std::copy_n(value, length, m_value.begin());
71  return true;
72  }
73 
74  void encodeTo(Encoder& encoder) const {
75  uint8_t* room = encoder.prependRoom(m_length);
76  if (room == nullptr) {
77  return;
78  }
79  std::copy_n(m_value.begin(), m_length, room);
80  encoder.prependTypeLength(TT::PitToken, m_length);
81  }
82 
83  bool decodeFrom(const Decoder::Tlv& d) {
84  return set(d.length, d.value);
85  }
86 
87  friend bool operator==(const PitToken& lhs, const PitToken& rhs) {
88  return lhs.m_length == rhs.m_length &&
89  std::equal(lhs.m_value.begin(), lhs.m_value.begin() + lhs.m_length, rhs.m_value.begin());
90  }
91 
92  NDNPH_DECLARE_NE(PitToken, friend)
93 
94 private:
95  std::array<uint8_t, NDNPH_PITTOKEN_MAX> m_value;
96  uint8_t m_length = 0;
97 
98  static_assert(NDNPH_PITTOKEN_MAX >= 4, "");
99  static_assert(NDNPH_PITTOKEN_MAX <= 32, "");
100 };
101 
104 public:
106  using L3MaxSize =
107  std::integral_constant<size_t, 1 + 1 + NDNPH_PITTOKEN_MAX + NackHeader::MaxSize::value>;
108 
109  void encodeL3Header(Encoder& encoder) const {
110  encoder.prepend(
111  [this](Encoder& encoder) {
112  if (pitToken) {
113  encoder.prepend(pitToken);
114  }
115  },
116  [this](Encoder& encoder) {
117  if (nack) {
118  encoder.prepend(nack);
119  }
120  });
121  }
122 
123  void copyL3HeaderFrom(const EncodableBase& src) {
124  pitToken = src.pitToken;
125  nack = src.nack;
126  }
127 
128 public:
132 };
133 
138 template<typename Payload>
139 class Encodable : public EncodableBase {
140 public:
141  explicit Encodable(Payload payload)
142  : payload(std::move(payload)) {}
143 
144  void encodeTo(Encoder& encoder) const {
146  Encoder l3h(l3hRegion);
147  encodeL3Header(l3h);
148  if (!l3h) {
149  encoder.setError();
150  return;
151  }
152 
153  if (frag.fragCount <= 1 && l3h.size() == 0) {
154  encoder.prepend(payload);
155  return;
156  }
157  encoder.prependTlv(
158  TT::LpPacket,
159  [this](Encoder& encoder) {
160  if (frag.fragCount > 1) {
161  encoder.prepend(frag);
162  }
163  },
164  tlv::Value(l3h), [this](Encoder& encoder) { encoder.prependTlv(TT::LpPayload, payload); });
165  }
166 
167 public:
168  Payload payload;
169 };
170 
176 template<typename L3, typename R = Encodable<L3>>
177 R
178 encode(L3 l3, PitToken pitToken = {}) {
179  R encodable(l3);
180  encodable.pitToken = pitToken;
181  return encodable;
182 }
183 
188 inline Encodable<Interest>
189 encode(Nack nack, PitToken pitToken = {}) {
190  auto encodable = encode(nack.getInterest(), pitToken);
191  encodable.nack = nack.getHeader();
192  return encodable;
193 }
194 
196 class Fragmenter : public WithRegion {
197 public:
199  class Fragment : public Encodable<tlv::Value> {
200  public:
201  using Encodable::Encodable;
202 
203  public:
204  const Fragment* next = nullptr;
205  };
206 
213  explicit Fragmenter(Region& region, uint16_t mtu)
214  : WithRegion(region)
215  , m_room(static_cast<int>(mtu) - FragmentOverhead) {
216  port::RandomSource::generate(reinterpret_cast<uint8_t*>(&m_nextSeqNum), sizeof(m_nextSeqNum));
217  }
218 
230  template<typename L3>
231  const Fragment* fragment(Encodable<L3> packet) {
232  region.reset();
233  size_t sizeofHeader = 0;
234  {
235  ScopedEncoder l3h(region);
236  packet.encodeL3Header(l3h);
237  if (!l3h) {
238  return nullptr;
239  }
240  sizeofHeader = l3h.size();
241  }
242 
243  Encoder payload(region);
244  packet.payload.encodeTo(payload);
245  if (!payload) {
246  payload.discard();
247  return nullptr;
248  }
249  payload.trim();
250 
251  return fragmentImpl(packet, sizeofHeader, tlv::Value(payload));
252  }
253 
254 private:
255  const Fragment* fragmentImpl(EncodableBase& input, size_t sizeofHeader, tlv::Value payload) {
256  int sizeofFirstFragment = m_room - sizeofHeader;
257  if (sizeofFirstFragment > static_cast<int>(payload.size())) {
258  auto frag = region.make<Fragment>(payload);
259  if (frag == nullptr) {
260  return nullptr;
261  }
262  frag->copyL3HeaderFrom(input);
263  return frag;
264  }
265  if (sizeofFirstFragment <= 0) {
266  return nullptr;
267  }
268 
269  auto first = region.make<Fragment>(tlv::Value(payload.begin(), sizeofFirstFragment));
270  if (first == nullptr) {
271  return nullptr;
272  }
273  first->copyL3HeaderFrom(input);
274 
275  auto prev = first;
276  uint8_t fragCount = 1;
277  for (size_t nextOffset, offset = sizeofFirstFragment; offset < payload.size();
278  offset = nextOffset) {
279  nextOffset = std::min(offset + m_room, payload.size());
280  auto frag =
281  region.make<Fragment>(tlv::Value(payload.begin() + offset, payload.begin() + nextOffset));
282  if (frag == nullptr) {
283  return nullptr;
284  }
285  prev->next = frag;
286  prev = frag;
287  ++fragCount;
288  }
289 
290  auto frag = first;
291  for (uint8_t fragIndex = 0; fragIndex < fragCount; ++fragIndex) {
292  frag->frag.seqNum = m_nextSeqNum++;
293  frag->frag.fragIndex = fragIndex;
294  frag->frag.fragCount = fragCount;
295  frag = const_cast<Fragment*>(frag->next);
296  }
297  return first;
298  }
299 
300 private:
301  enum {
302  FragmentOverhead = 1 + 3 + // LpPacket TL
303  1 + 1 + 8 + // LpSeqNum
304  1 + 1 + 1 + // FragIndex
305  1 + 1 + 1 + // FragCount
306  1 + 3 // LpPayload TL
307  };
308 
309  uint64_t m_nextSeqNum = 0;
310  int m_room = 0;
311 };
312 
314 class L3Header {
315 public:
316  std::tuple<bool, L3Header> clone(Region& region) const {
317  L3Header copy;
318  copy.pitToken = pitToken;
319  if (!nack) {
320  return std::make_tuple(true, copy);
321  }
322  copy.nack = nack.clone(region);
323  return std::make_tuple(!!copy.nack, copy);
324  }
325 
326 public:
329 };
330 
332 class Fragment : public FragmentHeader {
333 public:
336 };
337 
340 public:
341  enum class Type : uint16_t {
342  None = 0,
345  Data = TT::Data,
346  Nack = TT::Nack,
347  };
348 
349  explicit PacketClassify() = default;
350 
351  explicit PacketClassify(L3Header l3header, tlv::Value payload)
352  : m_l3header(l3header)
353  , m_payload(payload) {
354  m_type = classifyType();
355  }
356 
358  m_type = Type::None;
359  m_l3header = L3Header();
360  m_frag = FragmentHeader();
361 
362  switch (input.type) {
363  case TT::Interest:
364  case TT::Data:
365  m_payload = tlv::Value(input.tlv, input.size);
366  m_type = classifyType();
367  return m_type != Type::None;
368  case TT::LpPacket:
369  break;
370  default:
371  return false;
372  }
373 
374  bool ok = EvDecoder::decodeEx(
376  [](uint32_t type) { return type < 800 || type > 959 || (type & 0x03) != 0x00; },
377  EvDecoder::defNni<TT::LpSeqNum, tlv::NNI8>(&m_frag.seqNum),
378  EvDecoder::defNni<TT::FragIndex>(&m_frag.fragIndex),
379  EvDecoder::defNni<TT::FragCount>(&m_frag.fragCount),
380  EvDecoder::def<TT::PitToken>(&m_l3header.pitToken),
381  EvDecoder::def<TT::Nack>([this](const Decoder::Tlv& d) {
382  m_type = Type::Nack;
383  m_l3header.nack = tlv::Value(d.tlv, d.size);
384  }),
385  EvDecoder::def<TT::LpPayload>(&m_payload));
386  if (!ok) {
387  return false;
388  }
389 
390  m_type = classifyType();
391  return m_type != Type::None;
392  }
393 
395  Type getType() const {
396  return m_type;
397  }
398 
400  const PitToken& getPitToken() const {
401  return m_l3header.pitToken;
402  }
403 
409  Fragment frag;
410  static_cast<FragmentHeader&>(frag) = m_frag;
411  frag.l3header = m_l3header;
412  frag.payload = m_payload;
413  return frag;
414  }
415 
420  bool decodeInterest(Interest interest) const {
421  return m_type == Type::Interest && m_payload.makeDecoder().decode(interest);
422  }
423 
428  bool decodeData(Data data) const {
429  return m_type == Type::Data && m_payload.makeDecoder().decode(data);
430  }
431 
436  bool decodeNack(Nack nack) const {
437  auto nackHeader = nack.getHeader();
438  auto interest = nack.getInterest();
439  return m_type == Type::Nack && m_l3header.nack.makeDecoder().decode(nackHeader) &&
440  m_payload.makeDecoder().decode(interest);
441  }
442 
443 private:
444  Type classifyType() const {
445  if (m_frag.fragCount > 1) {
446  return Type::Fragment;
447  }
448  for (auto l3 : m_payload.makeDecoder()) {
449  switch (l3.type) {
450  case TT::Interest:
451  return !!m_l3header.nack ? Type::Nack : Type::Interest;
452  case TT::Data:
453  return Type::Data;
454  default:
455  return Type::None;
456  }
457  }
458  return Type::None;
459  }
460 
461 private:
462  Type m_type = Type::None;
463  FragmentHeader m_frag;
464  L3Header m_l3header;
465  tlv::Value m_payload;
466 };
467 
471 class Reassembler : public WithRegion {
472 public:
478  explicit Reassembler(Region& region)
479  : WithRegion(region) {}
480 
488  void discard() {
489  m_buffer = nullptr;
490  }
491 
507  bool add(const Fragment& frag) {
508  if (frag.fragIndex == 0) {
509  return begin(frag);
510  }
511  return append(frag);
512  }
513 
521  if (m_nextFragIndex != m_fragCount) {
522  return PacketClassify();
523  }
524  return PacketClassify(m_l3header, tlv::Value(m_buffer, m_size));
525  }
526 
527 private:
528  bool begin(const Fragment& frag) {
529  discard();
530  region.reset();
531 
532  bool ok = false;
533  std::tie(ok, m_l3header) = frag.l3header.clone(region);
534  m_capacity = region.available();
535  m_buffer = region.alloc(m_capacity);
536  if (!ok || m_buffer == nullptr) {
537  return false;
538  }
539 
540  m_size = 0;
541  m_seqNumBase = frag.getSeqNumBase();
542  m_nextFragIndex = 0;
543  m_fragCount = frag.fragCount;
544  return append(frag);
545  }
546 
547  bool append(const Fragment& frag) {
548  if (m_buffer == nullptr || frag.getSeqNumBase() != m_seqNumBase ||
549  frag.fragIndex != m_nextFragIndex || frag.fragCount != m_fragCount ||
550  frag.payload.size() > m_capacity - m_size) {
551  return false;
552  }
553 
554  std::copy(frag.payload.begin(), frag.payload.end(), &m_buffer[m_size]);
555  m_size += frag.payload.size();
556  ++m_nextFragIndex;
557  return true;
558  }
559 
560 private:
561  L3Header m_l3header;
562  uint8_t* m_buffer = nullptr;
563  size_t m_capacity = 0;
564  size_t m_size = 0;
565  uint64_t m_seqNumBase = 0;
566  uint8_t m_nextFragIndex = 0;
567  uint8_t m_fragCount = 0;
568 };
569 
570 } // namespace lp
571 } // namespace ndnph
572 
573 #endif // NDNPH_PACKET_LP_HPP
static bool generate(uint8_t *output, size_t count)
Data packet.
Definition: data.hpp:136
Decoded TLV.
Definition: decoder.hpp:13
const uint8_t * tlv
Definition: decoder.hpp:42
size_t length
Definition: decoder.hpp:39
size_t size
Definition: decoder.hpp:43
const uint8_t * value
Definition: decoder.hpp:40
TLV encoder that accepts items in reverse order.
Definition: encoder.hpp:10
bool prepend(const First &first, const Arg &... arg)
Prepend a sequence of values.
Definition: encoder.hpp:123
void trim() const
Release unused space to the Region.
Definition: encoder.hpp:58
bool prependTypeLength(uint32_t type, size_t length)
Prepend TLV-TYPE and TLV-LENGTH.
Definition: encoder.hpp:103
void discard()
Release all space to the Region.
Definition: encoder.hpp:72
uint8_t * prependRoom(size_t size)
Make room to prepend an object.
Definition: encoder.hpp:90
bool prependTlv(uint32_t type, OmitEmptyTag omitEmpty, const Arg &... arg)
Prepend TLV, measuring TLV-LENGTH automatically.
Definition: encoder.hpp:143
void setError()
Indicate an error has occurred.
Definition: encoder.hpp:166
size_t size() const
Get output size.
Definition: encoder.hpp:44
Definition: ev-decoder.hpp:95
static bool decodeEx(const Decoder::Tlv &input, std::initializer_list< uint32_t > topTypes, const UnknownCallback &unknownCb, const IsCritical &isCritical, const E &... defs)
Decode input TLV with a sequence of element definitions.
Definition: ev-decoder.hpp:145
Interest packet.
Definition: interest.hpp:284
Nack header field.
Definition: nack.hpp:22
Nack packet.
Definition: nack.hpp:73
Interest getInterest() const
Access the Interest.
Definition: nack.hpp:87
NackHeader getHeader() const
Access the Nack header.
Definition: nack.hpp:78
Region-based memory allocator thats owns memory of NDNph objects.
Definition: region.hpp:9
Encoder that auto-discards upon destruction.
Definition: encoder.hpp:198
Region with statically allocated memory.
Definition: region.hpp:143
Base class of an object associated with a Region.
Definition: region.hpp:182
Common fields during encoding.
Definition: lp.hpp:103
void encodeL3Header(Encoder &encoder) const
Definition: lp.hpp:109
FragmentHeader frag
Definition: lp.hpp:129
NackHeader nack
Definition: lp.hpp:131
std::integral_constant< size_t, 1+1+NDNPH_PITTOKEN_MAX+NackHeader::MaxSize::value > L3MaxSize
Maximum encoded size of L3 headers.
Definition: lp.hpp:107
PitToken pitToken
Definition: lp.hpp:130
void copyL3HeaderFrom(const EncodableBase &src)
Definition: lp.hpp:123
Encodable type of an LpPacket.
Definition: lp.hpp:139
Encodable(Payload payload)
Definition: lp.hpp:141
Payload payload
Definition: lp.hpp:168
void encodeTo(Encoder &encoder) const
Definition: lp.hpp:144
Fragment header fields.
Definition: lp.hpp:12
void encodeTo(Encoder &encoder) const
Definition: lp.hpp:21
std::integral_constant< size_t, 1+1+8+1+1+1+1+1+1 > MaxSize
Maximum encoded size.
Definition: lp.hpp:15
uint64_t seqNum
Definition: lp.hpp:29
uint64_t getSeqNumBase() const
Definition: lp.hpp:17
uint8_t fragIndex
Definition: lp.hpp:30
uint8_t fragCount
Definition: lp.hpp:31
Decoded fragment.
Definition: lp.hpp:332
tlv::Value payload
Definition: lp.hpp:335
L3Header l3header
Definition: lp.hpp:334
Singly linked list of encodable fragments.
Definition: lp.hpp:199
NDNLPv2 fragmenter.
Definition: lp.hpp:196
Fragmenter(Region &region, uint16_t mtu)
Constructor.
Definition: lp.hpp:213
const Fragment * fragment(Encodable< L3 > packet)
Fragment an LP packet.
Definition: lp.hpp:231
Decoded L3 header fields.
Definition: lp.hpp:314
std::tuple< bool, L3Header > clone(Region &region) const
Definition: lp.hpp:316
tlv::Value nack
Definition: lp.hpp:328
PitToken pitToken
Definition: lp.hpp:327
Decode NDNLPv2 packet for classification.
Definition: lp.hpp:339
Type getType() const
Determine L3 packet type.
Definition: lp.hpp:395
Fragment getFragment() const
Retrieve fragment.
Definition: lp.hpp:408
bool decodeFrom(const Decoder::Tlv &input)
Definition: lp.hpp:357
bool decodeNack(Nack nack) const
Decode Nack.
Definition: lp.hpp:436
bool decodeData(Data data) const
Decode payload as Data.
Definition: lp.hpp:428
Type
Definition: lp.hpp:341
const PitToken & getPitToken() const
Retrieve PIT token.
Definition: lp.hpp:400
PacketClassify(L3Header l3header, tlv::Value payload)
Definition: lp.hpp:351
bool decodeInterest(Interest interest) const
Decode payload as Interest.
Definition: lp.hpp:420
PIT token field.
Definition: lp.hpp:35
bool decodeFrom(const Decoder::Tlv &d)
Definition: lp.hpp:83
const uint8_t * value() const
Definition: lp.hpp:55
static PitToken from4(uint32_t n)
Construct 4-octet PIT token from uint32.
Definition: lp.hpp:38
uint32_t to4() const
Interpret 4-octet PIT token as uint32.
Definition: lp.hpp:60
bool set(size_t length, const uint8_t *value)
Assign PIT token length and value.
Definition: lp.hpp:65
void encodeTo(Encoder &encoder) const
Definition: lp.hpp:74
size_t length() const
Definition: lp.hpp:51
friend bool operator==(const PitToken &lhs, const PitToken &rhs)
Definition: lp.hpp:87
NDNLPv2 fragmenter.
Definition: lp.hpp:471
PacketClassify reassemble() const
Reassemble the packet if it's complete.
Definition: lp.hpp:520
void discard()
Discard the reassembly buffer.
Definition: lp.hpp:488
bool add(const Fragment &frag)
Add a fragment.
Definition: lp.hpp:507
Reassembler(Region &region)
Constructor.
Definition: lp.hpp:478
NonNegativeInteger encoding.
Definition: nni.hpp:118
A sequence of bytes, usually TLV-VALUE.
Definition: value.hpp:11
const uint8_t * begin() const
Definition: value.hpp:38
size_t size() const
Definition: value.hpp:46
Definition: nni.hpp:12
static void writeValue(uint8_t *room, T n)
static T readValue(const uint8_t *input)
#define NDNPH_PITTOKEN_MAX
Maximum length of PIT token.
Definition: common.hpp:45
@ Interest
Definition: an.hpp:30
@ LpSeqNum
Definition: an.hpp:11
@ LpPacket
Definition: an.hpp:9
@ LpPayload
Definition: an.hpp:10
@ Nack
Definition: an.hpp:15
@ Data
Definition: an.hpp:41
@ FragCount
Definition: an.hpp:13
@ PitToken
Definition: an.hpp:14
@ FragIndex
Definition: an.hpp:12
bool input(Region &region, T &target, std::istream &is=std::cin)
Read and decode from input stream.
Definition: io.hpp:15
R encode(L3 l3, PitToken pitToken={})
Encode Interest or Data as LpPacket, optionally with PIT token.
Definition: lp.hpp:178
Definition: fs.hpp:33
#define NDNPH_DECLARE_NE(T, specifier)
Declare operator!= in terms of operator==.
Definition: operators.hpp:7