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