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