1 /*
2  *  Copyright (c) 2020, 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 includes implementation for the RA-based routing management.
32  *
33  */
34 
35 #include "border_router/routing_manager.hpp"
36 
37 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
38 
39 #include <string.h>
40 
41 #include <openthread/border_router.h>
42 #include <openthread/platform/border_routing.h>
43 #include <openthread/platform/infra_if.h>
44 
45 #include "common/code_utils.hpp"
46 #include "common/debug.hpp"
47 #include "common/locator_getters.hpp"
48 #include "common/log.hpp"
49 #include "common/num_utils.hpp"
50 #include "common/numeric_limits.hpp"
51 #include "common/random.hpp"
52 #include "common/settings.hpp"
53 #include "common/type_traits.hpp"
54 #include "instance/instance.hpp"
55 #include "meshcop/extended_panid.hpp"
56 #include "net/ip6.hpp"
57 #include "net/nat64_translator.hpp"
58 #include "net/nd6.hpp"
59 #include "thread/mle_router.hpp"
60 #include "thread/network_data_leader.hpp"
61 #include "thread/network_data_local.hpp"
62 #include "thread/network_data_notifier.hpp"
63 
64 namespace ot {
65 
66 namespace BorderRouter {
67 
68 RegisterLogModule("RoutingManager");
69 
RoutingManager(Instance & aInstance)70 RoutingManager::RoutingManager(Instance &aInstance)
71     : InstanceLocator(aInstance)
72     , mIsRunning(false)
73     , mIsEnabled(false)
74     , mInfraIf(aInstance)
75     , mOmrPrefixManager(aInstance)
76     , mRioAdvertiser(aInstance)
77     , mOnLinkPrefixManager(aInstance)
78 #if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
79     , mNetDataPeerBrTracker(aInstance)
80 #endif
81     , mRxRaTracker(aInstance)
82     , mRoutePublisher(aInstance)
83 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
84     , mNat64PrefixManager(aInstance)
85 #endif
86 #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
87     , mPdPrefixManager(aInstance)
88 #endif
89     , mRsSender(aInstance)
90     , mRoutingPolicyTimer(aInstance)
91 {
92     mBrUlaPrefix.Clear();
93 }
94 
Init(uint32_t aInfraIfIndex,bool aInfraIfIsRunning)95 Error RoutingManager::Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning)
96 {
97     Error error;
98 
99     VerifyOrExit(GetState() == kStateUninitialized || GetState() == kStateDisabled, error = kErrorInvalidState);
100 
101     if (!mInfraIf.IsInitialized())
102     {
103         LogInfo("Initializing - InfraIfIndex:%lu", ToUlong(aInfraIfIndex));
104         SuccessOrExit(error = mInfraIf.Init(aInfraIfIndex));
105         SuccessOrExit(error = LoadOrGenerateRandomBrUlaPrefix());
106         mOmrPrefixManager.Init(mBrUlaPrefix);
107 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
108         mNat64PrefixManager.GenerateLocalPrefix(mBrUlaPrefix);
109 #endif
110         mOnLinkPrefixManager.Init();
111     }
112     else if (aInfraIfIndex != mInfraIf.GetIfIndex())
113     {
114         LogInfo("Reinitializing - InfraIfIndex:%lu -> %lu", ToUlong(mInfraIf.GetIfIndex()), ToUlong(aInfraIfIndex));
115 
116 #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_AUTO_ENABLE_ON_INFRA_IF
117         IgnoreError(Get<Dns::Multicast::Core>().SetEnabled(false, mInfraIf.GetIfIndex()));
118 #endif
119 
120         mInfraIf.SetIfIndex(aInfraIfIndex);
121     }
122 
123     error = mInfraIf.HandleStateChanged(mInfraIf.GetIfIndex(), aInfraIfIsRunning);
124 
125 exit:
126     if (error != kErrorNone)
127     {
128         mInfraIf.Deinit();
129     }
130 
131     return error;
132 }
133 
SetEnabled(bool aEnabled)134 Error RoutingManager::SetEnabled(bool aEnabled)
135 {
136     Error error = kErrorNone;
137 
138     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
139 
140     VerifyOrExit(aEnabled != mIsEnabled);
141 
142     mIsEnabled = aEnabled;
143     LogInfo("%s", mIsEnabled ? "Enabling" : "Disabling");
144     EvaluateState();
145 
146 exit:
147     return error;
148 }
149 
GetState(void) const150 RoutingManager::State RoutingManager::GetState(void) const
151 {
152     State state = kStateUninitialized;
153 
154     VerifyOrExit(IsInitialized());
155     VerifyOrExit(IsEnabled(), state = kStateDisabled);
156 
157     state = IsRunning() ? kStateRunning : kStateStopped;
158 
159 exit:
160     return state;
161 }
162 
GetOmrPrefix(Ip6::Prefix & aPrefix) const163 Error RoutingManager::GetOmrPrefix(Ip6::Prefix &aPrefix) const
164 {
165     Error error = kErrorNone;
166 
167     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
168     aPrefix = mOmrPrefixManager.GetGeneratedPrefix();
169 
170 exit:
171     return error;
172 }
173 
174 #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
GetPdOmrPrefix(PrefixTableEntry & aPrefixInfo) const175 Error RoutingManager::GetPdOmrPrefix(PrefixTableEntry &aPrefixInfo) const
176 {
177     Error error = kErrorNone;
178 
179     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
180     error = mPdPrefixManager.GetPrefixInfo(aPrefixInfo);
181 
182 exit:
183     return error;
184 }
185 
GetPdProcessedRaInfo(PdProcessedRaInfo & aPdProcessedRaInfo)186 Error RoutingManager::GetPdProcessedRaInfo(PdProcessedRaInfo &aPdProcessedRaInfo)
187 {
188     Error error = kErrorNone;
189 
190     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
191     error = mPdPrefixManager.GetProcessedRaInfo(aPdProcessedRaInfo);
192 
193 exit:
194     return error;
195 }
196 #endif
197 
GetFavoredOmrPrefix(Ip6::Prefix & aPrefix,RoutePreference & aPreference) const198 Error RoutingManager::GetFavoredOmrPrefix(Ip6::Prefix &aPrefix, RoutePreference &aPreference) const
199 {
200     Error error = kErrorNone;
201 
202     VerifyOrExit(IsRunning(), error = kErrorInvalidState);
203     aPrefix     = mOmrPrefixManager.GetFavoredPrefix().GetPrefix();
204     aPreference = mOmrPrefixManager.GetFavoredPrefix().GetPreference();
205 
206 exit:
207     return error;
208 }
209 
GetOnLinkPrefix(Ip6::Prefix & aPrefix) const210 Error RoutingManager::GetOnLinkPrefix(Ip6::Prefix &aPrefix) const
211 {
212     Error error = kErrorNone;
213 
214     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
215     aPrefix = mOnLinkPrefixManager.GetLocalPrefix();
216 
217 exit:
218     return error;
219 }
220 
GetFavoredOnLinkPrefix(Ip6::Prefix & aPrefix) const221 Error RoutingManager::GetFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const
222 {
223     Error error = kErrorNone;
224 
225     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
226     aPrefix = mOnLinkPrefixManager.GetFavoredDiscoveredPrefix();
227 
228     if (aPrefix.GetLength() == 0)
229     {
230         aPrefix = mOnLinkPrefixManager.GetLocalPrefix();
231     }
232 
233 exit:
234     return error;
235 }
236 
237 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
SetNat64PrefixManagerEnabled(bool aEnabled)238 void RoutingManager::SetNat64PrefixManagerEnabled(bool aEnabled)
239 {
240     // PrefixManager will start itself if routing manager is running.
241     mNat64PrefixManager.SetEnabled(aEnabled);
242 }
243 
GetNat64Prefix(Ip6::Prefix & aPrefix)244 Error RoutingManager::GetNat64Prefix(Ip6::Prefix &aPrefix)
245 {
246     Error error = kErrorNone;
247 
248     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
249     aPrefix = mNat64PrefixManager.GetLocalPrefix();
250 
251 exit:
252     return error;
253 }
254 
GetFavoredNat64Prefix(Ip6::Prefix & aPrefix,RoutePreference & aRoutePreference)255 Error RoutingManager::GetFavoredNat64Prefix(Ip6::Prefix &aPrefix, RoutePreference &aRoutePreference)
256 {
257     Error error = kErrorNone;
258 
259     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
260     aPrefix = mNat64PrefixManager.GetFavoredPrefix(aRoutePreference);
261 
262 exit:
263     return error;
264 }
265 #endif
266 
LoadOrGenerateRandomBrUlaPrefix(void)267 Error RoutingManager::LoadOrGenerateRandomBrUlaPrefix(void)
268 {
269     Error error     = kErrorNone;
270     bool  generated = false;
271 
272     if (Get<Settings>().Read<Settings::BrUlaPrefix>(mBrUlaPrefix) != kErrorNone || !IsValidBrUlaPrefix(mBrUlaPrefix))
273     {
274         Ip6::NetworkPrefix randomUlaPrefix;
275 
276         LogNote("No valid /48 BR ULA prefix found in settings, generating new one");
277 
278         SuccessOrExit(error = randomUlaPrefix.GenerateRandomUla());
279 
280         mBrUlaPrefix.Set(randomUlaPrefix);
281         mBrUlaPrefix.SetSubnetId(0);
282         mBrUlaPrefix.SetLength(kBrUlaPrefixLength);
283 
284         IgnoreError(Get<Settings>().Save<Settings::BrUlaPrefix>(mBrUlaPrefix));
285         generated = true;
286     }
287 
288     OT_UNUSED_VARIABLE(generated);
289 
290     LogNote("BR ULA prefix: %s (%s)", mBrUlaPrefix.ToString().AsCString(), generated ? "generated" : "loaded");
291 
292 exit:
293     if (error != kErrorNone)
294     {
295         LogCrit("Failed to generate random /48 BR ULA prefix");
296     }
297     return error;
298 }
299 
EvaluateState(void)300 void RoutingManager::EvaluateState(void)
301 {
302     if (mIsEnabled && Get<Mle::MleRouter>().IsAttached() && mInfraIf.IsRunning())
303     {
304         Start();
305     }
306     else
307     {
308         Stop();
309     }
310 }
311 
Start(void)312 void RoutingManager::Start(void)
313 {
314     if (!mIsRunning)
315     {
316         LogInfo("Starting");
317 
318         mIsRunning = true;
319         mRxRaTracker.Start();
320         mOnLinkPrefixManager.Start();
321         mOmrPrefixManager.Start();
322         mRoutePublisher.Start();
323         mRsSender.Start();
324 #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
325         mPdPrefixManager.Start();
326 #endif
327 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
328         mNat64PrefixManager.Start();
329 #endif
330     }
331 }
332 
Stop(void)333 void RoutingManager::Stop(void)
334 {
335     VerifyOrExit(mIsRunning);
336 
337     mOmrPrefixManager.Stop();
338     mOnLinkPrefixManager.Stop();
339 #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
340     mPdPrefixManager.Stop();
341 #endif
342 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
343     mNat64PrefixManager.Stop();
344 #endif
345 
346     SendRouterAdvertisement(kInvalidateAllPrevPrefixes);
347 
348     mRxRaTracker.Stop();
349 
350     mTxRaInfo.mTxCount = 0;
351 
352     mRsSender.Stop();
353 
354     mRoutingPolicyTimer.Stop();
355 
356     mRoutePublisher.Stop();
357 
358     LogInfo("Stopped");
359 
360     mIsRunning = false;
361 
362 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
363     if (Get<Srp::Server>().IsAutoEnableMode())
364     {
365         Get<Srp::Server>().Disable();
366     }
367 #endif
368 
369 exit:
370     return;
371 }
372 
SetExtraRouterAdvertOptions(const uint8_t * aOptions,uint16_t aLength)373 Error RoutingManager::SetExtraRouterAdvertOptions(const uint8_t *aOptions, uint16_t aLength)
374 {
375     Error error = kErrorNone;
376 
377     if (aOptions == nullptr)
378     {
379         mExtraRaOptions.Free();
380     }
381     else
382     {
383         error = mExtraRaOptions.SetFrom(aOptions, aLength);
384     }
385 
386     return error;
387 }
388 
389 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
HandleSrpServerAutoEnableMode(void)390 void RoutingManager::HandleSrpServerAutoEnableMode(void)
391 {
392     VerifyOrExit(Get<Srp::Server>().IsAutoEnableMode());
393 
394     if (IsInitialPolicyEvaluationDone())
395     {
396         Get<Srp::Server>().Enable();
397     }
398     else
399     {
400         Get<Srp::Server>().Disable();
401     }
402 
403 exit:
404     return;
405 }
406 #endif
407 
HandleReceived(const InfraIf::Icmp6Packet & aPacket,const Ip6::Address & aSrcAddress)408 void RoutingManager::HandleReceived(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
409 {
410     const Ip6::Icmp::Header *icmp6Header;
411 
412     VerifyOrExit(mIsRunning);
413 
414     icmp6Header = reinterpret_cast<const Ip6::Icmp::Header *>(aPacket.GetBytes());
415 
416     switch (icmp6Header->GetType())
417     {
418     case Ip6::Icmp::Header::kTypeRouterAdvert:
419         HandleRouterAdvertisement(aPacket, aSrcAddress);
420         break;
421     case Ip6::Icmp::Header::kTypeRouterSolicit:
422         HandleRouterSolicit(aPacket, aSrcAddress);
423         break;
424     case Ip6::Icmp::Header::kTypeNeighborAdvert:
425         HandleNeighborAdvertisement(aPacket);
426         break;
427     default:
428         break;
429     }
430 
431 exit:
432     return;
433 }
434 
HandleNotifierEvents(Events aEvents)435 void RoutingManager::HandleNotifierEvents(Events aEvents)
436 {
437     if (aEvents.Contains(kEventThreadRoleChanged))
438     {
439         mRioAdvertiser.HandleRoleChanged();
440     }
441 
442     mRoutePublisher.HandleNotifierEvents(aEvents);
443 
444 #if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
445     mNetDataPeerBrTracker.HandleNotifierEvents(aEvents);
446 #endif
447 
448     VerifyOrExit(IsInitialized() && IsEnabled());
449 
450     if (aEvents.Contains(kEventThreadRoleChanged))
451     {
452         EvaluateState();
453     }
454 
455     if (mIsRunning && aEvents.Contains(kEventThreadNetdataChanged))
456     {
457         mRxRaTracker.HandleNetDataChange();
458         mOnLinkPrefixManager.HandleNetDataChange();
459         ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
460     }
461 
462     if (aEvents.Contains(kEventThreadExtPanIdChanged))
463     {
464         mOnLinkPrefixManager.HandleExtPanIdChange();
465     }
466 
467 exit:
468     return;
469 }
470 
EvaluateRoutingPolicy(void)471 void RoutingManager::EvaluateRoutingPolicy(void)
472 {
473     OT_ASSERT(mIsRunning);
474 
475     LogInfo("Evaluating routing policy");
476 
477     mOnLinkPrefixManager.Evaluate();
478     mOmrPrefixManager.Evaluate();
479     mRoutePublisher.Evaluate();
480 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
481     mNat64PrefixManager.Evaluate();
482 #endif
483 
484     if (IsInitialPolicyEvaluationDone())
485     {
486         SendRouterAdvertisement(kAdvPrefixesFromNetData);
487     }
488 
489 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
490     if (Get<Srp::Server>().IsAutoEnableMode() && IsInitialPolicyEvaluationDone())
491     {
492         // If SRP server uses the auto-enable mode, we enable the SRP
493         // server on the first RA transmission after we are done with
494         // initial prefix/route configurations. Note that if SRP server
495         // is already enabled, calling `Enable()` again does nothing.
496 
497         Get<Srp::Server>().Enable();
498     }
499 #endif
500 
501     ScheduleRoutingPolicyEvaluation(kForNextRa);
502 }
503 
IsInitialPolicyEvaluationDone(void) const504 bool RoutingManager::IsInitialPolicyEvaluationDone(void) const
505 {
506     // This method indicates whether or not we are done with the
507     // initial policy evaluation and prefix and route setup, i.e.,
508     // the OMR and on-link prefixes are determined, advertised in
509     // the emitted Router Advert message on infrastructure side
510     // and published in the Thread Network Data.
511 
512     return mIsRunning && !mOmrPrefixManager.GetFavoredPrefix().IsEmpty() &&
513            mOnLinkPrefixManager.IsInitalEvaluationDone();
514 }
515 
ScheduleRoutingPolicyEvaluation(ScheduleMode aMode)516 void RoutingManager::ScheduleRoutingPolicyEvaluation(ScheduleMode aMode)
517 {
518     TimeMilli now      = TimerMilli::GetNow();
519     uint32_t  delay    = 0;
520     uint32_t  interval = 0;
521     uint16_t  jitter   = 0;
522     TimeMilli evaluateTime;
523 
524     VerifyOrExit(mIsRunning);
525 
526     switch (aMode)
527     {
528     case kImmediately:
529         break;
530 
531     case kForNextRa:
532         if (mTxRaInfo.mTxCount <= kInitalRaTxCount)
533         {
534             interval = kInitalRaInterval;
535             jitter   = kInitialRaJitter;
536         }
537         else
538         {
539             interval = kRaBeaconInterval;
540             jitter   = kRaBeaconJitter;
541         }
542 
543         break;
544 
545     case kAfterRandomDelay:
546         interval = kEvaluationInterval;
547         jitter   = kEvaluationJitter;
548         break;
549 
550     case kToReplyToRs:
551         interval = kRsReplyInterval;
552         jitter   = kRsReplyJitter;
553         break;
554     }
555 
556     delay = Random::NonCrypto::AddJitter(interval, jitter);
557 
558     // Ensure we wait a min delay after last RA tx
559     evaluateTime = Max(now + delay, mTxRaInfo.mLastTxTime + kMinDelayBetweenRas);
560 
561     mRoutingPolicyTimer.FireAtIfEarlier(evaluateTime);
562 
563 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
564     {
565         uint32_t duration = evaluateTime - now;
566 
567         if (duration == 0)
568         {
569             LogInfo("Will evaluate routing policy immediately");
570         }
571         else
572         {
573             String<Uptime::kStringSize> string;
574 
575             Uptime::UptimeToString(duration, string, /* aIncludeMsec */ true);
576             LogInfo("Will evaluate routing policy in %s (%lu msec)", string.AsCString() + 3, ToUlong(duration));
577         }
578     }
579 #endif
580 
581 exit:
582     return;
583 }
584 
SendRouterAdvertisement(RouterAdvTxMode aRaTxMode)585 void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode)
586 {
587     Error                   error = kErrorNone;
588     RouterAdvert::TxMessage raMsg;
589     RouterAdvert::Header    header;
590     Ip6::Address            destAddress;
591     InfraIf::Icmp6Packet    packet;
592 
593     LogInfo("Preparing RA");
594 
595     if (mRxRaTracker.GetLocalRaHeaderToMirror().IsValid())
596     {
597         header = mRxRaTracker.GetLocalRaHeaderToMirror();
598     }
599     else
600     {
601         header.SetToDefault();
602     }
603 
604     mRxRaTracker.SetHeaderFlagsOn(header);
605 
606     SuccessOrExit(error = raMsg.AppendHeader(header));
607 
608     LogInfo("- RA Header - flags - M:%u O:%u", header.IsManagedAddressConfigFlagSet(), header.IsOtherConfigFlagSet());
609     LogInfo("- RA Header - default route - lifetime:%u", header.GetRouterLifetime());
610 
611 #if OPENTHREAD_CONFIG_BORDER_ROUTING_STUB_ROUTER_FLAG_IN_EMITTED_RA_ENABLE
612     SuccessOrExit(error = raMsg.AppendFlagsExtensionOption(/* aStubRouterFlag */ true));
613     LogInfo("- FlagsExt - StubRouter:1");
614 #endif
615 
616     // Append PIO for local on-link prefix if is either being
617     // advertised or deprecated and for old prefix if is being
618     // deprecated.
619 
620     SuccessOrExit(error = mOnLinkPrefixManager.AppendAsPiosTo(raMsg));
621 
622     if (aRaTxMode == kInvalidateAllPrevPrefixes)
623     {
624         SuccessOrExit(error = mRioAdvertiser.InvalidatPrevRios(raMsg));
625     }
626     else
627     {
628         SuccessOrExit(error = mRioAdvertiser.AppendRios(raMsg));
629     }
630 
631     if (mExtraRaOptions.GetLength() > 0)
632     {
633         SuccessOrExit(error = raMsg.AppendBytes(mExtraRaOptions.GetBytes(), mExtraRaOptions.GetLength()));
634     }
635 
636     VerifyOrExit(raMsg.ContainsAnyOptions());
637 
638     destAddress.SetToLinkLocalAllNodesMulticast();
639     raMsg.GetAsPacket(packet);
640 
641     mTxRaInfo.IncrementTxCountAndSaveHash(packet);
642 
643     SuccessOrExit(error = mInfraIf.Send(packet, destAddress));
644 
645     mTxRaInfo.mLastTxTime = TimerMilli::GetNow();
646     Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaTxSuccess++;
647     LogInfo("Sent RA on %s", mInfraIf.ToString().AsCString());
648     DumpDebg("[BR-CERT] direction=send | type=RA |", packet.GetBytes(), packet.GetLength());
649 
650 exit:
651     if (error != kErrorNone)
652     {
653         Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaTxFailure++;
654         LogWarn("Failed to send RA on %s: %s", mInfraIf.ToString().AsCString(), ErrorToString(error));
655     }
656 }
657 
CalculateExpirationTime(TimeMilli aUpdateTime,uint32_t aLifetime)658 TimeMilli RoutingManager::CalculateExpirationTime(TimeMilli aUpdateTime, uint32_t aLifetime)
659 {
660     // `aLifetime` is in unit of seconds. We clamp the lifetime to max
661     // interval supported by `Timer` (`2^31` msec or ~24.8 days).
662     // This ensures that the time calculation fits within `TimeMilli`
663     // range.
664 
665     static constexpr uint32_t kMaxLifetime = Time::MsecToSec(Timer::kMaxDelay);
666 
667     return aUpdateTime + Time::SecToMsec(Min(aLifetime, kMaxLifetime));
668 }
669 
IsValidBrUlaPrefix(const Ip6::Prefix & aBrUlaPrefix)670 bool RoutingManager::IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix)
671 {
672     return aBrUlaPrefix.mLength == kBrUlaPrefixLength && aBrUlaPrefix.mPrefix.mFields.m8[0] == 0xfd;
673 }
674 
IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig & aOnMeshPrefixConfig)675 bool RoutingManager::IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig)
676 {
677     return IsValidOmrPrefix(aOnMeshPrefixConfig.GetPrefix()) && aOnMeshPrefixConfig.mOnMesh &&
678            aOnMeshPrefixConfig.mSlaac && aOnMeshPrefixConfig.mStable;
679 }
680 
IsValidOmrPrefix(const Ip6::Prefix & aPrefix)681 bool RoutingManager::IsValidOmrPrefix(const Ip6::Prefix &aPrefix)
682 {
683     // Accept ULA/GUA prefixes with 64-bit length.
684     return (aPrefix.GetLength() == kOmrPrefixLength) && !aPrefix.IsLinkLocal() && !aPrefix.IsMulticast();
685 }
686 
IsValidOnLinkPrefix(const PrefixInfoOption & aPio)687 bool RoutingManager::IsValidOnLinkPrefix(const PrefixInfoOption &aPio)
688 {
689     Ip6::Prefix prefix;
690 
691     aPio.GetPrefix(prefix);
692 
693     return IsValidOnLinkPrefix(prefix) && aPio.IsOnLinkFlagSet() && aPio.IsAutoAddrConfigFlagSet();
694 }
695 
IsValidOnLinkPrefix(const Ip6::Prefix & aOnLinkPrefix)696 bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix)
697 {
698     return (aOnLinkPrefix.GetLength() == kOnLinkPrefixLength) && !aOnLinkPrefix.IsLinkLocal() &&
699            !aOnLinkPrefix.IsMulticast();
700 }
701 
HandleRsSenderFinished(TimeMilli aStartTime)702 void RoutingManager::HandleRsSenderFinished(TimeMilli aStartTime)
703 {
704     // This is a callback from `RsSender` and is invoked when it
705     // finishes a cycle of sending Router Solicitations. `aStartTime`
706     // specifies the start time of the RS transmission cycle.
707     //
708     // We remove or deprecate old entries in discovered table that are
709     // not refreshed during Router Solicitation. We also invalidate
710     // the learned RA header if it is not refreshed during Router
711     // Solicitation.
712 
713     mRxRaTracker.RemoveOrDeprecateOldEntries(aStartTime);
714     ScheduleRoutingPolicyEvaluation(kImmediately);
715 }
716 
HandleRouterSolicit(const InfraIf::Icmp6Packet & aPacket,const Ip6::Address & aSrcAddress)717 void RoutingManager::HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
718 {
719     OT_UNUSED_VARIABLE(aPacket);
720     OT_UNUSED_VARIABLE(aSrcAddress);
721 
722     Get<Ip6::Ip6>().GetBorderRoutingCounters().mRsRx++;
723     LogInfo("Received RS from %s on %s", aSrcAddress.ToString().AsCString(), mInfraIf.ToString().AsCString());
724 
725     ScheduleRoutingPolicyEvaluation(kToReplyToRs);
726 }
727 
HandleNeighborAdvertisement(const InfraIf::Icmp6Packet & aPacket)728 void RoutingManager::HandleNeighborAdvertisement(const InfraIf::Icmp6Packet &aPacket)
729 {
730     const NeighborAdvertMessage *naMsg;
731 
732     VerifyOrExit(aPacket.GetLength() >= sizeof(NeighborAdvertMessage));
733     naMsg = reinterpret_cast<const NeighborAdvertMessage *>(aPacket.GetBytes());
734 
735     mRxRaTracker.ProcessNeighborAdvertMessage(*naMsg);
736 
737 exit:
738     return;
739 }
740 
HandleRouterAdvertisement(const InfraIf::Icmp6Packet & aPacket,const Ip6::Address & aSrcAddress)741 void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
742 {
743     RouterAdvert::RxMessage raMsg(aPacket);
744     RouterAdvOrigin         raOrigin = kAnotherRouter;
745 
746     OT_ASSERT(mIsRunning);
747 
748     VerifyOrExit(raMsg.IsValid());
749 
750     Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaRx++;
751 
752     if (mInfraIf.HasAddress(aSrcAddress))
753     {
754         raOrigin = mTxRaInfo.IsRaFromManager(raMsg) ? kThisBrRoutingManager : kThisBrOtherEntity;
755     }
756 
757     LogInfo("Received RA from %s on %s %s", aSrcAddress.ToString().AsCString(), mInfraIf.ToString().AsCString(),
758             RouterAdvOriginToString(raOrigin));
759 
760     DumpDebg("[BR-CERT] direction=recv | type=RA |", aPacket.GetBytes(), aPacket.GetLength());
761 
762     mRxRaTracker.ProcessRouterAdvertMessage(raMsg, aSrcAddress, raOrigin);
763 
764 exit:
765     return;
766 }
767 
HandleRaPrefixTableChanged(void)768 void RoutingManager::HandleRaPrefixTableChanged(void)
769 {
770     // This is a callback from `mRxRaTracker` indicating that
771     // there has been a change in the table.
772 
773     VerifyOrExit(mIsRunning);
774 
775     mOnLinkPrefixManager.HandleRaPrefixTableChanged();
776     mRoutePublisher.Evaluate();
777 
778 exit:
779     return;
780 }
781 
HandleLocalOnLinkPrefixChanged(void)782 void RoutingManager::HandleLocalOnLinkPrefixChanged(void)
783 {
784     // This is a callback from `OnLinkPrefixManager` indicating
785     // that the local on-link prefix is changed. The local on-link
786     // prefix is derived from extended PAN ID.
787 
788     VerifyOrExit(mIsRunning);
789 
790     mRoutePublisher.Evaluate();
791     mRxRaTracker.HandleLocalOnLinkPrefixChanged();
792     ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
793 
794 exit:
795     return;
796 }
797 
NetworkDataContainsUlaRoute(void) const798 bool RoutingManager::NetworkDataContainsUlaRoute(void) const
799 {
800     // Determine whether leader Network Data contains a route
801     // prefix which is either the ULA prefix `fc00::/7` or
802     // a sub-prefix of it (e.g., default route).
803 
804     NetworkData::Iterator            iterator = NetworkData::kIteratorInit;
805     NetworkData::ExternalRouteConfig routeConfig;
806     bool                             contains = false;
807 
808     while (Get<NetworkData::Leader>().GetNextExternalRoute(iterator, routeConfig) == kErrorNone)
809     {
810         if (routeConfig.mStable && RoutePublisher::GetUlaPrefix().ContainsPrefix(routeConfig.GetPrefix()))
811         {
812             contains = true;
813             break;
814         }
815     }
816 
817     return contains;
818 }
819 
820 #if OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE
821 
CheckReachabilityToSendIcmpError(const Message & aMessage,const Ip6::Header & aIp6Header)822 void RoutingManager::CheckReachabilityToSendIcmpError(const Message &aMessage, const Ip6::Header &aIp6Header)
823 {
824     bool                            matchesUlaOmrLowPrf = false;
825     NetworkData::Iterator           iterator            = NetworkData::kIteratorInit;
826     NetworkData::OnMeshPrefixConfig prefixConfig;
827     Ip6::MessageInfo                messageInfo;
828 
829     VerifyOrExit(IsRunning() && IsInitialPolicyEvaluationDone());
830 
831     VerifyOrExit(!aIp6Header.GetDestination().IsMulticast());
832 
833     // Validate that source matches a ULA OMR prefix with low preference
834     // (indicating it is not infrastructure-derived).
835 
836     while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
837     {
838         if (IsValidOmrPrefix(prefixConfig) && prefixConfig.GetPrefix().IsUniqueLocal() &&
839             aIp6Header.GetSource().MatchesPrefix(prefixConfig.GetPrefix()))
840         {
841             if (prefixConfig.GetPreference() >= NetworkData::kRoutePreferenceMedium)
842             {
843                 matchesUlaOmrLowPrf = false;
844                 break;
845             }
846 
847             matchesUlaOmrLowPrf = true;
848 
849             // Keep checking other prefixes, as the same prefix might
850             // be added with a higher preference by another BR.
851         }
852     }
853 
854     VerifyOrExit(matchesUlaOmrLowPrf);
855 
856     VerifyOrExit(!mRxRaTracker.IsAddressOnLink(aIp6Header.GetDestination()));
857     VerifyOrExit(!mRxRaTracker.IsAddressReachableThroughExplicitRoute(aIp6Header.GetDestination()));
858     VerifyOrExit(!Get<NetworkData::Leader>().IsNat64(aIp6Header.GetDestination()));
859 
860     LogInfo("Send ICMP unreachable for fwd msg with local ULA src and non-local dst");
861     LogInfo("   src: %s", aIp6Header.GetSource().ToString().AsCString());
862     LogInfo("   dst: %s", aIp6Header.GetDestination().ToString().AsCString());
863 
864     messageInfo.Clear();
865     messageInfo.SetPeerAddr(aIp6Header.GetSource());
866 
867     IgnoreError(Get<Ip6::Icmp>().SendError(Ip6::Icmp::Header::kTypeDstUnreach,
868                                            Ip6::Icmp::Header::kCodeDstUnreachProhibited, messageInfo, aMessage));
869 
870 exit:
871     return;
872 }
873 
874 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE
875 
876 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
877 
LogPrefixInfoOption(const Ip6::Prefix & aPrefix,uint32_t aValidLifetime,uint32_t aPreferredLifetime)878 void RoutingManager::LogPrefixInfoOption(const Ip6::Prefix &aPrefix,
879                                          uint32_t           aValidLifetime,
880                                          uint32_t           aPreferredLifetime)
881 {
882     LogInfo("- PIO %s (valid:%lu, preferred:%lu)", aPrefix.ToString().AsCString(), ToUlong(aValidLifetime),
883             ToUlong(aPreferredLifetime));
884 }
885 
LogRouteInfoOption(const Ip6::Prefix & aPrefix,uint32_t aLifetime,RoutePreference aPreference)886 void RoutingManager::LogRouteInfoOption(const Ip6::Prefix &aPrefix, uint32_t aLifetime, RoutePreference aPreference)
887 {
888     LogInfo("- RIO %s (lifetime:%lu, prf:%s)", aPrefix.ToString().AsCString(), ToUlong(aLifetime),
889             RoutePreferenceToString(aPreference));
890 }
891 
RouterAdvOriginToString(RouterAdvOrigin aRaOrigin)892 const char *RoutingManager::RouterAdvOriginToString(RouterAdvOrigin aRaOrigin)
893 {
894     static const char *const kOriginStrings[] = {
895         "",                          // (0) kAnotherRouter
896         "(this BR routing-manager)", // (1) kThisBrRoutingManager
897         "(this BR other sw entity)", // (2) kThisBrOtherEntity
898     };
899 
900     static_assert(0 == kAnotherRouter, "kAnotherRouter value is incorrect");
901     static_assert(1 == kThisBrRoutingManager, "kThisBrRoutingManager value is incorrect");
902     static_assert(2 == kThisBrOtherEntity, "kThisBrOtherEntity value is incorrect");
903 
904     return kOriginStrings[aRaOrigin];
905 }
906 
907 #else
908 
LogPrefixInfoOption(const Ip6::Prefix &,uint32_t,uint32_t)909 void RoutingManager::LogPrefixInfoOption(const Ip6::Prefix &, uint32_t, uint32_t) {}
LogRouteInfoOption(const Ip6::Prefix &,uint32_t,RoutePreference)910 void RoutingManager::LogRouteInfoOption(const Ip6::Prefix &, uint32_t, RoutePreference) {}
911 
912 #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
913 
914 //---------------------------------------------------------------------------------------------------------------------
915 // LifetimedPrefix
916 
CalculateExpirationTime(uint32_t aLifetime) const917 TimeMilli RoutingManager::LifetimedPrefix::CalculateExpirationTime(uint32_t aLifetime) const
918 {
919     // `aLifetime` is in unit of seconds. This method ensures
920     // that the time calculation fits with `TimeMilli` range.
921 
922     return RoutingManager::CalculateExpirationTime(mLastUpdateTime, aLifetime);
923 }
924 
925 //---------------------------------------------------------------------------------------------------------------------
926 // OnLinkPrefix
927 
SetFrom(const PrefixInfoOption & aPio)928 void RoutingManager::OnLinkPrefix::SetFrom(const PrefixInfoOption &aPio)
929 {
930     aPio.GetPrefix(mPrefix);
931     mValidLifetime     = aPio.GetValidLifetime();
932     mPreferredLifetime = aPio.GetPreferredLifetime();
933     mLastUpdateTime    = TimerMilli::GetNow();
934 }
935 
SetFrom(const PrefixTableEntry & aPrefixTableEntry)936 void RoutingManager::OnLinkPrefix::SetFrom(const PrefixTableEntry &aPrefixTableEntry)
937 {
938     mPrefix            = AsCoreType(&aPrefixTableEntry.mPrefix);
939     mValidLifetime     = aPrefixTableEntry.mValidLifetime;
940     mPreferredLifetime = aPrefixTableEntry.mPreferredLifetime;
941     mLastUpdateTime    = TimerMilli::GetNow();
942 }
943 
IsDeprecated(void) const944 bool RoutingManager::OnLinkPrefix::IsDeprecated(void) const { return GetDeprecationTime() <= TimerMilli::GetNow(); }
945 
GetDeprecationTime(void) const946 TimeMilli RoutingManager::OnLinkPrefix::GetDeprecationTime(void) const
947 {
948     return CalculateExpirationTime(mPreferredLifetime);
949 }
950 
GetStaleTime(void) const951 TimeMilli RoutingManager::OnLinkPrefix::GetStaleTime(void) const
952 {
953     return CalculateExpirationTime(Min(kStaleTime, mPreferredLifetime));
954 }
955 
AdoptValidAndPreferredLifetimesFrom(const OnLinkPrefix & aPrefix)956 void RoutingManager::OnLinkPrefix::AdoptValidAndPreferredLifetimesFrom(const OnLinkPrefix &aPrefix)
957 {
958     constexpr uint32_t kTwoHoursInSeconds = 2 * 3600;
959 
960     // Per RFC 4862 section 5.5.3.e:
961     //
962     // 1.  If the received Valid Lifetime is greater than 2 hours or
963     //     greater than RemainingLifetime, set the valid lifetime of the
964     //     corresponding address to the advertised Valid Lifetime.
965     // 2.  If RemainingLifetime is less than or equal to 2 hours, ignore
966     //     the Prefix Information option with regards to the valid
967     //     lifetime, unless ...
968     // 3.  Otherwise, reset the valid lifetime of the corresponding
969     //     address to 2 hours.
970 
971     if (aPrefix.mValidLifetime > kTwoHoursInSeconds || aPrefix.GetExpireTime() > GetExpireTime())
972     {
973         mValidLifetime = aPrefix.mValidLifetime;
974     }
975     else if (GetExpireTime() > TimerMilli::GetNow() + TimeMilli::SecToMsec(kTwoHoursInSeconds))
976     {
977         mValidLifetime = kTwoHoursInSeconds;
978     }
979 
980     mPreferredLifetime = aPrefix.GetPreferredLifetime();
981     mLastUpdateTime    = aPrefix.GetLastUpdateTime();
982 }
983 
CopyInfoTo(PrefixTableEntry & aEntry,TimeMilli aNow) const984 void RoutingManager::OnLinkPrefix::CopyInfoTo(PrefixTableEntry &aEntry, TimeMilli aNow) const
985 {
986     aEntry.mPrefix              = GetPrefix();
987     aEntry.mIsOnLink            = true;
988     aEntry.mMsecSinceLastUpdate = aNow - GetLastUpdateTime();
989     aEntry.mValidLifetime       = GetValidLifetime();
990     aEntry.mPreferredLifetime   = GetPreferredLifetime();
991 }
992 
IsFavoredOver(const Ip6::Prefix & aPrefix) const993 bool RoutingManager::OnLinkPrefix::IsFavoredOver(const Ip6::Prefix &aPrefix) const
994 {
995     bool isFavored = false;
996 
997     // Validate that the `OnLinkPrefix` is eligible to be considered a
998     // favored on-link prefix. It must not be deprecated and have a
999     // preferred lifetime exceeding a minimum (1800 seconds).
1000 
1001     VerifyOrExit(!IsDeprecated());
1002     VerifyOrExit(GetPreferredLifetime() >= kFavoredMinPreferredLifetime);
1003 
1004     // Numerically smaller prefix is favored (unless `aPrefix` is empty).
1005 
1006     VerifyOrExit(aPrefix.GetLength() != 0, isFavored = true);
1007 
1008     isFavored = GetPrefix() < aPrefix;
1009 
1010 exit:
1011     return isFavored;
1012 }
1013 
1014 //---------------------------------------------------------------------------------------------------------------------
1015 // RoutePrefix
1016 
SetFrom(const RouteInfoOption & aRio)1017 void RoutingManager::RoutePrefix::SetFrom(const RouteInfoOption &aRio)
1018 {
1019     aRio.GetPrefix(mPrefix);
1020     mValidLifetime   = aRio.GetRouteLifetime();
1021     mRoutePreference = aRio.GetPreference();
1022     mLastUpdateTime  = TimerMilli::GetNow();
1023 }
1024 
SetFrom(const RouterAdvert::Header & aRaHeader)1025 void RoutingManager::RoutePrefix::SetFrom(const RouterAdvert::Header &aRaHeader)
1026 {
1027     mPrefix.Clear();
1028     mValidLifetime   = aRaHeader.GetRouterLifetime();
1029     mRoutePreference = aRaHeader.GetDefaultRouterPreference();
1030     mLastUpdateTime  = TimerMilli::GetNow();
1031 }
1032 
GetStaleTime(void) const1033 TimeMilli RoutingManager::RoutePrefix::GetStaleTime(void) const
1034 {
1035     return CalculateExpirationTime(Min(kStaleTime, mValidLifetime));
1036 }
1037 
CopyInfoTo(PrefixTableEntry & aEntry,TimeMilli aNow) const1038 void RoutingManager::RoutePrefix::CopyInfoTo(PrefixTableEntry &aEntry, TimeMilli aNow) const
1039 {
1040     aEntry.mPrefix              = GetPrefix();
1041     aEntry.mIsOnLink            = false;
1042     aEntry.mMsecSinceLastUpdate = aNow - GetLastUpdateTime();
1043     aEntry.mValidLifetime       = GetValidLifetime();
1044     aEntry.mPreferredLifetime   = 0;
1045     aEntry.mRoutePreference     = static_cast<otRoutePreference>(GetRoutePreference());
1046 }
1047 
1048 //---------------------------------------------------------------------------------------------------------------------
1049 // NetDataPeerBrTracker
1050 
1051 #if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
NetDataPeerBrTracker(Instance & aInstance)1052 RoutingManager::NetDataPeerBrTracker::NetDataPeerBrTracker(Instance &aInstance)
1053     : InstanceLocator(aInstance)
1054 {
1055 }
1056 
CountPeerBrs(uint32_t & aMinAge) const1057 uint16_t RoutingManager::NetDataPeerBrTracker::CountPeerBrs(uint32_t &aMinAge) const
1058 {
1059     uint32_t uptime = Uptime::MsecToSec(Get<Uptime>().GetUptime());
1060     uint16_t count  = 0;
1061 
1062     aMinAge = NumericLimits<uint16_t>::kMax;
1063 
1064     for (const PeerBr &peerBr : mPeerBrs)
1065     {
1066         count++;
1067         aMinAge = Min(aMinAge, peerBr.GetAge(uptime));
1068     }
1069 
1070     if (count == 0)
1071     {
1072         aMinAge = 0;
1073     }
1074 
1075     return count;
1076 }
1077 
GetNext(PrefixTableIterator & aIterator,PeerBrEntry & aEntry) const1078 Error RoutingManager::NetDataPeerBrTracker::GetNext(PrefixTableIterator &aIterator, PeerBrEntry &aEntry) const
1079 {
1080     using Iterator = RxRaTracker::Iterator;
1081 
1082     Iterator &iterator = static_cast<Iterator &>(aIterator);
1083     Error     error;
1084 
1085     SuccessOrExit(error = iterator.AdvanceToNextPeerBr(mPeerBrs.GetHead()));
1086 
1087     aEntry.mRloc16 = iterator.GetPeerBrEntry()->mRloc16;
1088     aEntry.mAge    = iterator.GetPeerBrEntry()->GetAge(iterator.GetInitUptime());
1089 
1090 exit:
1091     return error;
1092 }
1093 
HandleNotifierEvents(Events aEvents)1094 void RoutingManager::NetDataPeerBrTracker::HandleNotifierEvents(Events aEvents)
1095 {
1096     NetworkData::Rlocs rlocs;
1097 
1098     VerifyOrExit(aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged));
1099 
1100     Get<NetworkData::Leader>().FindRlocs(NetworkData::kBrProvidingExternalIpConn, NetworkData::kAnyRole, rlocs);
1101 
1102     // Remove `PeerBr` entries no longer found in Network Data,
1103     // or they match the device RLOC16. Then allocate and add
1104     // entries for newly discovered peers.
1105 
1106     mPeerBrs.RemoveAndFreeAllMatching(PeerBr::Filter(rlocs));
1107     mPeerBrs.RemoveAndFreeAllMatching(Get<Mle::Mle>().GetRloc16());
1108 
1109     for (uint16_t rloc16 : rlocs)
1110     {
1111         PeerBr *newEntry;
1112 
1113         if (Get<Mle::Mle>().HasRloc16(rloc16) || mPeerBrs.ContainsMatching(rloc16))
1114         {
1115             continue;
1116         }
1117 
1118         newEntry = PeerBr::Allocate();
1119         VerifyOrExit(newEntry != nullptr, LogWarn("Failed to allocate `PeerBr` entry"));
1120 
1121         newEntry->mRloc16       = rloc16;
1122         newEntry->mDiscoverTime = Uptime::MsecToSec(Get<Uptime>().GetUptime());
1123 
1124         mPeerBrs.Push(*newEntry);
1125     }
1126 
1127 exit:
1128     return;
1129 }
1130 
1131 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
1132 
1133 //---------------------------------------------------------------------------------------------------------------------
1134 // RxRaTracker
1135 
RxRaTracker(Instance & aInstance)1136 RoutingManager::RxRaTracker::RxRaTracker(Instance &aInstance)
1137     : InstanceLocator(aInstance)
1138     , mExpirationTimer(aInstance)
1139     , mStaleTimer(aInstance)
1140     , mRouterTimer(aInstance)
1141     , mSignalTask(aInstance)
1142 {
1143     mLocalRaHeader.Clear();
1144 }
1145 
Start(void)1146 void RoutingManager::RxRaTracker::Start(void) { HandleNetDataChange(); }
1147 
Stop(void)1148 void RoutingManager::RxRaTracker::Stop(void)
1149 {
1150     mRouters.Free();
1151     mLocalRaHeader.Clear();
1152     mDecisionFactors.Clear();
1153 
1154     mExpirationTimer.Stop();
1155     mStaleTimer.Stop();
1156     mRouterTimer.Stop();
1157 }
1158 
ProcessRouterAdvertMessage(const RouterAdvert::RxMessage & aRaMessage,const Ip6::Address & aSrcAddress,RouterAdvOrigin aRaOrigin)1159 void RoutingManager::RxRaTracker::ProcessRouterAdvertMessage(const RouterAdvert::RxMessage &aRaMessage,
1160                                                              const Ip6::Address            &aSrcAddress,
1161                                                              RouterAdvOrigin                aRaOrigin)
1162 {
1163     // Process a received RA message and update the prefix table.
1164 
1165     Router *router;
1166 
1167     VerifyOrExit(aRaOrigin != kThisBrRoutingManager);
1168 
1169     router = mRouters.FindMatching(aSrcAddress);
1170 
1171     if (router == nullptr)
1172     {
1173         Entry<Router> *newEntry = AllocateEntry<Router>();
1174 
1175         if (newEntry == nullptr)
1176         {
1177             LogWarn("Received RA from too many routers, ignore RA from %s", aSrcAddress.ToString().AsCString());
1178             ExitNow();
1179         }
1180 
1181         router = newEntry;
1182         router->Clear();
1183         router->mDiscoverTime = Uptime::MsecToSec(Get<Uptime>().GetUptime());
1184         router->mAddress      = aSrcAddress;
1185 
1186         mRouters.Push(*newEntry);
1187     }
1188 
1189     // RA message can indicate router provides default route in the RA
1190     // message header and can also include an RIO for `::/0`. When
1191     // processing an RA message, the preference and lifetime values
1192     // in a `::/0` RIO override the preference and lifetime values in
1193     // the RA header (per RFC 4191 section 3.1).
1194 
1195     ProcessRaHeader(aRaMessage.GetHeader(), *router, aRaOrigin);
1196 
1197     for (const Option &option : aRaMessage)
1198     {
1199         switch (option.GetType())
1200         {
1201         case Option::kTypePrefixInfo:
1202             ProcessPrefixInfoOption(static_cast<const PrefixInfoOption &>(option), *router);
1203             break;
1204 
1205         case Option::kTypeRouteInfo:
1206             ProcessRouteInfoOption(static_cast<const RouteInfoOption &>(option), *router);
1207             break;
1208 
1209         case Option::kTypeRaFlagsExtension:
1210             ProcessRaFlagsExtOption(static_cast<const RaFlagsExtOption &>(option), *router);
1211             break;
1212 
1213         default:
1214             break;
1215         }
1216     }
1217 
1218     router->mIsLocalDevice = (aRaOrigin == kThisBrOtherEntity);
1219 
1220     router->ResetReachabilityState();
1221 
1222     Evaluate();
1223 
1224 exit:
1225     return;
1226 }
1227 
ProcessRaHeader(const RouterAdvert::Header & aRaHeader,Router & aRouter,RouterAdvOrigin aRaOrigin)1228 void RoutingManager::RxRaTracker::ProcessRaHeader(const RouterAdvert::Header &aRaHeader,
1229                                                   Router                     &aRouter,
1230                                                   RouterAdvOrigin             aRaOrigin)
1231 {
1232     bool                managedFlag = aRaHeader.IsManagedAddressConfigFlagSet();
1233     bool                otherFlag   = aRaHeader.IsOtherConfigFlagSet();
1234     Entry<RoutePrefix> *entry;
1235     Ip6::Prefix         prefix;
1236 
1237     LogInfo("- RA Header - flags - M:%u O:%u", managedFlag, otherFlag);
1238 
1239     if (aRouter.mManagedAddressConfigFlag != managedFlag)
1240     {
1241         aRouter.mManagedAddressConfigFlag = managedFlag;
1242     }
1243 
1244     if (aRouter.mOtherConfigFlag != otherFlag)
1245     {
1246         aRouter.mOtherConfigFlag = otherFlag;
1247     }
1248 
1249     if (aRaOrigin == kThisBrOtherEntity)
1250     {
1251         // Update `mLocalRaHeader`, which tracks the RA header of
1252         // locally generated RA by another sw entity running on this
1253         // device.
1254 
1255         RouterAdvert::Header oldHeader = mLocalRaHeader;
1256 
1257         if (aRaHeader.GetRouterLifetime() == 0)
1258         {
1259             mLocalRaHeader.Clear();
1260         }
1261         else
1262         {
1263             mLocalRaHeader           = aRaHeader;
1264             mLocalRaHeaderUpdateTime = TimerMilli::GetNow();
1265 
1266             // The checksum is set to zero which indicates to platform
1267             // that it needs to do the calculation and update it.
1268 
1269             mLocalRaHeader.SetChecksum(0);
1270         }
1271 
1272         if (mLocalRaHeader != oldHeader)
1273         {
1274             Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
1275         }
1276     }
1277 
1278     prefix.Clear();
1279     entry = aRouter.mRoutePrefixes.FindMatching(prefix);
1280 
1281     LogInfo("- RA Header - default route - lifetime:%u", aRaHeader.GetRouterLifetime());
1282 
1283     if (entry == nullptr)
1284     {
1285         VerifyOrExit(aRaHeader.GetRouterLifetime() != 0);
1286 
1287         entry = AllocateEntry<RoutePrefix>();
1288 
1289         if (entry == nullptr)
1290         {
1291             LogWarn("Discovered too many prefixes, ignore default route from RA header");
1292             ExitNow();
1293         }
1294 
1295         entry->SetFrom(aRaHeader);
1296         aRouter.mRoutePrefixes.Push(*entry);
1297     }
1298     else
1299     {
1300         entry->SetFrom(aRaHeader);
1301     }
1302 
1303 exit:
1304     return;
1305 }
1306 
ProcessPrefixInfoOption(const PrefixInfoOption & aPio,Router & aRouter)1307 void RoutingManager::RxRaTracker::ProcessPrefixInfoOption(const PrefixInfoOption &aPio, Router &aRouter)
1308 {
1309     Ip6::Prefix          prefix;
1310     Entry<OnLinkPrefix> *entry;
1311     bool                 disregard;
1312 
1313     VerifyOrExit(aPio.IsValid());
1314     aPio.GetPrefix(prefix);
1315 
1316     if (!IsValidOnLinkPrefix(aPio))
1317     {
1318         LogInfo("- PIO %s - ignore since not a valid on-link prefix", prefix.ToString().AsCString());
1319         ExitNow();
1320     }
1321 
1322     // Disregard the PIO prefix if it matches our local on-link prefix,
1323     // as this indicates it's likely from a peer Border Router connected
1324     // to the same Thread mesh.
1325 
1326     disregard = (prefix == Get<RoutingManager>().mOnLinkPrefixManager.GetLocalPrefix());
1327 
1328 #if !OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
1329     VerifyOrExit(!disregard);
1330 #endif
1331 
1332     LogPrefixInfoOption(prefix, aPio.GetValidLifetime(), aPio.GetPreferredLifetime());
1333 
1334     entry = aRouter.mOnLinkPrefixes.FindMatching(prefix);
1335 
1336     if (entry == nullptr)
1337     {
1338         VerifyOrExit(aPio.GetValidLifetime() != 0);
1339 
1340         entry = AllocateEntry<OnLinkPrefix>();
1341 
1342         if (entry == nullptr)
1343         {
1344             LogWarn("Discovered too many prefixes, ignore on-link prefix %s", prefix.ToString().AsCString());
1345             ExitNow();
1346         }
1347 
1348         entry->SetFrom(aPio);
1349         aRouter.mOnLinkPrefixes.Push(*entry);
1350     }
1351     else
1352     {
1353         OnLinkPrefix newPrefix;
1354 
1355         newPrefix.SetFrom(aPio);
1356         entry->AdoptValidAndPreferredLifetimesFrom(newPrefix);
1357     }
1358 
1359     entry->SetDisregardFlag(disregard);
1360 
1361 exit:
1362     return;
1363 }
1364 
ProcessRouteInfoOption(const RouteInfoOption & aRio,Router & aRouter)1365 void RoutingManager::RxRaTracker::ProcessRouteInfoOption(const RouteInfoOption &aRio, Router &aRouter)
1366 {
1367     Ip6::Prefix         prefix;
1368     Entry<RoutePrefix> *entry;
1369     bool                disregard;
1370 
1371     VerifyOrExit(aRio.IsValid());
1372     aRio.GetPrefix(prefix);
1373 
1374     VerifyOrExit(!prefix.IsLinkLocal() && !prefix.IsMulticast());
1375 
1376     // Disregard our own advertised OMR prefixes and those currently
1377     // present in the Thread Network Data. This implies it is likely
1378     // from a peer Thread BR connected to the same Thread mesh.
1379     //
1380     // There should be eventual parity between the `RioAdvertiser`
1381     // prefixes and the OMR prefixes in Network Data, but temporary
1382     // discrepancies can occur due to the tx timing of RAs and time
1383     // required to update Network Data (registering with leader). So
1384     // both checks are necessary.
1385 
1386     disregard = (Get<RoutingManager>().mOmrPrefixManager.GetLocalPrefix().GetPrefix() == prefix) ||
1387                 Get<RoutingManager>().mRioAdvertiser.HasAdvertised(prefix) ||
1388                 Get<NetworkData::Leader>().ContainsOmrPrefix(prefix);
1389 
1390 #if !OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
1391     VerifyOrExit(!disregard);
1392 #endif
1393 
1394     LogRouteInfoOption(prefix, aRio.GetRouteLifetime(), aRio.GetPreference());
1395 
1396     entry = aRouter.mRoutePrefixes.FindMatching(prefix);
1397 
1398     if (entry == nullptr)
1399     {
1400         VerifyOrExit(aRio.GetRouteLifetime() != 0);
1401 
1402         entry = AllocateEntry<RoutePrefix>();
1403 
1404         if (entry == nullptr)
1405         {
1406             LogWarn("Discovered too many prefixes, ignore route prefix %s", prefix.ToString().AsCString());
1407             ExitNow();
1408         }
1409 
1410         entry->SetFrom(aRio);
1411         aRouter.mRoutePrefixes.Push(*entry);
1412     }
1413     else
1414     {
1415         entry->SetFrom(aRio);
1416     }
1417 
1418     entry->SetDisregardFlag(disregard);
1419 
1420 exit:
1421     return;
1422 }
1423 
ProcessRaFlagsExtOption(const RaFlagsExtOption & aRaFlagsOption,Router & aRouter)1424 void RoutingManager::RxRaTracker::ProcessRaFlagsExtOption(const RaFlagsExtOption &aRaFlagsOption, Router &aRouter)
1425 {
1426     VerifyOrExit(aRaFlagsOption.IsValid());
1427     aRouter.mStubRouterFlag = aRaFlagsOption.IsStubRouterFlagSet();
1428 
1429     LogInfo("- FlagsExt - StubRouter:%u", aRouter.mStubRouterFlag);
1430 
1431 exit:
1432     return;
1433 }
1434 
1435 #if !OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE
1436 
1437 template <>
AllocateEntry(void)1438 RoutingManager::RxRaTracker::Entry<RoutingManager::RxRaTracker::Router> *RoutingManager::RxRaTracker::AllocateEntry(
1439     void)
1440 {
1441     Entry<Router> *router = mRouterPool.Allocate();
1442 
1443     VerifyOrExit(router != nullptr);
1444     router->Init(GetInstance());
1445 
1446 exit:
1447     return router;
1448 }
1449 
1450 template <class PrefixType>
AllocateEntry(void)1451 RoutingManager::RxRaTracker::Entry<PrefixType> *RoutingManager::RxRaTracker::AllocateEntry(void)
1452 {
1453     static_assert(TypeTraits::IsSame<PrefixType, OnLinkPrefix>::kValue ||
1454                       TypeTraits::IsSame<PrefixType, RoutePrefix>::kValue,
1455                   "PrefixType MSUT be either RoutePrefix or OnLinkPrefix");
1456 
1457     Entry<PrefixType> *entry       = nullptr;
1458     SharedEntry       *sharedEntry = mEntryPool.Allocate();
1459 
1460     VerifyOrExit(sharedEntry != nullptr);
1461     entry = &sharedEntry->GetEntry<PrefixType>();
1462     entry->Init(GetInstance());
1463 
1464 exit:
1465     return entry;
1466 }
1467 
Free(void)1468 template <> void RoutingManager::RxRaTracker::Entry<RoutingManager::RxRaTracker::Router>::Free(void)
1469 {
1470     mOnLinkPrefixes.Free();
1471     mRoutePrefixes.Free();
1472     Get<RoutingManager>().mRxRaTracker.mRouterPool.Free(*this);
1473 }
1474 
Free(void)1475 template <class PrefixType> void RoutingManager::RxRaTracker::Entry<PrefixType>::Free(void)
1476 {
1477     static_assert(TypeTraits::IsSame<PrefixType, OnLinkPrefix>::kValue ||
1478                       TypeTraits::IsSame<PrefixType, RoutePrefix>::kValue,
1479                   "PrefixType MSUT be either RoutePrefix or OnLinkPrefix");
1480 
1481     Get<RoutingManager>().mRxRaTracker.mEntryPool.Free(*reinterpret_cast<SharedEntry *>(this));
1482 }
1483 
1484 #endif // !OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE
1485 
HandleLocalOnLinkPrefixChanged(void)1486 void RoutingManager::RxRaTracker::HandleLocalOnLinkPrefixChanged(void)
1487 {
1488     const Ip6::Prefix &prefix    = Get<RoutingManager>().mOnLinkPrefixManager.GetLocalPrefix();
1489     bool               didChange = false;
1490 
1491     // When `TRACK_PEER_BR_INFO_ENABLE` is enabled, we mark
1492     // to disregard any on-link prefix entries matching the new
1493     // local on-link prefix. Otherwise, we can remove and free
1494     // them.
1495 
1496     for (Router &router : mRouters)
1497     {
1498 #if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
1499         OnLinkPrefix *entry = router.mOnLinkPrefixes.FindMatching(prefix);
1500 
1501         if ((entry != nullptr) && !entry->ShouldDisregard())
1502         {
1503             entry->SetDisregardFlag(true);
1504             didChange = true;
1505         }
1506 #else
1507         didChange |= router.mOnLinkPrefixes.RemoveAndFreeAllMatching(prefix);
1508 #endif
1509     }
1510 
1511     VerifyOrExit(didChange);
1512 
1513     Evaluate();
1514 
1515 exit:
1516     return;
1517 }
1518 
HandleNetDataChange(void)1519 void RoutingManager::RxRaTracker::HandleNetDataChange(void)
1520 {
1521     NetworkData::Iterator           iterator = NetworkData::kIteratorInit;
1522     NetworkData::OnMeshPrefixConfig prefixConfig;
1523     bool                            didChange = false;
1524 
1525     while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
1526     {
1527         if (!IsValidOmrPrefix(prefixConfig))
1528         {
1529             continue;
1530         }
1531 
1532         for (Router &router : mRouters)
1533         {
1534 #if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
1535             RoutePrefix *entry = router.mRoutePrefixes.FindMatching(prefixConfig.GetPrefix());
1536 
1537             if ((entry != nullptr) && !entry->ShouldDisregard())
1538             {
1539                 entry->SetDisregardFlag(true);
1540                 didChange = true;
1541             }
1542 #else
1543             didChange |= router.mRoutePrefixes.RemoveAndFreeAllMatching(prefixConfig.GetPrefix());
1544 #endif
1545         }
1546     }
1547 
1548     if (didChange)
1549     {
1550         Evaluate();
1551     }
1552 }
1553 
RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold)1554 void RoutingManager::RxRaTracker::RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold)
1555 {
1556     // Remove route prefix entries and deprecate on-link entries in
1557     // the table that are old (not updated since `aTimeThreshold`).
1558 
1559     for (Router &router : mRouters)
1560     {
1561         for (OnLinkPrefix &entry : router.mOnLinkPrefixes)
1562         {
1563             if (entry.GetLastUpdateTime() <= aTimeThreshold)
1564             {
1565                 entry.ClearPreferredLifetime();
1566             }
1567         }
1568 
1569         for (RoutePrefix &entry : router.mRoutePrefixes)
1570         {
1571             if (entry.GetLastUpdateTime() <= aTimeThreshold)
1572             {
1573                 entry.ClearValidLifetime();
1574             }
1575         }
1576     }
1577 
1578     if (mLocalRaHeader.IsValid() && (mLocalRaHeaderUpdateTime <= aTimeThreshold))
1579     {
1580         mLocalRaHeader.Clear();
1581     }
1582 
1583     Evaluate();
1584 }
1585 
Evaluate(void)1586 void RoutingManager::RxRaTracker::Evaluate(void)
1587 {
1588     DecisionFactors oldFactors = mDecisionFactors;
1589     TimeMilli       now        = TimerMilli::GetNow();
1590     NextFireTime    routerTimeoutTime(now);
1591     NextFireTime    entryExpireTime(now);
1592     NextFireTime    staleTime(now);
1593 
1594     //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1595     // Remove expired prefix entries in routers and then remove any
1596     // router that has no prefix entries or flags.
1597 
1598     mRouters.RemoveAndFreeAllMatching(Router::EmptyChecker(now));
1599 
1600     //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1601     // Determine decision factors (favored on-link prefix, has any
1602     // ULA/non-ULA on-link/route prefix, M/O flags).
1603 
1604     mDecisionFactors.Clear();
1605 
1606     for (Router &router : mRouters)
1607     {
1608         router.mAllEntriesDisregarded = true;
1609 
1610         mDecisionFactors.UpdateFlagsFrom(router);
1611 
1612         for (OnLinkPrefix &entry : router.mOnLinkPrefixes)
1613         {
1614             mDecisionFactors.UpdateFrom(entry);
1615             entry.SetStaleTimeCalculated(false);
1616 
1617             router.mAllEntriesDisregarded &= entry.ShouldDisregard();
1618         }
1619 
1620         for (RoutePrefix &entry : router.mRoutePrefixes)
1621         {
1622             mDecisionFactors.UpdateFrom(entry);
1623             entry.SetStaleTimeCalculated(false);
1624 
1625             router.mAllEntriesDisregarded &= entry.ShouldDisregard();
1626         }
1627     }
1628 
1629     if (oldFactors != mDecisionFactors)
1630     {
1631         mSignalTask.Post();
1632     }
1633 
1634     //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1635     // Schedule timers
1636 
1637     // If multiple routers advertise the same on-link or route prefix,
1638     // the stale time for the prefix is determined by the latest stale
1639     // time among all corresponding entries.
1640     //
1641     // The "StaleTimeCalculated" flag is used to ensure stale time is
1642     // calculated only once for each unique prefix. Initially, this
1643     // flag is cleared on all entries. As we iterate over routers and
1644     // their entries, `DetermineStaleTimeFor()` will consider all
1645     // matching entries and mark "StaleTimeCalculated" flag on them.
1646 
1647     for (Router &router : mRouters)
1648     {
1649         if (router.ShouldCheckReachability())
1650         {
1651             router.DetermineReachabilityTimeout();
1652             routerTimeoutTime.UpdateIfEarlier(router.mTimeoutTime);
1653         }
1654 
1655         for (const OnLinkPrefix &entry : router.mOnLinkPrefixes)
1656         {
1657             entryExpireTime.UpdateIfEarlier(entry.GetExpireTime());
1658 
1659             if (!entry.IsStaleTimeCalculated())
1660             {
1661                 DetermineStaleTimeFor(entry, staleTime);
1662             }
1663         }
1664 
1665         for (const RoutePrefix &entry : router.mRoutePrefixes)
1666         {
1667             entryExpireTime.UpdateIfEarlier(entry.GetExpireTime());
1668 
1669             if (!entry.IsStaleTimeCalculated())
1670             {
1671                 DetermineStaleTimeFor(entry, staleTime);
1672             }
1673         }
1674     }
1675 
1676     if (mLocalRaHeader.IsValid())
1677     {
1678         uint16_t interval = kStaleTime;
1679 
1680         if (mLocalRaHeader.GetRouterLifetime() > 0)
1681         {
1682             interval = Min(interval, mLocalRaHeader.GetRouterLifetime());
1683         }
1684 
1685         staleTime.UpdateIfEarlier(CalculateExpirationTime(mLocalRaHeaderUpdateTime, interval));
1686     }
1687 
1688     mRouterTimer.FireAt(routerTimeoutTime);
1689     mExpirationTimer.FireAt(entryExpireTime);
1690     mStaleTimer.FireAt(staleTime);
1691 }
1692 
DetermineStaleTimeFor(const OnLinkPrefix & aPrefix,NextFireTime & aStaleTime)1693 void RoutingManager::RxRaTracker::DetermineStaleTimeFor(const OnLinkPrefix &aPrefix, NextFireTime &aStaleTime)
1694 {
1695     TimeMilli prefixStaleTime = aStaleTime.GetNow();
1696     bool      found           = false;
1697 
1698     for (Router &router : mRouters)
1699     {
1700         for (OnLinkPrefix &entry : router.mOnLinkPrefixes)
1701         {
1702             if (!entry.Matches(aPrefix.GetPrefix()))
1703             {
1704                 continue;
1705             }
1706 
1707             entry.SetStaleTimeCalculated(true);
1708 
1709             if (entry.IsDeprecated())
1710             {
1711                 continue;
1712             }
1713 
1714             prefixStaleTime = Max(prefixStaleTime, Max(aStaleTime.GetNow(), entry.GetStaleTime()));
1715             found           = true;
1716         }
1717     }
1718 
1719     if (found)
1720     {
1721         aStaleTime.UpdateIfEarlier(prefixStaleTime);
1722     }
1723 }
1724 
DetermineStaleTimeFor(const RoutePrefix & aPrefix,NextFireTime & aStaleTime)1725 void RoutingManager::RxRaTracker::DetermineStaleTimeFor(const RoutePrefix &aPrefix, NextFireTime &aStaleTime)
1726 {
1727     TimeMilli prefixStaleTime = aStaleTime.GetNow();
1728     bool      found           = false;
1729 
1730     for (Router &router : mRouters)
1731     {
1732         for (RoutePrefix &entry : router.mRoutePrefixes)
1733         {
1734             if (!entry.Matches(aPrefix.GetPrefix()))
1735             {
1736                 continue;
1737             }
1738 
1739             entry.SetStaleTimeCalculated(true);
1740 
1741             prefixStaleTime = Max(prefixStaleTime, Max(aStaleTime.GetNow(), entry.GetStaleTime()));
1742             found           = true;
1743         }
1744     }
1745 
1746     if (found)
1747     {
1748         aStaleTime.UpdateIfEarlier(prefixStaleTime);
1749     }
1750 }
1751 
HandleStaleTimer(void)1752 void RoutingManager::RxRaTracker::HandleStaleTimer(void)
1753 {
1754     VerifyOrExit(Get<RoutingManager>().IsRunning());
1755 
1756     LogInfo("Stale timer expired");
1757     Get<RoutingManager>().mRsSender.Start();
1758 
1759 exit:
1760     return;
1761 }
1762 
HandleExpirationTimer(void)1763 void RoutingManager::RxRaTracker::HandleExpirationTimer(void) { Evaluate(); }
1764 
HandleSignalTask(void)1765 void RoutingManager::RxRaTracker::HandleSignalTask(void) { Get<RoutingManager>().HandleRaPrefixTableChanged(); }
1766 
ProcessNeighborAdvertMessage(const NeighborAdvertMessage & aNaMessage)1767 void RoutingManager::RxRaTracker::ProcessNeighborAdvertMessage(const NeighborAdvertMessage &aNaMessage)
1768 {
1769     Router *router;
1770 
1771     VerifyOrExit(aNaMessage.IsValid());
1772 
1773     router = mRouters.FindMatching(aNaMessage.GetTargetAddress());
1774     VerifyOrExit(router != nullptr);
1775 
1776     LogInfo("Received NA from router %s", router->mAddress.ToString().AsCString());
1777 
1778     router->ResetReachabilityState();
1779 
1780     Evaluate();
1781 
1782 exit:
1783     return;
1784 }
1785 
HandleRouterTimer(void)1786 void RoutingManager::RxRaTracker::HandleRouterTimer(void)
1787 {
1788     TimeMilli now = TimerMilli::GetNow();
1789 
1790     for (Router &router : mRouters)
1791     {
1792         if (!router.ShouldCheckReachability() || (router.mTimeoutTime > now))
1793         {
1794             continue;
1795         }
1796 
1797         router.mNsProbeCount++;
1798 
1799         if (router.IsReachable())
1800         {
1801             router.mTimeoutTime = now + ((router.mNsProbeCount < Router::kMaxNsProbes) ? Router::kNsProbeRetryInterval
1802                                                                                        : Router::kNsProbeTimeout);
1803             SendNeighborSolicitToRouter(router);
1804         }
1805         else
1806         {
1807             LogInfo("No response to all Neighbor Solicitations attempts from router %s - marking it unreachable",
1808                     router.mAddress.ToString().AsCString());
1809 
1810             // Remove route prefix entries and deprecate on-link prefix entries
1811             // of the unreachable router.
1812 
1813             for (OnLinkPrefix &entry : router.mOnLinkPrefixes)
1814             {
1815                 if (!entry.IsDeprecated())
1816                 {
1817                     entry.ClearPreferredLifetime();
1818                 }
1819             }
1820 
1821             for (RoutePrefix &entry : router.mRoutePrefixes)
1822             {
1823                 entry.ClearValidLifetime();
1824             }
1825         }
1826     }
1827 
1828     Evaluate();
1829 }
1830 
SendNeighborSolicitToRouter(const Router & aRouter)1831 void RoutingManager::RxRaTracker::SendNeighborSolicitToRouter(const Router &aRouter)
1832 {
1833     InfraIf::Icmp6Packet   packet;
1834     NeighborSolicitMessage neighborSolicitMsg;
1835 
1836     VerifyOrExit(!Get<RoutingManager>().mRsSender.IsInProgress());
1837 
1838     neighborSolicitMsg.SetTargetAddress(aRouter.mAddress);
1839     packet.InitFrom(neighborSolicitMsg);
1840 
1841     IgnoreError(Get<RoutingManager>().mInfraIf.Send(packet, aRouter.mAddress));
1842 
1843     LogInfo("Sent Neighbor Solicitation to %s - attempt:%u/%u", aRouter.mAddress.ToString().AsCString(),
1844             aRouter.mNsProbeCount, Router::kMaxNsProbes);
1845 
1846 exit:
1847     return;
1848 }
1849 
SetHeaderFlagsOn(RouterAdvert::Header & aHeader) const1850 void RoutingManager::RxRaTracker::SetHeaderFlagsOn(RouterAdvert::Header &aHeader) const
1851 {
1852     if (mDecisionFactors.mHeaderManagedAddressConfigFlag)
1853     {
1854         aHeader.SetManagedAddressConfigFlag();
1855     }
1856 
1857     if (mDecisionFactors.mHeaderOtherConfigFlag)
1858     {
1859         aHeader.SetOtherConfigFlag();
1860     }
1861 }
1862 
IsAddressOnLink(const Ip6::Address & aAddress) const1863 bool RoutingManager::RxRaTracker::IsAddressOnLink(const Ip6::Address &aAddress) const
1864 {
1865     bool isOnLink = false;
1866 
1867     for (const Router &router : mRouters)
1868     {
1869         for (const OnLinkPrefix &onLinkPrefix : router.mOnLinkPrefixes)
1870         {
1871             isOnLink = aAddress.MatchesPrefix(onLinkPrefix.GetPrefix());
1872             VerifyOrExit(!isOnLink);
1873         }
1874     }
1875 
1876 exit:
1877     return isOnLink;
1878 }
1879 
IsAddressReachableThroughExplicitRoute(const Ip6::Address & aAddress) const1880 bool RoutingManager::RxRaTracker::IsAddressReachableThroughExplicitRoute(const Ip6::Address &aAddress) const
1881 {
1882     // Checks whether the `aAddress` matches any discovered route
1883     // prefix excluding `::/0`.
1884 
1885     bool isReachable = false;
1886 
1887     for (const Router &router : mRouters)
1888     {
1889         for (const RoutePrefix &routePrefix : router.mRoutePrefixes)
1890         {
1891             if (routePrefix.GetPrefix().GetLength() == 0)
1892             {
1893                 continue;
1894             }
1895 
1896             isReachable = aAddress.MatchesPrefix(routePrefix.GetPrefix());
1897             VerifyOrExit(!isReachable);
1898         }
1899     }
1900 
1901 exit:
1902     return isReachable;
1903 }
1904 
InitIterator(PrefixTableIterator & aIterator) const1905 void RoutingManager::RxRaTracker::InitIterator(PrefixTableIterator &aIterator) const
1906 {
1907     static_cast<Iterator &>(aIterator).Init(mRouters.GetHead(), Uptime::MsecToSec(Get<Uptime>().GetUptime()));
1908 }
1909 
GetNextEntry(PrefixTableIterator & aIterator,PrefixTableEntry & aEntry) const1910 Error RoutingManager::RxRaTracker::GetNextEntry(PrefixTableIterator &aIterator, PrefixTableEntry &aEntry) const
1911 {
1912     Error     error    = kErrorNone;
1913     Iterator &iterator = static_cast<Iterator &>(aIterator);
1914 
1915     ClearAllBytes(aEntry);
1916 
1917     SuccessOrExit(error = iterator.AdvanceToNextEntry());
1918 
1919     iterator.GetRouter()->CopyInfoTo(aEntry.mRouter, iterator.GetInitTime(), iterator.GetInitUptime());
1920 
1921     switch (iterator.GetEntryType())
1922     {
1923     case Iterator::kOnLinkPrefix:
1924         iterator.GetEntry<OnLinkPrefix>()->CopyInfoTo(aEntry, iterator.GetInitTime());
1925         break;
1926     case Iterator::kRoutePrefix:
1927         iterator.GetEntry<RoutePrefix>()->CopyInfoTo(aEntry, iterator.GetInitTime());
1928         break;
1929     }
1930 
1931 exit:
1932     return error;
1933 }
1934 
GetNextRouter(PrefixTableIterator & aIterator,RouterEntry & aEntry) const1935 Error RoutingManager::RxRaTracker::GetNextRouter(PrefixTableIterator &aIterator, RouterEntry &aEntry) const
1936 {
1937     Error     error    = kErrorNone;
1938     Iterator &iterator = static_cast<Iterator &>(aIterator);
1939 
1940     ClearAllBytes(aEntry);
1941 
1942     SuccessOrExit(error = iterator.AdvanceToNextRouter(Iterator::kRouterIterator));
1943     iterator.GetRouter()->CopyInfoTo(aEntry, iterator.GetInitTime(), iterator.GetInitUptime());
1944 
1945 exit:
1946     return error;
1947 }
1948 
1949 //---------------------------------------------------------------------------------------------------------------------
1950 // RxRaTracker::Iterator
1951 
Init(const Entry<Router> * aRoutersHead,uint32_t aUptime)1952 void RoutingManager::RxRaTracker::Iterator::Init(const Entry<Router> *aRoutersHead, uint32_t aUptime)
1953 {
1954     SetInitUptime(aUptime);
1955     SetInitTime();
1956     SetType(kUnspecified);
1957     SetRouter(aRoutersHead);
1958     SetEntry(nullptr);
1959     SetEntryType(kRoutePrefix);
1960 }
1961 
AdvanceToNextRouter(Type aType)1962 Error RoutingManager::RxRaTracker::Iterator::AdvanceToNextRouter(Type aType)
1963 {
1964     Error error = kErrorNone;
1965 
1966     if (GetType() == kUnspecified)
1967     {
1968         // On the first call, when iterator type is `kUnspecified`, we
1969         // set the type, and keep the `GetRouter()` as is so to start
1970         // from the first router in the list.
1971 
1972         SetType(aType);
1973     }
1974     else
1975     {
1976         // On subsequent call, we ensure that the iterator type
1977         // matches what we expect and advance to the next router on
1978         // the list.
1979 
1980         VerifyOrExit(GetType() == aType, error = kErrorInvalidArgs);
1981         VerifyOrExit(GetRouter() != nullptr, error = kErrorNone);
1982         SetRouter(GetRouter()->GetNext());
1983     }
1984 
1985     VerifyOrExit(GetRouter() != nullptr, error = kErrorNotFound);
1986 
1987 exit:
1988     return error;
1989 }
1990 
AdvanceToNextEntry(void)1991 Error RoutingManager::RxRaTracker::Iterator::AdvanceToNextEntry(void)
1992 {
1993     Error error = kErrorNone;
1994 
1995     VerifyOrExit(GetRouter() != nullptr, error = kErrorNotFound);
1996 
1997     if (HasEntry())
1998     {
1999         switch (GetEntryType())
2000         {
2001         case kOnLinkPrefix:
2002             SetEntry(GetEntry<OnLinkPrefix>()->GetNext());
2003             break;
2004         case kRoutePrefix:
2005             SetEntry(GetEntry<RoutePrefix>()->GetNext());
2006             break;
2007         }
2008     }
2009 
2010     while (!HasEntry())
2011     {
2012         switch (GetEntryType())
2013         {
2014         case kOnLinkPrefix:
2015 
2016             // Transition from on-link prefixes to route prefixes of
2017             // the current router.
2018 
2019             SetEntry(GetRouter()->mRoutePrefixes.GetHead());
2020             SetEntryType(kRoutePrefix);
2021             break;
2022 
2023         case kRoutePrefix:
2024 
2025             // Transition to the next router and start with its on-link
2026             // prefixes.
2027             //
2028             // On the first call when iterator type is `kUnspecified`,
2029             // `AdvanceToNextRouter()` sets the type and starts from
2030             // the first router.
2031 
2032             SuccessOrExit(error = AdvanceToNextRouter(kPrefixIterator));
2033             SetEntry(GetRouter()->mOnLinkPrefixes.GetHead());
2034             SetEntryType(kOnLinkPrefix);
2035             break;
2036         }
2037     }
2038 
2039 exit:
2040     return error;
2041 }
2042 
2043 #if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
2044 
AdvanceToNextPeerBr(const PeerBr * aPeerBrsHead)2045 Error RoutingManager::RxRaTracker::Iterator::AdvanceToNextPeerBr(const PeerBr *aPeerBrsHead)
2046 {
2047     Error error = kErrorNone;
2048 
2049     if (GetType() == kUnspecified)
2050     {
2051         SetType(kPeerBrIterator);
2052         SetEntry(aPeerBrsHead);
2053     }
2054     else
2055     {
2056         VerifyOrExit(GetType() == kPeerBrIterator, error = kErrorInvalidArgs);
2057         VerifyOrExit(GetPeerBrEntry() != nullptr, error = kErrorNotFound);
2058         SetEntry(GetPeerBrEntry()->GetNext());
2059     }
2060 
2061     VerifyOrExit(GetPeerBrEntry() != nullptr, error = kErrorNotFound);
2062 
2063 exit:
2064     return error;
2065 }
2066 
2067 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
2068 
2069 //---------------------------------------------------------------------------------------------------------------------
2070 // RxRaTracker::Router
2071 
ShouldCheckReachability(void) const2072 bool RoutingManager::RxRaTracker::Router::ShouldCheckReachability(void) const
2073 {
2074     // Perform reachability check (send NS probes) only if the router:
2075     // - Is not already marked as unreachable (due to failed NS probes)
2076     // - Is not the local device itself (to avoid potential issues with
2077     //   the platform receiving/processing NAs from itself).
2078 
2079     return IsReachable() && !mIsLocalDevice;
2080 }
2081 
ResetReachabilityState(void)2082 void RoutingManager::RxRaTracker::Router::ResetReachabilityState(void)
2083 {
2084     // Called when an RA or NA is received and processed.
2085 
2086     mNsProbeCount   = 0;
2087     mLastUpdateTime = TimerMilli::GetNow();
2088     mTimeoutTime    = mLastUpdateTime + Random::NonCrypto::AddJitter(kReachableInterval, kJitter);
2089 }
2090 
DetermineReachabilityTimeout(void)2091 void RoutingManager::RxRaTracker::Router::DetermineReachabilityTimeout(void)
2092 {
2093     uint32_t interval;
2094 
2095     VerifyOrExit(ShouldCheckReachability());
2096     VerifyOrExit(mNsProbeCount == 0);
2097 
2098     // If all of the router's prefix entries are marked as
2099     // disregarded (excluded from any decisions), it indicates that
2100     // this router is likely a peer BR connected to the same Thread
2101     // mesh. We use a longer reachability check interval for such
2102     // peer BRs.
2103 
2104     interval     = mAllEntriesDisregarded ? kPeerBrReachableInterval : kReachableInterval;
2105     mTimeoutTime = mLastUpdateTime + Random::NonCrypto::AddJitter(interval, kJitter);
2106 
2107 exit:
2108     return;
2109 }
2110 
Matches(const EmptyChecker & aChecker)2111 bool RoutingManager::RxRaTracker::Router::Matches(const EmptyChecker &aChecker)
2112 {
2113     // First removes all expired on-link or router prefixes. Then
2114     // checks whether or not the router has any useful info.
2115 
2116     bool hasFlags = false;
2117 
2118     mOnLinkPrefixes.RemoveAndFreeAllMatching(aChecker);
2119     mRoutePrefixes.RemoveAndFreeAllMatching(aChecker);
2120 
2121     // Router can be removed if it does not advertise M or O flags and
2122     // also does not have any advertised prefix entries (RIO/PIO). If
2123     // the router already failed to respond to max NS probe attempts,
2124     // we consider it as offline and therefore do not consider its
2125     // flags anymore.
2126 
2127     if (IsReachable())
2128     {
2129         hasFlags = (mManagedAddressConfigFlag || mOtherConfigFlag);
2130     }
2131 
2132     return !hasFlags && mOnLinkPrefixes.IsEmpty() && mRoutePrefixes.IsEmpty();
2133 }
2134 
IsPeerBr(void) const2135 bool RoutingManager::RxRaTracker::Router::IsPeerBr(void) const
2136 {
2137     // Determines whether the router is a peer BR (connected to the
2138     // same Thread mesh network). It must have at least one entry
2139     // (on-link or route) and all entries should be marked to be
2140     // disregarded. While this model is generally effective to detect
2141     // peer BRs, it may not be 100% accurate in all scenarios.
2142 
2143     return mAllEntriesDisregarded && !(mOnLinkPrefixes.IsEmpty() && mRoutePrefixes.IsEmpty());
2144 }
2145 
CopyInfoTo(RouterEntry & aEntry,TimeMilli aNow,uint32_t aUptime) const2146 void RoutingManager::RxRaTracker::Router::CopyInfoTo(RouterEntry &aEntry, TimeMilli aNow, uint32_t aUptime) const
2147 {
2148     aEntry.mAddress                  = mAddress;
2149     aEntry.mMsecSinceLastUpdate      = aNow - mLastUpdateTime;
2150     aEntry.mAge                      = aUptime - mDiscoverTime;
2151     aEntry.mManagedAddressConfigFlag = mManagedAddressConfigFlag;
2152     aEntry.mOtherConfigFlag          = mOtherConfigFlag;
2153     aEntry.mStubRouterFlag           = mStubRouterFlag;
2154     aEntry.mIsLocalDevice            = mIsLocalDevice;
2155     aEntry.mIsReachable              = IsReachable();
2156     aEntry.mIsPeerBr                 = IsPeerBr();
2157 }
2158 
2159 //---------------------------------------------------------------------------------------------------------------------
2160 // RxRaTracker::DecisionFactors
2161 
UpdateFlagsFrom(const Router & aRouter)2162 void RoutingManager::RxRaTracker::DecisionFactors::UpdateFlagsFrom(const Router &aRouter)
2163 {
2164     // Determine the `M` and `O` flags to include in the RA message
2165     // header to be emitted.
2166     //
2167     // If any discovered router on infrastructure which is not itself a
2168     // stub router (e.g., another Thread BR) includes the `M` or `O`
2169     // flag, we also include the same flag.
2170 
2171     VerifyOrExit(!aRouter.mStubRouterFlag);
2172     VerifyOrExit(aRouter.IsReachable());
2173 
2174     if (aRouter.mManagedAddressConfigFlag)
2175     {
2176         mHeaderManagedAddressConfigFlag = true;
2177     }
2178 
2179     if (aRouter.mOtherConfigFlag)
2180     {
2181         mHeaderOtherConfigFlag = true;
2182     }
2183 
2184 exit:
2185     return;
2186 }
2187 
UpdateFrom(const OnLinkPrefix & aOnLinkPrefix)2188 void RoutingManager::RxRaTracker::DecisionFactors::UpdateFrom(const OnLinkPrefix &aOnLinkPrefix)
2189 {
2190     VerifyOrExit(!aOnLinkPrefix.ShouldDisregard());
2191 
2192     if (aOnLinkPrefix.GetPrefix().IsUniqueLocal())
2193     {
2194         mHasUlaOnLink = true;
2195     }
2196     else
2197     {
2198         mHasNonUlaOnLink = true;
2199     }
2200 
2201     if (aOnLinkPrefix.IsFavoredOver(mFavoredOnLinkPrefix))
2202     {
2203         mFavoredOnLinkPrefix = aOnLinkPrefix.GetPrefix();
2204     }
2205 
2206 exit:
2207     return;
2208 }
2209 
UpdateFrom(const RoutePrefix & aRoutePrefix)2210 void RoutingManager::RxRaTracker::DecisionFactors::UpdateFrom(const RoutePrefix &aRoutePrefix)
2211 {
2212     VerifyOrExit(!aRoutePrefix.ShouldDisregard());
2213 
2214     if (!mHasNonUlaRoute)
2215     {
2216         mHasNonUlaRoute = !aRoutePrefix.GetPrefix().IsUniqueLocal();
2217     }
2218 
2219 exit:
2220     return;
2221 }
2222 
2223 //---------------------------------------------------------------------------------------------------------------------
2224 // FavoredOmrPrefix
2225 
IsInfrastructureDerived(void) const2226 bool RoutingManager::FavoredOmrPrefix::IsInfrastructureDerived(void) const
2227 {
2228     // Indicate whether the OMR prefix is infrastructure-derived which
2229     // can be identified as a valid OMR prefix with preference of
2230     // medium or higher.
2231 
2232     return !IsEmpty() && (mPreference >= NetworkData::kRoutePreferenceMedium);
2233 }
2234 
SetFrom(const NetworkData::OnMeshPrefixConfig & aOnMeshPrefixConfig)2235 void RoutingManager::FavoredOmrPrefix::SetFrom(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig)
2236 {
2237     mPrefix         = aOnMeshPrefixConfig.GetPrefix();
2238     mPreference     = aOnMeshPrefixConfig.GetPreference();
2239     mIsDomainPrefix = aOnMeshPrefixConfig.mDp;
2240 }
2241 
SetFrom(const OmrPrefix & aOmrPrefix)2242 void RoutingManager::FavoredOmrPrefix::SetFrom(const OmrPrefix &aOmrPrefix)
2243 {
2244     mPrefix         = aOmrPrefix.GetPrefix();
2245     mPreference     = aOmrPrefix.GetPreference();
2246     mIsDomainPrefix = aOmrPrefix.IsDomainPrefix();
2247 }
2248 
IsFavoredOver(const NetworkData::OnMeshPrefixConfig & aOmrPrefixConfig) const2249 bool RoutingManager::FavoredOmrPrefix::IsFavoredOver(const NetworkData::OnMeshPrefixConfig &aOmrPrefixConfig) const
2250 {
2251     // This method determines whether this OMR prefix is favored
2252     // over another prefix. A prefix with higher preference is
2253     // favored. If the preference is the same, then the smaller
2254     // prefix (in the sense defined by `Ip6::Prefix`) is favored.
2255 
2256     bool isFavored = (mPreference > aOmrPrefixConfig.GetPreference());
2257 
2258     OT_ASSERT(IsValidOmrPrefix(aOmrPrefixConfig));
2259 
2260     if (mPreference == aOmrPrefixConfig.GetPreference())
2261     {
2262         isFavored = (mPrefix < aOmrPrefixConfig.GetPrefix());
2263     }
2264 
2265     return isFavored;
2266 }
2267 
2268 //---------------------------------------------------------------------------------------------------------------------
2269 // OmrPrefixManager
2270 
OmrPrefixManager(Instance & aInstance)2271 RoutingManager::OmrPrefixManager::OmrPrefixManager(Instance &aInstance)
2272     : InstanceLocator(aInstance)
2273     , mIsLocalAddedInNetData(false)
2274     , mDefaultRoute(false)
2275 {
2276 }
2277 
Init(const Ip6::Prefix & aBrUlaPrefix)2278 void RoutingManager::OmrPrefixManager::Init(const Ip6::Prefix &aBrUlaPrefix)
2279 {
2280     mGeneratedPrefix = aBrUlaPrefix;
2281     mGeneratedPrefix.SetSubnetId(kOmrPrefixSubnetId);
2282     mGeneratedPrefix.SetLength(kOmrPrefixLength);
2283 
2284     LogInfo("Generated local OMR prefix: %s", mGeneratedPrefix.ToString().AsCString());
2285 }
2286 
Start(void)2287 void RoutingManager::OmrPrefixManager::Start(void) { DetermineFavoredPrefix(); }
2288 
Stop(void)2289 void RoutingManager::OmrPrefixManager::Stop(void)
2290 {
2291     RemoveLocalFromNetData();
2292     mFavoredPrefix.Clear();
2293 }
2294 
DetermineFavoredPrefix(void)2295 void RoutingManager::OmrPrefixManager::DetermineFavoredPrefix(void)
2296 {
2297     // Determine the favored OMR prefix present in Network Data.
2298 
2299     NetworkData::Iterator           iterator = NetworkData::kIteratorInit;
2300     NetworkData::OnMeshPrefixConfig prefixConfig;
2301 
2302     mFavoredPrefix.Clear();
2303 
2304     while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
2305     {
2306         if (!IsValidOmrPrefix(prefixConfig) || !prefixConfig.mPreferred)
2307         {
2308             continue;
2309         }
2310 
2311         if (mFavoredPrefix.IsEmpty() || !mFavoredPrefix.IsFavoredOver(prefixConfig))
2312         {
2313             mFavoredPrefix.SetFrom(prefixConfig);
2314         }
2315     }
2316 }
2317 
Evaluate(void)2318 void RoutingManager::OmrPrefixManager::Evaluate(void)
2319 {
2320     OT_ASSERT(Get<RoutingManager>().IsRunning());
2321 
2322     DetermineFavoredPrefix();
2323 
2324     // Determine the local prefix and remove outdated prefix published by us.
2325 #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
2326     if (Get<RoutingManager>().mPdPrefixManager.HasPrefix())
2327     {
2328         if (mLocalPrefix.GetPrefix() != Get<RoutingManager>().mPdPrefixManager.GetPrefix())
2329         {
2330             RemoveLocalFromNetData();
2331             mLocalPrefix.mPrefix         = Get<RoutingManager>().mPdPrefixManager.GetPrefix();
2332             mLocalPrefix.mPreference     = RoutePreference::kRoutePreferenceMedium;
2333             mLocalPrefix.mIsDomainPrefix = false;
2334             LogInfo("Setting local OMR prefix to PD prefix: %s", mLocalPrefix.GetPrefix().ToString().AsCString());
2335         }
2336     }
2337     else
2338 #endif
2339         if (mLocalPrefix.GetPrefix() != mGeneratedPrefix)
2340     {
2341         RemoveLocalFromNetData();
2342         mLocalPrefix.mPrefix         = mGeneratedPrefix;
2343         mLocalPrefix.mPreference     = RoutePreference::kRoutePreferenceLow;
2344         mLocalPrefix.mIsDomainPrefix = false;
2345         LogInfo("Setting local OMR prefix to generated prefix: %s", mLocalPrefix.GetPrefix().ToString().AsCString());
2346     }
2347 
2348     // Decide if we need to add or remove our local OMR prefix.
2349     if (mFavoredPrefix.IsEmpty() || mFavoredPrefix.GetPreference() < mLocalPrefix.GetPreference())
2350     {
2351         if (mFavoredPrefix.IsEmpty())
2352         {
2353             LogInfo("No favored OMR prefix found in Thread network.");
2354         }
2355         else
2356         {
2357             LogInfo("Replacing favored OMR prefix %s with higher preference local prefix %s.",
2358                     mFavoredPrefix.GetPrefix().ToString().AsCString(), mLocalPrefix.GetPrefix().ToString().AsCString());
2359         }
2360 
2361         // The `mFavoredPrefix` remains empty if we fail to publish
2362         // the local OMR prefix.
2363         SuccessOrExit(AddLocalToNetData());
2364 
2365         mFavoredPrefix.SetFrom(mLocalPrefix);
2366     }
2367     else if (mFavoredPrefix.GetPrefix() == mLocalPrefix.GetPrefix())
2368     {
2369         IgnoreError(AddLocalToNetData());
2370     }
2371     else if (mIsLocalAddedInNetData)
2372     {
2373         LogInfo("There is already a favored OMR prefix %s in the Thread network",
2374                 mFavoredPrefix.GetPrefix().ToString().AsCString());
2375 
2376         RemoveLocalFromNetData();
2377     }
2378 
2379 exit:
2380     return;
2381 }
2382 
ShouldAdvertiseLocalAsRio(void) const2383 bool RoutingManager::OmrPrefixManager::ShouldAdvertiseLocalAsRio(void) const
2384 {
2385     // Determines whether the local OMR prefix should be advertised as
2386     // RIO in emitted RAs. To advertise, we must have decided to
2387     // publish it, and it must already be added and present in the
2388     // Network Data. This ensures that we only advertise the local
2389     // OMR prefix in emitted RAs when, as a Border Router, we can
2390     // accept and route messages using an OMR-based address
2391     // destination, which requires the prefix to be present in
2392     // Network Data. Similarly, we stop advertising (and start
2393     // deprecating) the OMR prefix in RAs as soon as we decide to
2394     // remove it. After requesting its removal from Network Data, it
2395     // may still be present in Network Data for a short interval due
2396     // to delays in registering changes with the leader.
2397 
2398     return mIsLocalAddedInNetData && Get<NetworkData::Leader>().ContainsOmrPrefix(mLocalPrefix.GetPrefix());
2399 }
2400 
AddLocalToNetData(void)2401 Error RoutingManager::OmrPrefixManager::AddLocalToNetData(void)
2402 {
2403     Error error = kErrorNone;
2404 
2405     VerifyOrExit(!mIsLocalAddedInNetData);
2406     SuccessOrExit(error = AddOrUpdateLocalInNetData());
2407     mIsLocalAddedInNetData = true;
2408 
2409 exit:
2410     return error;
2411 }
2412 
AddOrUpdateLocalInNetData(void)2413 Error RoutingManager::OmrPrefixManager::AddOrUpdateLocalInNetData(void)
2414 {
2415     // Add the local OMR prefix in Thread Network Data or update it
2416     // (e.g., change default route flag) if it is already added.
2417 
2418     Error                           error;
2419     NetworkData::OnMeshPrefixConfig config;
2420 
2421     config.Clear();
2422     config.mPrefix       = mLocalPrefix.GetPrefix();
2423     config.mStable       = true;
2424     config.mSlaac        = true;
2425     config.mPreferred    = true;
2426     config.mOnMesh       = true;
2427     config.mDefaultRoute = mDefaultRoute;
2428     config.mPreference   = mLocalPrefix.GetPreference();
2429 
2430     error = Get<NetworkData::Local>().AddOnMeshPrefix(config);
2431 
2432     if (error != kErrorNone)
2433     {
2434         LogWarn("Failed to %s %s in Thread Network Data: %s", !mIsLocalAddedInNetData ? "add" : "update",
2435                 LocalToString().AsCString(), ErrorToString(error));
2436         ExitNow();
2437     }
2438 
2439     Get<NetworkData::Notifier>().HandleServerDataUpdated();
2440 
2441     LogInfo("%s %s in Thread Network Data", !mIsLocalAddedInNetData ? "Added" : "Updated", LocalToString().AsCString());
2442 
2443 exit:
2444     return error;
2445 }
2446 
RemoveLocalFromNetData(void)2447 void RoutingManager::OmrPrefixManager::RemoveLocalFromNetData(void)
2448 {
2449     Error error = kErrorNone;
2450 
2451     VerifyOrExit(mIsLocalAddedInNetData);
2452 
2453     error = Get<NetworkData::Local>().RemoveOnMeshPrefix(mLocalPrefix.GetPrefix());
2454 
2455     if (error != kErrorNone)
2456     {
2457         LogWarn("Failed to remove %s from Thread Network Data: %s", LocalToString().AsCString(), ErrorToString(error));
2458         ExitNow();
2459     }
2460 
2461     mIsLocalAddedInNetData = false;
2462     Get<NetworkData::Notifier>().HandleServerDataUpdated();
2463     LogInfo("Removed %s from Thread Network Data", LocalToString().AsCString());
2464 
2465 exit:
2466     return;
2467 }
2468 
UpdateDefaultRouteFlag(bool aDefaultRoute)2469 void RoutingManager::OmrPrefixManager::UpdateDefaultRouteFlag(bool aDefaultRoute)
2470 {
2471     VerifyOrExit(aDefaultRoute != mDefaultRoute);
2472 
2473     mDefaultRoute = aDefaultRoute;
2474 
2475     VerifyOrExit(mIsLocalAddedInNetData);
2476     IgnoreError(AddOrUpdateLocalInNetData());
2477 
2478 exit:
2479     return;
2480 }
2481 
LocalToString(void) const2482 RoutingManager::OmrPrefixManager::InfoString RoutingManager::OmrPrefixManager::LocalToString(void) const
2483 {
2484     InfoString string;
2485 
2486     string.Append("local OMR prefix %s (def-route:%s)", mLocalPrefix.GetPrefix().ToString().AsCString(),
2487                   ToYesNo(mDefaultRoute));
2488     return string;
2489 }
2490 
2491 //---------------------------------------------------------------------------------------------------------------------
2492 // OnLinkPrefixManager
2493 
OnLinkPrefixManager(Instance & aInstance)2494 RoutingManager::OnLinkPrefixManager::OnLinkPrefixManager(Instance &aInstance)
2495     : InstanceLocator(aInstance)
2496     , mState(kIdle)
2497     , mTimer(aInstance)
2498 {
2499     mLocalPrefix.Clear();
2500     mFavoredDiscoveredPrefix.Clear();
2501     mOldLocalPrefixes.Clear();
2502 }
2503 
SetState(State aState)2504 void RoutingManager::OnLinkPrefixManager::SetState(State aState)
2505 {
2506     VerifyOrExit(mState != aState);
2507 
2508     LogInfo("Local on-link prefix state: %s -> %s (%s)", StateToString(mState), StateToString(aState),
2509             mLocalPrefix.ToString().AsCString());
2510     mState = aState;
2511 
2512     // Mark the Advertising PIO (AP) flag in the published route, when
2513     // the local on-link prefix is being published, advertised, or
2514     // deprecated.
2515 
2516     Get<RoutingManager>().mRoutePublisher.UpdateAdvPioFlags(aState != kIdle);
2517 
2518 exit:
2519     return;
2520 }
2521 
Init(void)2522 void RoutingManager::OnLinkPrefixManager::Init(void)
2523 {
2524     TimeMilli                now = TimerMilli::GetNow();
2525     Settings::BrOnLinkPrefix savedPrefix;
2526     bool                     refreshStoredPrefixes = false;
2527 
2528     // Restore old prefixes from `Settings`
2529 
2530     for (int index = 0; Get<Settings>().ReadBrOnLinkPrefix(index, savedPrefix) == kErrorNone; index++)
2531     {
2532         uint32_t   lifetime;
2533         OldPrefix *entry;
2534 
2535         if (mOldLocalPrefixes.ContainsMatching(savedPrefix.GetPrefix()))
2536         {
2537             // We should not see duplicate entries in `Settings`
2538             // but if we do we refresh the stored prefixes to make
2539             // it consistent.
2540             refreshStoredPrefixes = true;
2541             continue;
2542         }
2543 
2544         entry = mOldLocalPrefixes.PushBack();
2545 
2546         if (entry == nullptr)
2547         {
2548             // If there are more stored prefixes, we refresh the
2549             // prefixes in `Settings` to remove the ones we cannot
2550             // handle.
2551 
2552             refreshStoredPrefixes = true;
2553             break;
2554         }
2555 
2556         lifetime = Min(savedPrefix.GetLifetime(), Time::MsecToSec(TimerMilli::kMaxDelay));
2557 
2558         entry->mPrefix     = savedPrefix.GetPrefix();
2559         entry->mExpireTime = now + Time::SecToMsec(lifetime);
2560 
2561         LogInfo("Restored old prefix %s, lifetime:%lu", entry->mPrefix.ToString().AsCString(), ToUlong(lifetime));
2562 
2563         mTimer.FireAtIfEarlier(entry->mExpireTime);
2564     }
2565 
2566     if (refreshStoredPrefixes)
2567     {
2568         // We clear the entries in `Settings` and re-write the entries
2569         // from `mOldLocalPrefixes` array.
2570 
2571         IgnoreError(Get<Settings>().DeleteAllBrOnLinkPrefixes());
2572 
2573         for (OldPrefix &oldPrefix : mOldLocalPrefixes)
2574         {
2575             SavePrefix(oldPrefix.mPrefix, oldPrefix.mExpireTime);
2576         }
2577     }
2578 
2579     GenerateLocalPrefix();
2580 }
2581 
GenerateLocalPrefix(void)2582 void RoutingManager::OnLinkPrefixManager::GenerateLocalPrefix(void)
2583 {
2584     const MeshCoP::ExtendedPanId &extPanId = Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId();
2585     OldPrefix                    *entry;
2586     Ip6::Prefix                   oldLocalPrefix = mLocalPrefix;
2587 
2588     // Global ID: 40 most significant bits of Extended PAN ID
2589     // Subnet ID: 16 least significant bits of Extended PAN ID
2590 
2591     mLocalPrefix.mPrefix.mFields.m8[0] = 0xfd;
2592     memcpy(mLocalPrefix.mPrefix.mFields.m8 + 1, extPanId.m8, 5);
2593     memcpy(mLocalPrefix.mPrefix.mFields.m8 + 6, extPanId.m8 + 6, 2);
2594 
2595     mLocalPrefix.SetLength(kOnLinkPrefixLength);
2596 
2597     // We ensure that the local prefix did change, since not all the
2598     // bytes in Extended PAN ID are used in derivation of the local prefix.
2599 
2600     VerifyOrExit(mLocalPrefix != oldLocalPrefix);
2601 
2602     LogNote("Local on-link prefix: %s", mLocalPrefix.ToString().AsCString());
2603 
2604     // Check if the new local prefix happens to be in `mOldLocalPrefixes` array.
2605     // If so, we remove it from the array and update the state accordingly.
2606 
2607     entry = mOldLocalPrefixes.FindMatching(mLocalPrefix);
2608 
2609     if (entry != nullptr)
2610     {
2611         SetState(kDeprecating);
2612         mExpireTime = entry->mExpireTime;
2613         mOldLocalPrefixes.Remove(*entry);
2614     }
2615     else
2616     {
2617         SetState(kIdle);
2618     }
2619 
2620 exit:
2621     return;
2622 }
2623 
Start(void)2624 void RoutingManager::OnLinkPrefixManager::Start(void) {}
2625 
Stop(void)2626 void RoutingManager::OnLinkPrefixManager::Stop(void)
2627 {
2628     mFavoredDiscoveredPrefix.Clear();
2629 
2630     switch (GetState())
2631     {
2632     case kIdle:
2633         break;
2634 
2635     case kPublishing:
2636     case kAdvertising:
2637     case kDeprecating:
2638         SetState(kDeprecating);
2639         break;
2640     }
2641 }
2642 
Evaluate(void)2643 void RoutingManager::OnLinkPrefixManager::Evaluate(void)
2644 {
2645     VerifyOrExit(!Get<RoutingManager>().mRsSender.IsInProgress());
2646 
2647     mFavoredDiscoveredPrefix = Get<RoutingManager>().mRxRaTracker.GetFavoredOnLinkPrefix();
2648 
2649     if ((mFavoredDiscoveredPrefix.GetLength() == 0) || (mFavoredDiscoveredPrefix == mLocalPrefix))
2650     {
2651         // We advertise the local on-link prefix if no other prefix is
2652         // discovered, or if the favored discovered prefix is the
2653         // same as the local prefix (for redundancy). Note that the
2654         // local on-link prefix, derived from the extended PAN ID, is
2655         // identical for all BRs on the same Thread mesh.
2656 
2657         PublishAndAdvertise();
2658 
2659         mFavoredDiscoveredPrefix.Clear();
2660     }
2661     else if (IsPublishingOrAdvertising())
2662     {
2663         // When an application-specific on-link prefix is received and
2664         // it is larger than the local prefix, we will not remove the
2665         // advertised local prefix. In this case, there will be two
2666         // on-link prefixes on the infra link. But all BRs will still
2667         // converge to the same smallest/favored on-link prefix and the
2668         // application-specific prefix is not used.
2669 
2670         if (!(mLocalPrefix < mFavoredDiscoveredPrefix))
2671         {
2672             LogInfo("Found a favored on-link prefix %s", mFavoredDiscoveredPrefix.ToString().AsCString());
2673             Deprecate();
2674         }
2675     }
2676 
2677 exit:
2678     return;
2679 }
2680 
IsInitalEvaluationDone(void) const2681 bool RoutingManager::OnLinkPrefixManager::IsInitalEvaluationDone(void) const
2682 {
2683     // This method indicates whether or not we are done with the
2684     // initial policy evaluation of the on-link prefixes, i.e., either
2685     // we have discovered a favored on-link prefix (being advertised by
2686     // another router on infra link) or we are advertising our local
2687     // on-link prefix.
2688 
2689     return (mFavoredDiscoveredPrefix.GetLength() != 0 || IsPublishingOrAdvertising());
2690 }
2691 
HandleRaPrefixTableChanged(void)2692 void RoutingManager::OnLinkPrefixManager::HandleRaPrefixTableChanged(void)
2693 {
2694     // This is a callback from `mRxRaTracker` indicating that
2695     // there has been a change in the table. If the favored on-link
2696     // prefix has changed, we trigger a re-evaluation of the routing
2697     // policy.
2698 
2699     if (Get<RoutingManager>().mRxRaTracker.GetFavoredOnLinkPrefix() != mFavoredDiscoveredPrefix)
2700     {
2701         Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
2702     }
2703 }
2704 
PublishAndAdvertise(void)2705 void RoutingManager::OnLinkPrefixManager::PublishAndAdvertise(void)
2706 {
2707     // Start publishing and advertising the local on-link prefix if
2708     // not already.
2709 
2710     switch (GetState())
2711     {
2712     case kIdle:
2713     case kDeprecating:
2714         break;
2715 
2716     case kPublishing:
2717     case kAdvertising:
2718         ExitNow();
2719     }
2720 
2721     SetState(kPublishing);
2722     ResetExpireTime(TimerMilli::GetNow());
2723 
2724     // We wait for the ULA `fc00::/7` route or a sub-prefix of it (e.g.,
2725     // default route) to be added in Network Data before
2726     // starting to advertise the local on-link prefix in RAs.
2727     // However, if it is already present in Network Data (e.g.,
2728     // added by another BR on the same Thread mesh), we can
2729     // immediately start advertising it.
2730 
2731     if (Get<RoutingManager>().NetworkDataContainsUlaRoute())
2732     {
2733         SetState(kAdvertising);
2734     }
2735 
2736 exit:
2737     return;
2738 }
2739 
Deprecate(void)2740 void RoutingManager::OnLinkPrefixManager::Deprecate(void)
2741 {
2742     // Deprecate the local on-link prefix if it was being advertised
2743     // before. While depreciating the prefix, we wait for the lifetime
2744     // timer to expire before unpublishing the prefix from the Network
2745     // Data. We also continue to include it as a PIO in the RA message
2746     // with zero preferred lifetime and the remaining valid lifetime
2747     // until the timer expires.
2748 
2749     switch (GetState())
2750     {
2751     case kPublishing:
2752     case kAdvertising:
2753         SetState(kDeprecating);
2754         break;
2755 
2756     case kIdle:
2757     case kDeprecating:
2758         break;
2759     }
2760 }
2761 
ShouldPublishUlaRoute(void) const2762 bool RoutingManager::OnLinkPrefixManager::ShouldPublishUlaRoute(void) const
2763 {
2764     // Determine whether or not we should publish ULA prefix. We need
2765     // to publish if we are in any of `kPublishing`, `kAdvertising`,
2766     // or `kDeprecating` states, or if there is at least one old local
2767     // prefix being deprecated.
2768 
2769     return (GetState() != kIdle) || !mOldLocalPrefixes.IsEmpty();
2770 }
2771 
ResetExpireTime(TimeMilli aNow)2772 void RoutingManager::OnLinkPrefixManager::ResetExpireTime(TimeMilli aNow)
2773 {
2774     mExpireTime = aNow + TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime);
2775     mTimer.FireAtIfEarlier(mExpireTime);
2776     SavePrefix(mLocalPrefix, mExpireTime);
2777 }
2778 
IsPublishingOrAdvertising(void) const2779 bool RoutingManager::OnLinkPrefixManager::IsPublishingOrAdvertising(void) const
2780 {
2781     return (GetState() == kPublishing) || (GetState() == kAdvertising);
2782 }
2783 
AppendAsPiosTo(RouterAdvert::TxMessage & aRaMessage)2784 Error RoutingManager::OnLinkPrefixManager::AppendAsPiosTo(RouterAdvert::TxMessage &aRaMessage)
2785 {
2786     Error error;
2787 
2788     SuccessOrExit(error = AppendCurPrefix(aRaMessage));
2789     error = AppendOldPrefixes(aRaMessage);
2790 
2791 exit:
2792     return error;
2793 }
2794 
AppendCurPrefix(RouterAdvert::TxMessage & aRaMessage)2795 Error RoutingManager::OnLinkPrefixManager::AppendCurPrefix(RouterAdvert::TxMessage &aRaMessage)
2796 {
2797     // Append the local on-link prefix to the `aRaMessage` as a PIO
2798     // only if it is being advertised or deprecated.
2799     //
2800     // If in `kAdvertising` state, we reset the expire time.
2801     // If in `kDeprecating` state, we include it as PIO with zero
2802     // preferred lifetime and the remaining valid lifetime.
2803 
2804     Error     error             = kErrorNone;
2805     uint32_t  validLifetime     = kDefaultOnLinkPrefixLifetime;
2806     uint32_t  preferredLifetime = kDefaultOnLinkPrefixLifetime;
2807     TimeMilli now               = TimerMilli::GetNow();
2808 
2809     switch (GetState())
2810     {
2811     case kAdvertising:
2812         ResetExpireTime(now);
2813         break;
2814 
2815     case kDeprecating:
2816         VerifyOrExit(mExpireTime > now);
2817         validLifetime     = TimeMilli::MsecToSec(mExpireTime - now);
2818         preferredLifetime = 0;
2819         break;
2820 
2821     case kIdle:
2822     case kPublishing:
2823         ExitNow();
2824     }
2825 
2826     SuccessOrExit(error = aRaMessage.AppendPrefixInfoOption(mLocalPrefix, validLifetime, preferredLifetime));
2827 
2828     LogPrefixInfoOption(mLocalPrefix, validLifetime, preferredLifetime);
2829 
2830 exit:
2831     return error;
2832 }
2833 
AppendOldPrefixes(RouterAdvert::TxMessage & aRaMessage)2834 Error RoutingManager::OnLinkPrefixManager::AppendOldPrefixes(RouterAdvert::TxMessage &aRaMessage)
2835 {
2836     Error     error = kErrorNone;
2837     TimeMilli now   = TimerMilli::GetNow();
2838     uint32_t  validLifetime;
2839 
2840     for (const OldPrefix &oldPrefix : mOldLocalPrefixes)
2841     {
2842         if (oldPrefix.mExpireTime < now)
2843         {
2844             continue;
2845         }
2846 
2847         validLifetime = TimeMilli::MsecToSec(oldPrefix.mExpireTime - now);
2848         SuccessOrExit(error = aRaMessage.AppendPrefixInfoOption(oldPrefix.mPrefix, validLifetime, 0));
2849 
2850         LogPrefixInfoOption(oldPrefix.mPrefix, validLifetime, 0);
2851     }
2852 
2853 exit:
2854     return error;
2855 }
2856 
HandleNetDataChange(void)2857 void RoutingManager::OnLinkPrefixManager::HandleNetDataChange(void)
2858 {
2859     VerifyOrExit(GetState() == kPublishing);
2860 
2861     if (Get<RoutingManager>().NetworkDataContainsUlaRoute())
2862     {
2863         SetState(kAdvertising);
2864         Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
2865     }
2866 
2867 exit:
2868     return;
2869 }
2870 
HandleExtPanIdChange(void)2871 void RoutingManager::OnLinkPrefixManager::HandleExtPanIdChange(void)
2872 {
2873     // If the current local prefix is being advertised or deprecated,
2874     // we save it in `mOldLocalPrefixes` and keep deprecating it. It will
2875     // be included in emitted RAs as PIO with zero preferred lifetime.
2876     // It will still be present in Network Data until its expire time
2877     // so to allow Thread nodes to continue to communicate with `InfraIf`
2878     // device using addresses based on this prefix.
2879 
2880     uint16_t    oldState  = GetState();
2881     Ip6::Prefix oldPrefix = mLocalPrefix;
2882 
2883     GenerateLocalPrefix();
2884 
2885     VerifyOrExit(oldPrefix != mLocalPrefix);
2886 
2887     switch (oldState)
2888     {
2889     case kIdle:
2890     case kPublishing:
2891         break;
2892 
2893     case kAdvertising:
2894     case kDeprecating:
2895         DeprecateOldPrefix(oldPrefix, mExpireTime);
2896         break;
2897     }
2898 
2899     Get<RoutingManager>().HandleLocalOnLinkPrefixChanged();
2900 
2901 exit:
2902     return;
2903 }
2904 
DeprecateOldPrefix(const Ip6::Prefix & aPrefix,TimeMilli aExpireTime)2905 void RoutingManager::OnLinkPrefixManager::DeprecateOldPrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime)
2906 {
2907     OldPrefix  *entry = nullptr;
2908     Ip6::Prefix removedPrefix;
2909 
2910     removedPrefix.Clear();
2911 
2912     VerifyOrExit(!mOldLocalPrefixes.ContainsMatching(aPrefix));
2913 
2914     LogInfo("Deprecating old on-link prefix %s", aPrefix.ToString().AsCString());
2915 
2916     if (!mOldLocalPrefixes.IsFull())
2917     {
2918         entry = mOldLocalPrefixes.PushBack();
2919     }
2920     else
2921     {
2922         // If there is no more room in `mOldLocalPrefixes` array
2923         // we evict the entry with the earliest expiration time.
2924 
2925         entry = &mOldLocalPrefixes[0];
2926 
2927         for (OldPrefix &oldPrefix : mOldLocalPrefixes)
2928         {
2929             if ((oldPrefix.mExpireTime < entry->mExpireTime))
2930             {
2931                 entry = &oldPrefix;
2932             }
2933         }
2934 
2935         removedPrefix = entry->mPrefix;
2936 
2937         IgnoreError(Get<Settings>().RemoveBrOnLinkPrefix(removedPrefix));
2938     }
2939 
2940     entry->mPrefix     = aPrefix;
2941     entry->mExpireTime = aExpireTime;
2942     mTimer.FireAtIfEarlier(aExpireTime);
2943 
2944     SavePrefix(aPrefix, aExpireTime);
2945 
2946 exit:
2947     return;
2948 }
2949 
SavePrefix(const Ip6::Prefix & aPrefix,TimeMilli aExpireTime)2950 void RoutingManager::OnLinkPrefixManager::SavePrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime)
2951 {
2952     Settings::BrOnLinkPrefix savedPrefix;
2953 
2954     savedPrefix.SetPrefix(aPrefix);
2955     savedPrefix.SetLifetime(TimeMilli::MsecToSec(aExpireTime - TimerMilli::GetNow()));
2956     IgnoreError(Get<Settings>().AddOrUpdateBrOnLinkPrefix(savedPrefix));
2957 }
2958 
HandleTimer(void)2959 void RoutingManager::OnLinkPrefixManager::HandleTimer(void)
2960 {
2961     NextFireTime nextExpireTime;
2962 
2963     Array<Ip6::Prefix, kMaxOldPrefixes> expiredPrefixes;
2964 
2965     switch (GetState())
2966     {
2967     case kIdle:
2968         break;
2969     case kPublishing:
2970     case kAdvertising:
2971     case kDeprecating:
2972         if (nextExpireTime.GetNow() >= mExpireTime)
2973         {
2974             IgnoreError(Get<Settings>().RemoveBrOnLinkPrefix(mLocalPrefix));
2975             SetState(kIdle);
2976         }
2977         else
2978         {
2979             nextExpireTime.UpdateIfEarlier(mExpireTime);
2980         }
2981         break;
2982     }
2983 
2984     for (OldPrefix &entry : mOldLocalPrefixes)
2985     {
2986         if (nextExpireTime.GetNow() >= entry.mExpireTime)
2987         {
2988             SuccessOrAssert(expiredPrefixes.PushBack(entry.mPrefix));
2989         }
2990         else
2991         {
2992             nextExpireTime.UpdateIfEarlier(entry.mExpireTime);
2993         }
2994     }
2995 
2996     for (const Ip6::Prefix &prefix : expiredPrefixes)
2997     {
2998         LogInfo("Old local on-link prefix %s expired", prefix.ToString().AsCString());
2999         IgnoreError(Get<Settings>().RemoveBrOnLinkPrefix(prefix));
3000         mOldLocalPrefixes.RemoveMatching(prefix);
3001     }
3002 
3003     mTimer.FireAtIfEarlier(nextExpireTime);
3004 
3005     Get<RoutingManager>().mRoutePublisher.Evaluate();
3006 }
3007 
StateToString(State aState)3008 const char *RoutingManager::OnLinkPrefixManager::StateToString(State aState)
3009 {
3010     static const char *const kStateStrings[] = {
3011         "Removed",     // (0) kIdle
3012         "Publishing",  // (1) kPublishing
3013         "Advertising", // (2) kAdvertising
3014         "Deprecating", // (3) kDeprecating
3015     };
3016 
3017     static_assert(0 == kIdle, "kIdle value is incorrect");
3018     static_assert(1 == kPublishing, "kPublishing value is incorrect");
3019     static_assert(2 == kAdvertising, "kAdvertising value is incorrect");
3020     static_assert(3 == kDeprecating, "kDeprecating value is incorrect");
3021 
3022     return kStateStrings[aState];
3023 }
3024 
3025 //---------------------------------------------------------------------------------------------------------------------
3026 // RioAdvertiser
3027 
RioAdvertiser(Instance & aInstance)3028 RoutingManager::RioAdvertiser::RioAdvertiser(Instance &aInstance)
3029     : InstanceLocator(aInstance)
3030     , mTimer(aInstance)
3031     , mPreference(NetworkData::kRoutePreferenceLow)
3032     , mUserSetPreference(false)
3033 {
3034 }
3035 
SetPreference(RoutePreference aPreference)3036 void RoutingManager::RioAdvertiser::SetPreference(RoutePreference aPreference)
3037 {
3038     LogInfo("User explicitly set RIO Preference to %s", RoutePreferenceToString(aPreference));
3039     mUserSetPreference = true;
3040     UpdatePreference(aPreference);
3041 }
3042 
ClearPreference(void)3043 void RoutingManager::RioAdvertiser::ClearPreference(void)
3044 {
3045     VerifyOrExit(mUserSetPreference);
3046 
3047     LogInfo("User cleared explicitly set RIO Preference");
3048     mUserSetPreference = false;
3049     SetPreferenceBasedOnRole();
3050 
3051 exit:
3052     return;
3053 }
3054 
HandleRoleChanged(void)3055 void RoutingManager::RioAdvertiser::HandleRoleChanged(void)
3056 {
3057     if (!mUserSetPreference)
3058     {
3059         SetPreferenceBasedOnRole();
3060     }
3061 }
3062 
SetPreferenceBasedOnRole(void)3063 void RoutingManager::RioAdvertiser::SetPreferenceBasedOnRole(void)
3064 {
3065     UpdatePreference(Get<Mle::Mle>().IsRouterOrLeader() ? NetworkData::kRoutePreferenceMedium
3066                                                         : NetworkData::kRoutePreferenceLow);
3067 }
3068 
UpdatePreference(RoutePreference aPreference)3069 void RoutingManager::RioAdvertiser::UpdatePreference(RoutePreference aPreference)
3070 {
3071     VerifyOrExit(mPreference != aPreference);
3072 
3073     LogInfo("RIO Preference changed: %s -> %s", RoutePreferenceToString(mPreference),
3074             RoutePreferenceToString(aPreference));
3075     mPreference = aPreference;
3076 
3077     Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
3078 
3079 exit:
3080     return;
3081 }
3082 
InvalidatPrevRios(RouterAdvert::TxMessage & aRaMessage)3083 Error RoutingManager::RioAdvertiser::InvalidatPrevRios(RouterAdvert::TxMessage &aRaMessage)
3084 {
3085     Error error = kErrorNone;
3086 
3087     for (const RioPrefix &prefix : mPrefixes)
3088     {
3089         RoutePreference preference = prefix.mIsDeprecating ? NetworkData::kRoutePreferenceLow : mPreference;
3090 
3091         SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, preference, aRaMessage));
3092     }
3093 
3094 #if OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE
3095     mPrefixes.Free();
3096 #endif
3097 
3098     mPrefixes.Clear();
3099     mTimer.Stop();
3100 
3101 exit:
3102     return error;
3103 }
3104 
AppendRios(RouterAdvert::TxMessage & aRaMessage)3105 Error RoutingManager::RioAdvertiser::AppendRios(RouterAdvert::TxMessage &aRaMessage)
3106 {
3107     Error                           error = kErrorNone;
3108     NextFireTime                    nextTime;
3109     RioPrefixArray                  oldPrefixes;
3110     NetworkData::Iterator           iterator = NetworkData::kIteratorInit;
3111     NetworkData::OnMeshPrefixConfig prefixConfig;
3112     const OmrPrefixManager         &omrPrefixManager = Get<RoutingManager>().mOmrPrefixManager;
3113 
3114 #if OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE
3115     oldPrefixes.TakeFrom(static_cast<RioPrefixArray &&>(mPrefixes));
3116 #else
3117     oldPrefixes = mPrefixes;
3118 #endif
3119 
3120     mPrefixes.Clear();
3121 
3122     // `mPrefixes` array can have a limited size. We add more
3123     // important prefixes first in the array to ensure they are
3124     // advertised in the RA message. Note that `Add()` method
3125     // will ensure to add a prefix only once (will check if
3126     // prefix is already present in the array).
3127 
3128     // (1) Local OMR prefix.
3129 
3130     if (omrPrefixManager.ShouldAdvertiseLocalAsRio())
3131     {
3132         mPrefixes.Add(omrPrefixManager.GetLocalPrefix().GetPrefix());
3133     }
3134 
3135     // (2) Favored OMR prefix.
3136 
3137     if (!omrPrefixManager.GetFavoredPrefix().IsEmpty() && !omrPrefixManager.GetFavoredPrefix().IsDomainPrefix())
3138     {
3139         mPrefixes.Add(omrPrefixManager.GetFavoredPrefix().GetPrefix());
3140     }
3141 
3142     // (3) All other OMR prefixes.
3143 
3144     iterator = NetworkData::kIteratorInit;
3145 
3146     while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
3147     {
3148         // The decision to include the local OMR prefix as a RIO is
3149         // delegated to `OmrPrefixManager.ShouldAdvertiseLocalAsRio()`
3150         // at step (1). Here, as we iterate over Network Data prefixes,
3151         // we exclude entries matching the local OMR prefix. This is
3152         // because `OmrPrefixManager` may have decided to stop advertising
3153         // it, while it might still be present in the Network Data due to
3154         // delays in registering changes with the leader.
3155 
3156         if (prefixConfig.mDp)
3157         {
3158             continue;
3159         }
3160 
3161         if (IsValidOmrPrefix(prefixConfig) &&
3162             (prefixConfig.GetPrefix() != omrPrefixManager.GetLocalPrefix().GetPrefix()))
3163         {
3164             mPrefixes.Add(prefixConfig.GetPrefix());
3165         }
3166     }
3167 
3168     // (4) All other on-mesh prefixes (excluding Domain Prefix).
3169 
3170     iterator = NetworkData::kIteratorInit;
3171 
3172     while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
3173     {
3174         if (prefixConfig.mOnMesh && !prefixConfig.mDp && !IsValidOmrPrefix(prefixConfig))
3175         {
3176             mPrefixes.Add(prefixConfig.GetPrefix());
3177         }
3178     }
3179 
3180     // Determine deprecating prefixes
3181 
3182     for (RioPrefix &prefix : oldPrefixes)
3183     {
3184         if (mPrefixes.ContainsMatching(prefix.mPrefix))
3185         {
3186             continue;
3187         }
3188 
3189         if (prefix.mIsDeprecating)
3190         {
3191             if (nextTime.GetNow() >= prefix.mExpirationTime)
3192             {
3193                 SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0,
3194                                                 NetworkData::kRoutePreferenceLow, aRaMessage));
3195                 continue;
3196             }
3197         }
3198         else
3199         {
3200             prefix.mIsDeprecating  = true;
3201             prefix.mExpirationTime = nextTime.GetNow() + kDeprecationTime;
3202         }
3203 
3204         if (mPrefixes.PushBack(prefix) != kErrorNone)
3205         {
3206             LogWarn("Too many deprecating on-mesh prefixes, removing %s", prefix.mPrefix.ToString().AsCString());
3207             SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, NetworkData::kRoutePreferenceLow,
3208                                             aRaMessage));
3209         }
3210 
3211         nextTime.UpdateIfEarlier(prefix.mExpirationTime);
3212     }
3213 
3214     // Advertise all prefixes in `mPrefixes`
3215 
3216     for (const RioPrefix &prefix : mPrefixes)
3217     {
3218         uint32_t        lifetime   = kDefaultOmrPrefixLifetime;
3219         RoutePreference preference = mPreference;
3220 
3221         if (prefix.mIsDeprecating)
3222         {
3223             lifetime   = TimeMilli::MsecToSec(prefix.mExpirationTime - nextTime.GetNow());
3224             preference = NetworkData::kRoutePreferenceLow;
3225         }
3226 
3227         SuccessOrExit(error = AppendRio(prefix.mPrefix, lifetime, preference, aRaMessage));
3228     }
3229 
3230     mTimer.FireAtIfEarlier(nextTime);
3231 
3232 exit:
3233     return error;
3234 }
3235 
AppendRio(const Ip6::Prefix & aPrefix,uint32_t aRouteLifetime,RoutePreference aPreference,RouterAdvert::TxMessage & aRaMessage)3236 Error RoutingManager::RioAdvertiser::AppendRio(const Ip6::Prefix       &aPrefix,
3237                                                uint32_t                 aRouteLifetime,
3238                                                RoutePreference          aPreference,
3239                                                RouterAdvert::TxMessage &aRaMessage)
3240 {
3241     Error error;
3242 
3243     SuccessOrExit(error = aRaMessage.AppendRouteInfoOption(aPrefix, aRouteLifetime, aPreference));
3244     LogRouteInfoOption(aPrefix, aRouteLifetime, aPreference);
3245 
3246 exit:
3247     return error;
3248 }
3249 
HandleTimer(void)3250 void RoutingManager::RioAdvertiser::HandleTimer(void)
3251 {
3252     Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kImmediately);
3253 }
3254 
Add(const Ip6::Prefix & aPrefix)3255 void RoutingManager::RioAdvertiser::RioPrefixArray::Add(const Ip6::Prefix &aPrefix)
3256 {
3257     // Checks if `aPrefix` is already present in the array and if not
3258     // adds it as a new entry.
3259 
3260     Error     error;
3261     RioPrefix newEntry;
3262 
3263     VerifyOrExit(!ContainsMatching(aPrefix));
3264 
3265     newEntry.Clear();
3266     newEntry.mPrefix = aPrefix;
3267 
3268     error = PushBack(newEntry);
3269 
3270     if (error != kErrorNone)
3271     {
3272         LogWarn("Too many on-mesh prefixes in net data, ignoring prefix %s", aPrefix.ToString().AsCString());
3273     }
3274 
3275 exit:
3276     return;
3277 }
3278 
3279 //---------------------------------------------------------------------------------------------------------------------
3280 // RoutePublisher
3281 
3282 const otIp6Prefix RoutingManager::RoutePublisher::kUlaPrefix = {
3283     {{{0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
3284     7,
3285 };
3286 
RoutePublisher(Instance & aInstance)3287 RoutingManager::RoutePublisher::RoutePublisher(Instance &aInstance)
3288     : InstanceLocator(aInstance)
3289     , mState(kDoNotPublish)
3290     , mPreference(NetworkData::kRoutePreferenceMedium)
3291     , mUserSetPreference(false)
3292     , mAdvPioFlag(false)
3293     , mTimer(aInstance)
3294 {
3295 }
3296 
Evaluate(void)3297 void RoutingManager::RoutePublisher::Evaluate(void)
3298 {
3299     State newState = kDoNotPublish;
3300 
3301     VerifyOrExit(Get<RoutingManager>().IsRunning());
3302 
3303     if (Get<RoutingManager>().mOmrPrefixManager.GetFavoredPrefix().IsInfrastructureDerived() &&
3304         Get<RoutingManager>().mRxRaTracker.ContainsDefaultOrNonUlaRoutePrefix())
3305     {
3306         newState = kPublishDefault;
3307     }
3308     else if (Get<RoutingManager>().mRxRaTracker.ContainsNonUlaOnLinkPrefix())
3309     {
3310         newState = kPublishDefault;
3311     }
3312     else if (Get<RoutingManager>().mRxRaTracker.ContainsUlaOnLinkPrefix() ||
3313              Get<RoutingManager>().mOnLinkPrefixManager.ShouldPublishUlaRoute())
3314     {
3315         newState = kPublishUla;
3316     }
3317 
3318 exit:
3319     if (newState != mState)
3320     {
3321         LogInfo("RoutePublisher state: %s -> %s", StateToString(mState), StateToString(newState));
3322         UpdatePublishedRoute(newState);
3323         Get<RoutingManager>().mOmrPrefixManager.UpdateDefaultRouteFlag(newState == kPublishDefault);
3324     }
3325 }
3326 
DeterminePrefixFor(State aState,Ip6::Prefix & aPrefix) const3327 void RoutingManager::RoutePublisher::DeterminePrefixFor(State aState, Ip6::Prefix &aPrefix) const
3328 {
3329     aPrefix.Clear();
3330 
3331     switch (aState)
3332     {
3333     case kDoNotPublish:
3334     case kPublishDefault:
3335         // `Clear()` will set the prefix to `::/0`.
3336         break;
3337     case kPublishUla:
3338         aPrefix = GetUlaPrefix();
3339         break;
3340     }
3341 }
3342 
UpdatePublishedRoute(State aNewState)3343 void RoutingManager::RoutePublisher::UpdatePublishedRoute(State aNewState)
3344 {
3345     // Updates the published route entry in Network Data, transitioning
3346     // from current `mState` to new `aNewState`. This method can be used
3347     // when there is no change to `mState` but a change to `mPreference`
3348     // or `mAdvPioFlag`.
3349 
3350     Ip6::Prefix                      oldPrefix;
3351     NetworkData::ExternalRouteConfig routeConfig;
3352 
3353     DeterminePrefixFor(mState, oldPrefix);
3354 
3355     if (aNewState == kDoNotPublish)
3356     {
3357         VerifyOrExit(mState != kDoNotPublish);
3358         IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(oldPrefix));
3359         ExitNow();
3360     }
3361 
3362     routeConfig.Clear();
3363     routeConfig.mPreference = mPreference;
3364     routeConfig.mAdvPio     = mAdvPioFlag;
3365     routeConfig.mStable     = true;
3366     DeterminePrefixFor(aNewState, routeConfig.GetPrefix());
3367 
3368     // If we were not publishing a route prefix before, publish the new
3369     // `routeConfig`. Otherwise, use `ReplacePublishedExternalRoute()` to
3370     // replace the previously published prefix entry. This ensures that we do
3371     // not have a situation where the previous route is removed while the new
3372     // one is not yet added in the Network Data.
3373 
3374     if (mState == kDoNotPublish)
3375     {
3376         SuccessOrAssert(Get<NetworkData::Publisher>().PublishExternalRoute(
3377             routeConfig, NetworkData::Publisher::kFromRoutingManager));
3378     }
3379     else
3380     {
3381         SuccessOrAssert(Get<NetworkData::Publisher>().ReplacePublishedExternalRoute(
3382             oldPrefix, routeConfig, NetworkData::Publisher::kFromRoutingManager));
3383     }
3384 
3385 exit:
3386     mState = aNewState;
3387 }
3388 
Unpublish(void)3389 void RoutingManager::RoutePublisher::Unpublish(void)
3390 {
3391     // Unpublish the previously published route based on `mState`
3392     // and update `mState`.
3393 
3394     Ip6::Prefix prefix;
3395 
3396     VerifyOrExit(mState != kDoNotPublish);
3397     DeterminePrefixFor(mState, prefix);
3398     IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(prefix));
3399     mState = kDoNotPublish;
3400 
3401 exit:
3402     return;
3403 }
3404 
UpdateAdvPioFlags(bool aAdvPioFlag)3405 void RoutingManager::RoutePublisher::UpdateAdvPioFlags(bool aAdvPioFlag)
3406 {
3407     VerifyOrExit(mAdvPioFlag != aAdvPioFlag);
3408     mAdvPioFlag = aAdvPioFlag;
3409     UpdatePublishedRoute(mState);
3410 
3411 exit:
3412     return;
3413 }
3414 
SetPreference(RoutePreference aPreference)3415 void RoutingManager::RoutePublisher::SetPreference(RoutePreference aPreference)
3416 {
3417     LogInfo("User explicitly set published route preference to %s", RoutePreferenceToString(aPreference));
3418     mUserSetPreference = true;
3419     mTimer.Stop();
3420     UpdatePreference(aPreference);
3421 }
3422 
ClearPreference(void)3423 void RoutingManager::RoutePublisher::ClearPreference(void)
3424 {
3425     VerifyOrExit(mUserSetPreference);
3426 
3427     LogInfo("User cleared explicitly set published route preference - set based on role");
3428     mUserSetPreference = false;
3429     SetPreferenceBasedOnRole();
3430 
3431 exit:
3432     return;
3433 }
3434 
SetPreferenceBasedOnRole(void)3435 void RoutingManager::RoutePublisher::SetPreferenceBasedOnRole(void)
3436 {
3437     RoutePreference preference = NetworkData::kRoutePreferenceMedium;
3438 
3439     if (Get<Mle::Mle>().IsChild() && (Get<Mle::Mle>().GetParent().GetTwoWayLinkQuality() != kLinkQuality3))
3440     {
3441         preference = NetworkData::kRoutePreferenceLow;
3442     }
3443 
3444     UpdatePreference(preference);
3445     mTimer.Stop();
3446 }
3447 
HandleNotifierEvents(Events aEvents)3448 void RoutingManager::RoutePublisher::HandleNotifierEvents(Events aEvents)
3449 {
3450     VerifyOrExit(!mUserSetPreference);
3451 
3452     if (aEvents.Contains(kEventThreadRoleChanged))
3453     {
3454         SetPreferenceBasedOnRole();
3455     }
3456 
3457     if (aEvents.Contains(kEventParentLinkQualityChanged))
3458     {
3459         VerifyOrExit(Get<Mle::Mle>().IsChild());
3460 
3461         if (Get<Mle::Mle>().GetParent().GetTwoWayLinkQuality() == kLinkQuality3)
3462         {
3463             VerifyOrExit(!mTimer.IsRunning());
3464             mTimer.Start(kDelayBeforePrfUpdateOnLinkQuality3);
3465         }
3466         else
3467         {
3468             UpdatePreference(NetworkData::kRoutePreferenceLow);
3469             mTimer.Stop();
3470         }
3471     }
3472 
3473 exit:
3474     return;
3475 }
3476 
HandleTimer(void)3477 void RoutingManager::RoutePublisher::HandleTimer(void) { SetPreferenceBasedOnRole(); }
3478 
UpdatePreference(RoutePreference aPreference)3479 void RoutingManager::RoutePublisher::UpdatePreference(RoutePreference aPreference)
3480 {
3481     VerifyOrExit(mPreference != aPreference);
3482 
3483     LogInfo("Published route preference changed: %s -> %s", RoutePreferenceToString(mPreference),
3484             RoutePreferenceToString(aPreference));
3485     mPreference = aPreference;
3486     UpdatePublishedRoute(mState);
3487 
3488 exit:
3489     return;
3490 }
3491 
StateToString(State aState)3492 const char *RoutingManager::RoutePublisher::StateToString(State aState)
3493 {
3494     static const char *const kStateStrings[] = {
3495         "none",      // (0) kDoNotPublish
3496         "def-route", // (1) kPublishDefault
3497         "ula",       // (2) kPublishUla
3498     };
3499 
3500     static_assert(0 == kDoNotPublish, "kDoNotPublish value is incorrect");
3501     static_assert(1 == kPublishDefault, "kPublishDefault value is incorrect");
3502     static_assert(2 == kPublishUla, "kPublishUla value is incorrect");
3503 
3504     return kStateStrings[aState];
3505 }
3506 
3507 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
3508 
3509 //---------------------------------------------------------------------------------------------------------------------
3510 // Nat64PrefixManager
3511 
Nat64PrefixManager(Instance & aInstance)3512 RoutingManager::Nat64PrefixManager::Nat64PrefixManager(Instance &aInstance)
3513     : InstanceLocator(aInstance)
3514     , mEnabled(false)
3515     , mTimer(aInstance)
3516 {
3517     mInfraIfPrefix.Clear();
3518     mLocalPrefix.Clear();
3519     mPublishedPrefix.Clear();
3520 }
3521 
SetEnabled(bool aEnabled)3522 void RoutingManager::Nat64PrefixManager::SetEnabled(bool aEnabled)
3523 {
3524     VerifyOrExit(mEnabled != aEnabled);
3525     mEnabled = aEnabled;
3526 
3527     if (aEnabled)
3528     {
3529         if (Get<RoutingManager>().IsRunning())
3530         {
3531             Start();
3532         }
3533     }
3534     else
3535     {
3536         Stop();
3537     }
3538 
3539 exit:
3540     return;
3541 }
3542 
Start(void)3543 void RoutingManager::Nat64PrefixManager::Start(void)
3544 {
3545     VerifyOrExit(mEnabled);
3546     LogInfo("Starting Nat64PrefixManager");
3547     mTimer.Start(0);
3548 
3549 exit:
3550     return;
3551 }
3552 
Stop(void)3553 void RoutingManager::Nat64PrefixManager::Stop(void)
3554 {
3555     LogInfo("Stopping Nat64PrefixManager");
3556 
3557     if (mPublishedPrefix.IsValidNat64())
3558     {
3559         IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(mPublishedPrefix));
3560     }
3561 
3562     mPublishedPrefix.Clear();
3563     mInfraIfPrefix.Clear();
3564     mTimer.Stop();
3565 
3566 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
3567     Get<Nat64::Translator>().ClearNat64Prefix();
3568 #endif
3569 }
3570 
GenerateLocalPrefix(const Ip6::Prefix & aBrUlaPrefix)3571 void RoutingManager::Nat64PrefixManager::GenerateLocalPrefix(const Ip6::Prefix &aBrUlaPrefix)
3572 {
3573     mLocalPrefix = aBrUlaPrefix;
3574     mLocalPrefix.SetSubnetId(kNat64PrefixSubnetId);
3575     mLocalPrefix.mPrefix.mFields.m32[2] = 0;
3576     mLocalPrefix.SetLength(kNat64PrefixLength);
3577 
3578     LogInfo("Generated local NAT64 prefix: %s", mLocalPrefix.ToString().AsCString());
3579 }
3580 
GetFavoredPrefix(RoutePreference & aPreference) const3581 const Ip6::Prefix &RoutingManager::Nat64PrefixManager::GetFavoredPrefix(RoutePreference &aPreference) const
3582 {
3583     const Ip6::Prefix *favoredPrefix = &mLocalPrefix;
3584 
3585     aPreference = NetworkData::kRoutePreferenceLow;
3586 
3587     if (mInfraIfPrefix.IsValidNat64() &&
3588         Get<RoutingManager>().mOmrPrefixManager.GetFavoredPrefix().IsInfrastructureDerived())
3589     {
3590         favoredPrefix = &mInfraIfPrefix;
3591         aPreference   = NetworkData::kRoutePreferenceMedium;
3592     }
3593 
3594     return *favoredPrefix;
3595 }
3596 
Evaluate(void)3597 void RoutingManager::Nat64PrefixManager::Evaluate(void)
3598 {
3599     Error                            error;
3600     Ip6::Prefix                      prefix;
3601     RoutePreference                  preference;
3602     NetworkData::ExternalRouteConfig netdataPrefixConfig;
3603     bool                             shouldPublish;
3604 
3605     VerifyOrExit(mEnabled);
3606 
3607     LogInfo("Evaluating NAT64 prefix");
3608 
3609     prefix = GetFavoredPrefix(preference);
3610 
3611     error = Get<NetworkData::Leader>().GetPreferredNat64Prefix(netdataPrefixConfig);
3612 
3613     // NAT64 prefix is expected to be published from this BR
3614     // when one of the following is true:
3615     //
3616     // - No NAT64 prefix in Network Data.
3617     // - The preferred NAT64 prefix in Network Data has lower
3618     //   preference than this BR's prefix.
3619     // - The preferred NAT64 prefix in Network Data was published
3620     //   by this BR.
3621     // - The preferred NAT64 prefix in Network Data is same as the
3622     //   discovered infrastructure prefix.
3623     //
3624     // TODO: change to check RLOC16 to determine if the NAT64 prefix
3625     // was published by this BR.
3626 
3627     shouldPublish =
3628         ((error == kErrorNotFound) || (netdataPrefixConfig.mPreference < preference) ||
3629          (netdataPrefixConfig.GetPrefix() == mPublishedPrefix) || (netdataPrefixConfig.GetPrefix() == mInfraIfPrefix));
3630 
3631     if (mPublishedPrefix.IsValidNat64() && (!shouldPublish || (prefix != mPublishedPrefix)))
3632     {
3633         IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(mPublishedPrefix));
3634         mPublishedPrefix.Clear();
3635     }
3636 
3637     if (shouldPublish && ((prefix != mPublishedPrefix) || (preference != mPublishedPreference)))
3638     {
3639         mPublishedPrefix     = prefix;
3640         mPublishedPreference = preference;
3641         Publish();
3642     }
3643 
3644 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
3645 
3646     // If a prefix other than `mLocalPrefix` is present, an external
3647     // translator is available. To bypass the NAT64 translator, we
3648     // clear its NAT64 prefix.
3649 
3650     if (mPublishedPrefix == mLocalPrefix)
3651     {
3652         Get<Nat64::Translator>().SetNat64Prefix(mLocalPrefix);
3653     }
3654     else
3655     {
3656         Get<Nat64::Translator>().ClearNat64Prefix();
3657     }
3658 #endif
3659 
3660 exit:
3661     return;
3662 }
3663 
Publish(void)3664 void RoutingManager::Nat64PrefixManager::Publish(void)
3665 {
3666     NetworkData::ExternalRouteConfig routeConfig;
3667 
3668     routeConfig.Clear();
3669     routeConfig.SetPrefix(mPublishedPrefix);
3670     routeConfig.mPreference = mPublishedPreference;
3671     routeConfig.mStable     = true;
3672     routeConfig.mNat64      = true;
3673 
3674     SuccessOrAssert(
3675         Get<NetworkData::Publisher>().PublishExternalRoute(routeConfig, NetworkData::Publisher::kFromRoutingManager));
3676 }
3677 
HandleTimer(void)3678 void RoutingManager::Nat64PrefixManager::HandleTimer(void)
3679 {
3680     OT_ASSERT(mEnabled);
3681 
3682     Discover();
3683 
3684     mTimer.Start(TimeMilli::SecToMsec(kDefaultNat64PrefixLifetime));
3685     LogInfo("NAT64 prefix timer scheduled in %lu seconds", ToUlong(kDefaultNat64PrefixLifetime));
3686 }
3687 
Discover(void)3688 void RoutingManager::Nat64PrefixManager::Discover(void)
3689 {
3690     Error error = Get<RoutingManager>().mInfraIf.DiscoverNat64Prefix();
3691 
3692     if (error == kErrorNone)
3693     {
3694         LogInfo("Discovering infraif NAT64 prefix");
3695     }
3696     else
3697     {
3698         LogWarn("Failed to discover infraif NAT64 prefix: %s", ErrorToString(error));
3699     }
3700 }
3701 
HandleDiscoverDone(const Ip6::Prefix & aPrefix)3702 void RoutingManager::Nat64PrefixManager::HandleDiscoverDone(const Ip6::Prefix &aPrefix)
3703 {
3704     mInfraIfPrefix = aPrefix;
3705 
3706     LogInfo("Infraif NAT64 prefix: %s", mInfraIfPrefix.IsValidNat64() ? mInfraIfPrefix.ToString().AsCString() : "none");
3707     Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
3708 }
3709 
GetState(void) const3710 Nat64::State RoutingManager::Nat64PrefixManager::GetState(void) const
3711 {
3712     Nat64::State state = Nat64::kStateDisabled;
3713 
3714     VerifyOrExit(mEnabled);
3715     VerifyOrExit(Get<RoutingManager>().IsRunning(), state = Nat64::kStateNotRunning);
3716     VerifyOrExit(mPublishedPrefix.IsValidNat64(), state = Nat64::kStateIdle);
3717     state = Nat64::kStateActive;
3718 
3719 exit:
3720     return state;
3721 }
3722 
3723 #endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
3724 
3725 //---------------------------------------------------------------------------------------------------------------------
3726 // RaInfo
3727 
IncrementTxCountAndSaveHash(const InfraIf::Icmp6Packet & aRaMessage)3728 void RoutingManager::TxRaInfo::IncrementTxCountAndSaveHash(const InfraIf::Icmp6Packet &aRaMessage)
3729 {
3730     mTxCount++;
3731     mLastHashIndex++;
3732 
3733     if (mLastHashIndex == kNumHashEntries)
3734     {
3735         mLastHashIndex = 0;
3736     }
3737 
3738     CalculateHash(RouterAdvert::RxMessage(aRaMessage), mHashes[mLastHashIndex]);
3739 }
3740 
IsRaFromManager(const RouterAdvert::RxMessage & aRaMessage) const3741 bool RoutingManager::TxRaInfo::IsRaFromManager(const RouterAdvert::RxMessage &aRaMessage) const
3742 {
3743     // Determines whether or not a received RA message was prepared by
3744     // by `RoutingManager` itself (is present in the saved `mHashes`).
3745 
3746     bool     isFromManager = false;
3747     uint16_t hashIndex     = mLastHashIndex;
3748     uint32_t count         = Min<uint32_t>(mTxCount, kNumHashEntries);
3749     Hash     hash;
3750 
3751     CalculateHash(aRaMessage, hash);
3752 
3753     for (; count > 0; count--)
3754     {
3755         if (mHashes[hashIndex] == hash)
3756         {
3757             isFromManager = true;
3758             break;
3759         }
3760 
3761         // Go to the previous index (ring buffer)
3762 
3763         if (hashIndex == 0)
3764         {
3765             hashIndex = kNumHashEntries - 1;
3766         }
3767         else
3768         {
3769             hashIndex--;
3770         }
3771     }
3772 
3773     return isFromManager;
3774 }
3775 
CalculateHash(const RouterAdvert::RxMessage & aRaMessage,Hash & aHash)3776 void RoutingManager::TxRaInfo::CalculateHash(const RouterAdvert::RxMessage &aRaMessage, Hash &aHash)
3777 {
3778     RouterAdvert::Header header;
3779     Crypto::Sha256       sha256;
3780 
3781     header = aRaMessage.GetHeader();
3782     header.SetChecksum(0);
3783 
3784     sha256.Start();
3785     sha256.Update(header);
3786     sha256.Update(aRaMessage.GetOptionStart(), aRaMessage.GetOptionLength());
3787     sha256.Finish(aHash);
3788 }
3789 
3790 //---------------------------------------------------------------------------------------------------------------------
3791 // RsSender
3792 
RsSender(Instance & aInstance)3793 RoutingManager::RsSender::RsSender(Instance &aInstance)
3794     : InstanceLocator(aInstance)
3795     , mTxCount(0)
3796     , mTimer(aInstance)
3797 {
3798 }
3799 
Start(void)3800 void RoutingManager::RsSender::Start(void)
3801 {
3802     uint32_t delay;
3803 
3804     VerifyOrExit(!IsInProgress());
3805 
3806     delay = Random::NonCrypto::GetUint32InRange(0, kMaxStartDelay);
3807 
3808     LogInfo("RsSender: Starting - will send first RS in %lu msec", ToUlong(delay));
3809 
3810     mTxCount   = 0;
3811     mStartTime = TimerMilli::GetNow();
3812     mTimer.Start(delay);
3813 
3814 exit:
3815     return;
3816 }
3817 
Stop(void)3818 void RoutingManager::RsSender::Stop(void) { mTimer.Stop(); }
3819 
SendRs(void)3820 Error RoutingManager::RsSender::SendRs(void)
3821 {
3822     Ip6::Address         destAddress;
3823     RouterSolicitMessage routerSolicit;
3824     InfraIf::Icmp6Packet packet;
3825     Error                error;
3826 
3827     packet.InitFrom(routerSolicit);
3828     destAddress.SetToLinkLocalAllRoutersMulticast();
3829 
3830     error = Get<RoutingManager>().mInfraIf.Send(packet, destAddress);
3831 
3832     if (error == kErrorNone)
3833     {
3834         Get<Ip6::Ip6>().GetBorderRoutingCounters().mRsTxSuccess++;
3835     }
3836     else
3837     {
3838         Get<Ip6::Ip6>().GetBorderRoutingCounters().mRsTxFailure++;
3839     }
3840     return error;
3841 }
3842 
HandleTimer(void)3843 void RoutingManager::RsSender::HandleTimer(void)
3844 {
3845     Error    error;
3846     uint32_t delay;
3847 
3848     if (mTxCount >= kMaxTxCount)
3849     {
3850         LogInfo("RsSender: Finished sending RS msgs and waiting for RAs");
3851         Get<RoutingManager>().HandleRsSenderFinished(mStartTime);
3852         ExitNow();
3853     }
3854 
3855     error = SendRs();
3856 
3857     if (error == kErrorNone)
3858     {
3859         mTxCount++;
3860         delay = (mTxCount == kMaxTxCount) ? kWaitOnLastAttempt : kTxInterval;
3861         LogInfo("RsSender: Sent RS %u/%u", mTxCount, kMaxTxCount);
3862     }
3863     else
3864     {
3865         LogCrit("RsSender: Failed to send RS %u/%u: %s", mTxCount + 1, kMaxTxCount, ErrorToString(error));
3866 
3867         // Note that `mTxCount` is intentionally not incremented
3868         // if the tx fails.
3869         delay = kRetryDelay;
3870     }
3871 
3872     mTimer.Start(delay);
3873 
3874 exit:
3875     return;
3876 }
3877 
3878 //---------------------------------------------------------------------------------------------------------------------
3879 // PdPrefixManager
3880 
3881 #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
3882 
PdPrefixManager(Instance & aInstance)3883 RoutingManager::PdPrefixManager::PdPrefixManager(Instance &aInstance)
3884     : InstanceLocator(aInstance)
3885     , mEnabled(false)
3886     , mIsRunning(false)
3887     , mNumPlatformPioProcessed(0)
3888     , mNumPlatformRaReceived(0)
3889     , mLastPlatformRaTime(0)
3890     , mTimer(aInstance)
3891 {
3892 }
3893 
SetEnabled(bool aEnabled)3894 void RoutingManager::PdPrefixManager::SetEnabled(bool aEnabled)
3895 {
3896     State oldState = GetState();
3897 
3898     VerifyOrExit(mEnabled != aEnabled);
3899     mEnabled = aEnabled;
3900     EvaluateStateChange(oldState);
3901 
3902 exit:
3903     return;
3904 }
3905 
StartStop(bool aStart)3906 void RoutingManager::PdPrefixManager::StartStop(bool aStart)
3907 {
3908     State oldState = GetState();
3909 
3910     VerifyOrExit(aStart != mIsRunning);
3911     mIsRunning = aStart;
3912     EvaluateStateChange(oldState);
3913 
3914 exit:
3915     return;
3916 }
3917 
GetState(void) const3918 RoutingManager::PdPrefixManager::State RoutingManager::PdPrefixManager::GetState(void) const
3919 {
3920     State state = kDhcp6PdStateDisabled;
3921 
3922     if (mEnabled)
3923     {
3924         state = mIsRunning ? kDhcp6PdStateRunning : kDhcp6PdStateStopped;
3925     }
3926 
3927     return state;
3928 }
3929 
EvaluateStateChange(Dhcp6PdState aOldState)3930 void RoutingManager::PdPrefixManager::EvaluateStateChange(Dhcp6PdState aOldState)
3931 {
3932     State newState = GetState();
3933 
3934     VerifyOrExit(aOldState != newState);
3935     LogInfo("PdPrefixManager: %s -> %s", StateToString(aOldState), StateToString(newState));
3936 
3937     switch (newState)
3938     {
3939     case kDhcp6PdStateDisabled:
3940     case kDhcp6PdStateStopped:
3941         WithdrawPrefix();
3942         break;
3943     case kDhcp6PdStateRunning:
3944         break;
3945     }
3946 
3947     mStateCallback.InvokeIfSet(static_cast<otBorderRoutingDhcp6PdState>(newState));
3948 
3949 exit:
3950     return;
3951 }
3952 
GetPrefixInfo(PrefixTableEntry & aInfo) const3953 Error RoutingManager::PdPrefixManager::GetPrefixInfo(PrefixTableEntry &aInfo) const
3954 {
3955     Error error = kErrorNone;
3956 
3957     VerifyOrExit(IsRunning() && HasPrefix(), error = kErrorNotFound);
3958 
3959     aInfo.mPrefix              = mPrefix.GetPrefix();
3960     aInfo.mValidLifetime       = mPrefix.GetValidLifetime();
3961     aInfo.mPreferredLifetime   = mPrefix.GetPreferredLifetime();
3962     aInfo.mMsecSinceLastUpdate = TimerMilli::GetNow() - mPrefix.GetLastUpdateTime();
3963 
3964 exit:
3965     return error;
3966 }
3967 
GetProcessedRaInfo(PdProcessedRaInfo & aPdProcessedRaInfo) const3968 Error RoutingManager::PdPrefixManager::GetProcessedRaInfo(PdProcessedRaInfo &aPdProcessedRaInfo) const
3969 {
3970     Error error = kErrorNone;
3971 
3972     VerifyOrExit(IsRunning() && HasPrefix(), error = kErrorNotFound);
3973 
3974     aPdProcessedRaInfo.mNumPlatformRaReceived   = mNumPlatformRaReceived;
3975     aPdProcessedRaInfo.mNumPlatformPioProcessed = mNumPlatformPioProcessed;
3976     aPdProcessedRaInfo.mLastPlatformRaMsec      = TimerMilli::GetNow() - mLastPlatformRaTime;
3977 
3978 exit:
3979     return error;
3980 }
3981 
WithdrawPrefix(void)3982 void RoutingManager::PdPrefixManager::WithdrawPrefix(void)
3983 {
3984     VerifyOrExit(HasPrefix());
3985 
3986     LogInfo("Withdrew DHCPv6 PD prefix %s", mPrefix.GetPrefix().ToString().AsCString());
3987 
3988     mPrefix.Clear();
3989     mTimer.Stop();
3990 
3991     Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kImmediately);
3992 
3993 exit:
3994     return;
3995 }
3996 
ProcessRa(const uint8_t * aRouterAdvert,const uint16_t aLength)3997 void RoutingManager::PdPrefixManager::ProcessRa(const uint8_t *aRouterAdvert, const uint16_t aLength)
3998 {
3999     // Processes a Router Advertisement (RA) message received on the
4000     // platform's Thread interface. This RA message, generated by
4001     // software entities like dnsmasq, radvd, or systemd-networkd, is
4002     // part of the DHCPv6 prefix delegation process for distributing
4003     // prefixes to interfaces.
4004 
4005     RouterAdvert::Icmp6Packet packet;
4006 
4007     packet.Init(aRouterAdvert, aLength);
4008     Process(&packet, nullptr);
4009 }
4010 
ProcessPrefix(const PrefixTableEntry & aPrefixTableEntry)4011 void RoutingManager::PdPrefixManager::ProcessPrefix(const PrefixTableEntry &aPrefixTableEntry)
4012 {
4013     // Processes a prefix delegated by a DHCPv6 Prefix Delegation
4014     // (PD) server.  Similar to `ProcessRa()`, but sets the prefix
4015     // directly instead of parsing an RA message. Calling this method
4016     // again with new values can update the prefix's lifetime.
4017 
4018     Process(nullptr, &aPrefixTableEntry);
4019 }
4020 
Process(const RouterAdvert::Icmp6Packet * aRaPacket,const PrefixTableEntry * aPrefixTableEntry)4021 void RoutingManager::PdPrefixManager::Process(const RouterAdvert::Icmp6Packet *aRaPacket,
4022                                               const PrefixTableEntry          *aPrefixTableEntry)
4023 {
4024     // Processes DHCPv6 Prefix Delegation (PD) prefixes, either from
4025     // an RA message or directly set. Requires either `aRaPacket` or
4026     // `aPrefixTableEntry` to be non-null.
4027 
4028     bool        currentPrefixUpdated = false;
4029     Error       error                = kErrorNone;
4030     PrefixEntry favoredEntry;
4031     PrefixEntry entry;
4032 
4033     VerifyOrExit(mEnabled, error = kErrorInvalidState);
4034 
4035     if (aRaPacket != nullptr)
4036     {
4037         RouterAdvert::RxMessage raMsg = RouterAdvert::RxMessage(*aRaPacket);
4038 
4039         VerifyOrExit(raMsg.IsValid(), error = kErrorParse);
4040 
4041         for (const Option &option : raMsg)
4042         {
4043             if (option.GetType() != Option::kTypePrefixInfo || !static_cast<const PrefixInfoOption &>(option).IsValid())
4044             {
4045                 continue;
4046             }
4047 
4048             mNumPlatformPioProcessed++;
4049             entry.SetFrom(static_cast<const PrefixInfoOption &>(option));
4050             currentPrefixUpdated |= ProcessPrefixEntry(entry, favoredEntry);
4051         }
4052 
4053         mNumPlatformRaReceived++;
4054         mLastPlatformRaTime = TimerMilli::GetNow();
4055     }
4056     else // aPrefixTableEntry != nullptr
4057     {
4058         entry.SetFrom(*aPrefixTableEntry);
4059         currentPrefixUpdated = ProcessPrefixEntry(entry, favoredEntry);
4060     }
4061 
4062     if (currentPrefixUpdated && mPrefix.IsDeprecated())
4063     {
4064         LogInfo("DHCPv6 PD prefix %s is deprecated", mPrefix.GetPrefix().ToString().AsCString());
4065         mPrefix.Clear();
4066         Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kImmediately);
4067     }
4068 
4069     if (favoredEntry.IsFavoredOver(mPrefix))
4070     {
4071         mPrefix              = favoredEntry;
4072         currentPrefixUpdated = true;
4073         LogInfo("DHCPv6 PD prefix set to %s", mPrefix.GetPrefix().ToString().AsCString());
4074         Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kImmediately);
4075     }
4076 
4077     if (HasPrefix() && currentPrefixUpdated)
4078     {
4079         mTimer.FireAt(mPrefix.GetDeprecationTime());
4080     }
4081     else
4082     {
4083         mTimer.Stop();
4084     }
4085 
4086 exit:
4087     LogWarnOnError(error, "process DHCPv6 delegated prefix");
4088     OT_UNUSED_VARIABLE(error);
4089 }
4090 
ProcessPrefixEntry(PrefixEntry & aEntry,PrefixEntry & aFavoredEntry)4091 bool RoutingManager::PdPrefixManager::ProcessPrefixEntry(PrefixEntry &aEntry, PrefixEntry &aFavoredEntry)
4092 {
4093     bool currentPrefixUpdated = false;
4094 
4095     if (!aEntry.IsValidPdPrefix())
4096     {
4097         LogWarn("Ignore invalid DHCPv6 PD prefix %s", aEntry.GetPrefix().ToString().AsCString());
4098         ExitNow();
4099     }
4100 
4101     aEntry.GetPrefix().Tidy();
4102     aEntry.GetPrefix().SetLength(kOmrPrefixLength);
4103 
4104     // Check if there is an update to the current prefix. The valid or
4105     // preferred lifetime may have changed.
4106 
4107     if (HasPrefix() && (mPrefix.GetPrefix() == aEntry.GetPrefix()))
4108     {
4109         currentPrefixUpdated = true;
4110         mPrefix              = aEntry;
4111     }
4112 
4113     VerifyOrExit(!aEntry.IsDeprecated());
4114 
4115     // Some platforms may delegate multiple prefixes. We'll select the
4116     // smallest one, as GUA prefixes (`2000::/3`) are inherently
4117     // smaller than ULA prefixes (`fc00::/7`). This rule prefers GUA
4118     // prefixes over ULA.
4119 
4120     if (aEntry.IsFavoredOver(aFavoredEntry))
4121     {
4122         aFavoredEntry = aEntry;
4123     }
4124 
4125 exit:
4126     return currentPrefixUpdated;
4127 }
4128 
IsValidPdPrefix(void) const4129 bool RoutingManager::PdPrefixManager::PrefixEntry::IsValidPdPrefix(void) const
4130 {
4131     // We should accept ULA prefix since it could be used by the internet infrastructure like NAT64.
4132 
4133     return !IsEmpty() && (GetPrefix().GetLength() <= kOmrPrefixLength) && !GetPrefix().IsLinkLocal() &&
4134            !GetPrefix().IsMulticast();
4135 }
4136 
IsFavoredOver(const PrefixEntry & aOther) const4137 bool RoutingManager::PdPrefixManager::PrefixEntry::IsFavoredOver(const PrefixEntry &aOther) const
4138 {
4139     bool isFavored;
4140 
4141     if (IsEmpty())
4142     {
4143         // Empty prefix is not favored over any (including another
4144         // empty prefix).
4145         isFavored = false;
4146         ExitNow();
4147     }
4148 
4149     if (aOther.IsEmpty())
4150     {
4151         // A non-empty prefix is favored over an empty one.
4152         isFavored = true;
4153         ExitNow();
4154     }
4155 
4156     // Numerically smaller prefix is favored.
4157 
4158     isFavored = GetPrefix() < aOther.GetPrefix();
4159 
4160 exit:
4161     return isFavored;
4162 }
4163 
StateToString(State aState)4164 const char *RoutingManager::PdPrefixManager::StateToString(State aState)
4165 {
4166     static const char *const kStateStrings[] = {
4167         "Disabled", // (0) kDisabled
4168         "Stopped",  // (1) kStopped
4169         "Running",  // (2) kRunning
4170     };
4171 
4172     static_assert(0 == kDhcp6PdStateDisabled, "kDhcp6PdStateDisabled value is incorrect");
4173     static_assert(1 == kDhcp6PdStateStopped, "kDhcp6PdStateStopped value is incorrect");
4174     static_assert(2 == kDhcp6PdStateRunning, "kDhcp6PdStateRunning value is incorrect");
4175 
4176     return kStateStrings[aState];
4177 }
4178 
otPlatBorderRoutingProcessIcmp6Ra(otInstance * aInstance,const uint8_t * aMessage,uint16_t aLength)4179 extern "C" void otPlatBorderRoutingProcessIcmp6Ra(otInstance *aInstance, const uint8_t *aMessage, uint16_t aLength)
4180 {
4181     AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().mPdPrefixManager.ProcessRa(aMessage, aLength);
4182 }
4183 
otPlatBorderRoutingProcessDhcp6PdPrefix(otInstance * aInstance,const otBorderRoutingPrefixTableEntry * aPrefixInfo)4184 extern "C" void otPlatBorderRoutingProcessDhcp6PdPrefix(otInstance                            *aInstance,
4185                                                         const otBorderRoutingPrefixTableEntry *aPrefixInfo)
4186 {
4187     AssertPointerIsNotNull(aPrefixInfo);
4188 
4189     AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().mPdPrefixManager.ProcessPrefix(*aPrefixInfo);
4190 }
4191 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
4192 
4193 } // namespace BorderRouter
4194 
4195 } // namespace ot
4196 
4197 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
4198