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().IsLinkLocal())
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().IsLinkLocal())
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         uint16_t    offset    = aMessage.GetOffset();
474         uint16_t    endOffset = offset + len;
475         bool        hasOption = false;
476         Ip6::Option option;
477 
478         for (; offset < endOffset; offset += option.GetSize())
479         {
480             SuccessOrExit(error = option.ParseFrom(aMessage, offset, endOffset));
481             hasOption = true;
482         }
483 
484         // Check if the last option can be compressed.
485         if (hasOption && option.IsPadding())
486         {
487             padLength = option.GetSize();
488             len -= padLength;
489         }
490     }
491 
492     VerifyOrExit(aMessage.GetOffset() + len + padLength <= aMessage.GetLength(), error = kErrorParse);
493 
494     aNextHeader = static_cast<uint8_t>(extHeader.GetNextHeader());
495 
496     SuccessOrExit(error = aFrameBuilder.AppendUint8(static_cast<uint8_t>(len)));
497     SuccessOrExit(error = aFrameBuilder.AppendBytesFromMessage(aMessage, aMessage.GetOffset(), len));
498     aMessage.MoveOffset(len + padLength);
499 
500 exit:
501     if (error != kErrorNone)
502     {
503         aMessage.SetOffset(startOffset);
504     }
505 
506     return error;
507 }
508 
CompressUdp(Message & aMessage,FrameBuilder & aFrameBuilder)509 Error Lowpan::CompressUdp(Message &aMessage, FrameBuilder &aFrameBuilder)
510 {
511     Error            error       = kErrorNone;
512     uint16_t         startOffset = aMessage.GetOffset();
513     Ip6::Udp::Header udpHeader;
514     uint16_t         source;
515     uint16_t         destination;
516 
517     SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), udpHeader));
518 
519     source      = udpHeader.GetSourcePort();
520     destination = udpHeader.GetDestinationPort();
521 
522     if ((source & 0xfff0) == 0xf0b0 && (destination & 0xfff0) == 0xf0b0)
523     {
524         SuccessOrExit(error = aFrameBuilder.AppendUint8(kUdpDispatch | 3));
525         SuccessOrExit(error = aFrameBuilder.AppendUint8((((source & 0xf) << 4) | (destination & 0xf)) & 0xff));
526     }
527     else if ((source & 0xff00) == 0xf000)
528     {
529         SuccessOrExit(error = aFrameBuilder.AppendUint8(kUdpDispatch | 2));
530         SuccessOrExit(error = aFrameBuilder.AppendUint8(source & 0xff));
531         SuccessOrExit(error = aFrameBuilder.AppendBigEndianUint16(destination));
532     }
533     else if ((destination & 0xff00) == 0xf000)
534     {
535         SuccessOrExit(error = aFrameBuilder.AppendUint8(kUdpDispatch | 1));
536         SuccessOrExit(error = aFrameBuilder.AppendBigEndianUint16(source));
537         SuccessOrExit(error = aFrameBuilder.AppendUint8(destination & 0xff));
538     }
539     else
540     {
541         SuccessOrExit(error = aFrameBuilder.AppendUint8(kUdpDispatch));
542         SuccessOrExit(error = aFrameBuilder.AppendBytes(&udpHeader, Ip6::Udp::Header::kLengthFieldOffset));
543     }
544 
545     SuccessOrExit(error = aFrameBuilder.AppendBigEndianUint16(udpHeader.GetChecksum()));
546 
547     aMessage.MoveOffset(sizeof(udpHeader));
548 
549 exit:
550     if (error != kErrorNone)
551     {
552         aMessage.SetOffset(startOffset);
553     }
554 
555     return error;
556 }
557 
DispatchToNextHeader(uint8_t aDispatch,uint8_t & aNextHeader)558 Error Lowpan::DispatchToNextHeader(uint8_t aDispatch, uint8_t &aNextHeader)
559 {
560     Error error = kErrorNone;
561 
562     if ((aDispatch & kExtHdrDispatchMask) == kExtHdrDispatch)
563     {
564         switch (aDispatch & kExtHdrEidMask)
565         {
566         case kExtHdrEidHbh:
567             aNextHeader = Ip6::kProtoHopOpts;
568             ExitNow();
569 
570         case kExtHdrEidRouting:
571             aNextHeader = Ip6::kProtoRouting;
572             ExitNow();
573 
574         case kExtHdrEidFragment:
575             aNextHeader = Ip6::kProtoFragment;
576             ExitNow();
577 
578         case kExtHdrEidDst:
579             aNextHeader = Ip6::kProtoDstOpts;
580             ExitNow();
581 
582         case kExtHdrEidIp6:
583             aNextHeader = Ip6::kProtoIp6;
584             ExitNow();
585         }
586     }
587     else if ((aDispatch & kUdpDispatchMask) == kUdpDispatch)
588     {
589         aNextHeader = Ip6::kProtoUdp;
590         ExitNow();
591     }
592 
593     error = kErrorParse;
594 
595 exit:
596     return error;
597 }
598 
DecompressBaseHeader(Ip6::Header & aIp6Header,bool & aCompressedNextHeader,const Mac::Addresses & aMacAddrs,FrameData & aFrameData)599 Error Lowpan::DecompressBaseHeader(Ip6::Header          &aIp6Header,
600                                    bool                 &aCompressedNextHeader,
601                                    const Mac::Addresses &aMacAddrs,
602                                    FrameData            &aFrameData)
603 {
604     Error    error = kErrorParse;
605     uint16_t hcCtl;
606     uint8_t  byte;
607     uint8_t  srcContextId = 0;
608     uint8_t  dstContextId = 0;
609     Context  srcContext;
610     Context  dstContext;
611     uint8_t  nextHeader;
612 
613     SuccessOrExit(aFrameData.ReadBigEndianUint16(hcCtl));
614 
615     // check Dispatch bits
616     VerifyOrExit((hcCtl & kHcDispatchMask) == kHcDispatch);
617 
618     // Context Identifier
619     if ((hcCtl & kHcContextId) != 0)
620     {
621         SuccessOrExit(aFrameData.ReadUint8(byte));
622 
623         srcContextId = (byte >> 4);
624         dstContextId = (byte & 0xf);
625     }
626 
627     FindContextForId(srcContextId, srcContext);
628     FindContextForId(dstContextId, dstContext);
629 
630     aIp6Header.Clear();
631     aIp6Header.InitVersionTrafficClassFlow();
632 
633     // Traffic Class and Flow Label
634     if ((hcCtl & kHcTrafficFlowMask) != kHcTrafficFlow)
635     {
636         uint8_t *ip6HeaderBytes = reinterpret_cast<uint8_t *>(&aIp6Header);
637 
638         VerifyOrExit(aFrameData.GetLength() > 0);
639 
640         ip6HeaderBytes[1] |= (aFrameData.GetBytes()[0] & 0xc0) >> 2;
641 
642         if ((hcCtl & kHcTrafficClass) == 0)
643         {
644             IgnoreError(aFrameData.ReadUint8(byte));
645             ip6HeaderBytes[0] |= (byte >> 2) & 0x0f;
646             ip6HeaderBytes[1] |= (byte << 6) & 0xc0;
647         }
648 
649         if ((hcCtl & kHcFlowLabel) == 0)
650         {
651             VerifyOrExit(aFrameData.GetLength() >= 3);
652             ip6HeaderBytes[1] |= aFrameData.GetBytes()[0] & 0x0f;
653             ip6HeaderBytes[2] |= aFrameData.GetBytes()[1];
654             ip6HeaderBytes[3] |= aFrameData.GetBytes()[2];
655             aFrameData.SkipOver(3);
656         }
657     }
658 
659     // Next Header
660     if ((hcCtl & kHcNextHeader) == 0)
661     {
662         SuccessOrExit(aFrameData.ReadUint8(byte));
663 
664         aIp6Header.SetNextHeader(byte);
665         aCompressedNextHeader = false;
666     }
667     else
668     {
669         aCompressedNextHeader = true;
670     }
671 
672     // Hop Limit
673     switch (hcCtl & kHcHopLimitMask)
674     {
675     case kHcHopLimit1:
676         aIp6Header.SetHopLimit(1);
677         break;
678 
679     case kHcHopLimit64:
680         aIp6Header.SetHopLimit(64);
681         break;
682 
683     case kHcHopLimit255:
684         aIp6Header.SetHopLimit(255);
685         break;
686 
687     default:
688         SuccessOrExit(aFrameData.ReadUint8(byte));
689         aIp6Header.SetHopLimit(byte);
690         break;
691     }
692 
693     // Source Address
694     switch (hcCtl & kHcSrcAddrModeMask)
695     {
696     case kHcSrcAddrMode0:
697         if ((hcCtl & kHcSrcAddrContext) == 0)
698         {
699             SuccessOrExit(aFrameData.Read(aIp6Header.GetSource()));
700         }
701 
702         break;
703 
704     case kHcSrcAddrMode1:
705         SuccessOrExit(aFrameData.Read(aIp6Header.GetSource().GetIid()));
706         break;
707 
708     case kHcSrcAddrMode2:
709         aIp6Header.GetSource().mFields.m8[11] = 0xff;
710         aIp6Header.GetSource().mFields.m8[12] = 0xfe;
711         SuccessOrExit(aFrameData.ReadBytes(aIp6Header.GetSource().mFields.m8 + 14, 2));
712         break;
713 
714     case kHcSrcAddrMode3:
715         IgnoreError(ComputeIid(aMacAddrs.mSource, srcContext, aIp6Header.GetSource().GetIid()));
716         break;
717     }
718 
719     if ((hcCtl & kHcSrcAddrModeMask) != kHcSrcAddrMode0)
720     {
721         if ((hcCtl & kHcSrcAddrContext) == 0)
722         {
723             aIp6Header.GetSource().mFields.m16[0] = BigEndian::HostSwap16(0xfe80);
724         }
725         else
726         {
727             VerifyOrExit(srcContext.mIsValid);
728             aIp6Header.GetSource().SetPrefix(srcContext.mPrefix);
729         }
730     }
731 
732     if ((hcCtl & kHcMulticast) == 0)
733     {
734         // Unicast Destination Address
735 
736         switch (hcCtl & kHcDstAddrModeMask)
737         {
738         case kHcDstAddrMode0:
739             VerifyOrExit((hcCtl & kHcDstAddrContext) == 0);
740             SuccessOrExit(aFrameData.Read(aIp6Header.GetDestination()));
741             break;
742 
743         case kHcDstAddrMode1:
744             SuccessOrExit(aFrameData.Read(aIp6Header.GetDestination().GetIid()));
745             break;
746 
747         case kHcDstAddrMode2:
748             aIp6Header.GetDestination().mFields.m8[11] = 0xff;
749             aIp6Header.GetDestination().mFields.m8[12] = 0xfe;
750             SuccessOrExit(aFrameData.ReadBytes(aIp6Header.GetDestination().mFields.m8 + 14, 2));
751             break;
752 
753         case kHcDstAddrMode3:
754             SuccessOrExit(ComputeIid(aMacAddrs.mDestination, dstContext, aIp6Header.GetDestination().GetIid()));
755             break;
756         }
757 
758         if ((hcCtl & kHcDstAddrContext) == 0)
759         {
760             if ((hcCtl & kHcDstAddrModeMask) != 0)
761             {
762                 aIp6Header.GetDestination().mFields.m16[0] = BigEndian::HostSwap16(0xfe80);
763             }
764         }
765         else
766         {
767             VerifyOrExit(dstContext.mIsValid);
768             aIp6Header.GetDestination().SetPrefix(dstContext.mPrefix);
769         }
770     }
771     else
772     {
773         // Multicast Destination Address
774 
775         aIp6Header.GetDestination().mFields.m8[0] = 0xff;
776 
777         if ((hcCtl & kHcDstAddrContext) == 0)
778         {
779             switch (hcCtl & kHcDstAddrModeMask)
780             {
781             case kHcDstAddrMode0:
782                 SuccessOrExit(aFrameData.Read(aIp6Header.GetDestination()));
783                 break;
784 
785             case kHcDstAddrMode1:
786                 SuccessOrExit(aFrameData.ReadUint8(aIp6Header.GetDestination().mFields.m8[1]));
787                 SuccessOrExit(aFrameData.ReadBytes(aIp6Header.GetDestination().mFields.m8 + 11, 5));
788                 break;
789 
790             case kHcDstAddrMode2:
791                 SuccessOrExit(aFrameData.ReadUint8(aIp6Header.GetDestination().mFields.m8[1]));
792                 SuccessOrExit(aFrameData.ReadBytes(aIp6Header.GetDestination().mFields.m8 + 13, 3));
793                 break;
794 
795             case kHcDstAddrMode3:
796                 aIp6Header.GetDestination().mFields.m8[1] = 0x02;
797                 SuccessOrExit(aFrameData.ReadUint8(aIp6Header.GetDestination().mFields.m8[15]));
798                 break;
799             }
800         }
801         else
802         {
803             switch (hcCtl & kHcDstAddrModeMask)
804             {
805             case 0:
806                 VerifyOrExit(dstContext.mIsValid);
807                 SuccessOrExit(aFrameData.ReadBytes(aIp6Header.GetDestination().mFields.m8 + 1, 2));
808                 aIp6Header.GetDestination().mFields.m8[3] = dstContext.mPrefix.GetLength();
809                 memcpy(aIp6Header.GetDestination().mFields.m8 + 4, dstContext.mPrefix.GetBytes(), 8);
810                 SuccessOrExit(aFrameData.ReadBytes(aIp6Header.GetDestination().mFields.m8 + 12, 4));
811                 break;
812 
813             default:
814                 ExitNow();
815             }
816         }
817     }
818 
819     if ((hcCtl & kHcNextHeader) != 0)
820     {
821         VerifyOrExit(aFrameData.GetLength() > 0);
822         SuccessOrExit(DispatchToNextHeader(*aFrameData.GetBytes(), nextHeader));
823         aIp6Header.SetNextHeader(nextHeader);
824     }
825 
826     error = kErrorNone;
827 
828 exit:
829     return error;
830 }
831 
DecompressExtensionHeader(Message & aMessage,FrameData & aFrameData)832 Error Lowpan::DecompressExtensionHeader(Message &aMessage, FrameData &aFrameData)
833 {
834     Error          error = kErrorParse;
835     uint8_t        hdr[2];
836     uint8_t        len;
837     uint8_t        ctl;
838     Ip6::PadOption padOption;
839 
840     SuccessOrExit(aFrameData.ReadUint8(ctl));
841 
842     // next header
843     if (ctl & kExtHdrNextHeader)
844     {
845         SuccessOrExit(aFrameData.ReadUint8(len));
846 
847         VerifyOrExit(aFrameData.CanRead(len + 1));
848         SuccessOrExit(DispatchToNextHeader(aFrameData.GetBytes()[len], hdr[0]));
849     }
850     else
851     {
852         SuccessOrExit(aFrameData.ReadUint8(hdr[0]));
853         SuccessOrExit(aFrameData.ReadUint8(len));
854 
855         VerifyOrExit(aFrameData.CanRead(len));
856     }
857 
858     // length
859     hdr[1] = BytesForBitSize(sizeof(hdr) + len) - 1;
860 
861     SuccessOrExit(aMessage.AppendBytes(hdr, sizeof(hdr)));
862     aMessage.MoveOffset(sizeof(hdr));
863 
864     // payload
865     SuccessOrExit(aMessage.AppendBytes(aFrameData.GetBytes(), len));
866     aMessage.MoveOffset(len);
867     aFrameData.SkipOver(len);
868 
869     // The RFC6282 says: "The trailing Pad1 or PadN option MAY be elided by the compressor.
870     // A decompressor MUST ensure that the containing header is padded out to a multiple of 8 octets
871     // in length, using a Pad1 or PadN option if necessary."
872 
873     if (padOption.InitToPadHeaderWithSize(len + sizeof(hdr)) == kErrorNone)
874     {
875         SuccessOrExit(aMessage.AppendBytes(&padOption, padOption.GetSize()));
876         aMessage.MoveOffset(padOption.GetSize());
877     }
878 
879     error = kErrorNone;
880 
881 exit:
882     return error;
883 }
884 
DecompressUdpHeader(Ip6::Udp::Header & aUdpHeader,FrameData & aFrameData)885 Error Lowpan::DecompressUdpHeader(Ip6::Udp::Header &aUdpHeader, FrameData &aFrameData)
886 {
887     Error    error = kErrorParse;
888     uint8_t  udpCtl;
889     uint8_t  byte;
890     uint16_t srcPort = 0;
891     uint16_t dstPort = 0;
892 
893     SuccessOrExit(aFrameData.ReadUint8(udpCtl));
894 
895     VerifyOrExit((udpCtl & kUdpDispatchMask) == kUdpDispatch);
896 
897     aUdpHeader.Clear();
898 
899     switch (udpCtl & kUdpPortMask)
900     {
901     case 0:
902         SuccessOrExit(aFrameData.ReadBigEndianUint16(srcPort));
903         SuccessOrExit(aFrameData.ReadBigEndianUint16(dstPort));
904         break;
905 
906     case 1:
907         SuccessOrExit(aFrameData.ReadBigEndianUint16(srcPort));
908         SuccessOrExit(aFrameData.ReadUint8(byte));
909         dstPort = (0xf000 | byte);
910         break;
911 
912     case 2:
913         SuccessOrExit(aFrameData.ReadUint8(byte));
914         srcPort = (0xf000 | byte);
915         SuccessOrExit(aFrameData.ReadBigEndianUint16(dstPort));
916         break;
917 
918     case 3:
919         SuccessOrExit(aFrameData.ReadUint8(byte));
920         srcPort = (0xf0b0 | (byte >> 4));
921         dstPort = (0xf0b0 | (byte & 0xf));
922         break;
923     }
924 
925     aUdpHeader.SetSourcePort(srcPort);
926     aUdpHeader.SetDestinationPort(dstPort);
927 
928     if ((udpCtl & kUdpChecksum) != 0)
929     {
930         ExitNow();
931     }
932     else
933     {
934         uint16_t checksum;
935 
936         SuccessOrExit(aFrameData.ReadBigEndianUint16(checksum));
937         aUdpHeader.SetChecksum(checksum);
938     }
939 
940     error = kErrorNone;
941 
942 exit:
943     return error;
944 }
945 
DecompressUdpHeader(Message & aMessage,FrameData & aFrameData,uint16_t aDatagramLength)946 Error Lowpan::DecompressUdpHeader(Message &aMessage, FrameData &aFrameData, uint16_t aDatagramLength)
947 {
948     Error            error;
949     Ip6::Udp::Header udpHeader;
950 
951     SuccessOrExit(error = DecompressUdpHeader(udpHeader, aFrameData));
952 
953     // length
954     if (aDatagramLength == 0)
955     {
956         udpHeader.SetLength(sizeof(udpHeader) + aFrameData.GetLength());
957     }
958     else
959     {
960         udpHeader.SetLength(aDatagramLength - aMessage.GetOffset());
961     }
962 
963     SuccessOrExit(error = aMessage.Append(udpHeader));
964     aMessage.MoveOffset(sizeof(udpHeader));
965 
966 exit:
967     return error;
968 }
969 
Decompress(Message & aMessage,const Mac::Addresses & aMacAddrs,FrameData & aFrameData,uint16_t aDatagramLength)970 Error Lowpan::Decompress(Message              &aMessage,
971                          const Mac::Addresses &aMacAddrs,
972                          FrameData            &aFrameData,
973                          uint16_t              aDatagramLength)
974 {
975     Error       error = kErrorParse;
976     Ip6::Header ip6Header;
977     bool        compressed;
978     uint16_t    ip6PayloadLength;
979     uint16_t    currentOffset = aMessage.GetOffset();
980 
981     SuccessOrExit(DecompressBaseHeader(ip6Header, compressed, aMacAddrs, aFrameData));
982 
983     SuccessOrExit(aMessage.Append(ip6Header));
984     aMessage.MoveOffset(sizeof(ip6Header));
985 
986     while (compressed)
987     {
988         uint8_t byte;
989 
990         VerifyOrExit(aFrameData.GetLength() > 0);
991         byte = *aFrameData.GetBytes();
992 
993         if ((byte & kExtHdrDispatchMask) == kExtHdrDispatch)
994         {
995             if ((byte & kExtHdrEidMask) == kExtHdrEidIp6)
996             {
997                 compressed = false;
998 
999                 aFrameData.SkipOver(sizeof(uint8_t));
1000 
1001                 SuccessOrExit(Decompress(aMessage, aMacAddrs, aFrameData, aDatagramLength));
1002             }
1003             else
1004             {
1005                 compressed = (byte & kExtHdrNextHeader) != 0;
1006                 SuccessOrExit(DecompressExtensionHeader(aMessage, aFrameData));
1007             }
1008         }
1009         else if ((byte & kUdpDispatchMask) == kUdpDispatch)
1010         {
1011             compressed = false;
1012             SuccessOrExit(DecompressUdpHeader(aMessage, aFrameData, aDatagramLength));
1013         }
1014         else
1015         {
1016             ExitNow();
1017         }
1018     }
1019 
1020     if (aDatagramLength)
1021     {
1022         ip6PayloadLength = BigEndian::HostSwap16(aDatagramLength - currentOffset - sizeof(Ip6::Header));
1023     }
1024     else
1025     {
1026         ip6PayloadLength =
1027             BigEndian::HostSwap16(aMessage.GetOffset() - currentOffset - sizeof(Ip6::Header) + aFrameData.GetLength());
1028     }
1029 
1030     aMessage.Write(currentOffset + Ip6::Header::kPayloadLengthFieldOffset, ip6PayloadLength);
1031 
1032     error = kErrorNone;
1033 
1034 exit:
1035     return error;
1036 }
1037 
DecompressEcn(const Message & aMessage,uint16_t aOffset) const1038 Ip6::Ecn Lowpan::DecompressEcn(const Message &aMessage, uint16_t aOffset) const
1039 {
1040     Ip6::Ecn ecn = Ip6::kEcnNotCapable;
1041     uint16_t hcCtl;
1042     uint8_t  byte;
1043 
1044     SuccessOrExit(aMessage.Read(aOffset, hcCtl));
1045     hcCtl = BigEndian::HostSwap16(hcCtl);
1046 
1047     VerifyOrExit((hcCtl & kHcDispatchMask) == kHcDispatch);
1048     aOffset += sizeof(uint16_t);
1049 
1050     if ((hcCtl & kHcTrafficFlowMask) == kHcTrafficFlow)
1051     {
1052         // ECN is elided and is zero (`kEcnNotCapable`).
1053         ExitNow();
1054     }
1055 
1056     // When ECN is not elided, it is always included as the
1057     // first two bits of the next byte.
1058     SuccessOrExit(aMessage.Read(aOffset, byte));
1059     ecn = static_cast<Ip6::Ecn>((byte & kEcnMask) >> kEcnOffset);
1060 
1061 exit:
1062     return ecn;
1063 }
1064 
MarkCompressedEcn(Message & aMessage,uint16_t aOffset)1065 void Lowpan::MarkCompressedEcn(Message &aMessage, uint16_t aOffset)
1066 {
1067     uint8_t byte;
1068 
1069     aOffset += sizeof(uint16_t);
1070     IgnoreError(aMessage.Read(aOffset, byte));
1071 
1072     byte &= ~kEcnMask;
1073     byte |= static_cast<uint8_t>(Ip6::kEcnMarked << kEcnOffset);
1074 
1075     aMessage.Write(aOffset, byte);
1076 }
1077 
1078 //---------------------------------------------------------------------------------------------------------------------
1079 // MeshHeader
1080 
Init(uint16_t aSource,uint16_t aDestination,uint8_t aHopsLeft)1081 void MeshHeader::Init(uint16_t aSource, uint16_t aDestination, uint8_t aHopsLeft)
1082 {
1083     mSource      = aSource;
1084     mDestination = aDestination;
1085     mHopsLeft    = aHopsLeft;
1086 }
1087 
IsMeshHeader(const FrameData & aFrameData)1088 bool MeshHeader::IsMeshHeader(const FrameData &aFrameData)
1089 {
1090     return (aFrameData.GetLength() >= kMinHeaderLength) && ((*aFrameData.GetBytes() & kDispatchMask) == kDispatch);
1091 }
1092 
ParseFrom(FrameData & aFrameData)1093 Error MeshHeader::ParseFrom(FrameData &aFrameData)
1094 {
1095     Error    error;
1096     uint16_t headerLength;
1097 
1098     SuccessOrExit(error = ParseFrom(aFrameData.GetBytes(), aFrameData.GetLength(), headerLength));
1099     aFrameData.SkipOver(headerLength);
1100 
1101 exit:
1102     return error;
1103 }
1104 
ParseFrom(const uint8_t * aFrame,uint16_t aFrameLength,uint16_t & aHeaderLength)1105 Error MeshHeader::ParseFrom(const uint8_t *aFrame, uint16_t aFrameLength, uint16_t &aHeaderLength)
1106 {
1107     Error   error = kErrorParse;
1108     uint8_t dispatch;
1109 
1110     VerifyOrExit(aFrameLength >= kMinHeaderLength);
1111     dispatch = *aFrame++;
1112 
1113     VerifyOrExit((dispatch & (kDispatchMask | kSourceShort | kDestShort)) == (kDispatch | kSourceShort | kDestShort));
1114 
1115     mHopsLeft = (dispatch & kHopsLeftMask);
1116 
1117     if (mHopsLeft == kDeepHopsLeft)
1118     {
1119         VerifyOrExit(aFrameLength >= kDeepHopsHeaderLength);
1120         mHopsLeft     = *aFrame++;
1121         aHeaderLength = kDeepHopsHeaderLength;
1122     }
1123     else
1124     {
1125         aHeaderLength = kMinHeaderLength;
1126     }
1127 
1128     mSource      = BigEndian::ReadUint16(aFrame);
1129     mDestination = BigEndian::ReadUint16(aFrame + 2);
1130 
1131     error = kErrorNone;
1132 
1133 exit:
1134     return error;
1135 }
1136 
ParseFrom(const Message & aMessage)1137 Error MeshHeader::ParseFrom(const Message &aMessage)
1138 {
1139     uint16_t headerLength;
1140 
1141     return ParseFrom(aMessage, headerLength);
1142 }
1143 
ParseFrom(const Message & aMessage,uint16_t & aHeaderLength)1144 Error MeshHeader::ParseFrom(const Message &aMessage, uint16_t &aHeaderLength)
1145 {
1146     uint8_t  frame[kDeepHopsHeaderLength];
1147     uint16_t frameLength;
1148 
1149     frameLength = aMessage.ReadBytes(/* aOffset */ 0, frame, sizeof(frame));
1150 
1151     return ParseFrom(frame, frameLength, aHeaderLength);
1152 }
1153 
GetHeaderLength(void) const1154 uint16_t MeshHeader::GetHeaderLength(void) const
1155 {
1156     return (mHopsLeft >= kDeepHopsLeft) ? kDeepHopsHeaderLength : kMinHeaderLength;
1157 }
1158 
DecrementHopsLeft(void)1159 void MeshHeader::DecrementHopsLeft(void)
1160 {
1161     if (mHopsLeft > 0)
1162     {
1163         mHopsLeft--;
1164     }
1165 }
1166 
AppendTo(FrameBuilder & aFrameBuilder) const1167 Error MeshHeader::AppendTo(FrameBuilder &aFrameBuilder) const
1168 {
1169     Error   error;
1170     uint8_t dispatch = (kDispatch | kSourceShort | kDestShort);
1171 
1172     if (mHopsLeft < kDeepHopsLeft)
1173     {
1174         SuccessOrExit(error = aFrameBuilder.AppendUint8(dispatch | mHopsLeft));
1175     }
1176     else
1177     {
1178         SuccessOrExit(error = aFrameBuilder.AppendUint8(dispatch | kDeepHopsLeft));
1179         SuccessOrExit(error = aFrameBuilder.AppendUint8(mHopsLeft));
1180     }
1181 
1182     SuccessOrExit(error = aFrameBuilder.AppendBigEndianUint16(mSource));
1183     SuccessOrExit(error = aFrameBuilder.AppendBigEndianUint16(mDestination));
1184 
1185 exit:
1186     return error;
1187 }
1188 
AppendTo(Message & aMessage) const1189 Error MeshHeader::AppendTo(Message &aMessage) const
1190 {
1191     uint8_t      frame[kDeepHopsHeaderLength];
1192     FrameBuilder frameBuilder;
1193 
1194     frameBuilder.Init(frame, sizeof(frame));
1195 
1196     IgnoreError(AppendTo(frameBuilder));
1197 
1198     return aMessage.AppendBytes(frameBuilder.GetBytes(), frameBuilder.GetLength());
1199 }
1200 
1201 //---------------------------------------------------------------------------------------------------------------------
1202 // FragmentHeader
1203 
IsFragmentHeader(const FrameData & aFrameData)1204 bool FragmentHeader::IsFragmentHeader(const FrameData &aFrameData)
1205 {
1206     return IsFragmentHeader(aFrameData.GetBytes(), aFrameData.GetLength());
1207 }
1208 
IsFragmentHeader(const uint8_t * aFrame,uint16_t aFrameLength)1209 bool FragmentHeader::IsFragmentHeader(const uint8_t *aFrame, uint16_t aFrameLength)
1210 {
1211     return (aFrameLength >= sizeof(FirstFrag)) && ((*aFrame & kDispatchMask) == kDispatch);
1212 }
1213 
ParseFrom(FrameData & aFrameData)1214 Error FragmentHeader::ParseFrom(FrameData &aFrameData)
1215 {
1216     Error    error;
1217     uint16_t headerLength;
1218 
1219     SuccessOrExit(error = ParseFrom(aFrameData.GetBytes(), aFrameData.GetLength(), headerLength));
1220     aFrameData.SkipOver(headerLength);
1221 
1222 exit:
1223     return error;
1224 }
1225 
ParseFrom(const uint8_t * aFrame,uint16_t aFrameLength,uint16_t & aHeaderLength)1226 Error FragmentHeader::ParseFrom(const uint8_t *aFrame, uint16_t aFrameLength, uint16_t &aHeaderLength)
1227 {
1228     Error error = kErrorParse;
1229 
1230     VerifyOrExit(IsFragmentHeader(aFrame, aFrameLength));
1231 
1232     mSize = BigEndian::ReadUint16(aFrame + kSizeIndex) & kSizeMask;
1233     mTag  = BigEndian::ReadUint16(aFrame + kTagIndex);
1234 
1235     if ((*aFrame & kOffsetFlag) == kOffsetFlag)
1236     {
1237         VerifyOrExit(aFrameLength >= sizeof(NextFrag));
1238         mOffset       = aFrame[kOffsetIndex] * 8;
1239         aHeaderLength = sizeof(NextFrag);
1240     }
1241     else
1242     {
1243         mOffset       = 0;
1244         aHeaderLength = sizeof(FirstFrag);
1245     }
1246 
1247     error = kErrorNone;
1248 
1249 exit:
1250     return error;
1251 }
1252 
ParseFrom(const Message & aMessage,uint16_t aOffset,uint16_t & aHeaderLength)1253 Error FragmentHeader::ParseFrom(const Message &aMessage, uint16_t aOffset, uint16_t &aHeaderLength)
1254 {
1255     uint8_t  frame[sizeof(NextFrag)];
1256     uint16_t frameLength;
1257 
1258     frameLength = aMessage.ReadBytes(aOffset, frame, sizeof(frame));
1259 
1260     return ParseFrom(frame, frameLength, aHeaderLength);
1261 }
1262 
1263 } // namespace Lowpan
1264 } // namespace ot
1265