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