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 IPv6 networking.
32 */
33
34 #include "ip6.hpp"
35
36 #include "backbone_router/bbr_leader.hpp"
37 #include "backbone_router/bbr_local.hpp"
38 #include "backbone_router/ndproxy_table.hpp"
39 #include "common/code_utils.hpp"
40 #include "common/debug.hpp"
41 #include "common/instance.hpp"
42 #include "common/locator_getters.hpp"
43 #include "common/log.hpp"
44 #include "common/message.hpp"
45 #include "common/random.hpp"
46 #include "net/checksum.hpp"
47 #include "net/icmp6.hpp"
48 #include "net/ip6_address.hpp"
49 #include "net/ip6_filter.hpp"
50 #include "net/netif.hpp"
51 #include "net/udp6.hpp"
52 #include "openthread/ip6.h"
53 #include "thread/mle.hpp"
54
55 using IcmpType = ot::Ip6::Icmp::Header::Type;
56
57 static const IcmpType sForwardICMPTypes[] = {
58 IcmpType::kTypeDstUnreach, IcmpType::kTypePacketToBig, IcmpType::kTypeTimeExceeded,
59 IcmpType::kTypeParameterProblem, IcmpType::kTypeEchoRequest, IcmpType::kTypeEchoReply,
60 };
61
62 namespace ot {
63 namespace Ip6 {
64
65 RegisterLogModule("Ip6");
66
Ip6(Instance & aInstance)67 Ip6::Ip6(Instance &aInstance)
68 : InstanceLocator(aInstance)
69 , mForwardingEnabled(false)
70 , mIsReceiveIp6FilterEnabled(false)
71 , mReceiveIp6DatagramCallback(nullptr)
72 , mReceiveIp6DatagramCallbackContext(nullptr)
73 , mSendQueueTask(aInstance, Ip6::HandleSendQueue)
74 , mIcmp(aInstance)
75 , mUdp(aInstance)
76 , mMpl(aInstance)
77 #if OPENTHREAD_CONFIG_TCP_ENABLE
78 , mTcp(aInstance)
79 #endif
80 {
81 }
82
NewMessage(uint16_t aReserved,const Message::Settings & aSettings)83 Message *Ip6::NewMessage(uint16_t aReserved, const Message::Settings &aSettings)
84 {
85 return Get<MessagePool>().Allocate(
86 Message::kTypeIp6, sizeof(Header) + sizeof(HopByHopHeader) + sizeof(OptionMpl) + aReserved, aSettings);
87 }
88
NewMessage(const uint8_t * aData,uint16_t aDataLength,const Message::Settings & aSettings)89 Message *Ip6::NewMessage(const uint8_t *aData, uint16_t aDataLength, const Message::Settings &aSettings)
90 {
91 Message *message = Get<MessagePool>().Allocate(Message::kTypeIp6, /* aReserveHeader */ 0, aSettings);
92
93 VerifyOrExit(message != nullptr);
94
95 if (message->AppendBytes(aData, aDataLength) != kErrorNone)
96 {
97 message->Free();
98 message = nullptr;
99 }
100
101 exit:
102 return message;
103 }
104
NewMessage(const uint8_t * aData,uint16_t aDataLength)105 Message *Ip6::NewMessage(const uint8_t *aData, uint16_t aDataLength)
106 {
107 Message * message = nullptr;
108 Message::Priority priority;
109
110 SuccessOrExit(GetDatagramPriority(aData, aDataLength, priority));
111 message = NewMessage(aData, aDataLength, Message::Settings(Message::kWithLinkSecurity, priority));
112
113 exit:
114 return message;
115 }
116
DscpToPriority(uint8_t aDscp)117 Message::Priority Ip6::DscpToPriority(uint8_t aDscp)
118 {
119 Message::Priority priority;
120 uint8_t cs = aDscp & kDscpCsMask;
121
122 switch (cs)
123 {
124 case kDscpCs1:
125 case kDscpCs2:
126 priority = Message::kPriorityLow;
127 break;
128
129 case kDscpCs0:
130 case kDscpCs3:
131 priority = Message::kPriorityNormal;
132 break;
133
134 case kDscpCs4:
135 case kDscpCs5:
136 case kDscpCs6:
137 case kDscpCs7:
138 priority = Message::kPriorityHigh;
139 break;
140
141 default:
142 priority = Message::kPriorityNormal;
143 break;
144 }
145
146 return priority;
147 }
148
PriorityToDscp(Message::Priority aPriority)149 uint8_t Ip6::PriorityToDscp(Message::Priority aPriority)
150 {
151 uint8_t dscp = kDscpCs0;
152
153 switch (aPriority)
154 {
155 case Message::kPriorityLow:
156 dscp = kDscpCs1;
157 break;
158
159 case Message::kPriorityNormal:
160 case Message::kPriorityNet:
161 dscp = kDscpCs0;
162 break;
163
164 case Message::kPriorityHigh:
165 dscp = kDscpCs4;
166 break;
167 }
168
169 return dscp;
170 }
171
GetDatagramPriority(const uint8_t * aData,uint16_t aDataLen,Message::Priority & aPriority)172 Error Ip6::GetDatagramPriority(const uint8_t *aData, uint16_t aDataLen, Message::Priority &aPriority)
173 {
174 Error error = kErrorNone;
175 const Header *header;
176
177 VerifyOrExit((aData != nullptr) && (aDataLen >= sizeof(Header)), error = kErrorInvalidArgs);
178
179 header = reinterpret_cast<const Header *>(aData);
180 VerifyOrExit(header->IsValid(), error = kErrorParse);
181 VerifyOrExit(sizeof(Header) + header->GetPayloadLength() == aDataLen, error = kErrorParse);
182
183 aPriority = DscpToPriority(header->GetDscp());
184
185 exit:
186 return error;
187 }
188
SetReceiveDatagramCallback(otIp6ReceiveCallback aCallback,void * aCallbackContext)189 void Ip6::SetReceiveDatagramCallback(otIp6ReceiveCallback aCallback, void *aCallbackContext)
190 {
191 mReceiveIp6DatagramCallback = aCallback;
192 mReceiveIp6DatagramCallbackContext = aCallbackContext;
193 }
194
AddMplOption(Message & aMessage,Header & aHeader)195 Error Ip6::AddMplOption(Message &aMessage, Header &aHeader)
196 {
197 Error error = kErrorNone;
198 HopByHopHeader hbhHeader;
199 OptionMpl mplOption;
200 OptionPadN padOption;
201
202 hbhHeader.SetNextHeader(aHeader.GetNextHeader());
203 hbhHeader.SetLength(0);
204 mMpl.InitOption(mplOption, aHeader.GetSource());
205
206 // Mpl option may require two bytes padding.
207 if ((mplOption.GetTotalLength() + sizeof(hbhHeader)) % 8)
208 {
209 padOption.Init(2);
210 SuccessOrExit(error = aMessage.PrependBytes(&padOption, padOption.GetTotalLength()));
211 }
212
213 SuccessOrExit(error = aMessage.PrependBytes(&mplOption, mplOption.GetTotalLength()));
214 SuccessOrExit(error = aMessage.Prepend(hbhHeader));
215 aHeader.SetPayloadLength(aHeader.GetPayloadLength() + sizeof(hbhHeader) + sizeof(mplOption));
216 aHeader.SetNextHeader(kProtoHopOpts);
217
218 exit:
219 return error;
220 }
221
AddTunneledMplOption(Message & aMessage,Header & aHeader,MessageInfo & aMessageInfo)222 Error Ip6::AddTunneledMplOption(Message &aMessage, Header &aHeader, MessageInfo &aMessageInfo)
223 {
224 Error error = kErrorNone;
225 Header tunnelHeader;
226 const Netif::UnicastAddress *source;
227 MessageInfo messageInfo(aMessageInfo);
228
229 // Use IP-in-IP encapsulation (RFC2473) and ALL_MPL_FORWARDERS address.
230 messageInfo.GetPeerAddr().SetToRealmLocalAllMplForwarders();
231
232 tunnelHeader.InitVersionTrafficClassFlow();
233 tunnelHeader.SetHopLimit(static_cast<uint8_t>(kDefaultHopLimit));
234 tunnelHeader.SetPayloadLength(aHeader.GetPayloadLength() + sizeof(tunnelHeader));
235 tunnelHeader.SetDestination(messageInfo.GetPeerAddr());
236 tunnelHeader.SetNextHeader(kProtoIp6);
237
238 VerifyOrExit((source = SelectSourceAddress(messageInfo)) != nullptr, error = kErrorInvalidSourceAddress);
239
240 tunnelHeader.SetSource(source->GetAddress());
241
242 SuccessOrExit(error = AddMplOption(aMessage, tunnelHeader));
243 SuccessOrExit(error = aMessage.Prepend(tunnelHeader));
244
245 exit:
246 return error;
247 }
248
InsertMplOption(Message & aMessage,Header & aHeader,MessageInfo & aMessageInfo)249 Error Ip6::InsertMplOption(Message &aMessage, Header &aHeader, MessageInfo &aMessageInfo)
250 {
251 Error error = kErrorNone;
252
253 VerifyOrExit(aHeader.GetDestination().IsMulticast() &&
254 aHeader.GetDestination().GetScope() >= Address::kRealmLocalScope);
255
256 if (aHeader.GetDestination().IsRealmLocalMulticast())
257 {
258 aMessage.RemoveHeader(sizeof(aHeader));
259
260 if (aHeader.GetNextHeader() == kProtoHopOpts)
261 {
262 HopByHopHeader hbh;
263 uint16_t hbhLength = 0;
264 OptionMpl mplOption;
265
266 // read existing hop-by-hop option header
267 SuccessOrExit(error = aMessage.Read(0, hbh));
268 hbhLength = (hbh.GetLength() + 1) * 8;
269
270 VerifyOrExit(hbhLength <= aHeader.GetPayloadLength(), error = kErrorParse);
271
272 // increase existing hop-by-hop option header length by 8 bytes
273 hbh.SetLength(hbh.GetLength() + 1);
274 aMessage.Write(0, hbh);
275
276 // make space for MPL Option + padding by shifting hop-by-hop option header
277 SuccessOrExit(error = aMessage.PrependBytes(nullptr, 8));
278 aMessage.CopyTo(8, 0, hbhLength, aMessage);
279
280 // insert MPL Option
281 mMpl.InitOption(mplOption, aHeader.GetSource());
282 aMessage.WriteBytes(hbhLength, &mplOption, mplOption.GetTotalLength());
283
284 // insert Pad Option (if needed)
285 if (mplOption.GetTotalLength() % 8)
286 {
287 OptionPadN padOption;
288 padOption.Init(8 - (mplOption.GetTotalLength() % 8));
289 aMessage.WriteBytes(hbhLength + mplOption.GetTotalLength(), &padOption, padOption.GetTotalLength());
290 }
291
292 // increase IPv6 Payload Length
293 aHeader.SetPayloadLength(aHeader.GetPayloadLength() + 8);
294 }
295 else
296 {
297 SuccessOrExit(error = AddMplOption(aMessage, aHeader));
298 }
299
300 SuccessOrExit(error = aMessage.Prepend(aHeader));
301 }
302 else
303 {
304 #if OPENTHREAD_FTD
305 if (aHeader.GetDestination().IsMulticastLargerThanRealmLocal() &&
306 Get<ChildTable>().HasSleepyChildWithAddress(aHeader.GetDestination()))
307 {
308 Message *messageCopy = nullptr;
309
310 if ((messageCopy = aMessage.Clone()) != nullptr)
311 {
312 IgnoreError(HandleDatagram(*messageCopy, nullptr, nullptr, /* aFromHost */ true));
313 LogInfo("Message copy for indirect transmission to sleepy children");
314 }
315 else
316 {
317 LogWarn("No enough buffer for message copy for indirect transmission to sleepy children");
318 }
319 }
320 #endif
321
322 SuccessOrExit(error = AddTunneledMplOption(aMessage, aHeader, aMessageInfo));
323 }
324
325 exit:
326 return error;
327 }
328
RemoveMplOption(Message & aMessage)329 Error Ip6::RemoveMplOption(Message &aMessage)
330 {
331 Error error = kErrorNone;
332 Header ip6Header;
333 HopByHopHeader hbh;
334 uint16_t offset;
335 uint16_t endOffset;
336 uint16_t mplOffset = 0;
337 uint8_t mplLength = 0;
338 bool remove = false;
339
340 offset = 0;
341 IgnoreError(aMessage.Read(offset, ip6Header));
342 offset += sizeof(ip6Header);
343 VerifyOrExit(ip6Header.GetNextHeader() == kProtoHopOpts);
344
345 IgnoreError(aMessage.Read(offset, hbh));
346 endOffset = offset + (hbh.GetLength() + 1) * 8;
347 VerifyOrExit(aMessage.GetLength() >= endOffset, error = kErrorParse);
348
349 offset += sizeof(hbh);
350
351 while (offset < endOffset)
352 {
353 OptionHeader option;
354
355 IgnoreError(aMessage.Read(offset, option));
356
357 switch (option.GetType())
358 {
359 case OptionMpl::kType:
360 // if multiple MPL options exist, discard packet
361 VerifyOrExit(mplOffset == 0, error = kErrorParse);
362
363 mplOffset = offset;
364 mplLength = option.GetLength();
365
366 VerifyOrExit(mplLength <= sizeof(OptionMpl) - sizeof(OptionHeader), error = kErrorParse);
367
368 if (mplOffset == sizeof(ip6Header) + sizeof(hbh) && hbh.GetLength() == 0)
369 {
370 // first and only IPv6 Option, remove IPv6 HBH Option header
371 remove = true;
372 }
373 else if (mplOffset + 8 == endOffset)
374 {
375 // last IPv6 Option, remove last 8 bytes
376 remove = true;
377 }
378
379 offset += sizeof(option) + option.GetLength();
380 break;
381
382 case OptionPad1::kType:
383 offset += sizeof(OptionPad1);
384 break;
385
386 case OptionPadN::kType:
387 offset += sizeof(option) + option.GetLength();
388 break;
389
390 default:
391 // encountered another option, now just replace MPL Option with PadN
392 remove = false;
393 offset += sizeof(option) + option.GetLength();
394 break;
395 }
396 }
397
398 // verify that IPv6 Options header is properly formed
399 VerifyOrExit(offset == endOffset, error = kErrorParse);
400
401 if (remove)
402 {
403 // last IPv6 Option, shrink HBH Option header
404 uint8_t buf[8];
405
406 offset = endOffset - sizeof(buf);
407
408 while (offset >= sizeof(buf))
409 {
410 IgnoreError(aMessage.Read(offset - sizeof(buf), buf));
411 aMessage.Write(offset, buf);
412 offset -= sizeof(buf);
413 }
414
415 aMessage.RemoveHeader(sizeof(buf));
416
417 if (mplOffset == sizeof(ip6Header) + sizeof(hbh))
418 {
419 // remove entire HBH header
420 ip6Header.SetNextHeader(hbh.GetNextHeader());
421 }
422 else
423 {
424 // update HBH header length
425 hbh.SetLength(hbh.GetLength() - 1);
426 aMessage.Write(sizeof(ip6Header), hbh);
427 }
428
429 ip6Header.SetPayloadLength(ip6Header.GetPayloadLength() - sizeof(buf));
430 aMessage.Write(0, ip6Header);
431 }
432 else if (mplOffset != 0)
433 {
434 // replace MPL Option with PadN Option
435 OptionPadN padOption;
436
437 padOption.Init(sizeof(OptionHeader) + mplLength);
438 aMessage.WriteBytes(mplOffset, &padOption, padOption.GetTotalLength());
439 }
440
441 exit:
442 return error;
443 }
444
EnqueueDatagram(Message & aMessage)445 void Ip6::EnqueueDatagram(Message &aMessage)
446 {
447 mSendQueue.Enqueue(aMessage);
448 mSendQueueTask.Post();
449 }
450
SendDatagram(Message & aMessage,MessageInfo & aMessageInfo,uint8_t aIpProto)451 Error Ip6::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t aIpProto)
452 {
453 Error error = kErrorNone;
454 Header header;
455 uint16_t payloadLength = aMessage.GetLength();
456
457 header.InitVersionTrafficClassFlow();
458 header.SetDscp(PriorityToDscp(aMessage.GetPriority()));
459 header.SetEcn(aMessageInfo.GetEcn());
460 header.SetPayloadLength(payloadLength);
461 header.SetNextHeader(aIpProto);
462
463 if (aMessageInfo.GetHopLimit() != 0 || aMessageInfo.ShouldAllowZeroHopLimit())
464 {
465 header.SetHopLimit(aMessageInfo.GetHopLimit());
466 }
467 else
468 {
469 header.SetHopLimit(static_cast<uint8_t>(kDefaultHopLimit));
470 }
471
472 if (aMessageInfo.GetSockAddr().IsUnspecified() || aMessageInfo.GetSockAddr().IsMulticast())
473 {
474 const Netif::UnicastAddress *source = SelectSourceAddress(aMessageInfo);
475
476 VerifyOrExit(source != nullptr, error = kErrorInvalidSourceAddress);
477 header.SetSource(source->GetAddress());
478 }
479 else
480 {
481 header.SetSource(aMessageInfo.GetSockAddr());
482 }
483
484 header.SetDestination(aMessageInfo.GetPeerAddr());
485
486 if (aMessageInfo.GetPeerAddr().IsRealmLocalMulticast())
487 {
488 SuccessOrExit(error = AddMplOption(aMessage, header));
489 }
490
491 SuccessOrExit(error = aMessage.Prepend(header));
492
493 Checksum::UpdateMessageChecksum(aMessage, header.GetSource(), header.GetDestination(), aIpProto);
494
495 if (aMessageInfo.GetPeerAddr().IsMulticastLargerThanRealmLocal())
496 {
497 #if OPENTHREAD_FTD
498 if (Get<ChildTable>().HasSleepyChildWithAddress(header.GetDestination()))
499 {
500 Message *messageCopy = aMessage.Clone();
501
502 if (messageCopy != nullptr)
503 {
504 LogInfo("Message copy for indirect transmission to sleepy children");
505 EnqueueDatagram(*messageCopy);
506 }
507 else
508 {
509 LogWarn("No enough buffer for message copy for indirect transmission to sleepy children");
510 }
511 }
512 #endif
513
514 SuccessOrExit(error = AddTunneledMplOption(aMessage, header, aMessageInfo));
515 }
516
517 aMessage.SetMulticastLoop(aMessageInfo.GetMulticastLoop());
518
519 if (aMessage.GetLength() > kMaxDatagramLength)
520 {
521 error = FragmentDatagram(aMessage, aIpProto);
522 }
523 else
524 {
525 EnqueueDatagram(aMessage);
526 }
527
528 exit:
529
530 return error;
531 }
532
HandleSendQueue(Tasklet & aTasklet)533 void Ip6::HandleSendQueue(Tasklet &aTasklet)
534 {
535 aTasklet.Get<Ip6>().HandleSendQueue();
536 }
537
HandleSendQueue(void)538 void Ip6::HandleSendQueue(void)
539 {
540 Message *message;
541
542 while ((message = mSendQueue.GetHead()) != nullptr)
543 {
544 mSendQueue.Dequeue(*message);
545 IgnoreError(HandleDatagram(*message, nullptr, nullptr, /* aFromHost */ false));
546 }
547 }
548
HandleOptions(Message & aMessage,Header & aHeader,bool aIsOutbound,bool & aReceive)549 Error Ip6::HandleOptions(Message &aMessage, Header &aHeader, bool aIsOutbound, bool &aReceive)
550 {
551 Error error = kErrorNone;
552 HopByHopHeader hbhHeader;
553 OptionHeader optionHeader;
554 uint16_t endOffset;
555
556 SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), hbhHeader));
557 endOffset = aMessage.GetOffset() + (hbhHeader.GetLength() + 1) * 8;
558
559 VerifyOrExit(endOffset <= aMessage.GetLength(), error = kErrorParse);
560
561 aMessage.MoveOffset(sizeof(optionHeader));
562
563 while (aMessage.GetOffset() < endOffset)
564 {
565 SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), optionHeader));
566
567 if (optionHeader.GetType() == OptionPad1::kType)
568 {
569 aMessage.MoveOffset(sizeof(OptionPad1));
570 continue;
571 }
572
573 VerifyOrExit(aMessage.GetOffset() + sizeof(optionHeader) + optionHeader.GetLength() <= endOffset,
574 error = kErrorParse);
575
576 switch (optionHeader.GetType())
577 {
578 case OptionMpl::kType:
579 SuccessOrExit(error = mMpl.ProcessOption(aMessage, aHeader.GetSource(), aIsOutbound, aReceive));
580 break;
581
582 default:
583 switch (optionHeader.GetAction())
584 {
585 case OptionHeader::kActionSkip:
586 break;
587
588 case OptionHeader::kActionDiscard:
589 ExitNow(error = kErrorDrop);
590
591 case OptionHeader::kActionForceIcmp:
592 // TODO: send icmp error
593 ExitNow(error = kErrorDrop);
594
595 case OptionHeader::kActionIcmp:
596 // TODO: send icmp error
597 ExitNow(error = kErrorDrop);
598 }
599
600 break;
601 }
602
603 aMessage.MoveOffset(sizeof(optionHeader) + optionHeader.GetLength());
604 }
605
606 exit:
607 return error;
608 }
609
610 #if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
FragmentDatagram(Message & aMessage,uint8_t aIpProto)611 Error Ip6::FragmentDatagram(Message &aMessage, uint8_t aIpProto)
612 {
613 Error error = kErrorNone;
614 Header header;
615 FragmentHeader fragmentHeader;
616 Message * fragment = nullptr;
617 uint16_t fragmentCnt = 0;
618 uint16_t payloadFragment = 0;
619 uint16_t offset = 0;
620
621 uint16_t maxPayloadFragment =
622 FragmentHeader::MakeDivisibleByEight(kMinimalMtu - aMessage.GetOffset() - sizeof(fragmentHeader));
623 uint16_t payloadLeft = aMessage.GetLength() - aMessage.GetOffset();
624
625 SuccessOrExit(error = aMessage.Read(0, header));
626 header.SetNextHeader(kProtoFragment);
627
628 fragmentHeader.Init();
629 fragmentHeader.SetIdentification(Random::NonCrypto::GetUint32());
630 fragmentHeader.SetNextHeader(aIpProto);
631 fragmentHeader.SetMoreFlag();
632
633 while (payloadLeft != 0)
634 {
635 if (payloadLeft < maxPayloadFragment)
636 {
637 fragmentHeader.ClearMoreFlag();
638
639 payloadFragment = payloadLeft;
640 payloadLeft = 0;
641
642 LogDebg("Last Fragment");
643 }
644 else
645 {
646 payloadLeft -= maxPayloadFragment;
647 payloadFragment = maxPayloadFragment;
648 }
649
650 offset = fragmentCnt * FragmentHeader::BytesToFragmentOffset(maxPayloadFragment);
651 fragmentHeader.SetOffset(offset);
652
653 VerifyOrExit((fragment = NewMessage(0)) != nullptr, error = kErrorNoBufs);
654 IgnoreError(fragment->SetPriority(aMessage.GetPriority()));
655 SuccessOrExit(error = fragment->SetLength(aMessage.GetOffset() + sizeof(fragmentHeader) + payloadFragment));
656
657 header.SetPayloadLength(payloadFragment + sizeof(fragmentHeader));
658 fragment->Write(0, header);
659
660 fragment->SetOffset(aMessage.GetOffset());
661 fragment->Write(aMessage.GetOffset(), fragmentHeader);
662
663 VerifyOrExit(aMessage.CopyTo(aMessage.GetOffset() + FragmentHeader::FragmentOffsetToBytes(offset),
664 aMessage.GetOffset() + sizeof(fragmentHeader), payloadFragment,
665 *fragment) == static_cast<int>(payloadFragment),
666 error = kErrorNoBufs);
667
668 EnqueueDatagram(*fragment);
669
670 fragmentCnt++;
671 fragment = nullptr;
672
673 LogInfo("Fragment %d with %d bytes sent", fragmentCnt, payloadFragment);
674 }
675
676 aMessage.Free();
677
678 exit:
679
680 if (error == kErrorNoBufs)
681 {
682 LogWarn("No buffer for Ip6 fragmentation");
683 }
684
685 FreeMessageOnError(fragment, error);
686 return error;
687 }
688
HandleFragment(Message & aMessage,Netif * aNetif,MessageInfo & aMessageInfo,bool aFromHost)689 Error Ip6::HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessageInfo, bool aFromHost)
690 {
691 Error error = kErrorNone;
692 Header header, headerBuffer;
693 FragmentHeader fragmentHeader;
694 Message * message = nullptr;
695 uint16_t offset = 0;
696 uint16_t payloadFragment = 0;
697 int assertValue = 0;
698 bool isFragmented = true;
699
700 OT_UNUSED_VARIABLE(assertValue);
701
702 SuccessOrExit(error = aMessage.Read(0, header));
703 SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), fragmentHeader));
704
705 if (fragmentHeader.GetOffset() == 0 && !fragmentHeader.IsMoreFlagSet())
706 {
707 isFragmented = false;
708 aMessage.MoveOffset(sizeof(fragmentHeader));
709 ExitNow();
710 }
711
712 for (Message &msg : mReassemblyList)
713 {
714 SuccessOrExit(error = msg.Read(0, headerBuffer));
715
716 if (msg.GetDatagramTag() == fragmentHeader.GetIdentification() &&
717 headerBuffer.GetSource() == header.GetSource() && headerBuffer.GetDestination() == header.GetDestination())
718 {
719 message = &msg;
720 break;
721 }
722 }
723
724 offset = FragmentHeader::FragmentOffsetToBytes(fragmentHeader.GetOffset());
725 payloadFragment = aMessage.GetLength() - aMessage.GetOffset() - sizeof(fragmentHeader);
726
727 LogInfo("Fragment with id %d received > %d bytes, offset %d", fragmentHeader.GetIdentification(), payloadFragment,
728 offset);
729
730 if (offset + payloadFragment + aMessage.GetOffset() > kMaxAssembledDatagramLength)
731 {
732 LogWarn("Packet too large for fragment buffer");
733 ExitNow(error = kErrorNoBufs);
734 }
735
736 if (message == nullptr)
737 {
738 LogDebg("start reassembly");
739 VerifyOrExit((message = NewMessage(0)) != nullptr, error = kErrorNoBufs);
740 mReassemblyList.Enqueue(*message);
741 SuccessOrExit(error = message->SetLength(aMessage.GetOffset()));
742
743 message->SetTimestampToNow();
744 message->SetOffset(0);
745 message->SetDatagramTag(fragmentHeader.GetIdentification());
746
747 // copying the non-fragmentable header to the fragmentation buffer
748 assertValue = aMessage.CopyTo(0, 0, aMessage.GetOffset(), *message);
749 OT_ASSERT(assertValue == aMessage.GetOffset());
750
751 Get<TimeTicker>().RegisterReceiver(TimeTicker::kIp6FragmentReassembler);
752 }
753
754 // increase message buffer if necessary
755 if (message->GetLength() < offset + payloadFragment + aMessage.GetOffset())
756 {
757 SuccessOrExit(error = message->SetLength(offset + payloadFragment + aMessage.GetOffset()));
758 }
759
760 // copy the fragment payload into the message buffer
761 assertValue = aMessage.CopyTo(aMessage.GetOffset() + sizeof(fragmentHeader), aMessage.GetOffset() + offset,
762 payloadFragment, *message);
763 OT_ASSERT(assertValue == static_cast<int>(payloadFragment));
764
765 // check if it is the last frame
766 if (!fragmentHeader.IsMoreFlagSet())
767 {
768 // use the offset value for the whole ip message length
769 message->SetOffset(aMessage.GetOffset() + offset + payloadFragment);
770
771 // creates the header for the reassembled ipv6 package
772 SuccessOrExit(error = aMessage.Read(0, header));
773 header.SetPayloadLength(message->GetLength() - sizeof(header));
774 header.SetNextHeader(fragmentHeader.GetNextHeader());
775 message->Write(0, header);
776
777 LogDebg("Reassembly complete.");
778
779 mReassemblyList.Dequeue(*message);
780
781 IgnoreError(HandleDatagram(*message, aNetif, aMessageInfo.mLinkInfo, aFromHost));
782 }
783
784 exit:
785 if (error != kErrorDrop && error != kErrorNone && isFragmented)
786 {
787 if (message != nullptr)
788 {
789 mReassemblyList.DequeueAndFree(*message);
790 }
791
792 LogWarn("Reassembly failed: %s", ErrorToString(error));
793 }
794
795 if (isFragmented)
796 {
797 // drop all fragments, the payload is stored in the fragment buffer
798 error = kErrorDrop;
799 }
800
801 return error;
802 }
803
CleanupFragmentationBuffer(void)804 void Ip6::CleanupFragmentationBuffer(void)
805 {
806 mReassemblyList.DequeueAndFreeAll();
807 }
808
HandleTimeTick(void)809 void Ip6::HandleTimeTick(void)
810 {
811 UpdateReassemblyList();
812
813 if (mReassemblyList.GetHead() == nullptr)
814 {
815 Get<TimeTicker>().UnregisterReceiver(TimeTicker::kIp6FragmentReassembler);
816 }
817 }
818
UpdateReassemblyList(void)819 void Ip6::UpdateReassemblyList(void)
820 {
821 TimeMilli now = TimerMilli::GetNow();
822
823 for (Message &message : mReassemblyList)
824 {
825 if (now - message.GetTimestamp() >= TimeMilli::SecToMsec(kIp6ReassemblyTimeout))
826 {
827 LogNote("Reassembly timeout.");
828 SendIcmpError(message, Icmp::Header::kTypeTimeExceeded, Icmp::Header::kCodeFragmReasTimeEx);
829
830 mReassemblyList.DequeueAndFree(message);
831 }
832 }
833 }
834
SendIcmpError(Message & aMessage,Icmp::Header::Type aIcmpType,Icmp::Header::Code aIcmpCode)835 void Ip6::SendIcmpError(Message &aMessage, Icmp::Header::Type aIcmpType, Icmp::Header::Code aIcmpCode)
836 {
837 Error error = kErrorNone;
838 Header header;
839 MessageInfo messageInfo;
840
841 SuccessOrExit(error = aMessage.Read(0, header));
842
843 messageInfo.SetPeerAddr(header.GetSource());
844 messageInfo.SetSockAddr(header.GetDestination());
845 messageInfo.SetHopLimit(header.GetHopLimit());
846 messageInfo.SetLinkInfo(nullptr);
847
848 error = mIcmp.SendError(aIcmpType, aIcmpCode, messageInfo, aMessage);
849
850 exit:
851
852 if (error != kErrorNone)
853 {
854 LogWarn("Failed to send ICMP error: %s", ErrorToString(error));
855 }
856 }
857
858 #else
FragmentDatagram(Message & aMessage,uint8_t aIpProto)859 Error Ip6::FragmentDatagram(Message &aMessage, uint8_t aIpProto)
860 {
861 OT_UNUSED_VARIABLE(aIpProto);
862
863 EnqueueDatagram(aMessage);
864
865 return kErrorNone;
866 }
867
HandleFragment(Message & aMessage,Netif * aNetif,MessageInfo & aMessageInfo,bool aFromHost)868 Error Ip6::HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessageInfo, bool aFromHost)
869 {
870 OT_UNUSED_VARIABLE(aNetif);
871 OT_UNUSED_VARIABLE(aMessageInfo);
872 OT_UNUSED_VARIABLE(aFromHost);
873
874 Error error = kErrorNone;
875 FragmentHeader fragmentHeader;
876
877 SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), fragmentHeader));
878
879 VerifyOrExit(fragmentHeader.GetOffset() == 0 && !fragmentHeader.IsMoreFlagSet(), error = kErrorDrop);
880
881 aMessage.MoveOffset(sizeof(fragmentHeader));
882
883 exit:
884 return error;
885 }
886 #endif // OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
887
HandleExtensionHeaders(Message & aMessage,Netif * aNetif,MessageInfo & aMessageInfo,Header & aHeader,uint8_t & aNextHeader,bool aIsOutbound,bool aFromHost,bool & aReceive)888 Error Ip6::HandleExtensionHeaders(Message & aMessage,
889 Netif * aNetif,
890 MessageInfo &aMessageInfo,
891 Header & aHeader,
892 uint8_t & aNextHeader,
893 bool aIsOutbound,
894 bool aFromHost,
895 bool & aReceive)
896 {
897 Error error = kErrorNone;
898 ExtensionHeader extHeader;
899
900 while (aReceive || aNextHeader == kProtoHopOpts)
901 {
902 SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), extHeader));
903
904 switch (aNextHeader)
905 {
906 case kProtoHopOpts:
907 SuccessOrExit(error = HandleOptions(aMessage, aHeader, aIsOutbound, aReceive));
908 break;
909
910 case kProtoFragment:
911 #if !OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
912 IgnoreError(ProcessReceiveCallback(aMessage, aMessageInfo, aNextHeader, aFromHost,
913 /* aAllowReceiveFilter */ false, Message::kCopyToUse));
914 #endif
915 SuccessOrExit(error = HandleFragment(aMessage, aNetif, aMessageInfo, aFromHost));
916 break;
917
918 case kProtoDstOpts:
919 SuccessOrExit(error = HandleOptions(aMessage, aHeader, aIsOutbound, aReceive));
920 break;
921
922 case kProtoIp6:
923 ExitNow();
924
925 case kProtoRouting:
926 case kProtoNone:
927 ExitNow(error = kErrorDrop);
928
929 default:
930 ExitNow();
931 }
932
933 aNextHeader = static_cast<uint8_t>(extHeader.GetNextHeader());
934 }
935
936 exit:
937 return error;
938 }
939
HandlePayload(Header & aIp6Header,Message & aMessage,MessageInfo & aMessageInfo,uint8_t aIpProto,Message::Ownership aMessageOwnership)940 Error Ip6::HandlePayload(Header & aIp6Header,
941 Message & aMessage,
942 MessageInfo & aMessageInfo,
943 uint8_t aIpProto,
944 Message::Ownership aMessageOwnership)
945 {
946 #if !OPENTHREAD_CONFIG_TCP_ENABLE
947 OT_UNUSED_VARIABLE(aIp6Header);
948 #endif
949
950 Error error = kErrorNone;
951 Message *message = (aMessageOwnership == Message::kTakeCustody) ? &aMessage : nullptr;
952
953 VerifyOrExit(aIpProto == kProtoTcp || aIpProto == kProtoUdp || aIpProto == kProtoIcmp6);
954
955 if (aMessageOwnership == Message::kCopyToUse)
956 {
957 VerifyOrExit((message = aMessage.Clone()) != nullptr, error = kErrorNoBufs);
958 }
959
960 switch (aIpProto)
961 {
962 #if OPENTHREAD_CONFIG_TCP_ENABLE
963 case kProtoTcp:
964 error = mTcp.HandleMessage(aIp6Header, *message, aMessageInfo);
965 if (error == kErrorDrop)
966 {
967 LogNote("Error TCP Checksum");
968 }
969 break;
970 #endif
971 case kProtoUdp:
972 error = mUdp.HandleMessage(*message, aMessageInfo);
973 if (error == kErrorDrop)
974 {
975 LogNote("Error UDP Checksum");
976 }
977 break;
978
979 case kProtoIcmp6:
980 error = mIcmp.HandleMessage(*message, aMessageInfo);
981 break;
982
983 default:
984 break;
985 }
986
987 exit:
988 if (error != kErrorNone)
989 {
990 LogNote("Failed to handle payload: %s", ErrorToString(error));
991 }
992
993 FreeMessage(message);
994
995 return error;
996 }
997
ProcessReceiveCallback(Message & aMessage,const MessageInfo & aMessageInfo,uint8_t aIpProto,bool aFromHost,bool aAllowReceiveFilter,Message::Ownership aMessageOwnership)998 Error Ip6::ProcessReceiveCallback(Message & aMessage,
999 const MessageInfo &aMessageInfo,
1000 uint8_t aIpProto,
1001 bool aFromHost,
1002 bool aAllowReceiveFilter,
1003 Message::Ownership aMessageOwnership)
1004 {
1005 Error error = kErrorNone;
1006 Message *message = &aMessage;
1007
1008 VerifyOrExit(!aFromHost, error = kErrorNoRoute);
1009 VerifyOrExit(mReceiveIp6DatagramCallback != nullptr, error = kErrorNoRoute);
1010
1011 // Do not forward reassembled IPv6 packets.
1012 VerifyOrExit(aMessage.GetLength() <= kMinimalMtu, error = kErrorDrop);
1013
1014 if (mIsReceiveIp6FilterEnabled && aAllowReceiveFilter)
1015 {
1016 #if !OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
1017 // do not pass messages sent to an RLOC/ALOC, except Service Locator
1018 bool isLocator = Get<Mle::Mle>().IsMeshLocalAddress(aMessageInfo.GetSockAddr()) &&
1019 aMessageInfo.GetSockAddr().GetIid().IsLocator();
1020
1021 VerifyOrExit(!isLocator || aMessageInfo.GetSockAddr().GetIid().IsAnycastServiceLocator(),
1022 error = kErrorNoRoute);
1023 #endif
1024
1025 switch (aIpProto)
1026 {
1027 case kProtoIcmp6:
1028 if (mIcmp.ShouldHandleEchoRequest(aMessageInfo))
1029 {
1030 Icmp::Header icmp;
1031 IgnoreError(aMessage.Read(aMessage.GetOffset(), icmp));
1032
1033 // do not pass ICMP Echo Request messages
1034 VerifyOrExit(icmp.GetType() != Icmp::Header::kTypeEchoRequest, error = kErrorDrop);
1035 }
1036
1037 break;
1038
1039 case kProtoUdp:
1040 {
1041 Udp::Header udp;
1042
1043 IgnoreError(aMessage.Read(aMessage.GetOffset(), udp));
1044 VerifyOrExit(Get<Udp>().ShouldUsePlatformUdp(udp.GetDestinationPort()) &&
1045 !Get<Udp>().IsPortInUse(udp.GetDestinationPort()),
1046 error = kErrorNoRoute);
1047 break;
1048 }
1049
1050 default:
1051 break;
1052 }
1053 }
1054
1055 switch (aMessageOwnership)
1056 {
1057 case Message::kTakeCustody:
1058 break;
1059
1060 case Message::kCopyToUse:
1061 message = aMessage.Clone();
1062
1063 if (message == nullptr)
1064 {
1065 LogWarn("No buff to clone msg (len: %d) to pass to host", aMessage.GetLength());
1066 ExitNow(error = kErrorNoBufs);
1067 }
1068
1069 break;
1070 }
1071
1072 IgnoreError(RemoveMplOption(*message));
1073 mReceiveIp6DatagramCallback(message, mReceiveIp6DatagramCallbackContext);
1074
1075 exit:
1076
1077 if ((error != kErrorNone) && (aMessageOwnership == Message::kTakeCustody))
1078 {
1079 aMessage.Free();
1080 }
1081
1082 return error;
1083 }
1084
SendRaw(Message & aMessage,bool aFromHost)1085 Error Ip6::SendRaw(Message &aMessage, bool aFromHost)
1086 {
1087 Error error = kErrorNone;
1088 Header header;
1089 MessageInfo messageInfo;
1090 bool freed = false;
1091
1092 SuccessOrExit(error = header.ParseFrom(aMessage));
1093 VerifyOrExit(!header.GetSource().IsMulticast(), error = kErrorInvalidSourceAddress);
1094
1095 messageInfo.SetPeerAddr(header.GetSource());
1096 messageInfo.SetSockAddr(header.GetDestination());
1097 messageInfo.SetHopLimit(header.GetHopLimit());
1098
1099 if (header.GetDestination().IsMulticast())
1100 {
1101 SuccessOrExit(error = InsertMplOption(aMessage, header, messageInfo));
1102 }
1103
1104 error = HandleDatagram(aMessage, nullptr, nullptr, aFromHost);
1105 freed = true;
1106
1107 exit:
1108
1109 if (!freed)
1110 {
1111 aMessage.Free();
1112 }
1113
1114 return error;
1115 }
1116
HandleDatagram(Message & aMessage,Netif * aNetif,const void * aLinkMessageInfo,bool aFromHost)1117 Error Ip6::HandleDatagram(Message &aMessage, Netif *aNetif, const void *aLinkMessageInfo, bool aFromHost)
1118 {
1119 Error error;
1120 MessageInfo messageInfo;
1121 Header header;
1122 bool receive;
1123 bool forwardThread;
1124 bool forwardHost;
1125 bool shouldFreeMessage;
1126 uint8_t nextHeader;
1127
1128 start:
1129 receive = false;
1130 forwardThread = false;
1131 forwardHost = false;
1132 shouldFreeMessage = true;
1133
1134 SuccessOrExit(error = header.ParseFrom(aMessage));
1135
1136 messageInfo.Clear();
1137 messageInfo.SetPeerAddr(header.GetSource());
1138 messageInfo.SetSockAddr(header.GetDestination());
1139 messageInfo.SetHopLimit(header.GetHopLimit());
1140 messageInfo.SetEcn(header.GetEcn());
1141 messageInfo.SetLinkInfo(aLinkMessageInfo);
1142
1143 // determine destination of packet
1144 if (header.GetDestination().IsMulticast())
1145 {
1146 Netif *netif;
1147
1148 if (aNetif != nullptr)
1149 {
1150 #if OPENTHREAD_FTD
1151 if (header.GetDestination().IsMulticastLargerThanRealmLocal() &&
1152 Get<ChildTable>().HasSleepyChildWithAddress(header.GetDestination()))
1153 {
1154 forwardThread = true;
1155 }
1156 #endif
1157
1158 netif = aNetif;
1159 }
1160 else
1161 {
1162 forwardThread = true;
1163
1164 netif = &Get<ThreadNetif>();
1165 }
1166
1167 forwardHost = header.GetDestination().IsMulticastLargerThanRealmLocal();
1168
1169 if ((aNetif != nullptr || aMessage.GetMulticastLoop()) && netif->IsMulticastSubscribed(header.GetDestination()))
1170 {
1171 receive = true;
1172 }
1173 else if (netif->IsMulticastPromiscuousEnabled())
1174 {
1175 forwardHost = true;
1176 }
1177 }
1178 else
1179 {
1180 // unicast
1181 if (Get<ThreadNetif>().HasUnicastAddress(header.GetDestination()))
1182 {
1183 receive = true;
1184 }
1185 else if (!header.GetDestination().IsLinkLocal())
1186 {
1187 forwardThread = true;
1188 }
1189 else if (aNetif == nullptr)
1190 {
1191 forwardThread = true;
1192 }
1193
1194 if (forwardThread && !ShouldForwardToThread(messageInfo, aFromHost))
1195 {
1196 forwardThread = false;
1197 forwardHost = true;
1198 }
1199 }
1200
1201 aMessage.SetOffset(sizeof(header));
1202
1203 // process IPv6 Extension Headers
1204 nextHeader = static_cast<uint8_t>(header.GetNextHeader());
1205 SuccessOrExit(error = HandleExtensionHeaders(aMessage, aNetif, messageInfo, header, nextHeader, aNetif == nullptr,
1206 aFromHost, receive));
1207
1208 // process IPv6 Payload
1209 if (receive)
1210 {
1211 if (nextHeader == kProtoIp6)
1212 {
1213 // Remove encapsulating header and start over.
1214 aMessage.RemoveHeader(aMessage.GetOffset());
1215 Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageReceive, aMessage);
1216 goto start;
1217 }
1218
1219 error = ProcessReceiveCallback(aMessage, messageInfo, nextHeader, aFromHost,
1220 /* aAllowReceiveFilter */ !forwardHost, Message::kCopyToUse);
1221
1222 if ((error == kErrorNone || error == kErrorNoRoute) && forwardHost)
1223 {
1224 forwardHost = false;
1225 }
1226
1227 error = HandlePayload(header, aMessage, messageInfo, nextHeader,
1228 (forwardThread || forwardHost ? Message::kCopyToUse : Message::kTakeCustody));
1229 shouldFreeMessage = forwardThread || forwardHost;
1230 }
1231
1232 if (forwardHost)
1233 {
1234 // try passing to host
1235 error = ProcessReceiveCallback(aMessage, messageInfo, nextHeader, aFromHost, /* aAllowReceiveFilter */ false,
1236 forwardThread ? Message::kCopyToUse : Message::kTakeCustody);
1237 shouldFreeMessage = forwardThread;
1238 }
1239
1240 if (forwardThread)
1241 {
1242 uint8_t hopLimit;
1243
1244 if (aNetif != nullptr)
1245 {
1246 VerifyOrExit(mForwardingEnabled);
1247 header.SetHopLimit(header.GetHopLimit() - 1);
1248 }
1249
1250 VerifyOrExit(header.GetHopLimit() > 0, error = kErrorDrop);
1251
1252 hopLimit = header.GetHopLimit();
1253 aMessage.Write(Header::kHopLimitFieldOffset, hopLimit);
1254
1255 if (nextHeader == kProtoIcmp6)
1256 {
1257 uint8_t icmpType;
1258 bool isAllowedType = false;
1259
1260 SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), icmpType));
1261 for (IcmpType type : sForwardICMPTypes)
1262 {
1263 if (icmpType == type)
1264 {
1265 isAllowedType = true;
1266 break;
1267 }
1268 }
1269 VerifyOrExit(isAllowedType, error = kErrorDrop);
1270 }
1271
1272 #if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1273 if (aFromHost && (nextHeader == kProtoUdp))
1274 {
1275 uint16_t destPort;
1276
1277 SuccessOrExit(error = aMessage.Read(aMessage.GetOffset() + Udp::Header::kDestPortFieldOffset, destPort));
1278 destPort = HostSwap16(destPort);
1279
1280 if (nextHeader == kProtoUdp)
1281 {
1282 VerifyOrExit(Get<Udp>().ShouldUsePlatformUdp(destPort), error = kErrorDrop);
1283 }
1284 }
1285 #endif
1286
1287 #if OPENTHREAD_CONFIG_MULTI_RADIO
1288 // Since the message will be forwarded, we clear the radio
1289 // type on the message to allow the radio type for tx to be
1290 // selected later (based on the radios supported by the next
1291 // hop).
1292 aMessage.ClearRadioType();
1293 #endif
1294
1295 // `SendMessage()` takes custody of message in the success case
1296 SuccessOrExit(error = Get<ThreadNetif>().SendMessage(aMessage));
1297 shouldFreeMessage = false;
1298 }
1299
1300 exit:
1301
1302 if (shouldFreeMessage)
1303 {
1304 aMessage.Free();
1305 }
1306
1307 return error;
1308 }
1309
ShouldForwardToThread(const MessageInfo & aMessageInfo,bool aFromHost) const1310 bool Ip6::ShouldForwardToThread(const MessageInfo &aMessageInfo, bool aFromHost) const
1311 {
1312 bool shouldForward = false;
1313
1314 if (aMessageInfo.GetSockAddr().IsMulticast() || aMessageInfo.GetSockAddr().IsLinkLocal())
1315 {
1316 shouldForward = true;
1317 }
1318 else if (IsOnLink(aMessageInfo.GetSockAddr()))
1319 {
1320 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
1321 shouldForward =
1322 (aFromHost || !Get<BackboneRouter::Manager>().ShouldForwardDuaToBackbone(aMessageInfo.GetSockAddr()));
1323 #else
1324 OT_UNUSED_VARIABLE(aFromHost);
1325 shouldForward = true;
1326 #endif
1327 }
1328 else if (Get<ThreadNetif>().RouteLookup(aMessageInfo.GetPeerAddr(), aMessageInfo.GetSockAddr(), nullptr) ==
1329 kErrorNone)
1330 {
1331 shouldForward = true;
1332 }
1333
1334 return shouldForward;
1335 }
1336
SelectSourceAddress(MessageInfo & aMessageInfo)1337 const Netif::UnicastAddress *Ip6::SelectSourceAddress(MessageInfo &aMessageInfo)
1338 {
1339 Address * destination = &aMessageInfo.GetPeerAddr();
1340 uint8_t destinationScope = destination->GetScope();
1341 const bool destinationIsRoutingLocator = Get<Mle::Mle>().IsRoutingLocator(*destination);
1342 const Netif::UnicastAddress *rvalAddr = nullptr;
1343 uint8_t rvalPrefixMatched = 0;
1344
1345 for (const Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
1346 {
1347 const Address *candidateAddr = &addr.GetAddress();
1348 uint8_t candidatePrefixMatched;
1349 uint8_t overrideScope;
1350
1351 if (Get<Mle::Mle>().IsAnycastLocator(*candidateAddr))
1352 {
1353 // Don't use anycast address as source address.
1354 continue;
1355 }
1356
1357 candidatePrefixMatched = destination->PrefixMatch(*candidateAddr);
1358
1359 if (candidatePrefixMatched >= addr.mPrefixLength)
1360 {
1361 candidatePrefixMatched = addr.mPrefixLength;
1362 overrideScope = addr.GetScope();
1363 }
1364 else
1365 {
1366 overrideScope = destinationScope;
1367 }
1368
1369 if (rvalAddr == nullptr)
1370 {
1371 // Rule 0: Prefer any address
1372 rvalAddr = &addr;
1373 rvalPrefixMatched = candidatePrefixMatched;
1374 }
1375 else if (*candidateAddr == *destination)
1376 {
1377 // Rule 1: Prefer same address
1378 rvalAddr = &addr;
1379 ExitNow();
1380 }
1381 else if (addr.GetScope() < rvalAddr->GetScope())
1382 {
1383 // Rule 2: Prefer appropriate scope
1384 if (addr.GetScope() >= overrideScope)
1385 {
1386 rvalAddr = &addr;
1387 rvalPrefixMatched = candidatePrefixMatched;
1388 }
1389 else
1390 {
1391 continue;
1392 }
1393 }
1394 else if (addr.GetScope() > rvalAddr->GetScope())
1395 {
1396 if (rvalAddr->GetScope() < overrideScope)
1397 {
1398 rvalAddr = &addr;
1399 rvalPrefixMatched = candidatePrefixMatched;
1400 }
1401 else
1402 {
1403 continue;
1404 }
1405 }
1406 else if (addr.mPreferred && !rvalAddr->mPreferred)
1407 {
1408 // Rule 3: Avoid deprecated addresses
1409 rvalAddr = &addr;
1410 rvalPrefixMatched = candidatePrefixMatched;
1411 }
1412 else if (candidatePrefixMatched > rvalPrefixMatched)
1413 {
1414 // Rule 6: Prefer matching label
1415 // Rule 7: Prefer public address
1416 // Rule 8: Use longest prefix matching
1417 rvalAddr = &addr;
1418 rvalPrefixMatched = candidatePrefixMatched;
1419 }
1420 else if ((candidatePrefixMatched == rvalPrefixMatched) &&
1421 (destinationIsRoutingLocator == Get<Mle::Mle>().IsRoutingLocator(*candidateAddr)))
1422 {
1423 // Additional rule: Prefer RLOC source for RLOC destination, EID source for anything else
1424 rvalAddr = &addr;
1425 rvalPrefixMatched = candidatePrefixMatched;
1426 }
1427 else
1428 {
1429 continue;
1430 }
1431
1432 // infer destination scope based on prefix match
1433 if (rvalPrefixMatched >= rvalAddr->mPrefixLength)
1434 {
1435 destinationScope = rvalAddr->GetScope();
1436 }
1437 }
1438
1439 exit:
1440 return rvalAddr;
1441 }
1442
IsOnLink(const Address & aAddress) const1443 bool Ip6::IsOnLink(const Address &aAddress) const
1444 {
1445 bool rval = false;
1446
1447 if (Get<ThreadNetif>().IsOnMesh(aAddress))
1448 {
1449 ExitNow(rval = true);
1450 }
1451
1452 for (const Netif::UnicastAddress &cur : Get<ThreadNetif>().GetUnicastAddresses())
1453 {
1454 if (cur.GetAddress().PrefixMatch(aAddress) >= cur.mPrefixLength)
1455 {
1456 ExitNow(rval = true);
1457 }
1458 }
1459
1460 exit:
1461 return rval;
1462 }
1463
1464 // LCOV_EXCL_START
1465
IpProtoToString(uint8_t aIpProto)1466 const char *Ip6::IpProtoToString(uint8_t aIpProto)
1467 {
1468 static constexpr Stringify::Entry kIpProtoTable[] = {
1469 {kProtoHopOpts, "HopOpts"}, {kProtoTcp, "TCP"}, {kProtoUdp, "UDP"},
1470 {kProtoIp6, "IP6"}, {kProtoRouting, "Routing"}, {kProtoFragment, "Frag"},
1471 {kProtoIcmp6, "ICMP6"}, {kProtoNone, "None"}, {kProtoDstOpts, "DstOpts"},
1472 };
1473
1474 static_assert(Stringify::IsSorted(kIpProtoTable), "kIpProtoTable is not sorted");
1475
1476 return Stringify::Lookup(aIpProto, kIpProtoTable, "Unknown");
1477 }
1478
EcnToString(Ecn aEcn)1479 const char *Ip6::EcnToString(Ecn aEcn)
1480 {
1481 static const char *const kEcnStrings[] = {
1482 "no", // (0) kEcnNotCapable
1483 "e1", // (1) kEcnCapable1 (ECT1)
1484 "e0", // (2) kEcnCapable0 (ECT0)
1485 "ce", // (3) kEcnMarked (Congestion Encountered)
1486 };
1487
1488 static_assert(0 == kEcnNotCapable, "kEcnNotCapable value is incorrect");
1489 static_assert(1 == kEcnCapable1, "kEcnCapable1 value is incorrect");
1490 static_assert(2 == kEcnCapable0, "kEcnCapable0 value is incorrect");
1491 static_assert(3 == kEcnMarked, "kEcnMarked value is incorrect");
1492
1493 return kEcnStrings[aEcn];
1494 }
1495
1496 // LCOV_EXCL_STOP
1497
1498 //---------------------------------------------------------------------------------------------------------------------
1499 // Headers
1500
ParseFrom(const Message & aMessage)1501 Error Headers::ParseFrom(const Message &aMessage)
1502 {
1503 Error error = kErrorParse;
1504
1505 Clear();
1506
1507 SuccessOrExit(mIp6Header.ParseFrom(aMessage));
1508
1509 switch (mIp6Header.GetNextHeader())
1510 {
1511 case kProtoUdp:
1512 SuccessOrExit(aMessage.Read(sizeof(Header), mHeader.mUdp));
1513 break;
1514 case kProtoTcp:
1515 SuccessOrExit(aMessage.Read(sizeof(Header), mHeader.mTcp));
1516 break;
1517 case kProtoIcmp6:
1518 SuccessOrExit(aMessage.Read(sizeof(Header), mHeader.mIcmp));
1519 break;
1520 default:
1521 break;
1522 }
1523
1524 error = kErrorNone;
1525
1526 exit:
1527 return error;
1528 }
1529
DecompressFrom(const Message & aMessage,uint16_t aOffset,const Mac::Address & aMacSource,const Mac::Address & aMacDest)1530 Error Headers::DecompressFrom(const Message & aMessage,
1531 uint16_t aOffset,
1532 const Mac::Address &aMacSource,
1533 const Mac::Address &aMacDest)
1534 {
1535 static constexpr uint16_t kReadLength = Lowpan::FragmentHeader::kSubsequentFragmentHeaderSize + sizeof(Headers);
1536
1537 uint8_t frameBuffer[kReadLength];
1538 uint16_t frameLength;
1539 FrameData frameData;
1540
1541 frameLength = aMessage.ReadBytes(aOffset, frameBuffer, sizeof(frameBuffer));
1542 frameData.Init(frameBuffer, frameLength);
1543
1544 return DecompressFrom(frameData, aMacSource, aMacDest, aMessage.GetInstance());
1545 }
1546
DecompressFrom(const FrameData & aFrameData,const Mac::Address & aMacSource,const Mac::Address & aMacDest,Instance & aInstance)1547 Error Headers::DecompressFrom(const FrameData & aFrameData,
1548 const Mac::Address &aMacSource,
1549 const Mac::Address &aMacDest,
1550 Instance & aInstance)
1551 {
1552 Error error = kErrorNone;
1553 FrameData frameData = aFrameData;
1554 Lowpan::FragmentHeader fragmentHeader;
1555 bool nextHeaderCompressed;
1556
1557 if (fragmentHeader.ParseFrom(frameData) == kErrorNone)
1558 {
1559 // Only the first fragment header is followed by a LOWPAN_IPHC header
1560 VerifyOrExit(fragmentHeader.GetDatagramOffset() == 0, error = kErrorNotFound);
1561 }
1562
1563 VerifyOrExit(Lowpan::Lowpan::IsLowpanHc(frameData), error = kErrorNotFound);
1564
1565 SuccessOrExit(error = aInstance.Get<Lowpan::Lowpan>().DecompressBaseHeader(mIp6Header, nextHeaderCompressed,
1566 aMacSource, aMacDest, frameData));
1567
1568 switch (mIp6Header.GetNextHeader())
1569 {
1570 case kProtoUdp:
1571 if (nextHeaderCompressed)
1572 {
1573 SuccessOrExit(error = aInstance.Get<Lowpan::Lowpan>().DecompressUdpHeader(mHeader.mUdp, frameData));
1574 }
1575 else
1576 {
1577 SuccessOrExit(error = frameData.Read(mHeader.mUdp));
1578 }
1579 break;
1580
1581 case kProtoTcp:
1582 SuccessOrExit(error = frameData.Read(mHeader.mTcp));
1583 break;
1584
1585 case kProtoIcmp6:
1586 SuccessOrExit(error = frameData.Read(mHeader.mIcmp));
1587 break;
1588
1589 default:
1590 break;
1591 }
1592
1593 exit:
1594 return error;
1595 }
1596
GetSourcePort(void) const1597 uint16_t Headers::GetSourcePort(void) const
1598 {
1599 uint16_t port = 0;
1600
1601 switch (GetIpProto())
1602 {
1603 case kProtoUdp:
1604 port = mHeader.mUdp.GetSourcePort();
1605 break;
1606
1607 case kProtoTcp:
1608 port = mHeader.mTcp.GetSourcePort();
1609 break;
1610
1611 default:
1612 break;
1613 }
1614
1615 return port;
1616 }
1617
GetDestinationPort(void) const1618 uint16_t Headers::GetDestinationPort(void) const
1619 {
1620 uint16_t port = 0;
1621
1622 switch (GetIpProto())
1623 {
1624 case kProtoUdp:
1625 port = mHeader.mUdp.GetDestinationPort();
1626 break;
1627
1628 case kProtoTcp:
1629 port = mHeader.mTcp.GetDestinationPort();
1630 break;
1631
1632 default:
1633 break;
1634 }
1635
1636 return port;
1637 }
1638
GetChecksum(void) const1639 uint16_t Headers::GetChecksum(void) const
1640 {
1641 uint16_t checksum = 0;
1642
1643 switch (GetIpProto())
1644 {
1645 case kProtoUdp:
1646 checksum = mHeader.mUdp.GetChecksum();
1647 break;
1648
1649 case kProtoTcp:
1650 checksum = mHeader.mTcp.GetChecksum();
1651 break;
1652
1653 case kProtoIcmp6:
1654 checksum = mHeader.mIcmp.GetChecksum();
1655 break;
1656
1657 default:
1658 break;
1659 }
1660
1661 return checksum;
1662 }
1663
1664 } // namespace Ip6
1665 } // namespace ot
1666