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