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