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