• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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