1 /*
2 * Copyright (c) 2016, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file implements 6LoWPAN header compression.
32 */
33
34 #include "lowpan.hpp"
35
36 #include "common/code_utils.hpp"
37 #include "common/debug.hpp"
38 #include "common/encoding.hpp"
39 #include "common/instance.hpp"
40 #include "common/locator_getters.hpp"
41 #include "net/ip6.hpp"
42 #include "net/udp6.hpp"
43 #include "thread/network_data_leader.hpp"
44 #include "thread/thread_netif.hpp"
45
46 using ot::Encoding::BigEndian::HostSwap16;
47 using ot::Encoding::BigEndian::ReadUint16;
48 using ot::Encoding::BigEndian::WriteUint16;
49
50 namespace ot {
51 namespace Lowpan {
52
Lowpan(Instance & aInstance)53 Lowpan::Lowpan(Instance &aInstance)
54 : InstanceLocator(aInstance)
55 {
56 }
57
CopyContext(const Context & aContext,Ip6::Address & aAddress)58 void Lowpan::CopyContext(const Context &aContext, Ip6::Address &aAddress)
59 {
60 aAddress.SetPrefix(aContext.mPrefix);
61 }
62
ComputeIid(const Mac::Address & aMacAddr,const Context & aContext,Ip6::Address & aIpAddress)63 Error Lowpan::ComputeIid(const Mac::Address &aMacAddr, const Context &aContext, Ip6::Address &aIpAddress)
64 {
65 Error error = kErrorNone;
66
67 switch (aMacAddr.GetType())
68 {
69 case Mac::Address::kTypeShort:
70 aIpAddress.GetIid().SetToLocator(aMacAddr.GetShort());
71 break;
72
73 case Mac::Address::kTypeExtended:
74 aIpAddress.GetIid().SetFromExtAddress(aMacAddr.GetExtended());
75 break;
76
77 default:
78 ExitNow(error = kErrorParse);
79 }
80
81 if (aContext.mPrefix.GetLength() > 64)
82 {
83 for (int i = (aContext.mPrefix.GetLength() & ~7); i < aContext.mPrefix.GetLength(); i++)
84 {
85 aIpAddress.mFields.m8[i / CHAR_BIT] &= ~(0x80 >> (i % CHAR_BIT));
86 aIpAddress.mFields.m8[i / CHAR_BIT] |= aContext.mPrefix.GetBytes()[i / CHAR_BIT] & (0x80 >> (i % CHAR_BIT));
87 }
88 }
89
90 exit:
91 return error;
92 }
93
CompressSourceIid(const Mac::Address & aMacAddr,const Ip6::Address & aIpAddr,const Context & aContext,uint16_t & aHcCtl,FrameBuilder & aFrameBuilder)94 Error Lowpan::CompressSourceIid(const Mac::Address &aMacAddr,
95 const Ip6::Address &aIpAddr,
96 const Context & aContext,
97 uint16_t & aHcCtl,
98 FrameBuilder & aFrameBuilder)
99 {
100 Error error = kErrorNone;
101 Ip6::Address ipaddr;
102 Mac::Address tmp;
103
104 IgnoreError(ComputeIid(aMacAddr, aContext, ipaddr));
105
106 if (ipaddr.GetIid() == aIpAddr.GetIid())
107 {
108 aHcCtl |= kHcSrcAddrMode3;
109 }
110 else
111 {
112 tmp.SetShort(aIpAddr.GetIid().GetLocator());
113 IgnoreError(ComputeIid(tmp, aContext, ipaddr));
114
115 if (ipaddr.GetIid() == aIpAddr.GetIid())
116 {
117 aHcCtl |= kHcSrcAddrMode2;
118 SuccessOrExit(error = aFrameBuilder.AppendBytes(aIpAddr.mFields.m8 + 14, 2));
119 }
120 else
121 {
122 aHcCtl |= kHcSrcAddrMode1;
123 SuccessOrExit(error = aFrameBuilder.Append(aIpAddr.GetIid()));
124 }
125 }
126
127 exit:
128 return error;
129 }
130
CompressDestinationIid(const Mac::Address & aMacAddr,const Ip6::Address & aIpAddr,const Context & aContext,uint16_t & aHcCtl,FrameBuilder & aFrameBuilder)131 Error Lowpan::CompressDestinationIid(const Mac::Address &aMacAddr,
132 const Ip6::Address &aIpAddr,
133 const Context & aContext,
134 uint16_t & aHcCtl,
135 FrameBuilder & aFrameBuilder)
136 {
137 Error error = kErrorNone;
138 Ip6::Address ipaddr;
139 Mac::Address tmp;
140
141 IgnoreError(ComputeIid(aMacAddr, aContext, ipaddr));
142
143 if (ipaddr.GetIid() == aIpAddr.GetIid())
144 {
145 aHcCtl |= kHcDstAddrMode3;
146 }
147 else
148 {
149 tmp.SetShort(aIpAddr.GetIid().GetLocator());
150 IgnoreError(ComputeIid(tmp, aContext, ipaddr));
151
152 if (ipaddr.GetIid() == aIpAddr.GetIid())
153 {
154 aHcCtl |= kHcDstAddrMode2;
155 SuccessOrExit(error = aFrameBuilder.AppendBytes(aIpAddr.mFields.m8 + 14, 2));
156 }
157 else
158 {
159 aHcCtl |= kHcDstAddrMode1;
160 SuccessOrExit(error = aFrameBuilder.Append(aIpAddr.GetIid()));
161 }
162 }
163
164 exit:
165 return error;
166 }
167
CompressMulticast(const Ip6::Address & aIpAddr,uint16_t & aHcCtl,FrameBuilder & aFrameBuilder)168 Error Lowpan::CompressMulticast(const Ip6::Address &aIpAddr, uint16_t &aHcCtl, FrameBuilder &aFrameBuilder)
169 {
170 Error error = kErrorNone;
171 Context multicastContext;
172
173 aHcCtl |= kHcMulticast;
174
175 for (unsigned int i = 2; i < sizeof(Ip6::Address); i++)
176 {
177 if (aIpAddr.mFields.m8[i])
178 {
179 // Check if multicast address can be compressed to 8-bits (ff02::00xx)
180 if (aIpAddr.mFields.m8[1] == 0x02 && i >= 15)
181 {
182 aHcCtl |= kHcDstAddrMode3;
183 SuccessOrExit(error = aFrameBuilder.AppendUint8(aIpAddr.mFields.m8[15]));
184 }
185 // Check if multicast address can be compressed to 32-bits (ffxx::00xx:xxxx)
186 else if (i >= 13)
187 {
188 aHcCtl |= kHcDstAddrMode2;
189 SuccessOrExit(error = aFrameBuilder.AppendUint8(aIpAddr.mFields.m8[1]));
190 SuccessOrExit(error = aFrameBuilder.AppendBytes(aIpAddr.mFields.m8 + 13, 3));
191 }
192 // Check if multicast address can be compressed to 48-bits (ffxx::00xx:xxxx:xxxx)
193 else if (i >= 11)
194 {
195 aHcCtl |= kHcDstAddrMode1;
196 SuccessOrExit(error = aFrameBuilder.AppendUint8(aIpAddr.mFields.m8[1]));
197 SuccessOrExit(error = aFrameBuilder.AppendBytes(aIpAddr.mFields.m8 + 11, 5));
198 }
199 else
200 {
201 // Check if multicast address can be compressed using Context ID 0.
202 if (Get<NetworkData::Leader>().GetContext(0, multicastContext) == kErrorNone &&
203 multicastContext.mPrefix.GetLength() == aIpAddr.mFields.m8[3] &&
204 memcmp(multicastContext.mPrefix.GetBytes(), aIpAddr.mFields.m8 + 4, 8) == 0)
205 {
206 aHcCtl |= kHcDstAddrContext | kHcDstAddrMode0;
207 SuccessOrExit(error = aFrameBuilder.AppendBytes(aIpAddr.mFields.m8 + 1, 2));
208 SuccessOrExit(error = aFrameBuilder.AppendBytes(aIpAddr.mFields.m8 + 12, 4));
209 }
210 else
211 {
212 SuccessOrExit(error = aFrameBuilder.Append(aIpAddr));
213 }
214 }
215
216 break;
217 }
218 }
219
220 exit:
221 return error;
222 }
223
Compress(Message & aMessage,const Mac::Address & aMacSource,const Mac::Address & aMacDest,FrameBuilder & aFrameBuilder)224 Error Lowpan::Compress(Message & aMessage,
225 const Mac::Address &aMacSource,
226 const Mac::Address &aMacDest,
227 FrameBuilder & aFrameBuilder)
228 {
229 Error error = kErrorNone;
230 uint8_t headerDepth = 0xff;
231
232 while (headerDepth > 0)
233 {
234 FrameBuilder frameBuilder = aFrameBuilder;
235
236 error = Compress(aMessage, aMacSource, aMacDest, aFrameBuilder, headerDepth);
237
238 // We exit if `Compress()` is successful. Otherwise we reset
239 // the `aFrameBuidler` to its earlier state (remove all
240 // appended content from the failed `Compress()` call) and
241 // try again with a different `headerDepth`.
242
243 VerifyOrExit(error != kErrorNone);
244 aFrameBuilder = frameBuilder;
245 }
246
247 exit:
248 return error;
249 }
250
Compress(Message & aMessage,const Mac::Address & aMacSource,const Mac::Address & aMacDest,FrameBuilder & aFrameBuilder,uint8_t & aHeaderDepth)251 Error Lowpan::Compress(Message & aMessage,
252 const Mac::Address &aMacSource,
253 const Mac::Address &aMacDest,
254 FrameBuilder & aFrameBuilder,
255 uint8_t & aHeaderDepth)
256 {
257 Error error = kErrorNone;
258 NetworkData::Leader &networkData = Get<NetworkData::Leader>();
259 uint16_t startOffset = aMessage.GetOffset();
260 uint16_t hcCtl = kHcDispatch;
261 uint16_t hcCtlOffset = 0;
262 Ip6::Header ip6Header;
263 uint8_t * ip6HeaderBytes = reinterpret_cast<uint8_t *>(&ip6Header);
264 Context srcContext, dstContext;
265 bool srcContextValid, dstContextValid;
266 uint8_t nextHeader;
267 uint8_t ecn;
268 uint8_t dscp;
269 uint8_t headerDepth = 0;
270 uint8_t headerMaxDepth = aHeaderDepth;
271
272 SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), ip6Header));
273
274 srcContextValid =
275 (networkData.GetContext(ip6Header.GetSource(), srcContext) == kErrorNone && srcContext.mCompressFlag);
276
277 if (!srcContextValid)
278 {
279 IgnoreError(networkData.GetContext(0, srcContext));
280 }
281
282 dstContextValid =
283 (networkData.GetContext(ip6Header.GetDestination(), dstContext) == kErrorNone && dstContext.mCompressFlag);
284
285 if (!dstContextValid)
286 {
287 IgnoreError(networkData.GetContext(0, dstContext));
288 }
289
290 // Lowpan HC Control Bits
291 hcCtlOffset = aFrameBuilder.GetLength();
292 SuccessOrExit(error = aFrameBuilder.AppendBigEndianUint16(hcCtl));
293
294 // Context Identifier
295 if (srcContext.mContextId != 0 || dstContext.mContextId != 0)
296 {
297 hcCtl |= kHcContextId;
298 SuccessOrExit(error = aFrameBuilder.AppendUint8(((srcContext.mContextId << 4) | dstContext.mContextId) & 0xff));
299 }
300
301 dscp = ((ip6HeaderBytes[0] << 2) & 0x3c) | (ip6HeaderBytes[1] >> 6);
302 ecn = (ip6HeaderBytes[1] << 2) & 0xc0;
303
304 // Flow Label
305 if (((ip6HeaderBytes[1] & 0x0f) == 0) && ((ip6HeaderBytes[2]) == 0) && ((ip6HeaderBytes[3]) == 0))
306 {
307 if (dscp == 0 && ecn == 0)
308 {
309 // Elide Flow Label and Traffic Class.
310 hcCtl |= kHcTrafficClass | kHcFlowLabel;
311 }
312 else
313 {
314 // Elide Flow Label and carry Traffic Class in-line.
315 hcCtl |= kHcFlowLabel;
316
317 SuccessOrExit(error = aFrameBuilder.AppendUint8(ecn | dscp));
318 }
319 }
320 else if (dscp == 0)
321 {
322 // Carry Flow Label and ECN only with 2-bit padding.
323 hcCtl |= kHcTrafficClass;
324
325 SuccessOrExit(error = aFrameBuilder.AppendUint8(ecn | (ip6HeaderBytes[1] & 0x0f)));
326 SuccessOrExit(error = aFrameBuilder.AppendBytes(ip6HeaderBytes + 2, 2));
327 }
328 else
329 {
330 // Carry Flow Label and Traffic Class in-line.
331 SuccessOrExit(error = aFrameBuilder.AppendUint8(ecn | dscp));
332 SuccessOrExit(error = aFrameBuilder.AppendUint8(ip6HeaderBytes[1] & 0x0f));
333 SuccessOrExit(error = aFrameBuilder.AppendBytes(ip6HeaderBytes + 2, 2));
334 }
335
336 // Next Header
337 switch (ip6Header.GetNextHeader())
338 {
339 case Ip6::kProtoHopOpts:
340 case Ip6::kProtoUdp:
341 case Ip6::kProtoIp6:
342 if (headerDepth + 1 < headerMaxDepth)
343 {
344 hcCtl |= kHcNextHeader;
345 break;
346 }
347 OT_FALL_THROUGH;
348
349 default:
350 SuccessOrExit(error = aFrameBuilder.AppendUint8(static_cast<uint8_t>(ip6Header.GetNextHeader())));
351 break;
352 }
353
354 // Hop Limit
355 switch (ip6Header.GetHopLimit())
356 {
357 case 1:
358 hcCtl |= kHcHopLimit1;
359 break;
360
361 case 64:
362 hcCtl |= kHcHopLimit64;
363 break;
364
365 case 255:
366 hcCtl |= kHcHopLimit255;
367 break;
368
369 default:
370 SuccessOrExit(error = aFrameBuilder.AppendUint8(ip6Header.GetHopLimit()));
371 break;
372 }
373
374 // Source Address
375 if (ip6Header.GetSource().IsUnspecified())
376 {
377 hcCtl |= kHcSrcAddrContext;
378 }
379 else if (ip6Header.GetSource().IsLinkLocal())
380 {
381 SuccessOrExit(error = CompressSourceIid(aMacSource, ip6Header.GetSource(), srcContext, hcCtl, aFrameBuilder));
382 }
383 else if (srcContextValid)
384 {
385 hcCtl |= kHcSrcAddrContext;
386 SuccessOrExit(error = CompressSourceIid(aMacSource, ip6Header.GetSource(), srcContext, hcCtl, aFrameBuilder));
387 }
388 else
389 {
390 SuccessOrExit(error = aFrameBuilder.Append(ip6Header.GetSource()));
391 }
392
393 // Destination Address
394 if (ip6Header.GetDestination().IsMulticast())
395 {
396 SuccessOrExit(error = CompressMulticast(ip6Header.GetDestination(), hcCtl, aFrameBuilder));
397 }
398 else if (ip6Header.GetDestination().IsLinkLocal())
399 {
400 SuccessOrExit(
401 error = CompressDestinationIid(aMacDest, ip6Header.GetDestination(), dstContext, hcCtl, aFrameBuilder));
402 }
403 else if (dstContextValid)
404 {
405 hcCtl |= kHcDstAddrContext;
406 SuccessOrExit(
407 error = CompressDestinationIid(aMacDest, ip6Header.GetDestination(), dstContext, hcCtl, aFrameBuilder));
408 }
409 else
410 {
411 SuccessOrExit(error = aFrameBuilder.Append(ip6Header.GetDestination()));
412 }
413
414 headerDepth++;
415
416 aMessage.MoveOffset(sizeof(ip6Header));
417
418 nextHeader = static_cast<uint8_t>(ip6Header.GetNextHeader());
419
420 while (headerDepth < headerMaxDepth)
421 {
422 switch (nextHeader)
423 {
424 case Ip6::kProtoHopOpts:
425 SuccessOrExit(error = CompressExtensionHeader(aMessage, aFrameBuilder, nextHeader));
426 break;
427
428 case Ip6::kProtoUdp:
429 error = CompressUdp(aMessage, aFrameBuilder);
430 ExitNow();
431
432 case Ip6::kProtoIp6:
433 // For IP-in-IP the NH bit of the LOWPAN_NHC encoding MUST be set to zero.
434 SuccessOrExit(error = aFrameBuilder.AppendUint8(kExtHdrDispatch | kExtHdrEidIp6));
435
436 error = Compress(aMessage, aMacSource, aMacDest, aFrameBuilder);
437
438 OT_FALL_THROUGH;
439
440 default:
441 ExitNow();
442 }
443
444 headerDepth++;
445 }
446
447 exit:
448 aHeaderDepth = headerDepth;
449
450 if (error == kErrorNone)
451 {
452 aFrameBuilder.Write<uint16_t>(hcCtlOffset, HostSwap16(hcCtl));
453 }
454 else
455 {
456 aMessage.SetOffset(startOffset);
457 }
458
459 return error;
460 }
461
CompressExtensionHeader(Message & aMessage,FrameBuilder & aFrameBuilder,uint8_t & aNextHeader)462 Error Lowpan::CompressExtensionHeader(Message &aMessage, FrameBuilder &aFrameBuilder, uint8_t &aNextHeader)
463 {
464 Error error = kErrorNone;
465 uint16_t startOffset = aMessage.GetOffset();
466 Ip6::ExtensionHeader extHeader;
467 uint16_t len;
468 uint8_t padLength = 0;
469 uint8_t tmpByte;
470
471 SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), extHeader));
472 aMessage.MoveOffset(sizeof(extHeader));
473
474 tmpByte = kExtHdrDispatch | kExtHdrEidHbh;
475
476 switch (extHeader.GetNextHeader())
477 {
478 case Ip6::kProtoUdp:
479 case Ip6::kProtoIp6:
480 tmpByte |= kExtHdrNextHeader;
481 break;
482
483 default:
484 SuccessOrExit(error = aFrameBuilder.AppendUint8(tmpByte));
485 tmpByte = static_cast<uint8_t>(extHeader.GetNextHeader());
486 break;
487 }
488
489 SuccessOrExit(error = aFrameBuilder.AppendUint8(tmpByte));
490
491 len = (extHeader.GetLength() + 1) * 8 - sizeof(extHeader);
492
493 // RFC 6282 does not support compressing large extension headers
494 VerifyOrExit(len <= kExtHdrMaxLength, error = kErrorFailed);
495
496 // RFC 6282 says: "IPv6 Hop-by-Hop and Destination Options Headers may use a trailing
497 // Pad1 or PadN to achieve 8-octet alignment. When there is a single trailing Pad1 or PadN
498 // option of 7 octets or less and the containing header is a multiple of 8 octets, the trailing
499 // Pad1 or PadN option MAY be elided by the compressor."
500 if (aNextHeader == Ip6::kProtoHopOpts || aNextHeader == Ip6::kProtoDstOpts)
501 {
502 uint16_t offset = aMessage.GetOffset();
503 Ip6::OptionHeader optionHeader;
504
505 while ((offset - aMessage.GetOffset()) < len)
506 {
507 SuccessOrExit(error = aMessage.Read(offset, optionHeader));
508
509 if (optionHeader.GetType() == Ip6::OptionPad1::kType)
510 {
511 offset += sizeof(Ip6::OptionPad1);
512 }
513 else
514 {
515 offset += sizeof(optionHeader) + optionHeader.GetLength();
516 }
517 }
518
519 // Check if the last option can be compressed.
520 if (optionHeader.GetType() == Ip6::OptionPad1::kType)
521 {
522 padLength = sizeof(Ip6::OptionPad1);
523 }
524 else if (optionHeader.GetType() == Ip6::OptionPadN::kType)
525 {
526 padLength = sizeof(optionHeader) + optionHeader.GetLength();
527 }
528
529 len -= padLength;
530 }
531
532 VerifyOrExit(aMessage.GetOffset() + len + padLength <= aMessage.GetLength(), error = kErrorParse);
533
534 aNextHeader = static_cast<uint8_t>(extHeader.GetNextHeader());
535
536 SuccessOrExit(error = aFrameBuilder.AppendUint8(static_cast<uint8_t>(len)));
537 SuccessOrExit(error = aFrameBuilder.AppendBytesFromMessage(aMessage, aMessage.GetOffset(), len));
538 aMessage.MoveOffset(len + padLength);
539
540 exit:
541 if (error != kErrorNone)
542 {
543 aMessage.SetOffset(startOffset);
544 }
545
546 return error;
547 }
548
CompressUdp(Message & aMessage,FrameBuilder & aFrameBuilder)549 Error Lowpan::CompressUdp(Message &aMessage, FrameBuilder &aFrameBuilder)
550 {
551 Error error = kErrorNone;
552 uint16_t startOffset = aMessage.GetOffset();
553 Ip6::Udp::Header udpHeader;
554 uint16_t source;
555 uint16_t destination;
556
557 SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), udpHeader));
558
559 source = udpHeader.GetSourcePort();
560 destination = udpHeader.GetDestinationPort();
561
562 if ((source & 0xfff0) == 0xf0b0 && (destination & 0xfff0) == 0xf0b0)
563 {
564 SuccessOrExit(error = aFrameBuilder.AppendUint8(kUdpDispatch | 3));
565 SuccessOrExit(error = aFrameBuilder.AppendUint8((((source & 0xf) << 4) | (destination & 0xf)) & 0xff));
566 }
567 else if ((source & 0xff00) == 0xf000)
568 {
569 SuccessOrExit(error = aFrameBuilder.AppendUint8(kUdpDispatch | 2));
570 SuccessOrExit(error = aFrameBuilder.AppendUint8(source & 0xff));
571 SuccessOrExit(error = aFrameBuilder.AppendBigEndianUint16(destination));
572 }
573 else if ((destination & 0xff00) == 0xf000)
574 {
575 SuccessOrExit(error = aFrameBuilder.AppendUint8(kUdpDispatch | 1));
576 SuccessOrExit(error = aFrameBuilder.AppendBigEndianUint16(source));
577 SuccessOrExit(error = aFrameBuilder.AppendUint8(destination & 0xff));
578 }
579 else
580 {
581 SuccessOrExit(error = aFrameBuilder.AppendUint8(kUdpDispatch));
582 SuccessOrExit(error = aFrameBuilder.AppendBytes(&udpHeader, Ip6::Udp::Header::kLengthFieldOffset));
583 }
584
585 SuccessOrExit(error = aFrameBuilder.AppendBigEndianUint16(udpHeader.GetChecksum()));
586
587 aMessage.MoveOffset(sizeof(udpHeader));
588
589 exit:
590 if (error != kErrorNone)
591 {
592 aMessage.SetOffset(startOffset);
593 }
594
595 return error;
596 }
597
DispatchToNextHeader(uint8_t aDispatch,uint8_t & aNextHeader)598 Error Lowpan::DispatchToNextHeader(uint8_t aDispatch, uint8_t &aNextHeader)
599 {
600 Error error = kErrorNone;
601
602 if ((aDispatch & kExtHdrDispatchMask) == kExtHdrDispatch)
603 {
604 switch (aDispatch & kExtHdrEidMask)
605 {
606 case kExtHdrEidHbh:
607 aNextHeader = Ip6::kProtoHopOpts;
608 ExitNow();
609
610 case kExtHdrEidRouting:
611 aNextHeader = Ip6::kProtoRouting;
612 ExitNow();
613
614 case kExtHdrEidFragment:
615 aNextHeader = Ip6::kProtoFragment;
616 ExitNow();
617
618 case kExtHdrEidDst:
619 aNextHeader = Ip6::kProtoDstOpts;
620 ExitNow();
621
622 case kExtHdrEidIp6:
623 aNextHeader = Ip6::kProtoIp6;
624 ExitNow();
625 }
626 }
627 else if ((aDispatch & kUdpDispatchMask) == kUdpDispatch)
628 {
629 aNextHeader = Ip6::kProtoUdp;
630 ExitNow();
631 }
632
633 error = kErrorParse;
634
635 exit:
636 return error;
637 }
638
DecompressBaseHeader(Ip6::Header & aIp6Header,bool & aCompressedNextHeader,const Mac::Address & aMacSource,const Mac::Address & aMacDest,FrameData & aFrameData)639 Error Lowpan::DecompressBaseHeader(Ip6::Header & aIp6Header,
640 bool & aCompressedNextHeader,
641 const Mac::Address &aMacSource,
642 const Mac::Address &aMacDest,
643 FrameData & aFrameData)
644 {
645 NetworkData::Leader &networkData = Get<NetworkData::Leader>();
646 Error error = kErrorParse;
647 uint16_t hcCtl;
648 uint8_t byte;
649 Context srcContext, dstContext;
650 bool srcContextValid = true, dstContextValid = true;
651 uint8_t nextHeader;
652
653 SuccessOrExit(aFrameData.ReadBigEndianUint16(hcCtl));
654
655 // check Dispatch bits
656 VerifyOrExit((hcCtl & kHcDispatchMask) == kHcDispatch);
657
658 // Context Identifier
659 srcContext.mPrefix.SetLength(0);
660 dstContext.mPrefix.SetLength(0);
661
662 if ((hcCtl & kHcContextId) != 0)
663 {
664 SuccessOrExit(aFrameData.ReadUint8(byte));
665
666 if (networkData.GetContext(byte >> 4, srcContext) != kErrorNone)
667 {
668 srcContextValid = false;
669 }
670
671 if (networkData.GetContext(byte & 0xf, dstContext) != kErrorNone)
672 {
673 dstContextValid = false;
674 }
675 }
676 else
677 {
678 IgnoreError(networkData.GetContext(0, srcContext));
679 IgnoreError(networkData.GetContext(0, dstContext));
680 }
681
682 aIp6Header.Clear();
683 aIp6Header.InitVersionTrafficClassFlow();
684
685 // Traffic Class and Flow Label
686 if ((hcCtl & kHcTrafficFlowMask) != kHcTrafficFlow)
687 {
688 uint8_t *ip6HeaderBytes = reinterpret_cast<uint8_t *>(&aIp6Header);
689
690 VerifyOrExit(aFrameData.GetLength() > 0);
691
692 ip6HeaderBytes[1] |= (aFrameData.GetBytes()[0] & 0xc0) >> 2;
693
694 if ((hcCtl & kHcTrafficClass) == 0)
695 {
696 IgnoreError(aFrameData.ReadUint8(byte));
697 ip6HeaderBytes[0] |= (byte >> 2) & 0x0f;
698 ip6HeaderBytes[1] |= (byte << 6) & 0xc0;
699 }
700
701 if ((hcCtl & kHcFlowLabel) == 0)
702 {
703 VerifyOrExit(aFrameData.GetLength() >= 3);
704 ip6HeaderBytes[1] |= aFrameData.GetBytes()[0] & 0x0f;
705 ip6HeaderBytes[2] |= aFrameData.GetBytes()[1];
706 ip6HeaderBytes[3] |= aFrameData.GetBytes()[2];
707 aFrameData.SkipOver(3);
708 }
709 }
710
711 // Next Header
712 if ((hcCtl & kHcNextHeader) == 0)
713 {
714 SuccessOrExit(aFrameData.ReadUint8(byte));
715
716 aIp6Header.SetNextHeader(byte);
717 aCompressedNextHeader = false;
718 }
719 else
720 {
721 aCompressedNextHeader = true;
722 }
723
724 // Hop Limit
725 switch (hcCtl & kHcHopLimitMask)
726 {
727 case kHcHopLimit1:
728 aIp6Header.SetHopLimit(1);
729 break;
730
731 case kHcHopLimit64:
732 aIp6Header.SetHopLimit(64);
733 break;
734
735 case kHcHopLimit255:
736 aIp6Header.SetHopLimit(255);
737 break;
738
739 default:
740 SuccessOrExit(aFrameData.ReadUint8(byte));
741 aIp6Header.SetHopLimit(byte);
742 break;
743 }
744
745 // Source Address
746 switch (hcCtl & kHcSrcAddrModeMask)
747 {
748 case kHcSrcAddrMode0:
749 if ((hcCtl & kHcSrcAddrContext) == 0)
750 {
751 SuccessOrExit(aFrameData.Read(aIp6Header.GetSource()));
752 }
753
754 break;
755
756 case kHcSrcAddrMode1:
757 SuccessOrExit(aFrameData.Read(aIp6Header.GetSource().GetIid()));
758 break;
759
760 case kHcSrcAddrMode2:
761 aIp6Header.GetSource().mFields.m8[11] = 0xff;
762 aIp6Header.GetSource().mFields.m8[12] = 0xfe;
763 SuccessOrExit(aFrameData.ReadBytes(aIp6Header.GetSource().mFields.m8 + 14, 2));
764 break;
765
766 case kHcSrcAddrMode3:
767 IgnoreError(ComputeIid(aMacSource, srcContext, aIp6Header.GetSource()));
768 break;
769 }
770
771 if ((hcCtl & kHcSrcAddrModeMask) != kHcSrcAddrMode0)
772 {
773 if ((hcCtl & kHcSrcAddrContext) == 0)
774 {
775 aIp6Header.GetSource().mFields.m16[0] = HostSwap16(0xfe80);
776 }
777 else
778 {
779 VerifyOrExit(srcContextValid);
780 CopyContext(srcContext, aIp6Header.GetSource());
781 }
782 }
783
784 if ((hcCtl & kHcMulticast) == 0)
785 {
786 // Unicast Destination Address
787
788 switch (hcCtl & kHcDstAddrModeMask)
789 {
790 case kHcDstAddrMode0:
791 VerifyOrExit((hcCtl & kHcDstAddrContext) == 0);
792 SuccessOrExit(aFrameData.Read(aIp6Header.GetDestination()));
793 break;
794
795 case kHcDstAddrMode1:
796 SuccessOrExit(aFrameData.Read(aIp6Header.GetDestination().GetIid()));
797 break;
798
799 case kHcDstAddrMode2:
800 aIp6Header.GetDestination().mFields.m8[11] = 0xff;
801 aIp6Header.GetDestination().mFields.m8[12] = 0xfe;
802 SuccessOrExit(aFrameData.ReadBytes(aIp6Header.GetDestination().mFields.m8 + 14, 2));
803 break;
804
805 case kHcDstAddrMode3:
806 SuccessOrExit(ComputeIid(aMacDest, dstContext, aIp6Header.GetDestination()));
807 break;
808 }
809
810 if ((hcCtl & kHcDstAddrContext) == 0)
811 {
812 if ((hcCtl & kHcDstAddrModeMask) != 0)
813 {
814 aIp6Header.GetDestination().mFields.m16[0] = HostSwap16(0xfe80);
815 }
816 }
817 else
818 {
819 VerifyOrExit(dstContextValid);
820 CopyContext(dstContext, aIp6Header.GetDestination());
821 }
822 }
823 else
824 {
825 // Multicast Destination Address
826
827 aIp6Header.GetDestination().mFields.m8[0] = 0xff;
828
829 if ((hcCtl & kHcDstAddrContext) == 0)
830 {
831 switch (hcCtl & kHcDstAddrModeMask)
832 {
833 case kHcDstAddrMode0:
834 SuccessOrExit(aFrameData.Read(aIp6Header.GetDestination()));
835 break;
836
837 case kHcDstAddrMode1:
838 SuccessOrExit(aFrameData.ReadUint8(aIp6Header.GetDestination().mFields.m8[1]));
839 SuccessOrExit(aFrameData.ReadBytes(aIp6Header.GetDestination().mFields.m8 + 11, 5));
840 break;
841
842 case kHcDstAddrMode2:
843 SuccessOrExit(aFrameData.ReadUint8(aIp6Header.GetDestination().mFields.m8[1]));
844 SuccessOrExit(aFrameData.ReadBytes(aIp6Header.GetDestination().mFields.m8 + 13, 3));
845 break;
846
847 case kHcDstAddrMode3:
848 aIp6Header.GetDestination().mFields.m8[1] = 0x02;
849 SuccessOrExit(aFrameData.ReadUint8(aIp6Header.GetDestination().mFields.m8[15]));
850 break;
851 }
852 }
853 else
854 {
855 switch (hcCtl & kHcDstAddrModeMask)
856 {
857 case 0:
858 VerifyOrExit(dstContextValid);
859 SuccessOrExit(aFrameData.ReadBytes(aIp6Header.GetDestination().mFields.m8 + 1, 2));
860 aIp6Header.GetDestination().mFields.m8[3] = dstContext.mPrefix.GetLength();
861 memcpy(aIp6Header.GetDestination().mFields.m8 + 4, dstContext.mPrefix.GetBytes(), 8);
862 SuccessOrExit(aFrameData.ReadBytes(aIp6Header.GetDestination().mFields.m8 + 12, 4));
863 break;
864
865 default:
866 ExitNow();
867 }
868 }
869 }
870
871 if ((hcCtl & kHcNextHeader) != 0)
872 {
873 VerifyOrExit(aFrameData.GetLength() > 0);
874 SuccessOrExit(DispatchToNextHeader(*aFrameData.GetBytes(), nextHeader));
875 aIp6Header.SetNextHeader(nextHeader);
876 }
877
878 error = kErrorNone;
879
880 exit:
881 return error;
882 }
883
DecompressExtensionHeader(Message & aMessage,FrameData & aFrameData)884 Error Lowpan::DecompressExtensionHeader(Message &aMessage, FrameData &aFrameData)
885 {
886 Error error = kErrorParse;
887 uint8_t hdr[2];
888 uint8_t len;
889 uint8_t ctl;
890 uint8_t padLength;
891
892 SuccessOrExit(aFrameData.ReadUint8(ctl));
893
894 // next header
895 if (ctl & kExtHdrNextHeader)
896 {
897 SuccessOrExit(aFrameData.ReadUint8(len));
898
899 VerifyOrExit(aFrameData.CanRead(len + 1));
900 SuccessOrExit(DispatchToNextHeader(aFrameData.GetBytes()[len], hdr[0]));
901 }
902 else
903 {
904 SuccessOrExit(aFrameData.ReadUint8(hdr[0]));
905 SuccessOrExit(aFrameData.ReadUint8(len));
906
907 VerifyOrExit(aFrameData.CanRead(len));
908 }
909
910 // length
911 hdr[1] = BitVectorBytes(sizeof(hdr) + len) - 1;
912
913 SuccessOrExit(aMessage.AppendBytes(hdr, sizeof(hdr)));
914 aMessage.MoveOffset(sizeof(hdr));
915
916 // payload
917 SuccessOrExit(aMessage.AppendBytes(aFrameData.GetBytes(), len));
918 aMessage.MoveOffset(len);
919 aFrameData.SkipOver(len);
920
921 // The RFC6282 says: "The trailing Pad1 or PadN option MAY be elided by the compressor.
922 // A decompressor MUST ensure that the containing header is padded out to a multiple of 8 octets
923 // in length, using a Pad1 or PadN option if necessary."
924 padLength = 8 - ((len + sizeof(hdr)) & 0x07);
925
926 if (padLength != 8)
927 {
928 if (padLength == 1)
929 {
930 Ip6::OptionPad1 optionPad1;
931
932 optionPad1.Init();
933 SuccessOrExit(aMessage.AppendBytes(&optionPad1, padLength));
934 }
935 else
936 {
937 Ip6::OptionPadN optionPadN;
938
939 optionPadN.Init(padLength);
940 SuccessOrExit(aMessage.AppendBytes(&optionPadN, padLength));
941 }
942
943 aMessage.MoveOffset(padLength);
944 }
945
946 error = kErrorNone;
947
948 exit:
949 return error;
950 }
951
DecompressUdpHeader(Ip6::Udp::Header & aUdpHeader,FrameData & aFrameData)952 Error Lowpan::DecompressUdpHeader(Ip6::Udp::Header &aUdpHeader, FrameData &aFrameData)
953 {
954 Error error = kErrorParse;
955 uint8_t udpCtl;
956 uint8_t byte;
957 uint16_t srcPort = 0;
958 uint16_t dstPort = 0;
959
960 SuccessOrExit(aFrameData.ReadUint8(udpCtl));
961
962 VerifyOrExit((udpCtl & kUdpDispatchMask) == kUdpDispatch);
963
964 aUdpHeader.Clear();
965
966 switch (udpCtl & kUdpPortMask)
967 {
968 case 0:
969 SuccessOrExit(aFrameData.ReadBigEndianUint16(srcPort));
970 SuccessOrExit(aFrameData.ReadBigEndianUint16(dstPort));
971 break;
972
973 case 1:
974 SuccessOrExit(aFrameData.ReadBigEndianUint16(srcPort));
975 SuccessOrExit(aFrameData.ReadUint8(byte));
976 dstPort = (0xf000 | byte);
977 break;
978
979 case 2:
980 SuccessOrExit(aFrameData.ReadUint8(byte));
981 srcPort = (0xf000 | byte);
982 SuccessOrExit(aFrameData.ReadBigEndianUint16(dstPort));
983 break;
984
985 case 3:
986 SuccessOrExit(aFrameData.ReadUint8(byte));
987 srcPort = (0xf0b0 | (byte >> 4));
988 dstPort = (0xf0b0 | (byte & 0xf));
989 break;
990 }
991
992 aUdpHeader.SetSourcePort(srcPort);
993 aUdpHeader.SetDestinationPort(dstPort);
994
995 if ((udpCtl & kUdpChecksum) != 0)
996 {
997 ExitNow();
998 }
999 else
1000 {
1001 uint16_t checksum;
1002
1003 SuccessOrExit(aFrameData.ReadBigEndianUint16(checksum));
1004 aUdpHeader.SetChecksum(checksum);
1005 }
1006
1007 error = kErrorNone;
1008
1009 exit:
1010 return error;
1011 }
1012
DecompressUdpHeader(Message & aMessage,FrameData & aFrameData,uint16_t aDatagramLength)1013 Error Lowpan::DecompressUdpHeader(Message &aMessage, FrameData &aFrameData, uint16_t aDatagramLength)
1014 {
1015 Error error;
1016 Ip6::Udp::Header udpHeader;
1017
1018 SuccessOrExit(error = DecompressUdpHeader(udpHeader, aFrameData));
1019
1020 // length
1021 if (aDatagramLength == 0)
1022 {
1023 udpHeader.SetLength(sizeof(udpHeader) + aFrameData.GetLength());
1024 }
1025 else
1026 {
1027 udpHeader.SetLength(aDatagramLength - aMessage.GetOffset());
1028 }
1029
1030 SuccessOrExit(error = aMessage.Append(udpHeader));
1031 aMessage.MoveOffset(sizeof(udpHeader));
1032
1033 exit:
1034 return error;
1035 }
1036
Decompress(Message & aMessage,const Mac::Address & aMacSource,const Mac::Address & aMacDest,FrameData & aFrameData,uint16_t aDatagramLength)1037 Error Lowpan::Decompress(Message & aMessage,
1038 const Mac::Address &aMacSource,
1039 const Mac::Address &aMacDest,
1040 FrameData & aFrameData,
1041 uint16_t aDatagramLength)
1042 {
1043 Error error = kErrorParse;
1044 Ip6::Header ip6Header;
1045 bool compressed;
1046 uint16_t ip6PayloadLength;
1047 uint16_t currentOffset = aMessage.GetOffset();
1048
1049 SuccessOrExit(DecompressBaseHeader(ip6Header, compressed, aMacSource, aMacDest, aFrameData));
1050
1051 SuccessOrExit(aMessage.Append(ip6Header));
1052 aMessage.MoveOffset(sizeof(ip6Header));
1053
1054 while (compressed)
1055 {
1056 uint8_t byte;
1057
1058 VerifyOrExit(aFrameData.GetLength() > 0);
1059 byte = *aFrameData.GetBytes();
1060
1061 if ((byte & kExtHdrDispatchMask) == kExtHdrDispatch)
1062 {
1063 if ((byte & kExtHdrEidMask) == kExtHdrEidIp6)
1064 {
1065 compressed = false;
1066
1067 aFrameData.SkipOver(sizeof(uint8_t));
1068
1069 SuccessOrExit(Decompress(aMessage, aMacSource, aMacDest, aFrameData, aDatagramLength));
1070 }
1071 else
1072 {
1073 compressed = (byte & kExtHdrNextHeader) != 0;
1074 SuccessOrExit(DecompressExtensionHeader(aMessage, aFrameData));
1075 }
1076 }
1077 else if ((byte & kUdpDispatchMask) == kUdpDispatch)
1078 {
1079 compressed = false;
1080 SuccessOrExit(DecompressUdpHeader(aMessage, aFrameData, aDatagramLength));
1081 }
1082 else
1083 {
1084 ExitNow();
1085 }
1086 }
1087
1088 if (aDatagramLength)
1089 {
1090 ip6PayloadLength = HostSwap16(aDatagramLength - currentOffset - sizeof(Ip6::Header));
1091 }
1092 else
1093 {
1094 ip6PayloadLength =
1095 HostSwap16(aMessage.GetOffset() - currentOffset - sizeof(Ip6::Header) + aFrameData.GetLength());
1096 }
1097
1098 aMessage.Write(currentOffset + Ip6::Header::kPayloadLengthFieldOffset, ip6PayloadLength);
1099
1100 error = kErrorNone;
1101
1102 exit:
1103 return error;
1104 }
1105
DecompressEcn(const Message & aMessage,uint16_t aOffset) const1106 Ip6::Ecn Lowpan::DecompressEcn(const Message &aMessage, uint16_t aOffset) const
1107 {
1108 Ip6::Ecn ecn = Ip6::kEcnNotCapable;
1109 uint16_t hcCtl;
1110 uint8_t byte;
1111
1112 SuccessOrExit(aMessage.Read(aOffset, hcCtl));
1113 hcCtl = HostSwap16(hcCtl);
1114
1115 VerifyOrExit((hcCtl & kHcDispatchMask) == kHcDispatch);
1116 aOffset += sizeof(uint16_t);
1117
1118 if ((hcCtl & kHcTrafficFlowMask) == kHcTrafficFlow)
1119 {
1120 // ECN is elided and is zero (`kEcnNotCapable`).
1121 ExitNow();
1122 }
1123
1124 // When ECN is not elided, it is always included as the
1125 // first two bits of the next byte.
1126 SuccessOrExit(aMessage.Read(aOffset, byte));
1127 ecn = static_cast<Ip6::Ecn>((byte & kEcnMask) >> kEcnOffset);
1128
1129 exit:
1130 return ecn;
1131 }
1132
MarkCompressedEcn(Message & aMessage,uint16_t aOffset)1133 void Lowpan::MarkCompressedEcn(Message &aMessage, uint16_t aOffset)
1134 {
1135 uint8_t byte;
1136
1137 aOffset += sizeof(uint16_t);
1138 IgnoreError(aMessage.Read(aOffset, byte));
1139
1140 byte &= ~kEcnMask;
1141 byte |= static_cast<uint8_t>(Ip6::kEcnMarked << kEcnOffset);
1142
1143 aMessage.Write(aOffset, byte);
1144 }
1145
1146 //---------------------------------------------------------------------------------------------------------------------
1147 // MeshHeader
1148
Init(uint16_t aSource,uint16_t aDestination,uint8_t aHopsLeft)1149 void MeshHeader::Init(uint16_t aSource, uint16_t aDestination, uint8_t aHopsLeft)
1150 {
1151 mSource = aSource;
1152 mDestination = aDestination;
1153 mHopsLeft = aHopsLeft;
1154 }
1155
IsMeshHeader(const FrameData & aFrameData)1156 bool MeshHeader::IsMeshHeader(const FrameData &aFrameData)
1157 {
1158 return (aFrameData.GetLength() >= kMinHeaderLength) && ((*aFrameData.GetBytes() & kDispatchMask) == kDispatch);
1159 }
1160
ParseFrom(FrameData & aFrameData)1161 Error MeshHeader::ParseFrom(FrameData &aFrameData)
1162 {
1163 Error error;
1164 uint16_t headerLength;
1165
1166 SuccessOrExit(error = ParseFrom(aFrameData.GetBytes(), aFrameData.GetLength(), headerLength));
1167 aFrameData.SkipOver(headerLength);
1168
1169 exit:
1170 return error;
1171 }
1172
ParseFrom(const uint8_t * aFrame,uint16_t aFrameLength,uint16_t & aHeaderLength)1173 Error MeshHeader::ParseFrom(const uint8_t *aFrame, uint16_t aFrameLength, uint16_t &aHeaderLength)
1174 {
1175 Error error = kErrorParse;
1176 uint8_t dispatch;
1177
1178 VerifyOrExit(aFrameLength >= kMinHeaderLength);
1179 dispatch = *aFrame++;
1180
1181 VerifyOrExit((dispatch & (kDispatchMask | kSourceShort | kDestShort)) == (kDispatch | kSourceShort | kDestShort));
1182
1183 mHopsLeft = (dispatch & kHopsLeftMask);
1184
1185 if (mHopsLeft == kDeepHopsLeft)
1186 {
1187 VerifyOrExit(aFrameLength >= kDeepHopsHeaderLength);
1188 mHopsLeft = *aFrame++;
1189 aHeaderLength = kDeepHopsHeaderLength;
1190 }
1191 else
1192 {
1193 aHeaderLength = kMinHeaderLength;
1194 }
1195
1196 mSource = ReadUint16(aFrame);
1197 mDestination = ReadUint16(aFrame + 2);
1198
1199 error = kErrorNone;
1200
1201 exit:
1202 return error;
1203 }
1204
ParseFrom(const Message & aMessage)1205 Error MeshHeader::ParseFrom(const Message &aMessage)
1206 {
1207 uint16_t headerLength;
1208
1209 return ParseFrom(aMessage, headerLength);
1210 }
1211
ParseFrom(const Message & aMessage,uint16_t & aHeaderLength)1212 Error MeshHeader::ParseFrom(const Message &aMessage, uint16_t &aHeaderLength)
1213 {
1214 uint8_t frame[kDeepHopsHeaderLength];
1215 uint16_t frameLength;
1216
1217 frameLength = aMessage.ReadBytes(/* aOffset */ 0, frame, sizeof(frame));
1218
1219 return ParseFrom(frame, frameLength, aHeaderLength);
1220 }
1221
GetHeaderLength(void) const1222 uint16_t MeshHeader::GetHeaderLength(void) const
1223 {
1224 return (mHopsLeft >= kDeepHopsLeft) ? kDeepHopsHeaderLength : kMinHeaderLength;
1225 }
1226
DecrementHopsLeft(void)1227 void MeshHeader::DecrementHopsLeft(void)
1228 {
1229 if (mHopsLeft > 0)
1230 {
1231 mHopsLeft--;
1232 }
1233 }
1234
WriteTo(uint8_t * aFrame) const1235 uint16_t MeshHeader::WriteTo(uint8_t *aFrame) const
1236 {
1237 uint8_t *cur = aFrame;
1238 uint8_t dispatch = (kDispatch | kSourceShort | kDestShort);
1239
1240 if (mHopsLeft < kDeepHopsLeft)
1241 {
1242 *cur++ = (dispatch | mHopsLeft);
1243 }
1244 else
1245 {
1246 *cur++ = (dispatch | kDeepHopsLeft);
1247 *cur++ = mHopsLeft;
1248 }
1249
1250 WriteUint16(mSource, cur);
1251 cur += sizeof(uint16_t);
1252
1253 WriteUint16(mDestination, cur);
1254 cur += sizeof(uint16_t);
1255
1256 return static_cast<uint16_t>(cur - aFrame);
1257 }
1258
AppendTo(Message & aMessage) const1259 Error MeshHeader::AppendTo(Message &aMessage) const
1260 {
1261 uint8_t frame[kDeepHopsHeaderLength];
1262 uint16_t headerLength;
1263
1264 headerLength = WriteTo(frame);
1265
1266 return aMessage.AppendBytes(frame, headerLength);
1267 }
1268
1269 //---------------------------------------------------------------------------------------------------------------------
1270 // FragmentHeader
1271
Init(uint16_t aSize,uint16_t aTag,uint16_t aOffset)1272 void FragmentHeader::Init(uint16_t aSize, uint16_t aTag, uint16_t aOffset)
1273 {
1274 mSize = (aSize & kSizeMask);
1275 mTag = aTag;
1276 mOffset = (aOffset & kOffsetMask);
1277 }
1278
IsFragmentHeader(const FrameData & aFrameData)1279 bool FragmentHeader::IsFragmentHeader(const FrameData &aFrameData)
1280 {
1281 return IsFragmentHeader(aFrameData.GetBytes(), aFrameData.GetLength());
1282 }
1283
IsFragmentHeader(const uint8_t * aFrame,uint16_t aFrameLength)1284 bool FragmentHeader::IsFragmentHeader(const uint8_t *aFrame, uint16_t aFrameLength)
1285 {
1286 return (aFrameLength >= kFirstFragmentHeaderSize) && ((*aFrame & kDispatchMask) == kDispatch);
1287 }
1288
ParseFrom(FrameData & aFrameData)1289 Error FragmentHeader::ParseFrom(FrameData &aFrameData)
1290 {
1291 Error error;
1292 uint16_t headerLength;
1293
1294 SuccessOrExit(error = ParseFrom(aFrameData.GetBytes(), aFrameData.GetLength(), headerLength));
1295 aFrameData.SkipOver(headerLength);
1296
1297 exit:
1298 return error;
1299 }
1300
ParseFrom(const uint8_t * aFrame,uint16_t aFrameLength,uint16_t & aHeaderLength)1301 Error FragmentHeader::ParseFrom(const uint8_t *aFrame, uint16_t aFrameLength, uint16_t &aHeaderLength)
1302 {
1303 Error error = kErrorParse;
1304
1305 VerifyOrExit(IsFragmentHeader(aFrame, aFrameLength));
1306
1307 mSize = ReadUint16(aFrame + kSizeIndex) & kSizeMask;
1308 mTag = ReadUint16(aFrame + kTagIndex);
1309
1310 if ((*aFrame & kOffsetFlag) == kOffsetFlag)
1311 {
1312 VerifyOrExit(aFrameLength >= kSubsequentFragmentHeaderSize);
1313 mOffset = aFrame[kOffsetIndex] * 8;
1314 aHeaderLength = kSubsequentFragmentHeaderSize;
1315 }
1316 else
1317 {
1318 mOffset = 0;
1319 aHeaderLength = kFirstFragmentHeaderSize;
1320 }
1321
1322 error = kErrorNone;
1323
1324 exit:
1325 return error;
1326 }
1327
ParseFrom(const Message & aMessage,uint16_t aOffset,uint16_t & aHeaderLength)1328 Error FragmentHeader::ParseFrom(const Message &aMessage, uint16_t aOffset, uint16_t &aHeaderLength)
1329 {
1330 uint8_t frame[kSubsequentFragmentHeaderSize];
1331 uint16_t frameLength;
1332
1333 frameLength = aMessage.ReadBytes(aOffset, frame, sizeof(frame));
1334
1335 return ParseFrom(frame, frameLength, aHeaderLength);
1336 }
1337
WriteTo(uint8_t * aFrame) const1338 uint16_t FragmentHeader::WriteTo(uint8_t *aFrame) const
1339 {
1340 uint8_t *cur = aFrame;
1341
1342 WriteUint16((static_cast<uint16_t>(kDispatch) << 8) + mSize, cur);
1343 cur += sizeof(uint16_t);
1344
1345 WriteUint16(mTag, cur);
1346 cur += sizeof(uint16_t);
1347
1348 if (mOffset != 0)
1349 {
1350 *aFrame |= kOffsetFlag;
1351 *cur++ = static_cast<uint8_t>(mOffset >> 3);
1352 }
1353
1354 return static_cast<uint16_t>(cur - aFrame);
1355 }
1356
1357 } // namespace Lowpan
1358 } // namespace ot
1359