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