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