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