• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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