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