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