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