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/infra_if.h>
43 
44 #include "common/code_utils.hpp"
45 #include "common/debug.hpp"
46 #include "common/instance.hpp"
47 #include "common/locator_getters.hpp"
48 #include "common/log.hpp"
49 #include "common/num_utils.hpp"
50 #include "common/random.hpp"
51 #include "common/settings.hpp"
52 #include "meshcop/extended_panid.hpp"
53 #include "net/ip6.hpp"
54 #include "net/nat64_translator.hpp"
55 #include "thread/network_data_leader.hpp"
56 #include "thread/network_data_local.hpp"
57 #include "thread/network_data_notifier.hpp"
58 
59 namespace ot {
60 
61 namespace BorderRouter {
62 
63 RegisterLogModule("BorderRouter");
64 
RoutingManager(Instance & aInstance)65 RoutingManager::RoutingManager(Instance &aInstance)
66     : InstanceLocator(aInstance)
67     , mIsRunning(false)
68     , mIsEnabled(false)
69     , mInfraIf(aInstance)
70     , mOmrPrefixManager(aInstance)
71     , mRioPreference(NetworkData::kRoutePreferenceLow)
72     , mUserSetRioPreference(false)
73     , mOnLinkPrefixManager(aInstance)
74     , mDiscoveredPrefixTable(aInstance)
75     , mRoutePublisher(aInstance)
76 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
77     , mNat64PrefixManager(aInstance)
78 #endif
79     , mRsSender(aInstance)
80     , mDiscoveredPrefixStaleTimer(aInstance)
81     , mRoutingPolicyTimer(aInstance)
82 {
83     mBrUlaPrefix.Clear();
84 }
85 
Init(uint32_t aInfraIfIndex,bool aInfraIfIsRunning)86 Error RoutingManager::Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning)
87 {
88     Error error;
89 
90     SuccessOrExit(error = mInfraIf.Init(aInfraIfIndex));
91 
92     SuccessOrExit(error = LoadOrGenerateRandomBrUlaPrefix());
93     mOmrPrefixManager.Init(mBrUlaPrefix);
94 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
95     mNat64PrefixManager.GenerateLocalPrefix(mBrUlaPrefix);
96 #endif
97     mOnLinkPrefixManager.Init();
98 
99     error = mInfraIf.HandleStateChanged(mInfraIf.GetIfIndex(), aInfraIfIsRunning);
100 
101 exit:
102     if (error != kErrorNone)
103     {
104         mInfraIf.Deinit();
105     }
106 
107     return error;
108 }
109 
SetEnabled(bool aEnabled)110 Error RoutingManager::SetEnabled(bool aEnabled)
111 {
112     Error error = kErrorNone;
113 
114     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
115 
116     VerifyOrExit(aEnabled != mIsEnabled);
117 
118     mIsEnabled = aEnabled;
119     EvaluateState();
120 
121 exit:
122     return error;
123 }
124 
GetState(void) const125 RoutingManager::State RoutingManager::GetState(void) const
126 {
127     State state = kStateUninitialized;
128 
129     VerifyOrExit(IsInitialized());
130     VerifyOrExit(IsEnabled(), state = kStateDisabled);
131 
132     state = IsRunning() ? kStateRunning : kStateStopped;
133 
134 exit:
135     return state;
136 }
137 
SetRouteInfoOptionPreference(RoutePreference aPreference)138 void RoutingManager::SetRouteInfoOptionPreference(RoutePreference aPreference)
139 {
140     LogInfo("User explicitly set RIO Preference to %s", RoutePreferenceToString(aPreference));
141     mUserSetRioPreference = true;
142     UpdateRioPreference(aPreference);
143 }
144 
ClearRouteInfoOptionPreference(void)145 void RoutingManager::ClearRouteInfoOptionPreference(void)
146 {
147     VerifyOrExit(mUserSetRioPreference);
148 
149     LogInfo("User cleared explicitly set RIO Preference");
150     mUserSetRioPreference = false;
151     SetRioPreferenceBasedOnRole();
152 
153 exit:
154     return;
155 }
156 
SetRioPreferenceBasedOnRole(void)157 void RoutingManager::SetRioPreferenceBasedOnRole(void)
158 {
159     UpdateRioPreference(Get<Mle::Mle>().IsRouterOrLeader() ? NetworkData::kRoutePreferenceMedium
160                                                            : NetworkData::kRoutePreferenceLow);
161 }
162 
UpdateRioPreference(RoutePreference aPreference)163 void RoutingManager::UpdateRioPreference(RoutePreference aPreference)
164 {
165     VerifyOrExit(mRioPreference != aPreference);
166 
167     LogInfo("RIO Preference changed: %s -> %s", RoutePreferenceToString(mRioPreference),
168             RoutePreferenceToString(aPreference));
169     mRioPreference = aPreference;
170 
171     VerifyOrExit(mIsRunning);
172     ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
173 
174 exit:
175     return;
176 }
177 
GetOmrPrefix(Ip6::Prefix & aPrefix) const178 Error RoutingManager::GetOmrPrefix(Ip6::Prefix &aPrefix) const
179 {
180     Error error = kErrorNone;
181 
182     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
183     aPrefix = mOmrPrefixManager.GetLocalPrefix().GetPrefix();
184 
185 exit:
186     return error;
187 }
188 
GetFavoredOmrPrefix(Ip6::Prefix & aPrefix,RoutePreference & aPreference) const189 Error RoutingManager::GetFavoredOmrPrefix(Ip6::Prefix &aPrefix, RoutePreference &aPreference) const
190 {
191     Error error = kErrorNone;
192 
193     VerifyOrExit(IsRunning(), error = kErrorInvalidState);
194     aPrefix     = mOmrPrefixManager.GetFavoredPrefix().GetPrefix();
195     aPreference = mOmrPrefixManager.GetFavoredPrefix().GetPreference();
196 
197 exit:
198     return error;
199 }
200 
GetOnLinkPrefix(Ip6::Prefix & aPrefix) const201 Error RoutingManager::GetOnLinkPrefix(Ip6::Prefix &aPrefix) const
202 {
203     Error error = kErrorNone;
204 
205     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
206     aPrefix = mOnLinkPrefixManager.GetLocalPrefix();
207 
208 exit:
209     return error;
210 }
211 
GetFavoredOnLinkPrefix(Ip6::Prefix & aPrefix) const212 Error RoutingManager::GetFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const
213 {
214     Error error = kErrorNone;
215 
216     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
217     aPrefix = mOnLinkPrefixManager.GetFavoredDiscoveredPrefix();
218 
219     if (aPrefix.GetLength() == 0)
220     {
221         aPrefix = mOnLinkPrefixManager.GetLocalPrefix();
222     }
223 
224 exit:
225     return error;
226 }
227 
228 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
SetNat64PrefixManagerEnabled(bool aEnabled)229 void RoutingManager::SetNat64PrefixManagerEnabled(bool aEnabled)
230 {
231     // PrefixManager will start itself if routing manager is running.
232     mNat64PrefixManager.SetEnabled(aEnabled);
233 }
234 
GetNat64Prefix(Ip6::Prefix & aPrefix)235 Error RoutingManager::GetNat64Prefix(Ip6::Prefix &aPrefix)
236 {
237     Error error = kErrorNone;
238 
239     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
240     aPrefix = mNat64PrefixManager.GetLocalPrefix();
241 
242 exit:
243     return error;
244 }
245 
GetFavoredNat64Prefix(Ip6::Prefix & aPrefix,RoutePreference & aRoutePreference)246 Error RoutingManager::GetFavoredNat64Prefix(Ip6::Prefix &aPrefix, RoutePreference &aRoutePreference)
247 {
248     Error error = kErrorNone;
249 
250     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
251     aPrefix = mNat64PrefixManager.GetFavoredPrefix(aRoutePreference);
252 
253 exit:
254     return error;
255 }
256 #endif
257 
LoadOrGenerateRandomBrUlaPrefix(void)258 Error RoutingManager::LoadOrGenerateRandomBrUlaPrefix(void)
259 {
260     Error error     = kErrorNone;
261     bool  generated = false;
262 
263     if (Get<Settings>().Read<Settings::BrUlaPrefix>(mBrUlaPrefix) != kErrorNone || !IsValidBrUlaPrefix(mBrUlaPrefix))
264     {
265         Ip6::NetworkPrefix randomUlaPrefix;
266 
267         LogNote("No valid /48 BR ULA prefix found in settings, generating new one");
268 
269         SuccessOrExit(error = randomUlaPrefix.GenerateRandomUla());
270 
271         mBrUlaPrefix.Set(randomUlaPrefix);
272         mBrUlaPrefix.SetSubnetId(0);
273         mBrUlaPrefix.SetLength(kBrUlaPrefixLength);
274 
275         IgnoreError(Get<Settings>().Save<Settings::BrUlaPrefix>(mBrUlaPrefix));
276         generated = true;
277     }
278 
279     OT_UNUSED_VARIABLE(generated);
280 
281     LogNote("BR ULA prefix: %s (%s)", mBrUlaPrefix.ToString().AsCString(), generated ? "generated" : "loaded");
282 
283 exit:
284     if (error != kErrorNone)
285     {
286         LogCrit("Failed to generate random /48 BR ULA prefix");
287     }
288     return error;
289 }
290 
EvaluateState(void)291 void RoutingManager::EvaluateState(void)
292 {
293     if (mIsEnabled && Get<Mle::MleRouter>().IsAttached() && mInfraIf.IsRunning())
294     {
295         Start();
296     }
297     else
298     {
299         Stop();
300     }
301 }
302 
Start(void)303 void RoutingManager::Start(void)
304 {
305     if (!mIsRunning)
306     {
307         LogInfo("Border Routing manager started");
308 
309         mIsRunning = true;
310         UpdateDiscoveredPrefixTableOnNetDataChange();
311         mOnLinkPrefixManager.Start();
312         mOmrPrefixManager.Start();
313         mRoutePublisher.Start();
314         mRsSender.Start();
315 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
316         mNat64PrefixManager.Start();
317 #endif
318     }
319 }
320 
Stop(void)321 void RoutingManager::Stop(void)
322 {
323     VerifyOrExit(mIsRunning);
324 
325     mOmrPrefixManager.Stop();
326     mOnLinkPrefixManager.Stop();
327 
328 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
329     mNat64PrefixManager.Stop();
330 #endif
331 
332     SendRouterAdvertisement(kInvalidateAllPrevPrefixes);
333 
334     mAdvertisedPrefixes.Clear();
335 
336     mDiscoveredPrefixTable.RemoveAllEntries();
337     mDiscoveredPrefixStaleTimer.Stop();
338 
339     mRaInfo.mTxCount = 0;
340 
341     mRsSender.Stop();
342 
343     mRoutingPolicyTimer.Stop();
344 
345     mRoutePublisher.Stop();
346 
347     LogInfo("Border Routing manager stopped");
348 
349     mIsRunning = false;
350 
351 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
352     if (Get<Srp::Server>().IsAutoEnableMode())
353     {
354         Get<Srp::Server>().Disable();
355     }
356 #endif
357 
358 exit:
359     return;
360 }
361 
362 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
HandleSrpServerAutoEnableMode(void)363 void RoutingManager::HandleSrpServerAutoEnableMode(void)
364 {
365     VerifyOrExit(Get<Srp::Server>().IsAutoEnableMode());
366 
367     if (IsInitalPolicyEvaluationDone())
368     {
369         Get<Srp::Server>().Enable();
370     }
371     else
372     {
373         Get<Srp::Server>().Disable();
374     }
375 
376 exit:
377     return;
378 }
379 #endif
380 
HandleReceived(const InfraIf::Icmp6Packet & aPacket,const Ip6::Address & aSrcAddress)381 void RoutingManager::HandleReceived(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
382 {
383     const Ip6::Icmp::Header *icmp6Header;
384 
385     VerifyOrExit(mIsRunning);
386 
387     icmp6Header = reinterpret_cast<const Ip6::Icmp::Header *>(aPacket.GetBytes());
388 
389     switch (icmp6Header->GetType())
390     {
391     case Ip6::Icmp::Header::kTypeRouterAdvert:
392         HandleRouterAdvertisement(aPacket, aSrcAddress);
393         break;
394     case Ip6::Icmp::Header::kTypeRouterSolicit:
395         HandleRouterSolicit(aPacket, aSrcAddress);
396         break;
397     case Ip6::Icmp::Header::kTypeNeighborAdvert:
398         HandleNeighborAdvertisement(aPacket);
399         break;
400     default:
401         break;
402     }
403 
404 exit:
405     return;
406 }
407 
HandleNotifierEvents(Events aEvents)408 void RoutingManager::HandleNotifierEvents(Events aEvents)
409 {
410     if (aEvents.Contains(kEventThreadRoleChanged) && !mUserSetRioPreference)
411     {
412         SetRioPreferenceBasedOnRole();
413         mRoutePublisher.HandleRoleChanged();
414     }
415 
416     VerifyOrExit(IsInitialized() && IsEnabled());
417 
418     if (aEvents.Contains(kEventThreadRoleChanged))
419     {
420         EvaluateState();
421     }
422 
423     if (mIsRunning && aEvents.Contains(kEventThreadNetdataChanged))
424     {
425         UpdateDiscoveredPrefixTableOnNetDataChange();
426         mOnLinkPrefixManager.HandleNetDataChange();
427         ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
428     }
429 
430     if (aEvents.Contains(kEventThreadExtPanIdChanged))
431     {
432         mOnLinkPrefixManager.HandleExtPanIdChange();
433     }
434 
435 exit:
436     return;
437 }
438 
UpdateDiscoveredPrefixTableOnNetDataChange(void)439 void RoutingManager::UpdateDiscoveredPrefixTableOnNetDataChange(void)
440 {
441     NetworkData::Iterator           iterator = NetworkData::kIteratorInit;
442     NetworkData::OnMeshPrefixConfig prefixConfig;
443 
444     // Remove all OMR prefixes in Network Data from the
445     // discovered prefix table. Also check if we have
446     // an OMR prefix with default route flag.
447 
448     while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
449     {
450         if (!IsValidOmrPrefix(prefixConfig))
451         {
452             continue;
453         }
454 
455         mDiscoveredPrefixTable.RemoveRoutePrefix(prefixConfig.GetPrefix());
456     }
457 }
458 
459 // This method evaluate the routing policy depends on prefix and route
460 // information on Thread Network and infra link. As a result, this
461 // method May send RA messages on infra link and publish/unpublish
462 // OMR and NAT64 prefix in the Thread network.
EvaluateRoutingPolicy(void)463 void RoutingManager::EvaluateRoutingPolicy(void)
464 {
465     OT_ASSERT(mIsRunning);
466 
467     LogInfo("Evaluating routing policy");
468 
469     mOnLinkPrefixManager.Evaluate();
470     mOmrPrefixManager.Evaluate();
471     mRoutePublisher.Evaluate();
472 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
473     mNat64PrefixManager.Evaluate();
474 #endif
475 
476     SendRouterAdvertisement(kAdvPrefixesFromNetData);
477 
478 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
479     if (Get<Srp::Server>().IsAutoEnableMode() && IsInitalPolicyEvaluationDone())
480     {
481         // If SRP server uses the auto-enable mode, we enable the SRP
482         // server on the first RA transmission after we are done with
483         // initial prefix/route configurations. Note that if SRP server
484         // is already enabled, calling `Enable()` again does nothing.
485 
486         Get<Srp::Server>().Enable();
487     }
488 #endif
489 
490     ScheduleRoutingPolicyEvaluation(kForNextRa);
491 }
492 
IsInitalPolicyEvaluationDone(void) const493 bool RoutingManager::IsInitalPolicyEvaluationDone(void) const
494 {
495     // This method indicates whether or not we are done with the
496     // initial policy evaluation and prefix and route setup, i.e.,
497     // the OMR and on-link prefixes are determined, advertised in
498     // the emitted Router Advert message on infrastructure side
499     // and published in the Thread Network Data.
500 
501     return mIsRunning && !mOmrPrefixManager.GetFavoredPrefix().IsEmpty() &&
502            mOnLinkPrefixManager.IsInitalEvaluationDone();
503 }
504 
ScheduleRoutingPolicyEvaluation(ScheduleMode aMode)505 void RoutingManager::ScheduleRoutingPolicyEvaluation(ScheduleMode aMode)
506 {
507     TimeMilli now   = TimerMilli::GetNow();
508     uint32_t  delay = 0;
509     TimeMilli evaluateTime;
510 
511     switch (aMode)
512     {
513     case kImmediately:
514         break;
515 
516     case kForNextRa:
517         delay = Random::NonCrypto::GetUint32InRange(Time::SecToMsec(kMinRtrAdvInterval),
518                                                     Time::SecToMsec(kMaxRtrAdvInterval));
519 
520         if (mRaInfo.mTxCount <= kMaxInitRtrAdvertisements && delay > Time::SecToMsec(kMaxInitRtrAdvInterval))
521         {
522             delay = Time::SecToMsec(kMaxInitRtrAdvInterval);
523         }
524         break;
525 
526     case kAfterRandomDelay:
527         delay = Random::NonCrypto::GetUint32InRange(kPolicyEvaluationMinDelay, kPolicyEvaluationMaxDelay);
528         break;
529 
530     case kToReplyToRs:
531         delay = Random::NonCrypto::GetUint32InRange(0, kRaReplyJitter);
532         break;
533     }
534 
535     // Ensure we wait a min delay after last RA tx
536     evaluateTime = Max(now + delay, mRaInfo.mLastTxTime + kMinDelayBetweenRtrAdvs);
537 
538     LogInfo("Start evaluating routing policy, scheduled in %lu milliseconds", ToUlong(evaluateTime - now));
539 
540     mRoutingPolicyTimer.FireAtIfEarlier(evaluateTime);
541 }
542 
SendRouterAdvertisement(RouterAdvTxMode aRaTxMode)543 void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode)
544 {
545     // RA message max length is derived to accommodate:
546     //
547     // - The RA header.
548     // - One PIO for current local on-link prefix.
549     // - At most `kMaxOldPrefixes` for old deprecating on-link prefixes.
550     // - At most twice `kMaxOnMeshPrefixes` RIO for on-mesh prefixes.
551     //   Factor two is used for RIO to account for entries invalidating
552     //   previous prefixes while adding new ones.
553 
554     static constexpr uint16_t kMaxRaLength =
555         sizeof(Ip6::Nd::RouterAdvertMessage::Header) + sizeof(Ip6::Nd::PrefixInfoOption) +
556         sizeof(Ip6::Nd::PrefixInfoOption) * OnLinkPrefixManager::kMaxOldPrefixes +
557         2 * kMaxOnMeshPrefixes * (sizeof(Ip6::Nd::RouteInfoOption) + sizeof(Ip6::Prefix));
558 
559     uint8_t                         buffer[kMaxRaLength];
560     Ip6::Nd::RouterAdvertMessage    raMsg(mRaInfo.mHeader, buffer);
561     NetworkData::Iterator           iterator;
562     NetworkData::OnMeshPrefixConfig prefixConfig;
563 
564     // Append PIO for local on-link prefix if is either being
565     // advertised or deprecated and for old prefix if is being
566     // deprecated.
567 
568     mOnLinkPrefixManager.AppendAsPiosTo(raMsg);
569 
570     // Determine which previously advertised prefixes need to be
571     // invalidated. Under `kInvalidateAllPrevPrefixes` mode we need
572     // to invalidate all. Under `kAdvPrefixesFromNetData` mode, we
573     // check Network Data entries and invalidate any previously
574     // advertised prefix that is no longer present in the Network
575     // Data. We go through all Network Data prefixes and mark the
576     // ones we find in `mAdvertisedPrefixes` as deleted by setting
577     // the prefix length to zero). By the end, the remaining entries
578     // in the array with a non-zero prefix length are invalidated.
579 
580     if (aRaTxMode != kInvalidateAllPrevPrefixes)
581     {
582         iterator = NetworkData::kIteratorInit;
583 
584         while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
585         {
586             if (!prefixConfig.mOnMesh || prefixConfig.mDp ||
587                 (prefixConfig.GetPrefix() == mOmrPrefixManager.GetLocalPrefix().GetPrefix()))
588             {
589                 continue;
590             }
591 
592             mAdvertisedPrefixes.MarkAsDeleted(prefixConfig.GetPrefix());
593         }
594 
595         if (mOmrPrefixManager.IsLocalAddedInNetData())
596         {
597             mAdvertisedPrefixes.MarkAsDeleted(mOmrPrefixManager.GetLocalPrefix().GetPrefix());
598         }
599     }
600 
601     for (const OnMeshPrefix &prefix : mAdvertisedPrefixes)
602     {
603         if (prefix.GetLength() != 0)
604         {
605             SuccessOrAssert(raMsg.AppendRouteInfoOption(prefix, /* aRouteLifetime */ 0, mRioPreference));
606             LogInfo("RouterAdvert: Added RIO for %s (lifetime=0)", prefix.ToString().AsCString());
607         }
608     }
609 
610     // Discover and add prefixes from Network Data to advertise as
611     // RIO in the Router Advertisement message.
612 
613     mAdvertisedPrefixes.Clear();
614 
615     if (aRaTxMode == kAdvPrefixesFromNetData)
616     {
617         // `mAdvertisedPrefixes` array has a limited size. We add more
618         // important prefixes first in the array to ensure they are
619         // advertised in the RA message. Note that `Add()` method
620         // will ensure to add a prefix only once (will check if
621         // prefix is already present in the array).
622 
623         // (1) Local OMR prefix.
624 
625         if (mOmrPrefixManager.IsLocalAddedInNetData())
626         {
627             mAdvertisedPrefixes.Add(mOmrPrefixManager.GetLocalPrefix().GetPrefix());
628         }
629 
630         // (2) Favored OMR prefix.
631 
632         if (!mOmrPrefixManager.GetFavoredPrefix().IsEmpty() && !mOmrPrefixManager.GetFavoredPrefix().IsDomainPrefix())
633         {
634             mAdvertisedPrefixes.Add(mOmrPrefixManager.GetFavoredPrefix().GetPrefix());
635         }
636 
637         // (3) All other OMR prefixes.
638 
639         iterator = NetworkData::kIteratorInit;
640 
641         while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
642         {
643             // Local OMR prefix is added to the array depending on
644             // `mOmrPrefixManager.IsLocalAddedInNetData()` at step (1).
645             // As we iterate through the Network Data prefixes, we skip
646             // over entries matching the local OMR prefix. This
647             // ensures that we stop including it in emitted RA
648             // message as soon as we decide to remove it from Network
649             // Data. Note that upon requesting it to be removed from
650             // Network Data the change needs to be registered with
651             // leader and can take some time to be updated in Network
652             // Data.
653 
654             if (prefixConfig.mDp)
655             {
656                 continue;
657             }
658 
659             if (IsValidOmrPrefix(prefixConfig) &&
660                 (prefixConfig.GetPrefix() != mOmrPrefixManager.GetLocalPrefix().GetPrefix()))
661             {
662                 mAdvertisedPrefixes.Add(prefixConfig.GetPrefix());
663             }
664         }
665 
666         // (4) All other on-mesh prefixes (excluding Domain Prefix).
667 
668         iterator = NetworkData::kIteratorInit;
669 
670         while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
671         {
672             if (prefixConfig.mOnMesh && !prefixConfig.mDp && !IsValidOmrPrefix(prefixConfig))
673             {
674                 mAdvertisedPrefixes.Add(prefixConfig.GetPrefix());
675             }
676         }
677 
678         for (const OnMeshPrefix &prefix : mAdvertisedPrefixes)
679         {
680             SuccessOrAssert(raMsg.AppendRouteInfoOption(prefix, kDefaultOmrPrefixLifetime, mRioPreference));
681             LogInfo("RouterAdvert: Added RIO for %s (lifetime=%lu)", prefix.ToString().AsCString(),
682                     ToUlong(kDefaultOmrPrefixLifetime));
683         }
684     }
685 
686     if (raMsg.ContainsAnyOptions())
687     {
688         Error        error;
689         Ip6::Address destAddress;
690 
691         ++mRaInfo.mTxCount;
692 
693         destAddress.SetToLinkLocalAllNodesMulticast();
694 
695         error = mInfraIf.Send(raMsg.GetAsPacket(), destAddress);
696 
697         if (error == kErrorNone)
698         {
699             mRaInfo.mLastTxTime = TimerMilli::GetNow();
700             Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaTxSuccess++;
701             LogInfo("Sent Router Advertisement on %s", mInfraIf.ToString().AsCString());
702             DumpDebg("[BR-CERT] direction=send | type=RA |", raMsg.GetAsPacket().GetBytes(),
703                      raMsg.GetAsPacket().GetLength());
704         }
705         else
706         {
707             Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaTxFailure++;
708             LogWarn("Failed to send Router Advertisement on %s: %s", mInfraIf.ToString().AsCString(),
709                     ErrorToString(error));
710         }
711     }
712 }
713 
IsReceivedRouterAdvertFromManager(const Ip6::Nd::RouterAdvertMessage & aRaMessage) const714 bool RoutingManager::IsReceivedRouterAdvertFromManager(const Ip6::Nd::RouterAdvertMessage &aRaMessage) const
715 {
716     // Determines whether or not a received RA message was prepared by
717     // by `RoutingManager` itself.
718 
719     bool        isFromManager = false;
720     uint16_t    rioCount      = 0;
721     Ip6::Prefix prefix;
722 
723     VerifyOrExit(aRaMessage.ContainsAnyOptions());
724 
725     for (const Ip6::Nd::Option &option : aRaMessage)
726     {
727         switch (option.GetType())
728         {
729         case Ip6::Nd::Option::kTypePrefixInfo:
730         {
731             const Ip6::Nd::PrefixInfoOption &pio = static_cast<const Ip6::Nd::PrefixInfoOption &>(option);
732 
733             VerifyOrExit(pio.IsValid());
734             pio.GetPrefix(prefix);
735 
736             // If it is a non-deprecated PIO, it should match the
737             // local on-link prefix.
738 
739             if (pio.GetPreferredLifetime() > 0)
740             {
741                 VerifyOrExit(prefix == mOnLinkPrefixManager.GetLocalPrefix());
742             }
743 
744             break;
745         }
746 
747         case Ip6::Nd::Option::kTypeRouteInfo:
748         {
749             // RIO (with non-zero lifetime) should match entries from
750             // `mAdvertisedPrefixes`. We keep track of the number
751             // of matched RIOs and check after the loop ends that all
752             // entries were seen.
753 
754             const Ip6::Nd::RouteInfoOption &rio = static_cast<const Ip6::Nd::RouteInfoOption &>(option);
755 
756             VerifyOrExit(rio.IsValid());
757             rio.GetPrefix(prefix);
758 
759             if (rio.GetRouteLifetime() != 0)
760             {
761                 VerifyOrExit(mAdvertisedPrefixes.Contains(prefix));
762                 rioCount++;
763             }
764 
765             break;
766         }
767 
768         default:
769             ExitNow();
770         }
771     }
772 
773     VerifyOrExit(rioCount == mAdvertisedPrefixes.GetLength());
774 
775     isFromManager = true;
776 
777 exit:
778     return isFromManager;
779 }
780 
IsValidBrUlaPrefix(const Ip6::Prefix & aBrUlaPrefix)781 bool RoutingManager::IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix)
782 {
783     return aBrUlaPrefix.mLength == kBrUlaPrefixLength && aBrUlaPrefix.mPrefix.mFields.m8[0] == 0xfd;
784 }
785 
IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig & aOnMeshPrefixConfig)786 bool RoutingManager::IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig)
787 {
788     return IsValidOmrPrefix(aOnMeshPrefixConfig.GetPrefix()) && aOnMeshPrefixConfig.mOnMesh &&
789            aOnMeshPrefixConfig.mSlaac && aOnMeshPrefixConfig.mStable;
790 }
791 
IsValidOmrPrefix(const Ip6::Prefix & aPrefix)792 bool RoutingManager::IsValidOmrPrefix(const Ip6::Prefix &aPrefix)
793 {
794     // Accept ULA/GUA prefixes with 64-bit length.
795     return (aPrefix.GetLength() == kOmrPrefixLength) && !aPrefix.IsLinkLocal() && !aPrefix.IsMulticast();
796 }
797 
IsValidOnLinkPrefix(const Ip6::Nd::PrefixInfoOption & aPio)798 bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Nd::PrefixInfoOption &aPio)
799 {
800     Ip6::Prefix prefix;
801 
802     aPio.GetPrefix(prefix);
803 
804     return IsValidOnLinkPrefix(prefix) && aPio.IsOnLinkFlagSet() && aPio.IsAutoAddrConfigFlagSet();
805 }
806 
IsValidOnLinkPrefix(const Ip6::Prefix & aOnLinkPrefix)807 bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix)
808 {
809     return aOnLinkPrefix.IsValid() && (aOnLinkPrefix.GetLength() > 0) && !aOnLinkPrefix.IsLinkLocal() &&
810            !aOnLinkPrefix.IsMulticast();
811 }
812 
HandleRsSenderFinished(TimeMilli aStartTime)813 void RoutingManager::HandleRsSenderFinished(TimeMilli aStartTime)
814 {
815     // This is a callback from `RsSender` and is invoked when it
816     // finishes a cycle of sending Router Solicitations. `aStartTime`
817     // specifies the start time of the RS transmission cycle.
818     //
819     // We remove or deprecate old entries in discovered table that are
820     // not refreshed during Router Solicitation. We also invalidate
821     // the learned RA header if it is not refreshed during Router
822     // Solicitation.
823 
824     mDiscoveredPrefixTable.RemoveOrDeprecateOldEntries(aStartTime);
825 
826     if (mRaInfo.mHeaderUpdateTime <= aStartTime)
827     {
828         UpdateRouterAdvertHeader(/* aRouterAdvertMessage */ nullptr);
829     }
830 
831     ScheduleRoutingPolicyEvaluation(kImmediately);
832 }
833 
HandleDiscoveredPrefixStaleTimer(void)834 void RoutingManager::HandleDiscoveredPrefixStaleTimer(void)
835 {
836     LogInfo("Stale On-Link or OMR Prefixes or RA messages are detected");
837     mRsSender.Start();
838 }
839 
HandleRouterSolicit(const InfraIf::Icmp6Packet & aPacket,const Ip6::Address & aSrcAddress)840 void RoutingManager::HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
841 {
842     OT_UNUSED_VARIABLE(aPacket);
843     OT_UNUSED_VARIABLE(aSrcAddress);
844 
845     Get<Ip6::Ip6>().GetBorderRoutingCounters().mRsRx++;
846     LogInfo("Received Router Solicitation from %s on %s", aSrcAddress.ToString().AsCString(),
847             mInfraIf.ToString().AsCString());
848 
849     ScheduleRoutingPolicyEvaluation(kToReplyToRs);
850 }
851 
HandleNeighborAdvertisement(const InfraIf::Icmp6Packet & aPacket)852 void RoutingManager::HandleNeighborAdvertisement(const InfraIf::Icmp6Packet &aPacket)
853 {
854     const Ip6::Nd::NeighborAdvertMessage *naMsg;
855 
856     VerifyOrExit(aPacket.GetLength() >= sizeof(naMsg));
857     naMsg = reinterpret_cast<const Ip6::Nd::NeighborAdvertMessage *>(aPacket.GetBytes());
858 
859     mDiscoveredPrefixTable.ProcessNeighborAdvertMessage(*naMsg);
860 
861 exit:
862     return;
863 }
864 
HandleRouterAdvertisement(const InfraIf::Icmp6Packet & aPacket,const Ip6::Address & aSrcAddress)865 void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
866 {
867     Ip6::Nd::RouterAdvertMessage routerAdvMessage(aPacket);
868 
869     OT_ASSERT(mIsRunning);
870 
871     VerifyOrExit(routerAdvMessage.IsValid());
872 
873     Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaRx++;
874     LogInfo("Received Router Advertisement from %s on %s", aSrcAddress.ToString().AsCString(),
875             mInfraIf.ToString().AsCString());
876     DumpDebg("[BR-CERT] direction=recv | type=RA |", aPacket.GetBytes(), aPacket.GetLength());
877 
878     mDiscoveredPrefixTable.ProcessRouterAdvertMessage(routerAdvMessage, aSrcAddress);
879 
880     // Remember the header and parameters of RA messages which are
881     // initiated from the infra interface.
882     if (mInfraIf.HasAddress(aSrcAddress))
883     {
884         UpdateRouterAdvertHeader(&routerAdvMessage);
885     }
886 
887 exit:
888     return;
889 }
890 
ShouldProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption & aPio,const Ip6::Prefix & aPrefix)891 bool RoutingManager::ShouldProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, const Ip6::Prefix &aPrefix)
892 {
893     // Indicate whether to process or skip a given prefix
894     // from a PIO (from received RA message).
895 
896     bool shouldProcess = false;
897 
898     VerifyOrExit(mIsRunning);
899 
900     if (!IsValidOnLinkPrefix(aPio))
901     {
902         LogInfo("Ignore invalid on-link prefix in PIO: %s", aPrefix.ToString().AsCString());
903         ExitNow();
904     }
905 
906     if (mOnLinkPrefixManager.IsPublishingOrAdvertising())
907     {
908         VerifyOrExit(aPrefix != mOnLinkPrefixManager.GetLocalPrefix());
909     }
910 
911     shouldProcess = true;
912 
913 exit:
914     return shouldProcess;
915 }
916 
ShouldProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption & aRio,const Ip6::Prefix & aPrefix)917 bool RoutingManager::ShouldProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, const Ip6::Prefix &aPrefix)
918 {
919     // Indicate whether to process or skip a given prefix
920     // from a RIO (from received RA message).
921 
922     OT_UNUSED_VARIABLE(aRio);
923 
924     bool shouldProcess = false;
925 
926     VerifyOrExit(mIsRunning);
927 
928     if (aPrefix.GetLength() == 0)
929     {
930         // Always process default route ::/0 prefix.
931         ExitNow(shouldProcess = true);
932     }
933 
934     if (!IsValidOmrPrefix(aPrefix))
935     {
936         LogInfo("Ignore RIO prefix %s since not a valid OMR prefix", aPrefix.ToString().AsCString());
937         ExitNow();
938     }
939 
940     VerifyOrExit(mOmrPrefixManager.GetLocalPrefix().GetPrefix() != aPrefix);
941 
942     // Ignore OMR prefixes advertised by ourselves or in current Thread Network Data.
943     // The `mAdvertisedPrefixes` and the OMR prefix set in Network Data should eventually
944     // be equal, but there is time that they are not synchronized immediately:
945     // 1. Network Data could contain more OMR prefixes than `mAdvertisedPrefixes` because
946     //    we added random delay before Evaluating routing policy when Network Data is changed.
947     // 2. `mAdvertisedPrefixes` could contain more OMR prefixes than Network Data because
948     //    it takes time to sync a new OMR prefix into Network Data (multicast loopback RA
949     //    messages are usually faster than Thread Network Data propagation).
950     // They are the reasons why we need both the checks.
951 
952     VerifyOrExit(!mAdvertisedPrefixes.Contains(aPrefix));
953     VerifyOrExit(!Get<RoutingManager>().NetworkDataContainsOmrPrefix(aPrefix));
954 
955     shouldProcess = true;
956 
957 exit:
958     return shouldProcess;
959 }
960 
HandleDiscoveredPrefixTableChanged(void)961 void RoutingManager::HandleDiscoveredPrefixTableChanged(void)
962 {
963     // This is a callback from `mDiscoveredPrefixTable` indicating that
964     // there has been a change in the table.
965 
966     VerifyOrExit(mIsRunning);
967 
968     ResetDiscoveredPrefixStaleTimer();
969     mOnLinkPrefixManager.HandleDiscoveredPrefixTableChanged();
970     mRoutePublisher.Evaluate();
971 
972 exit:
973     return;
974 }
975 
NetworkDataContainsOmrPrefix(const Ip6::Prefix & aPrefix) const976 bool RoutingManager::NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) const
977 {
978     NetworkData::Iterator           iterator = NetworkData::kIteratorInit;
979     NetworkData::OnMeshPrefixConfig onMeshPrefixConfig;
980     bool                            contains = false;
981 
982     while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, onMeshPrefixConfig) == kErrorNone)
983     {
984         if (IsValidOmrPrefix(onMeshPrefixConfig) && onMeshPrefixConfig.GetPrefix() == aPrefix)
985         {
986             contains = true;
987             break;
988         }
989     }
990 
991     return contains;
992 }
993 
NetworkDataContainsUlaRoute(void) const994 bool RoutingManager::NetworkDataContainsUlaRoute(void) const
995 {
996     // Determine whether leader Network Data contains a route
997     // prefix which is either the ULA prefix `fc00::/7` or
998     // a sub-prefix of it (e.g., default route).
999 
1000     NetworkData::Iterator            iterator = NetworkData::kIteratorInit;
1001     NetworkData::ExternalRouteConfig routeConfig;
1002     bool                             contains = false;
1003 
1004     while (Get<NetworkData::Leader>().GetNextExternalRoute(iterator, routeConfig) == kErrorNone)
1005     {
1006         if (routeConfig.mStable && RoutePublisher::GetUlaPrefix().ContainsPrefix(routeConfig.GetPrefix()))
1007         {
1008             contains = true;
1009             break;
1010         }
1011     }
1012 
1013     return contains;
1014 }
1015 
UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage * aRouterAdvertMessage)1016 void RoutingManager::UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage)
1017 {
1018     // Updates the `mRaInfo` from the given RA message.
1019 
1020     Ip6::Nd::RouterAdvertMessage::Header oldHeader;
1021 
1022     if (aRouterAdvertMessage != nullptr)
1023     {
1024         // We skip and do not update RA header if the received RA message
1025         // was not prepared and sent by `RoutingManager` itself.
1026 
1027         VerifyOrExit(!IsReceivedRouterAdvertFromManager(*aRouterAdvertMessage));
1028     }
1029 
1030     oldHeader                 = mRaInfo.mHeader;
1031     mRaInfo.mHeaderUpdateTime = TimerMilli::GetNow();
1032 
1033     if (aRouterAdvertMessage == nullptr || aRouterAdvertMessage->GetHeader().GetRouterLifetime() == 0)
1034     {
1035         mRaInfo.mHeader.SetToDefault();
1036         mRaInfo.mIsHeaderFromHost = false;
1037     }
1038     else
1039     {
1040         // The checksum is set to zero in `mRaInfo.mHeader`
1041         // which indicates to platform that it needs to do the
1042         // calculation and update it.
1043 
1044         mRaInfo.mHeader = aRouterAdvertMessage->GetHeader();
1045         mRaInfo.mHeader.SetChecksum(0);
1046         mRaInfo.mIsHeaderFromHost = true;
1047     }
1048 
1049     ResetDiscoveredPrefixStaleTimer();
1050 
1051     if (mRaInfo.mHeader != oldHeader)
1052     {
1053         // If there was a change to the header, start timer to
1054         // reevaluate routing policy and send RA message with new
1055         // header.
1056 
1057         ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
1058     }
1059 
1060 exit:
1061     return;
1062 }
1063 
ResetDiscoveredPrefixStaleTimer(void)1064 void RoutingManager::ResetDiscoveredPrefixStaleTimer(void)
1065 {
1066     TimeMilli now = TimerMilli::GetNow();
1067     TimeMilli nextStaleTime;
1068 
1069     OT_ASSERT(mIsRunning);
1070 
1071     // The stale timer triggers sending RS to check the state of
1072     // discovered prefixes and host RA messages.
1073 
1074     nextStaleTime = mDiscoveredPrefixTable.CalculateNextStaleTime(now);
1075 
1076     // Check for stale Router Advertisement Message if learnt from Host.
1077     if (mRaInfo.mIsHeaderFromHost)
1078     {
1079         TimeMilli raStaleTime = Max(now, mRaInfo.mHeaderUpdateTime + Time::SecToMsec(kRtrAdvStaleTime));
1080 
1081         nextStaleTime = Min(nextStaleTime, raStaleTime);
1082     }
1083 
1084     if (nextStaleTime == now.GetDistantFuture())
1085     {
1086         if (mDiscoveredPrefixStaleTimer.IsRunning())
1087         {
1088             LogDebg("Prefix stale timer stopped");
1089         }
1090 
1091         mDiscoveredPrefixStaleTimer.Stop();
1092     }
1093     else
1094     {
1095         mDiscoveredPrefixStaleTimer.FireAt(nextStaleTime);
1096         LogDebg("Prefix stale timer scheduled in %lu ms", ToUlong(nextStaleTime - now));
1097     }
1098 }
1099 
1100 //---------------------------------------------------------------------------------------------------------------------
1101 // DiscoveredPrefixTable
1102 
DiscoveredPrefixTable(Instance & aInstance)1103 RoutingManager::DiscoveredPrefixTable::DiscoveredPrefixTable(Instance &aInstance)
1104     : InstanceLocator(aInstance)
1105     , mEntryTimer(aInstance)
1106     , mRouterTimer(aInstance)
1107     , mSignalTask(aInstance)
1108 {
1109 }
1110 
ProcessRouterAdvertMessage(const Ip6::Nd::RouterAdvertMessage & aRaMessage,const Ip6::Address & aSrcAddress)1111 void RoutingManager::DiscoveredPrefixTable::ProcessRouterAdvertMessage(const Ip6::Nd::RouterAdvertMessage &aRaMessage,
1112                                                                        const Ip6::Address                 &aSrcAddress)
1113 {
1114     // Process a received RA message and update the prefix table.
1115 
1116     Router *router = mRouters.FindMatching(aSrcAddress);
1117 
1118     if (router == nullptr)
1119     {
1120         router = mRouters.PushBack();
1121 
1122         if (router == nullptr)
1123         {
1124             LogWarn("Received RA from too many routers, ignore RA from %s", aSrcAddress.ToString().AsCString());
1125             ExitNow();
1126         }
1127 
1128         router->mAddress = aSrcAddress;
1129         router->mEntries.Clear();
1130     }
1131 
1132     // RA message can indicate router provides default route in the RA
1133     // message header and can also include an RIO for `::/0`. When
1134     // processing an RA message, the preference and lifetime values
1135     // in a `::/0` RIO override the preference and lifetime values in
1136     // the RA header (per RFC 4191 section 3.1).
1137 
1138     ProcessDefaultRoute(aRaMessage.GetHeader(), *router);
1139 
1140     for (const Ip6::Nd::Option &option : aRaMessage)
1141     {
1142         switch (option.GetType())
1143         {
1144         case Ip6::Nd::Option::kTypePrefixInfo:
1145             ProcessPrefixInfoOption(static_cast<const Ip6::Nd::PrefixInfoOption &>(option), *router);
1146             break;
1147 
1148         case Ip6::Nd::Option::kTypeRouteInfo:
1149             ProcessRouteInfoOption(static_cast<const Ip6::Nd::RouteInfoOption &>(option), *router);
1150             break;
1151 
1152         default:
1153             break;
1154         }
1155     }
1156 
1157     UpdateRouterOnRx(*router);
1158 
1159     RemoveRoutersWithNoEntries();
1160 
1161 exit:
1162     return;
1163 }
1164 
ProcessDefaultRoute(const Ip6::Nd::RouterAdvertMessage::Header & aRaHeader,Router & aRouter)1165 void RoutingManager::DiscoveredPrefixTable::ProcessDefaultRoute(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader,
1166                                                                 Router                                     &aRouter)
1167 {
1168     Entry      *entry;
1169     Ip6::Prefix prefix;
1170 
1171     prefix.Clear();
1172     entry = aRouter.mEntries.FindMatching(Entry::Matcher(prefix, Entry::kTypeRoute));
1173 
1174     if (entry == nullptr)
1175     {
1176         VerifyOrExit(aRaHeader.GetRouterLifetime() != 0);
1177 
1178         entry = AllocateEntry();
1179 
1180         if (entry == nullptr)
1181         {
1182             LogWarn("Discovered too many prefixes, ignore default route from RA header");
1183             ExitNow();
1184         }
1185 
1186         entry->SetFrom(aRaHeader);
1187         aRouter.mEntries.Push(*entry);
1188     }
1189     else
1190     {
1191         entry->SetFrom(aRaHeader);
1192     }
1193 
1194     mEntryTimer.FireAtIfEarlier(entry->GetExpireTime());
1195 
1196     SignalTableChanged();
1197 
1198 exit:
1199     return;
1200 }
1201 
ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption & aPio,Router & aRouter)1202 void RoutingManager::DiscoveredPrefixTable::ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio,
1203                                                                     Router                          &aRouter)
1204 {
1205     Ip6::Prefix prefix;
1206     Entry      *entry;
1207 
1208     VerifyOrExit(aPio.IsValid());
1209     aPio.GetPrefix(prefix);
1210 
1211     VerifyOrExit(Get<RoutingManager>().ShouldProcessPrefixInfoOption(aPio, prefix));
1212 
1213     LogInfo("Processing PIO (%s, %lu seconds)", prefix.ToString().AsCString(), ToUlong(aPio.GetValidLifetime()));
1214 
1215     entry = aRouter.mEntries.FindMatching(Entry::Matcher(prefix, Entry::kTypeOnLink));
1216 
1217     if (entry == nullptr)
1218     {
1219         VerifyOrExit(aPio.GetValidLifetime() != 0);
1220 
1221         entry = AllocateEntry();
1222 
1223         if (entry == nullptr)
1224         {
1225             LogWarn("Discovered too many prefixes, ignore on-link prefix %s", prefix.ToString().AsCString());
1226             ExitNow();
1227         }
1228 
1229         entry->SetFrom(aPio);
1230         aRouter.mEntries.Push(*entry);
1231     }
1232     else
1233     {
1234         Entry newEntry;
1235 
1236         newEntry.SetFrom(aPio);
1237         entry->AdoptValidAndPreferredLifetimesFrom(newEntry);
1238     }
1239 
1240     mEntryTimer.FireAtIfEarlier(entry->GetExpireTime());
1241 
1242     SignalTableChanged();
1243 
1244 exit:
1245     return;
1246 }
1247 
ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption & aRio,Router & aRouter)1248 void RoutingManager::DiscoveredPrefixTable::ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio,
1249                                                                    Router                         &aRouter)
1250 {
1251     Ip6::Prefix prefix;
1252     Entry      *entry;
1253 
1254     VerifyOrExit(aRio.IsValid());
1255     aRio.GetPrefix(prefix);
1256 
1257     VerifyOrExit(Get<RoutingManager>().ShouldProcessRouteInfoOption(aRio, prefix));
1258 
1259     LogInfo("Processing RIO (%s, %lu seconds)", prefix.ToString().AsCString(), ToUlong(aRio.GetRouteLifetime()));
1260 
1261     entry = aRouter.mEntries.FindMatching(Entry::Matcher(prefix, Entry::kTypeRoute));
1262 
1263     if (entry == nullptr)
1264     {
1265         VerifyOrExit(aRio.GetRouteLifetime() != 0);
1266 
1267         entry = AllocateEntry();
1268 
1269         if (entry == nullptr)
1270         {
1271             LogWarn("Discovered too many prefixes, ignore route prefix %s", prefix.ToString().AsCString());
1272             ExitNow();
1273         }
1274 
1275         entry->SetFrom(aRio);
1276         aRouter.mEntries.Push(*entry);
1277     }
1278     else
1279     {
1280         entry->SetFrom(aRio);
1281     }
1282 
1283     mEntryTimer.FireAtIfEarlier(entry->GetExpireTime());
1284 
1285     SignalTableChanged();
1286 
1287 exit:
1288     return;
1289 }
1290 
Contains(const Entry::Checker & aChecker) const1291 bool RoutingManager::DiscoveredPrefixTable::Contains(const Entry::Checker &aChecker) const
1292 {
1293     bool contains = false;
1294 
1295     for (const Router &router : mRouters)
1296     {
1297         if (router.mEntries.ContainsMatching(aChecker))
1298         {
1299             contains = true;
1300             break;
1301         }
1302     }
1303 
1304     return contains;
1305 }
1306 
ContainsDefaultOrNonUlaRoutePrefix(void) const1307 bool RoutingManager::DiscoveredPrefixTable::ContainsDefaultOrNonUlaRoutePrefix(void) const
1308 {
1309     return Contains(Entry::Checker(Entry::Checker::kIsNotUla, Entry::kTypeRoute));
1310 }
1311 
ContainsNonUlaOnLinkPrefix(void) const1312 bool RoutingManager::DiscoveredPrefixTable::ContainsNonUlaOnLinkPrefix(void) const
1313 {
1314     return Contains(Entry::Checker(Entry::Checker::kIsNotUla, Entry::kTypeOnLink));
1315 }
1316 
ContainsUlaOnLinkPrefix(void) const1317 bool RoutingManager::DiscoveredPrefixTable::ContainsUlaOnLinkPrefix(void) const
1318 {
1319     return Contains(Entry::Checker(Entry::Checker::kIsUla, Entry::kTypeOnLink));
1320 }
1321 
FindFavoredOnLinkPrefix(Ip6::Prefix & aPrefix) const1322 void RoutingManager::DiscoveredPrefixTable::FindFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const
1323 {
1324     // Find the smallest preferred on-link prefix entry in the table
1325     // and return it in `aPrefix`. If there is none, `aPrefix` is
1326     // cleared (prefix length is set to zero).
1327 
1328     aPrefix.Clear();
1329 
1330     for (const Router &router : mRouters)
1331     {
1332         for (const Entry &entry : router.mEntries)
1333         {
1334             if (!entry.IsOnLinkPrefix() || entry.IsDeprecated())
1335             {
1336                 continue;
1337             }
1338 
1339             if ((aPrefix.GetLength() == 0) || (entry.GetPrefix() < aPrefix))
1340             {
1341                 aPrefix = entry.GetPrefix();
1342             }
1343         }
1344     }
1345 }
1346 
RemoveOnLinkPrefix(const Ip6::Prefix & aPrefix)1347 void RoutingManager::DiscoveredPrefixTable::RemoveOnLinkPrefix(const Ip6::Prefix &aPrefix)
1348 {
1349     RemovePrefix(Entry::Matcher(aPrefix, Entry::kTypeOnLink));
1350 }
1351 
RemoveRoutePrefix(const Ip6::Prefix & aPrefix)1352 void RoutingManager::DiscoveredPrefixTable::RemoveRoutePrefix(const Ip6::Prefix &aPrefix)
1353 {
1354     RemovePrefix(Entry::Matcher(aPrefix, Entry::kTypeRoute));
1355 }
1356 
RemovePrefix(const Entry::Matcher & aMatcher)1357 void RoutingManager::DiscoveredPrefixTable::RemovePrefix(const Entry::Matcher &aMatcher)
1358 {
1359     // Removes all entries matching a given prefix from the table.
1360 
1361     LinkedList<Entry> removedEntries;
1362 
1363     for (Router &router : mRouters)
1364     {
1365         router.mEntries.RemoveAllMatching(aMatcher, removedEntries);
1366     }
1367 
1368     VerifyOrExit(!removedEntries.IsEmpty());
1369 
1370     FreeEntries(removedEntries);
1371     RemoveRoutersWithNoEntries();
1372 
1373     SignalTableChanged();
1374 
1375 exit:
1376     return;
1377 }
1378 
RemoveAllEntries(void)1379 void RoutingManager::DiscoveredPrefixTable::RemoveAllEntries(void)
1380 {
1381     // Remove all entries from the table and unpublish them
1382     // from Network Data.
1383 
1384     for (Router &router : mRouters)
1385     {
1386         Entry *entry;
1387 
1388         while ((entry = router.mEntries.Pop()) != nullptr)
1389         {
1390             FreeEntry(*entry);
1391             SignalTableChanged();
1392         }
1393     }
1394 
1395     RemoveRoutersWithNoEntries();
1396     mEntryTimer.Stop();
1397 }
1398 
RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold)1399 void RoutingManager::DiscoveredPrefixTable::RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold)
1400 {
1401     // Remove route prefix entries and deprecate on-link entries in
1402     // the table that are old (not updated since `aTimeThreshold`).
1403 
1404     for (Router &router : mRouters)
1405     {
1406         for (Entry &entry : router.mEntries)
1407         {
1408             if (entry.GetLastUpdateTime() <= aTimeThreshold)
1409             {
1410                 if (entry.IsOnLinkPrefix())
1411                 {
1412                     entry.ClearPreferredLifetime();
1413                 }
1414                 else
1415                 {
1416                     entry.ClearValidLifetime();
1417                 }
1418 
1419                 SignalTableChanged();
1420             }
1421         }
1422     }
1423 
1424     RemoveExpiredEntries();
1425 }
1426 
RemoveOrDeprecateEntriesFromInactiveRouters(void)1427 void RoutingManager::DiscoveredPrefixTable::RemoveOrDeprecateEntriesFromInactiveRouters(void)
1428 {
1429     // Remove route prefix entries and deprecate on-link prefix entries
1430     // in the table for routers that have reached the max NS probe
1431     // attempts and considered as inactive.
1432 
1433     for (Router &router : mRouters)
1434     {
1435         if (router.mNsProbeCount <= Router::kMaxNsProbes)
1436         {
1437             continue;
1438         }
1439 
1440         for (Entry &entry : router.mEntries)
1441         {
1442             if (entry.IsOnLinkPrefix() && !entry.IsDeprecated())
1443             {
1444                 entry.ClearPreferredLifetime();
1445                 SignalTableChanged();
1446             }
1447             else
1448             {
1449                 entry.ClearValidLifetime();
1450             }
1451         }
1452     }
1453 
1454     RemoveExpiredEntries();
1455 }
1456 
CalculateNextStaleTime(TimeMilli aNow) const1457 TimeMilli RoutingManager::DiscoveredPrefixTable::CalculateNextStaleTime(TimeMilli aNow) const
1458 {
1459     TimeMilli onLinkStaleTime = aNow;
1460     TimeMilli routeStaleTime  = aNow.GetDistantFuture();
1461     bool      foundOnLink     = false;
1462 
1463     // For on-link prefixes, we consider stale time as when all on-link
1464     // prefixes become stale (the latest stale time) but for route
1465     // prefixes we consider the earliest stale time.
1466 
1467     for (const Router &router : mRouters)
1468     {
1469         for (const Entry &entry : router.mEntries)
1470         {
1471             TimeMilli entryStaleTime = Max(aNow, entry.GetStaleTime());
1472 
1473             if (entry.IsOnLinkPrefix() && !entry.IsDeprecated())
1474             {
1475                 onLinkStaleTime = Max(onLinkStaleTime, entryStaleTime);
1476                 foundOnLink     = true;
1477             }
1478 
1479             if (!entry.IsOnLinkPrefix())
1480             {
1481                 routeStaleTime = Min(routeStaleTime, entryStaleTime);
1482             }
1483         }
1484     }
1485 
1486     return foundOnLink ? Min(onLinkStaleTime, routeStaleTime) : routeStaleTime;
1487 }
1488 
RemoveRoutersWithNoEntries(void)1489 void RoutingManager::DiscoveredPrefixTable::RemoveRoutersWithNoEntries(void)
1490 {
1491     mRouters.RemoveAllMatching(Router::kContainsNoEntries);
1492 }
1493 
FreeEntries(LinkedList<Entry> & aEntries)1494 void RoutingManager::DiscoveredPrefixTable::FreeEntries(LinkedList<Entry> &aEntries)
1495 {
1496     // Frees all entries in the given list `aEntries` (put them back
1497     // in the entry pool).
1498 
1499     Entry *entry;
1500 
1501     while ((entry = aEntries.Pop()) != nullptr)
1502     {
1503         FreeEntry(*entry);
1504     }
1505 }
1506 
FindFavoredEntryToPublish(const Ip6::Prefix & aPrefix) const1507 const RoutingManager::DiscoveredPrefixTable::Entry *RoutingManager::DiscoveredPrefixTable::FindFavoredEntryToPublish(
1508     const Ip6::Prefix &aPrefix) const
1509 {
1510     // Finds the favored entry matching a given `aPrefix` in the table
1511     // to publish in the Network Data. We can have multiple entries
1512     // in the table matching the same `aPrefix` from different
1513     // routers and potentially with different preference values. We
1514     // select the one with the highest preference as the favored
1515     // entry to publish.
1516 
1517     const Entry *favoredEntry = nullptr;
1518 
1519     for (const Router &router : mRouters)
1520     {
1521         for (const Entry &entry : router.mEntries)
1522         {
1523             if (entry.GetPrefix() != aPrefix)
1524             {
1525                 continue;
1526             }
1527 
1528             if ((favoredEntry == nullptr) || (entry.GetPreference() > favoredEntry->GetPreference()))
1529             {
1530                 favoredEntry = &entry;
1531             }
1532         }
1533     }
1534 
1535     return favoredEntry;
1536 }
1537 
HandleEntryTimer(void)1538 void RoutingManager::DiscoveredPrefixTable::HandleEntryTimer(void) { RemoveExpiredEntries(); }
1539 
RemoveExpiredEntries(void)1540 void RoutingManager::DiscoveredPrefixTable::RemoveExpiredEntries(void)
1541 {
1542     TimeMilli         now            = TimerMilli::GetNow();
1543     TimeMilli         nextExpireTime = now.GetDistantFuture();
1544     LinkedList<Entry> expiredEntries;
1545 
1546     for (Router &router : mRouters)
1547     {
1548         router.mEntries.RemoveAllMatching(Entry::ExpirationChecker(now), expiredEntries);
1549     }
1550 
1551     RemoveRoutersWithNoEntries();
1552 
1553     if (!expiredEntries.IsEmpty())
1554     {
1555         SignalTableChanged();
1556     }
1557 
1558     FreeEntries(expiredEntries);
1559 
1560     // Determine the next expire time and schedule timer.
1561 
1562     for (const Router &router : mRouters)
1563     {
1564         for (const Entry &entry : router.mEntries)
1565         {
1566             nextExpireTime = Min(nextExpireTime, entry.GetExpireTime());
1567         }
1568     }
1569 
1570     if (nextExpireTime != now.GetDistantFuture())
1571     {
1572         mEntryTimer.FireAt(nextExpireTime);
1573     }
1574 }
1575 
SignalTableChanged(void)1576 void RoutingManager::DiscoveredPrefixTable::SignalTableChanged(void) { mSignalTask.Post(); }
1577 
ProcessNeighborAdvertMessage(const Ip6::Nd::NeighborAdvertMessage & aNaMessage)1578 void RoutingManager::DiscoveredPrefixTable::ProcessNeighborAdvertMessage(
1579     const Ip6::Nd::NeighborAdvertMessage &aNaMessage)
1580 {
1581     Router *router;
1582 
1583     VerifyOrExit(aNaMessage.IsValid());
1584 
1585     router = mRouters.FindMatching(aNaMessage.GetTargetAddress());
1586     VerifyOrExit(router != nullptr);
1587 
1588     LogInfo("Received NA from router %s", router->mAddress.ToString().AsCString());
1589 
1590     UpdateRouterOnRx(*router);
1591 
1592 exit:
1593     return;
1594 }
1595 
UpdateRouterOnRx(Router & aRouter)1596 void RoutingManager::DiscoveredPrefixTable::UpdateRouterOnRx(Router &aRouter)
1597 {
1598     aRouter.mNsProbeCount = 0;
1599     aRouter.mTimeout = TimerMilli::GetNow() + Random::NonCrypto::AddJitter(Router::kActiveTimeout, Router::kJitter);
1600 
1601     mRouterTimer.FireAtIfEarlier(aRouter.mTimeout);
1602 }
1603 
HandleRouterTimer(void)1604 void RoutingManager::DiscoveredPrefixTable::HandleRouterTimer(void)
1605 {
1606     TimeMilli now      = TimerMilli::GetNow();
1607     TimeMilli nextTime = now.GetDistantFuture();
1608 
1609     for (Router &router : mRouters)
1610     {
1611         if (router.mNsProbeCount > Router::kMaxNsProbes)
1612         {
1613             continue;
1614         }
1615 
1616         // If the `router` emitting RA has an address belonging to
1617         // infra interface, it indicates that the RAs are from
1618         // same device. In this case we skip performing NS probes.
1619         // This addresses situation where platform may not be
1620         // be able to receive and pass the NA message response
1621         // from device itself.
1622 
1623         if (Get<RoutingManager>().mInfraIf.HasAddress(router.mAddress))
1624         {
1625             continue;
1626         }
1627 
1628         if (router.mTimeout <= now)
1629         {
1630             router.mNsProbeCount++;
1631 
1632             if (router.mNsProbeCount > Router::kMaxNsProbes)
1633             {
1634                 LogInfo("No response to all Neighbor Solicitations attempts from router %s",
1635                         router.mAddress.ToString().AsCString());
1636                 continue;
1637             }
1638 
1639             router.mTimeout = now + ((router.mNsProbeCount < Router::kMaxNsProbes) ? Router::kNsProbeRetryInterval
1640                                                                                    : Router::kNsProbeTimeout);
1641 
1642             SendNeighborSolicitToRouter(router);
1643         }
1644 
1645         nextTime = Min(nextTime, router.mTimeout);
1646     }
1647 
1648     RemoveOrDeprecateEntriesFromInactiveRouters();
1649 
1650     if (nextTime != now.GetDistantFuture())
1651     {
1652         mRouterTimer.FireAtIfEarlier(nextTime);
1653     }
1654 }
1655 
SendNeighborSolicitToRouter(const Router & aRouter)1656 void RoutingManager::DiscoveredPrefixTable::SendNeighborSolicitToRouter(const Router &aRouter)
1657 {
1658     InfraIf::Icmp6Packet            packet;
1659     Ip6::Nd::NeighborSolicitMessage neighborSolicitMsg;
1660 
1661     VerifyOrExit(!Get<RoutingManager>().mRsSender.IsInProgress());
1662 
1663     neighborSolicitMsg.SetTargetAddress(aRouter.mAddress);
1664     packet.InitFrom(neighborSolicitMsg);
1665 
1666     IgnoreError(Get<RoutingManager>().mInfraIf.Send(packet, aRouter.mAddress));
1667 
1668     LogInfo("Sent Neighbor Solicitation to %s - attempt:%u/%u", aRouter.mAddress.ToString().AsCString(),
1669             aRouter.mNsProbeCount, Router::kMaxNsProbes);
1670 
1671 exit:
1672     return;
1673 }
1674 
InitIterator(PrefixTableIterator & aIterator) const1675 void RoutingManager::DiscoveredPrefixTable::InitIterator(PrefixTableIterator &aIterator) const
1676 {
1677     Iterator &iterator = static_cast<Iterator &>(aIterator);
1678 
1679     iterator.SetInitTime();
1680     iterator.SetRouter(mRouters.Front());
1681     iterator.SetEntry(mRouters.IsEmpty() ? nullptr : mRouters[0].mEntries.GetHead());
1682 }
1683 
GetNextEntry(PrefixTableIterator & aIterator,PrefixTableEntry & aEntry) const1684 Error RoutingManager::DiscoveredPrefixTable::GetNextEntry(PrefixTableIterator &aIterator,
1685                                                           PrefixTableEntry    &aEntry) const
1686 {
1687     Error     error    = kErrorNone;
1688     Iterator &iterator = static_cast<Iterator &>(aIterator);
1689 
1690     VerifyOrExit(iterator.GetRouter() != nullptr, error = kErrorNotFound);
1691     OT_ASSERT(iterator.GetEntry() != nullptr);
1692 
1693     aEntry.mRouterAddress       = iterator.GetRouter()->mAddress;
1694     aEntry.mPrefix              = iterator.GetEntry()->GetPrefix();
1695     aEntry.mIsOnLink            = iterator.GetEntry()->IsOnLinkPrefix();
1696     aEntry.mMsecSinceLastUpdate = iterator.GetInitTime() - iterator.GetEntry()->GetLastUpdateTime();
1697     aEntry.mValidLifetime       = iterator.GetEntry()->GetValidLifetime();
1698     aEntry.mPreferredLifetime   = aEntry.mIsOnLink ? iterator.GetEntry()->GetPreferredLifetime() : 0;
1699     aEntry.mRoutePreference =
1700         static_cast<otRoutePreference>(aEntry.mIsOnLink ? 0 : iterator.GetEntry()->GetRoutePreference());
1701 
1702     // Advance the iterator
1703     iterator.SetEntry(iterator.GetEntry()->GetNext());
1704 
1705     if (iterator.GetEntry() == nullptr)
1706     {
1707         if (iterator.GetRouter() != mRouters.Back())
1708         {
1709             iterator.SetRouter(iterator.GetRouter() + 1);
1710             iterator.SetEntry(iterator.GetRouter()->mEntries.GetHead());
1711         }
1712         else
1713         {
1714             iterator.SetRouter(nullptr);
1715         }
1716     }
1717 
1718 exit:
1719     return error;
1720 }
1721 
1722 //---------------------------------------------------------------------------------------------------------------------
1723 // DiscoveredPrefixTable::Entry
1724 
SetFrom(const Ip6::Nd::RouterAdvertMessage::Header & aRaHeader)1725 void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader)
1726 {
1727     mPrefix.Clear();
1728     mType                    = kTypeRoute;
1729     mValidLifetime           = aRaHeader.GetRouterLifetime();
1730     mShared.mRoutePreference = aRaHeader.GetDefaultRouterPreference();
1731     mLastUpdateTime          = TimerMilli::GetNow();
1732 }
1733 
SetFrom(const Ip6::Nd::PrefixInfoOption & aPio)1734 void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::PrefixInfoOption &aPio)
1735 {
1736     aPio.GetPrefix(mPrefix);
1737     mType                      = kTypeOnLink;
1738     mValidLifetime             = aPio.GetValidLifetime();
1739     mShared.mPreferredLifetime = aPio.GetPreferredLifetime();
1740     mLastUpdateTime            = TimerMilli::GetNow();
1741 }
1742 
SetFrom(const Ip6::Nd::RouteInfoOption & aRio)1743 void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::RouteInfoOption &aRio)
1744 {
1745     aRio.GetPrefix(mPrefix);
1746     mType                    = kTypeRoute;
1747     mValidLifetime           = aRio.GetRouteLifetime();
1748     mShared.mRoutePreference = aRio.GetPreference();
1749     mLastUpdateTime          = TimerMilli::GetNow();
1750 }
1751 
operator ==(const Entry & aOther) const1752 bool RoutingManager::DiscoveredPrefixTable::Entry::operator==(const Entry &aOther) const
1753 {
1754     return (mType == aOther.mType) && (mPrefix == aOther.mPrefix);
1755 }
1756 
Matches(const Matcher & aMatcher) const1757 bool RoutingManager::DiscoveredPrefixTable::Entry::Matches(const Matcher &aMatcher) const
1758 {
1759     return (mType == aMatcher.mType) && (mPrefix == aMatcher.mPrefix);
1760 }
1761 
Matches(const Checker & aChecker) const1762 bool RoutingManager::DiscoveredPrefixTable::Entry::Matches(const Checker &aChecker) const
1763 {
1764     return (mType == aChecker.mType) && (mPrefix.IsUniqueLocal() == (aChecker.mMode == Checker::kIsUla));
1765 }
1766 
Matches(const ExpirationChecker & aChecker) const1767 bool RoutingManager::DiscoveredPrefixTable::Entry::Matches(const ExpirationChecker &aChecker) const
1768 {
1769     return GetExpireTime() <= aChecker.mNow;
1770 }
1771 
GetExpireTime(void) const1772 TimeMilli RoutingManager::DiscoveredPrefixTable::Entry::GetExpireTime(void) const
1773 {
1774     return mLastUpdateTime + CalculateExpireDelay(mValidLifetime);
1775 }
1776 
GetStaleTime(void) const1777 TimeMilli RoutingManager::DiscoveredPrefixTable::Entry::GetStaleTime(void) const
1778 {
1779     uint32_t delay = Min(kRtrAdvStaleTime, IsOnLinkPrefix() ? GetPreferredLifetime() : mValidLifetime);
1780 
1781     return mLastUpdateTime + TimeMilli::SecToMsec(delay);
1782 }
1783 
IsDeprecated(void) const1784 bool RoutingManager::DiscoveredPrefixTable::Entry::IsDeprecated(void) const
1785 {
1786     OT_ASSERT(IsOnLinkPrefix());
1787 
1788     return mLastUpdateTime + TimeMilli::SecToMsec(GetPreferredLifetime()) <= TimerMilli::GetNow();
1789 }
1790 
GetPreference(void) const1791 RoutingManager::RoutePreference RoutingManager::DiscoveredPrefixTable::Entry::GetPreference(void) const
1792 {
1793     // Returns the preference level to use when we publish
1794     // the prefix entry in Network Data.
1795 
1796     return IsOnLinkPrefix() ? NetworkData::kRoutePreferenceMedium : GetRoutePreference();
1797 }
1798 
AdoptValidAndPreferredLifetimesFrom(const Entry & aEntry)1799 void RoutingManager::DiscoveredPrefixTable::Entry::AdoptValidAndPreferredLifetimesFrom(const Entry &aEntry)
1800 {
1801     constexpr uint32_t kTwoHoursInSeconds = 2 * 3600;
1802 
1803     // Per RFC 4862 section 5.5.3.e:
1804     //
1805     // 1.  If the received Valid Lifetime is greater than 2 hours or
1806     //     greater than RemainingLifetime, set the valid lifetime of the
1807     //     corresponding address to the advertised Valid Lifetime.
1808     // 2.  If RemainingLifetime is less than or equal to 2 hours, ignore
1809     //     the Prefix Information option with regards to the valid
1810     //     lifetime, unless ...
1811     // 3.  Otherwise, reset the valid lifetime of the corresponding
1812     //     address to 2 hours.
1813 
1814     if (aEntry.mValidLifetime > kTwoHoursInSeconds || aEntry.GetExpireTime() > GetExpireTime())
1815     {
1816         mValidLifetime = aEntry.mValidLifetime;
1817     }
1818     else if (GetExpireTime() > TimerMilli::GetNow() + TimeMilli::SecToMsec(kTwoHoursInSeconds))
1819     {
1820         mValidLifetime = kTwoHoursInSeconds;
1821     }
1822 
1823     mShared.mPreferredLifetime = aEntry.GetPreferredLifetime();
1824     mLastUpdateTime            = aEntry.GetLastUpdateTime();
1825 }
1826 
CalculateExpireDelay(uint32_t aValidLifetime)1827 uint32_t RoutingManager::DiscoveredPrefixTable::Entry::CalculateExpireDelay(uint32_t aValidLifetime)
1828 {
1829     uint32_t delay;
1830 
1831     if (aValidLifetime * static_cast<uint64_t>(1000) > Timer::kMaxDelay)
1832     {
1833         delay = Timer::kMaxDelay;
1834     }
1835     else
1836     {
1837         delay = aValidLifetime * 1000;
1838     }
1839 
1840     return delay;
1841 }
1842 
1843 //---------------------------------------------------------------------------------------------------------------------
1844 // FavoredOmrPrefix
1845 
IsInfrastructureDerived(void) const1846 bool RoutingManager::FavoredOmrPrefix::IsInfrastructureDerived(void) const
1847 {
1848     // Indicate whether the OMR prefix is infrastructure-derived which
1849     // can be identified as a valid OMR prefix with preference of
1850     // medium or higher.
1851 
1852     return !IsEmpty() && (mPreference >= NetworkData::kRoutePreferenceMedium);
1853 }
1854 
SetFrom(const NetworkData::OnMeshPrefixConfig & aOnMeshPrefixConfig)1855 void RoutingManager::FavoredOmrPrefix::SetFrom(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig)
1856 {
1857     mPrefix         = aOnMeshPrefixConfig.GetPrefix();
1858     mPreference     = aOnMeshPrefixConfig.GetPreference();
1859     mIsDomainPrefix = aOnMeshPrefixConfig.mDp;
1860 }
1861 
SetFrom(const OmrPrefix & aOmrPrefix)1862 void RoutingManager::FavoredOmrPrefix::SetFrom(const OmrPrefix &aOmrPrefix)
1863 {
1864     mPrefix         = aOmrPrefix.GetPrefix();
1865     mPreference     = aOmrPrefix.GetPreference();
1866     mIsDomainPrefix = aOmrPrefix.IsDomainPrefix();
1867 }
1868 
IsFavoredOver(const NetworkData::OnMeshPrefixConfig & aOmrPrefixConfig) const1869 bool RoutingManager::FavoredOmrPrefix::IsFavoredOver(const NetworkData::OnMeshPrefixConfig &aOmrPrefixConfig) const
1870 {
1871     // This method determines whether this OMR prefix is favored
1872     // over another prefix. A prefix with higher preference is
1873     // favored. If the preference is the same, then the smaller
1874     // prefix (in the sense defined by `Ip6::Prefix`) is favored.
1875 
1876     bool isFavored = (mPreference > aOmrPrefixConfig.GetPreference());
1877 
1878     OT_ASSERT(IsValidOmrPrefix(aOmrPrefixConfig));
1879 
1880     if (mPreference == aOmrPrefixConfig.GetPreference())
1881     {
1882         isFavored = (mPrefix < aOmrPrefixConfig.GetPrefix());
1883     }
1884 
1885     return isFavored;
1886 }
1887 
1888 //---------------------------------------------------------------------------------------------------------------------
1889 // OmrPrefixManager
1890 
OmrPrefixManager(Instance & aInstance)1891 RoutingManager::OmrPrefixManager::OmrPrefixManager(Instance &aInstance)
1892     : InstanceLocator(aInstance)
1893     , mIsLocalAddedInNetData(false)
1894     , mDefaultRoute(false)
1895 {
1896 }
1897 
Init(const Ip6::Prefix & aBrUlaPrefix)1898 void RoutingManager::OmrPrefixManager::Init(const Ip6::Prefix &aBrUlaPrefix)
1899 {
1900     mLocalPrefix.mPrefix = aBrUlaPrefix;
1901     mLocalPrefix.mPrefix.SetSubnetId(kOmrPrefixSubnetId);
1902     mLocalPrefix.mPrefix.SetLength(kOmrPrefixLength);
1903     mLocalPrefix.mPreference     = NetworkData::kRoutePreferenceLow;
1904     mLocalPrefix.mIsDomainPrefix = false;
1905 
1906     LogInfo("Generated local OMR prefix: %s", mLocalPrefix.mPrefix.ToString().AsCString());
1907 }
1908 
Start(void)1909 void RoutingManager::OmrPrefixManager::Start(void) { DetermineFavoredPrefix(); }
1910 
Stop(void)1911 void RoutingManager::OmrPrefixManager::Stop(void)
1912 {
1913     RemoveLocalFromNetData();
1914     mFavoredPrefix.Clear();
1915 }
1916 
DetermineFavoredPrefix(void)1917 void RoutingManager::OmrPrefixManager::DetermineFavoredPrefix(void)
1918 {
1919     // Determine the favored OMR prefix present in Network Data.
1920 
1921     NetworkData::Iterator           iterator = NetworkData::kIteratorInit;
1922     NetworkData::OnMeshPrefixConfig prefixConfig;
1923 
1924     mFavoredPrefix.Clear();
1925 
1926     while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
1927     {
1928         if (!IsValidOmrPrefix(prefixConfig) || !prefixConfig.mPreferred)
1929         {
1930             continue;
1931         }
1932 
1933         if (mFavoredPrefix.IsEmpty() || !mFavoredPrefix.IsFavoredOver(prefixConfig))
1934         {
1935             mFavoredPrefix.SetFrom(prefixConfig);
1936         }
1937     }
1938 }
1939 
Evaluate(void)1940 void RoutingManager::OmrPrefixManager::Evaluate(void)
1941 {
1942     OT_ASSERT(Get<RoutingManager>().IsRunning());
1943 
1944     DetermineFavoredPrefix();
1945 
1946     // Decide if we need to add or remove our local OMR prefix.
1947 
1948     if (mFavoredPrefix.IsEmpty())
1949     {
1950         LogInfo("No favored OMR prefix found in Thread network");
1951 
1952         // The `mFavoredPrefix` remains empty if we fail to publish
1953         // the local OMR prefix.
1954         SuccessOrExit(AddLocalToNetData());
1955 
1956         mFavoredPrefix.SetFrom(mLocalPrefix);
1957     }
1958     else if (mFavoredPrefix.GetPrefix() == mLocalPrefix.GetPrefix())
1959     {
1960         IgnoreError(AddLocalToNetData());
1961     }
1962     else if (mIsLocalAddedInNetData)
1963     {
1964         LogInfo("There is already a favored OMR prefix %s in the Thread network",
1965                 mFavoredPrefix.GetPrefix().ToString().AsCString());
1966 
1967         RemoveLocalFromNetData();
1968     }
1969 
1970 exit:
1971     return;
1972 }
1973 
AddLocalToNetData(void)1974 Error RoutingManager::OmrPrefixManager::AddLocalToNetData(void)
1975 {
1976     Error error = kErrorNone;
1977 
1978     VerifyOrExit(!mIsLocalAddedInNetData);
1979     SuccessOrExit(error = AddOrUpdateLocalInNetData());
1980     mIsLocalAddedInNetData = true;
1981 
1982 exit:
1983     return error;
1984 }
1985 
AddOrUpdateLocalInNetData(void)1986 Error RoutingManager::OmrPrefixManager::AddOrUpdateLocalInNetData(void)
1987 {
1988     // Add the local OMR prefix in Thread Network Data or update it
1989     // (e.g., change default route flag) if it is already added.
1990 
1991     Error                           error;
1992     NetworkData::OnMeshPrefixConfig config;
1993 
1994     config.Clear();
1995     config.mPrefix       = mLocalPrefix.GetPrefix();
1996     config.mStable       = true;
1997     config.mSlaac        = true;
1998     config.mPreferred    = true;
1999     config.mOnMesh       = true;
2000     config.mDefaultRoute = mDefaultRoute;
2001     config.mPreference   = mLocalPrefix.GetPreference();
2002 
2003     error = Get<NetworkData::Local>().AddOnMeshPrefix(config);
2004 
2005     if (error != kErrorNone)
2006     {
2007         LogWarn("Failed to %s %s in Thread Network Data: %s", !mIsLocalAddedInNetData ? "add" : "update",
2008                 LocalToString().AsCString(), ErrorToString(error));
2009         ExitNow();
2010     }
2011 
2012     Get<NetworkData::Notifier>().HandleServerDataUpdated();
2013 
2014     LogInfo("%s %s in Thread Network Data", !mIsLocalAddedInNetData ? "Added" : "Updated", LocalToString().AsCString());
2015 
2016 exit:
2017     return error;
2018 }
2019 
RemoveLocalFromNetData(void)2020 void RoutingManager::OmrPrefixManager::RemoveLocalFromNetData(void)
2021 {
2022     Error error = kErrorNone;
2023 
2024     VerifyOrExit(mIsLocalAddedInNetData);
2025 
2026     error = Get<NetworkData::Local>().RemoveOnMeshPrefix(mLocalPrefix.GetPrefix());
2027 
2028     if (error != kErrorNone)
2029     {
2030         LogWarn("Failed to remove %s from Thread Network Data: %s", LocalToString().AsCString(), ErrorToString(error));
2031         ExitNow();
2032     }
2033 
2034     mIsLocalAddedInNetData = false;
2035     Get<NetworkData::Notifier>().HandleServerDataUpdated();
2036     LogInfo("Removed %s from Thread Network Data", LocalToString().AsCString());
2037 
2038 exit:
2039     return;
2040 }
2041 
UpdateDefaultRouteFlag(bool aDefaultRoute)2042 void RoutingManager::OmrPrefixManager::UpdateDefaultRouteFlag(bool aDefaultRoute)
2043 {
2044     VerifyOrExit(aDefaultRoute != mDefaultRoute);
2045 
2046     mDefaultRoute = aDefaultRoute;
2047 
2048     VerifyOrExit(mIsLocalAddedInNetData);
2049     IgnoreError(AddOrUpdateLocalInNetData());
2050 
2051 exit:
2052     return;
2053 }
2054 
LocalToString(void) const2055 RoutingManager::OmrPrefixManager::InfoString RoutingManager::OmrPrefixManager::LocalToString(void) const
2056 {
2057     InfoString string;
2058 
2059     string.Append("local OMR prefix %s (def-route:%s)", mLocalPrefix.GetPrefix().ToString().AsCString(),
2060                   ToYesNo(mDefaultRoute));
2061     return string;
2062 }
2063 
2064 //---------------------------------------------------------------------------------------------------------------------
2065 // OnLinkPrefixManager
2066 
OnLinkPrefixManager(Instance & aInstance)2067 RoutingManager::OnLinkPrefixManager::OnLinkPrefixManager(Instance &aInstance)
2068     : InstanceLocator(aInstance)
2069     , mState(kIdle)
2070     , mTimer(aInstance)
2071 {
2072     mLocalPrefix.Clear();
2073     mFavoredDiscoveredPrefix.Clear();
2074     mOldLocalPrefixes.Clear();
2075 }
2076 
Init(void)2077 void RoutingManager::OnLinkPrefixManager::Init(void)
2078 {
2079     TimeMilli                now = TimerMilli::GetNow();
2080     Settings::BrOnLinkPrefix savedPrefix;
2081     bool                     refreshStoredPrefixes = false;
2082 
2083     // Restore old prefixes from `Settings`
2084 
2085     for (int index = 0; Get<Settings>().ReadBrOnLinkPrefix(index, savedPrefix) == kErrorNone; index++)
2086     {
2087         uint32_t   lifetime;
2088         OldPrefix *entry;
2089 
2090         if (mOldLocalPrefixes.ContainsMatching(savedPrefix.GetPrefix()))
2091         {
2092             // We should not see duplicate entries in `Settings`
2093             // but if we do we refresh the stored prefixes to make
2094             // it consistent.
2095             refreshStoredPrefixes = true;
2096             continue;
2097         }
2098 
2099         entry = mOldLocalPrefixes.PushBack();
2100 
2101         if (entry == nullptr)
2102         {
2103             // If there are more stored prefixes, we refresh the
2104             // prefixes in `Settings` to remove the ones we cannot
2105             // handle.
2106 
2107             refreshStoredPrefixes = true;
2108             break;
2109         }
2110 
2111         lifetime = Min(savedPrefix.GetLifetime(), Time::MsecToSec(TimerMilli::kMaxDelay));
2112 
2113         entry->mPrefix     = savedPrefix.GetPrefix();
2114         entry->mExpireTime = now + Time::SecToMsec(lifetime);
2115 
2116         LogInfo("Restored old prefix %s, lifetime:%lu", entry->mPrefix.ToString().AsCString(), ToUlong(lifetime));
2117 
2118         mTimer.FireAtIfEarlier(entry->mExpireTime);
2119     }
2120 
2121     if (refreshStoredPrefixes)
2122     {
2123         // We clear the entries in `Settings` and re-write the entries
2124         // from `mOldLocalPrefixes` array.
2125 
2126         IgnoreError(Get<Settings>().DeleteAllBrOnLinkPrefixes());
2127 
2128         for (OldPrefix &oldPrefix : mOldLocalPrefixes)
2129         {
2130             SavePrefix(oldPrefix.mPrefix, oldPrefix.mExpireTime);
2131         }
2132     }
2133 
2134     GenerateLocalPrefix();
2135 }
2136 
GenerateLocalPrefix(void)2137 void RoutingManager::OnLinkPrefixManager::GenerateLocalPrefix(void)
2138 {
2139     const MeshCoP::ExtendedPanId &extPanId = Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId();
2140     OldPrefix                    *entry;
2141     Ip6::Prefix                   oldLocalPrefix = mLocalPrefix;
2142 
2143     // Global ID: 40 most significant bits of Extended PAN ID
2144     // Subnet ID: 16 least significant bits of Extended PAN ID
2145 
2146     mLocalPrefix.mPrefix.mFields.m8[0] = 0xfd;
2147     memcpy(mLocalPrefix.mPrefix.mFields.m8 + 1, extPanId.m8, 5);
2148     memcpy(mLocalPrefix.mPrefix.mFields.m8 + 6, extPanId.m8 + 6, 2);
2149 
2150     mLocalPrefix.SetLength(kOnLinkPrefixLength);
2151 
2152     // We ensure that the local prefix did change, since not all the
2153     // bytes in Extended PAN ID are used in derivation of the local prefix.
2154 
2155     VerifyOrExit(mLocalPrefix != oldLocalPrefix);
2156 
2157     LogNote("Local on-link prefix: %s", mLocalPrefix.ToString().AsCString());
2158 
2159     // Check if the new local prefix happens to be in `mOldLocalPrefixes` array.
2160     // If so, we remove it from the array and set `mState` accordingly.
2161 
2162     entry = mOldLocalPrefixes.FindMatching(mLocalPrefix);
2163 
2164     if (entry != nullptr)
2165     {
2166         mState      = kDeprecating;
2167         mExpireTime = entry->mExpireTime;
2168         mOldLocalPrefixes.Remove(*entry);
2169     }
2170     else
2171     {
2172         mState = kIdle;
2173     }
2174 
2175 exit:
2176     return;
2177 }
2178 
Start(void)2179 void RoutingManager::OnLinkPrefixManager::Start(void) {}
2180 
Stop(void)2181 void RoutingManager::OnLinkPrefixManager::Stop(void)
2182 {
2183     mFavoredDiscoveredPrefix.Clear();
2184 
2185     switch (mState)
2186     {
2187     case kIdle:
2188         break;
2189 
2190     case kPublishing:
2191     case kAdvertising:
2192     case kDeprecating:
2193         mState = kDeprecating;
2194         break;
2195     }
2196 }
2197 
Evaluate(void)2198 void RoutingManager::OnLinkPrefixManager::Evaluate(void)
2199 {
2200     VerifyOrExit(!Get<RoutingManager>().mRsSender.IsInProgress());
2201 
2202     Get<RoutingManager>().mDiscoveredPrefixTable.FindFavoredOnLinkPrefix(mFavoredDiscoveredPrefix);
2203 
2204     if ((mFavoredDiscoveredPrefix.GetLength() == 0) || (mFavoredDiscoveredPrefix == mLocalPrefix))
2205     {
2206         // We need to advertise our local on-link prefix when there is
2207         // no discovered on-link prefix. If the favored discovered
2208         // prefix is the same as our local on-link prefix we also
2209         // start advertising the local prefix to add redundancy. Note
2210         // that local on-link prefix is derived from extended PAN ID
2211         // and therefore is the same for all BRs on the same Thread
2212         // mesh.
2213 
2214         PublishAndAdvertise();
2215 
2216         // We remove the local on-link prefix from discovered prefix
2217         // table, in case it was previously discovered and included in
2218         // the table (now as a deprecating entry). We remove it with
2219         // `kKeepInNetData` flag to ensure that the prefix is not
2220         // unpublished from network data.
2221         //
2222         // Note that `ShouldProcessPrefixInfoOption()` will also check
2223         // not allow the local on-link prefix to be added in the prefix
2224         // table while we are advertising it.
2225 
2226         Get<RoutingManager>().mDiscoveredPrefixTable.RemoveOnLinkPrefix(mLocalPrefix);
2227 
2228         mFavoredDiscoveredPrefix.Clear();
2229     }
2230     else if (IsPublishingOrAdvertising())
2231     {
2232         // When an application-specific on-link prefix is received and
2233         // it is larger than the local prefix, we will not remove the
2234         // advertised local prefix. In this case, there will be two
2235         // on-link prefixes on the infra link. But all BRs will still
2236         // converge to the same smallest/favored on-link prefix and the
2237         // application-specific prefix is not used.
2238 
2239         if (!(mLocalPrefix < mFavoredDiscoveredPrefix))
2240         {
2241             LogInfo("EvaluateOnLinkPrefix: There is already favored on-link prefix %s",
2242                     mFavoredDiscoveredPrefix.ToString().AsCString());
2243             Deprecate();
2244         }
2245     }
2246 
2247 exit:
2248     return;
2249 }
2250 
IsInitalEvaluationDone(void) const2251 bool RoutingManager::OnLinkPrefixManager::IsInitalEvaluationDone(void) const
2252 {
2253     // This method indicates whether or not we are done with the
2254     // initial policy evaluation of the on-link prefixes, i.e., either
2255     // we have discovered a favored on-link prefix (being advertised by
2256     // another router on infra link) or we are advertising our local
2257     // on-link prefix.
2258 
2259     return (mFavoredDiscoveredPrefix.GetLength() != 0 || IsPublishingOrAdvertising());
2260 }
2261 
HandleDiscoveredPrefixTableChanged(void)2262 void RoutingManager::OnLinkPrefixManager::HandleDiscoveredPrefixTableChanged(void)
2263 {
2264     // This is a callback from `mDiscoveredPrefixTable` indicating that
2265     // there has been a change in the table. If the favored on-link
2266     // prefix has changed, we trigger a re-evaluation of the routing
2267     // policy.
2268 
2269     Ip6::Prefix newFavoredPrefix;
2270 
2271     Get<RoutingManager>().mDiscoveredPrefixTable.FindFavoredOnLinkPrefix(newFavoredPrefix);
2272 
2273     if (newFavoredPrefix != mFavoredDiscoveredPrefix)
2274     {
2275         Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
2276     }
2277 }
2278 
PublishAndAdvertise(void)2279 void RoutingManager::OnLinkPrefixManager::PublishAndAdvertise(void)
2280 {
2281     // Start publishing and advertising the local on-link prefix if
2282     // not already.
2283 
2284     switch (mState)
2285     {
2286     case kIdle:
2287     case kDeprecating:
2288         break;
2289 
2290     case kPublishing:
2291     case kAdvertising:
2292         ExitNow();
2293     }
2294 
2295     mState = kPublishing;
2296     ResetExpireTime(TimerMilli::GetNow());
2297 
2298     // We wait for the ULA `fc00::/7` route or a sub-prefix of it (e.g.,
2299     // default route) to be added in Network Data before
2300     // starting to advertise the local on-link prefix in RAs.
2301     // However, if it is already present in Network Data (e.g.,
2302     // added by another BR on the same Thread mesh), we can
2303     // immediately start advertising it.
2304 
2305     if (Get<RoutingManager>().NetworkDataContainsUlaRoute())
2306     {
2307         EnterAdvertisingState();
2308     }
2309 
2310 exit:
2311     return;
2312 }
2313 
Deprecate(void)2314 void RoutingManager::OnLinkPrefixManager::Deprecate(void)
2315 {
2316     // Deprecate the local on-link prefix if it was being advertised
2317     // before. While depreciating the prefix, we wait for the lifetime
2318     // timer to expire before unpublishing the prefix from the Network
2319     // Data. We also continue to include it as a PIO in the RA message
2320     // with zero preferred lifetime and the remaining valid lifetime
2321     // until the timer expires.
2322 
2323     switch (mState)
2324     {
2325     case kPublishing:
2326     case kAdvertising:
2327         mState = kDeprecating;
2328         LogInfo("Deprecate local on-link prefix %s", mLocalPrefix.ToString().AsCString());
2329         break;
2330 
2331     case kIdle:
2332     case kDeprecating:
2333         break;
2334     }
2335 }
2336 
ShouldPublishUlaRoute(void) const2337 bool RoutingManager::OnLinkPrefixManager::ShouldPublishUlaRoute(void) const
2338 {
2339     // Determine whether or not we should publish ULA prefix. We need
2340     // to publish if we are in any of `kPublishing`, `kAdvertising`,
2341     // or `kDeprecating` states, or if there is at least one old local
2342     // prefix being deprecated.
2343 
2344     return (mState != kIdle) || !mOldLocalPrefixes.IsEmpty();
2345 }
2346 
ResetExpireTime(TimeMilli aNow)2347 void RoutingManager::OnLinkPrefixManager::ResetExpireTime(TimeMilli aNow)
2348 {
2349     mExpireTime = aNow + TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime);
2350     mTimer.FireAtIfEarlier(mExpireTime);
2351     SavePrefix(mLocalPrefix, mExpireTime);
2352 }
2353 
EnterAdvertisingState(void)2354 void RoutingManager::OnLinkPrefixManager::EnterAdvertisingState(void)
2355 {
2356     mState = kAdvertising;
2357     LogInfo("Start advertising local on-link prefix %s", mLocalPrefix.ToString().AsCString());
2358 }
2359 
IsPublishingOrAdvertising(void) const2360 bool RoutingManager::OnLinkPrefixManager::IsPublishingOrAdvertising(void) const
2361 {
2362     return (mState == kPublishing) || (mState == kAdvertising);
2363 }
2364 
AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage & aRaMessage)2365 void RoutingManager::OnLinkPrefixManager::AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage &aRaMessage)
2366 {
2367     AppendCurPrefix(aRaMessage);
2368     AppendOldPrefixes(aRaMessage);
2369 }
2370 
AppendCurPrefix(Ip6::Nd::RouterAdvertMessage & aRaMessage)2371 void RoutingManager::OnLinkPrefixManager::AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage)
2372 {
2373     // Append the local on-link prefix to the `aRaMessage` as a PIO
2374     // only if it is being advertised or deprecated.
2375     //
2376     // If in `kAdvertising` state, we reset the expire time.
2377     // If in `kDeprecating` state, we include it as PIO with zero
2378     // preferred lifetime and the remaining valid lifetime.
2379 
2380     uint32_t  validLifetime     = kDefaultOnLinkPrefixLifetime;
2381     uint32_t  preferredLifetime = kDefaultOnLinkPrefixLifetime;
2382     TimeMilli now               = TimerMilli::GetNow();
2383 
2384     switch (mState)
2385     {
2386     case kAdvertising:
2387         ResetExpireTime(now);
2388         break;
2389 
2390     case kDeprecating:
2391         VerifyOrExit(mExpireTime > now);
2392         validLifetime     = TimeMilli::MsecToSec(mExpireTime - now);
2393         preferredLifetime = 0;
2394         break;
2395 
2396     case kIdle:
2397     case kPublishing:
2398         ExitNow();
2399     }
2400 
2401     SuccessOrAssert(aRaMessage.AppendPrefixInfoOption(mLocalPrefix, validLifetime, preferredLifetime));
2402 
2403     LogInfo("RouterAdvert: Added PIO for %s (valid=%lu, preferred=%lu)", mLocalPrefix.ToString().AsCString(),
2404             ToUlong(validLifetime), ToUlong(preferredLifetime));
2405 
2406 exit:
2407     return;
2408 }
2409 
AppendOldPrefixes(Ip6::Nd::RouterAdvertMessage & aRaMessage)2410 void RoutingManager::OnLinkPrefixManager::AppendOldPrefixes(Ip6::Nd::RouterAdvertMessage &aRaMessage)
2411 {
2412     TimeMilli now = TimerMilli::GetNow();
2413     uint32_t  validLifetime;
2414 
2415     for (const OldPrefix &oldPrefix : mOldLocalPrefixes)
2416     {
2417         if (oldPrefix.mExpireTime < now)
2418         {
2419             continue;
2420         }
2421 
2422         validLifetime = TimeMilli::MsecToSec(oldPrefix.mExpireTime - now);
2423         SuccessOrAssert(aRaMessage.AppendPrefixInfoOption(oldPrefix.mPrefix, validLifetime, 0));
2424 
2425         LogInfo("RouterAdvert: Added PIO for %s (valid=%lu, preferred=0)", oldPrefix.mPrefix.ToString().AsCString(),
2426                 ToUlong(validLifetime));
2427     }
2428 }
2429 
HandleNetDataChange(void)2430 void RoutingManager::OnLinkPrefixManager::HandleNetDataChange(void)
2431 {
2432     VerifyOrExit(mState == kPublishing);
2433 
2434     if (Get<RoutingManager>().NetworkDataContainsUlaRoute())
2435     {
2436         EnterAdvertisingState();
2437         Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
2438     }
2439 
2440 exit:
2441     return;
2442 }
2443 
HandleExtPanIdChange(void)2444 void RoutingManager::OnLinkPrefixManager::HandleExtPanIdChange(void)
2445 {
2446     // If the current local prefix is being advertised or deprecated,
2447     // we save it in `mOldLocalPrefixes` and keep deprecating it. It will
2448     // be included in emitted RAs as PIO with zero preferred lifetime.
2449     // It will still be present in Network Data until its expire time
2450     // so to allow Thread nodes to continue to communicate with `InfraIf`
2451     // device using addresses based on this prefix.
2452 
2453     uint16_t    oldState  = mState;
2454     Ip6::Prefix oldPrefix = mLocalPrefix;
2455 
2456     GenerateLocalPrefix();
2457 
2458     VerifyOrExit(oldPrefix != mLocalPrefix);
2459 
2460     switch (oldState)
2461     {
2462     case kIdle:
2463     case kPublishing:
2464         break;
2465 
2466     case kAdvertising:
2467     case kDeprecating:
2468         DeprecateOldPrefix(oldPrefix, mExpireTime);
2469         break;
2470     }
2471 
2472     if (Get<RoutingManager>().mIsRunning)
2473     {
2474         Get<RoutingManager>().mRoutePublisher.Evaluate();
2475         Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
2476     }
2477 
2478 exit:
2479     return;
2480 }
2481 
DeprecateOldPrefix(const Ip6::Prefix & aPrefix,TimeMilli aExpireTime)2482 void RoutingManager::OnLinkPrefixManager::DeprecateOldPrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime)
2483 {
2484     OldPrefix  *entry = nullptr;
2485     Ip6::Prefix removedPrefix;
2486 
2487     removedPrefix.Clear();
2488 
2489     VerifyOrExit(!mOldLocalPrefixes.ContainsMatching(aPrefix));
2490 
2491     LogInfo("Deprecating old on-link prefix %s", aPrefix.ToString().AsCString());
2492 
2493     if (!mOldLocalPrefixes.IsFull())
2494     {
2495         entry = mOldLocalPrefixes.PushBack();
2496     }
2497     else
2498     {
2499         // If there is no more room in `mOldLocalPrefixes` array
2500         // we evict the entry with the earliest expiration time.
2501 
2502         entry = &mOldLocalPrefixes[0];
2503 
2504         for (OldPrefix &oldPrefix : mOldLocalPrefixes)
2505         {
2506             if ((oldPrefix.mExpireTime < entry->mExpireTime))
2507             {
2508                 entry = &oldPrefix;
2509             }
2510         }
2511 
2512         removedPrefix = entry->mPrefix;
2513 
2514         IgnoreError(Get<Settings>().RemoveBrOnLinkPrefix(removedPrefix));
2515     }
2516 
2517     entry->mPrefix     = aPrefix;
2518     entry->mExpireTime = aExpireTime;
2519     mTimer.FireAtIfEarlier(aExpireTime);
2520 
2521     SavePrefix(aPrefix, aExpireTime);
2522 
2523 exit:
2524     return;
2525 }
2526 
SavePrefix(const Ip6::Prefix & aPrefix,TimeMilli aExpireTime)2527 void RoutingManager::OnLinkPrefixManager::SavePrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime)
2528 {
2529     Settings::BrOnLinkPrefix savedPrefix;
2530 
2531     savedPrefix.SetPrefix(aPrefix);
2532     savedPrefix.SetLifetime(TimeMilli::MsecToSec(aExpireTime - TimerMilli::GetNow()));
2533     IgnoreError(Get<Settings>().AddOrUpdateBrOnLinkPrefix(savedPrefix));
2534 }
2535 
HandleTimer(void)2536 void RoutingManager::OnLinkPrefixManager::HandleTimer(void)
2537 {
2538     TimeMilli                           now            = TimerMilli::GetNow();
2539     TimeMilli                           nextExpireTime = now.GetDistantFuture();
2540     Array<Ip6::Prefix, kMaxOldPrefixes> expiredPrefixes;
2541 
2542     switch (mState)
2543     {
2544     case kIdle:
2545         break;
2546     case kPublishing:
2547     case kAdvertising:
2548     case kDeprecating:
2549         if (now >= mExpireTime)
2550         {
2551             LogInfo("Local on-link prefix %s expired", mLocalPrefix.ToString().AsCString());
2552             IgnoreError(Get<Settings>().RemoveBrOnLinkPrefix(mLocalPrefix));
2553             mState = kIdle;
2554         }
2555         else
2556         {
2557             nextExpireTime = mExpireTime;
2558         }
2559         break;
2560     }
2561 
2562     for (OldPrefix &entry : mOldLocalPrefixes)
2563     {
2564         if (now >= entry.mExpireTime)
2565         {
2566             SuccessOrAssert(expiredPrefixes.PushBack(entry.mPrefix));
2567         }
2568         else
2569         {
2570             nextExpireTime = Min(nextExpireTime, entry.mExpireTime);
2571         }
2572     }
2573 
2574     for (const Ip6::Prefix &prefix : expiredPrefixes)
2575     {
2576         LogInfo("Old local on-link prefix %s expired", prefix.ToString().AsCString());
2577         IgnoreError(Get<Settings>().RemoveBrOnLinkPrefix(prefix));
2578         mOldLocalPrefixes.RemoveMatching(prefix);
2579     }
2580 
2581     if (nextExpireTime != now.GetDistantFuture())
2582     {
2583         mTimer.FireAtIfEarlier(nextExpireTime);
2584     }
2585 
2586     Get<RoutingManager>().mRoutePublisher.Evaluate();
2587 }
2588 
2589 //---------------------------------------------------------------------------------------------------------------------
2590 // OnMeshPrefixArray
2591 
Add(const OnMeshPrefix & aPrefix)2592 void RoutingManager::OnMeshPrefixArray::Add(const OnMeshPrefix &aPrefix)
2593 {
2594     // Checks if `aPrefix` is already present in the array and if not
2595     // adds it as new entry.
2596 
2597     Error error;
2598 
2599     VerifyOrExit(!Contains(aPrefix));
2600 
2601     error = PushBack(aPrefix);
2602 
2603     if (error != kErrorNone)
2604     {
2605         LogWarn("Too many on-mesh prefixes in net data, ignoring prefix %s", aPrefix.ToString().AsCString());
2606     }
2607 
2608 exit:
2609     return;
2610 }
2611 
MarkAsDeleted(const OnMeshPrefix & aPrefix)2612 void RoutingManager::OnMeshPrefixArray::MarkAsDeleted(const OnMeshPrefix &aPrefix)
2613 {
2614     // Searches for a matching entry to `aPrefix` and if found marks
2615     // it as deleted by setting prefix length to zero.
2616 
2617     OnMeshPrefix *entry = Find(aPrefix);
2618 
2619     if (entry != nullptr)
2620     {
2621         entry->SetLength(0);
2622     }
2623 }
2624 
2625 //---------------------------------------------------------------------------------------------------------------------
2626 // RoutePublisher
2627 
2628 const otIp6Prefix RoutingManager::RoutePublisher::kUlaPrefix = {
2629     {{{0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
2630     7,
2631 };
2632 
RoutePublisher(Instance & aInstance)2633 RoutingManager::RoutePublisher::RoutePublisher(Instance &aInstance)
2634     : InstanceLocator(aInstance)
2635     , mState(kDoNotPublish)
2636     , mPreference(NetworkData::kRoutePreferenceMedium)
2637     , mUserSetPreference(false)
2638 {
2639 }
2640 
Evaluate(void)2641 void RoutingManager::RoutePublisher::Evaluate(void)
2642 {
2643     State newState = kDoNotPublish;
2644 
2645     VerifyOrExit(Get<RoutingManager>().IsRunning());
2646 
2647     if (Get<RoutingManager>().mOmrPrefixManager.GetFavoredPrefix().IsInfrastructureDerived() &&
2648         Get<RoutingManager>().mDiscoveredPrefixTable.ContainsDefaultOrNonUlaRoutePrefix())
2649     {
2650         newState = kPublishDefault;
2651     }
2652     else if (Get<RoutingManager>().mDiscoveredPrefixTable.ContainsNonUlaOnLinkPrefix())
2653     {
2654         newState = kPublishDefault;
2655     }
2656     else if (Get<RoutingManager>().mDiscoveredPrefixTable.ContainsUlaOnLinkPrefix() ||
2657              Get<RoutingManager>().mOnLinkPrefixManager.ShouldPublishUlaRoute())
2658     {
2659         newState = kPublishUla;
2660     }
2661 
2662 exit:
2663     if (newState != mState)
2664     {
2665         LogInfo("RoutePublisher state: %s -> %s", StateToString(mState), StateToString(newState));
2666         UpdatePublishedRoute(newState);
2667         Get<RoutingManager>().mOmrPrefixManager.UpdateDefaultRouteFlag(newState == kPublishDefault);
2668     }
2669 }
2670 
DeterminePrefixFor(State aState,Ip6::Prefix & aPrefix) const2671 void RoutingManager::RoutePublisher::DeterminePrefixFor(State aState, Ip6::Prefix &aPrefix) const
2672 {
2673     aPrefix.Clear();
2674 
2675     switch (aState)
2676     {
2677     case kDoNotPublish:
2678     case kPublishDefault:
2679         // `Clear()` will set the prefix `::/0`.
2680         break;
2681     case kPublishUla:
2682         aPrefix = GetUlaPrefix();
2683         break;
2684     }
2685 }
2686 
UpdatePublishedRoute(State aNewState)2687 void RoutingManager::RoutePublisher::UpdatePublishedRoute(State aNewState)
2688 {
2689     // Updates the published route entry in Network Data, transitioning
2690     // from current `mState` to new `aNewState`. This method can be used
2691     // when there is no change to `mState` but a change to `mPreference`.
2692 
2693     Ip6::Prefix                      oldPrefix;
2694     NetworkData::ExternalRouteConfig routeConfig;
2695 
2696     DeterminePrefixFor(mState, oldPrefix);
2697 
2698     if (aNewState == kDoNotPublish)
2699     {
2700         VerifyOrExit(mState != kDoNotPublish);
2701         IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(oldPrefix));
2702         ExitNow();
2703     }
2704 
2705     routeConfig.Clear();
2706     routeConfig.mPreference = mPreference;
2707     routeConfig.mStable     = true;
2708     DeterminePrefixFor(aNewState, routeConfig.GetPrefix());
2709 
2710     // If we were not publishing a route prefix before, publish the new
2711     // `routeConfig`. Otherwise, use `ReplacePublishedExternalRoute()` to
2712     // replace the previously published prefix entry. This ensures that we do
2713     // not have a situation where the previous route is removed while the new
2714     // one is not yet added in the Network Data.
2715 
2716     if (mState == kDoNotPublish)
2717     {
2718         SuccessOrAssert(Get<NetworkData::Publisher>().PublishExternalRoute(
2719             routeConfig, NetworkData::Publisher::kFromRoutingManager));
2720     }
2721     else
2722     {
2723         SuccessOrAssert(Get<NetworkData::Publisher>().ReplacePublishedExternalRoute(
2724             oldPrefix, routeConfig, NetworkData::Publisher::kFromRoutingManager));
2725     }
2726 
2727 exit:
2728     mState = aNewState;
2729 }
2730 
Unpublish(void)2731 void RoutingManager::RoutePublisher::Unpublish(void)
2732 {
2733     // Unpublish the previously published route based on `mState`
2734     // and update `mState`.
2735 
2736     Ip6::Prefix prefix;
2737 
2738     VerifyOrExit(mState != kDoNotPublish);
2739     DeterminePrefixFor(mState, prefix);
2740     IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(prefix));
2741     mState = kDoNotPublish;
2742 
2743 exit:
2744     return;
2745 }
2746 
SetPreference(RoutePreference aPreference)2747 void RoutingManager::RoutePublisher::SetPreference(RoutePreference aPreference)
2748 {
2749     LogInfo("User explicitly set published route preference to %s", RoutePreferenceToString(aPreference));
2750     mUserSetPreference = true;
2751     UpdatePreference(aPreference);
2752 }
2753 
ClearPreference(void)2754 void RoutingManager::RoutePublisher::ClearPreference(void)
2755 {
2756     VerifyOrExit(mUserSetPreference);
2757 
2758     LogInfo("User cleared explicitly set published route preference - set based on role");
2759     mUserSetPreference = false;
2760     SetPreferenceBasedOnRole();
2761 
2762 exit:
2763     return;
2764 }
2765 
SetPreferenceBasedOnRole(void)2766 void RoutingManager::RoutePublisher::SetPreferenceBasedOnRole(void)
2767 {
2768     UpdatePreference(Get<Mle::Mle>().IsRouterOrLeader() ? NetworkData::kRoutePreferenceMedium
2769                                                         : NetworkData::kRoutePreferenceLow);
2770 }
2771 
HandleRoleChanged(void)2772 void RoutingManager::RoutePublisher::HandleRoleChanged(void)
2773 {
2774     if (!mUserSetPreference)
2775     {
2776         SetPreferenceBasedOnRole();
2777     }
2778 }
2779 
UpdatePreference(RoutePreference aPreference)2780 void RoutingManager::RoutePublisher::UpdatePreference(RoutePreference aPreference)
2781 {
2782     VerifyOrExit(mPreference != aPreference);
2783 
2784     LogInfo("Published route preference changed: %s -> %s", RoutePreferenceToString(mPreference),
2785             RoutePreferenceToString(aPreference));
2786     mPreference = aPreference;
2787     UpdatePublishedRoute(mState);
2788 
2789 exit:
2790     return;
2791 }
2792 
StateToString(State aState)2793 const char *RoutingManager::RoutePublisher::StateToString(State aState)
2794 {
2795     static const char *const kStateStrings[] = {
2796         "none",      // (0) kDoNotPublish
2797         "def-route", // (1) kPublishDefault
2798         "ula",       // (2) kPublishUla
2799     };
2800 
2801     static_assert(0 == kDoNotPublish, "kDoNotPublish value is incorrect");
2802     static_assert(1 == kPublishDefault, "kPublishDefault value is incorrect");
2803     static_assert(2 == kPublishUla, "kPublishUla value is incorrect");
2804 
2805     return kStateStrings[aState];
2806 }
2807 
2808 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
2809 
2810 //---------------------------------------------------------------------------------------------------------------------
2811 // Nat64PrefixManager
2812 
Nat64PrefixManager(Instance & aInstance)2813 RoutingManager::Nat64PrefixManager::Nat64PrefixManager(Instance &aInstance)
2814     : InstanceLocator(aInstance)
2815     , mEnabled(false)
2816     , mTimer(aInstance)
2817 {
2818     mInfraIfPrefix.Clear();
2819     mLocalPrefix.Clear();
2820     mPublishedPrefix.Clear();
2821 }
2822 
SetEnabled(bool aEnabled)2823 void RoutingManager::Nat64PrefixManager::SetEnabled(bool aEnabled)
2824 {
2825     VerifyOrExit(mEnabled != aEnabled);
2826     mEnabled = aEnabled;
2827 
2828     if (aEnabled)
2829     {
2830         if (Get<RoutingManager>().IsRunning())
2831         {
2832             Start();
2833         }
2834     }
2835     else
2836     {
2837         Stop();
2838     }
2839 
2840 exit:
2841     return;
2842 }
2843 
Start(void)2844 void RoutingManager::Nat64PrefixManager::Start(void)
2845 {
2846     VerifyOrExit(mEnabled);
2847     LogInfo("Starting Nat64PrefixManager");
2848     mTimer.Start(0);
2849 
2850 exit:
2851     return;
2852 }
2853 
Stop(void)2854 void RoutingManager::Nat64PrefixManager::Stop(void)
2855 {
2856     LogInfo("Stopping Nat64PrefixManager");
2857 
2858     if (mPublishedPrefix.IsValidNat64())
2859     {
2860         IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(mPublishedPrefix));
2861     }
2862 
2863     mPublishedPrefix.Clear();
2864     mInfraIfPrefix.Clear();
2865     mTimer.Stop();
2866 
2867 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
2868     Get<Nat64::Translator>().ClearNat64Prefix();
2869 #endif
2870 }
2871 
GenerateLocalPrefix(const Ip6::Prefix & aBrUlaPrefix)2872 void RoutingManager::Nat64PrefixManager::GenerateLocalPrefix(const Ip6::Prefix &aBrUlaPrefix)
2873 {
2874     mLocalPrefix = aBrUlaPrefix;
2875     mLocalPrefix.SetSubnetId(kNat64PrefixSubnetId);
2876     mLocalPrefix.mPrefix.mFields.m32[2] = 0;
2877     mLocalPrefix.SetLength(kNat64PrefixLength);
2878 
2879     LogInfo("Generated local NAT64 prefix: %s", mLocalPrefix.ToString().AsCString());
2880 }
2881 
GetFavoredPrefix(RoutePreference & aPreference) const2882 const Ip6::Prefix &RoutingManager::Nat64PrefixManager::GetFavoredPrefix(RoutePreference &aPreference) const
2883 {
2884     const Ip6::Prefix *favoredPrefix = &mLocalPrefix;
2885 
2886     aPreference = NetworkData::kRoutePreferenceLow;
2887 
2888     if (mInfraIfPrefix.IsValidNat64() &&
2889         Get<RoutingManager>().mOmrPrefixManager.GetFavoredPrefix().IsInfrastructureDerived())
2890     {
2891         favoredPrefix = &mInfraIfPrefix;
2892         aPreference   = NetworkData::kRoutePreferenceMedium;
2893     }
2894 
2895     return *favoredPrefix;
2896 }
2897 
Evaluate(void)2898 void RoutingManager::Nat64PrefixManager::Evaluate(void)
2899 {
2900     Error                            error;
2901     Ip6::Prefix                      prefix;
2902     RoutePreference                  preference;
2903     NetworkData::ExternalRouteConfig netdataPrefixConfig;
2904     bool                             shouldPublish;
2905 
2906     VerifyOrExit(mEnabled);
2907 
2908     LogInfo("Evaluating NAT64 prefix");
2909 
2910     prefix = GetFavoredPrefix(preference);
2911 
2912     error = Get<NetworkData::Leader>().GetPreferredNat64Prefix(netdataPrefixConfig);
2913 
2914     // NAT64 prefix is expected to be published from this BR
2915     // when one of the following is true:
2916     //
2917     // - No NAT64 prefix in Network Data.
2918     // - The preferred NAT64 prefix in Network Data has lower
2919     //   preference than this BR's prefix.
2920     // - The preferred NAT64 prefix in Network Data was published
2921     //   by this BR.
2922     // - The preferred NAT64 prefix in Network Data is same as the
2923     //   discovered infrastructure prefix.
2924     //
2925     // TODO: change to check RLOC16 to determine if the NAT64 prefix
2926     // was published by this BR.
2927 
2928     shouldPublish =
2929         ((error == kErrorNotFound) || (netdataPrefixConfig.mPreference < preference) ||
2930          (netdataPrefixConfig.GetPrefix() == mPublishedPrefix) || (netdataPrefixConfig.GetPrefix() == mInfraIfPrefix));
2931 
2932     if (mPublishedPrefix.IsValidNat64() && (!shouldPublish || (prefix != mPublishedPrefix)))
2933     {
2934         IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(mPublishedPrefix));
2935         mPublishedPrefix.Clear();
2936     }
2937 
2938     if (shouldPublish && ((prefix != mPublishedPrefix) || (preference != mPublishedPreference)))
2939     {
2940         mPublishedPrefix     = prefix;
2941         mPublishedPreference = preference;
2942         Publish();
2943     }
2944 
2945 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
2946     // When there is an prefix other than mLocalPrefix, means there is an external translator available. So we bypass
2947     // the NAT64 translator by clearing the NAT64 prefix in the translator.
2948     if (mPublishedPrefix == mLocalPrefix)
2949     {
2950         Get<Nat64::Translator>().SetNat64Prefix(mLocalPrefix);
2951     }
2952     else
2953     {
2954         Get<Nat64::Translator>().ClearNat64Prefix();
2955     }
2956 #endif
2957 
2958 exit:
2959     return;
2960 }
2961 
Publish(void)2962 void RoutingManager::Nat64PrefixManager::Publish(void)
2963 {
2964     NetworkData::ExternalRouteConfig routeConfig;
2965 
2966     routeConfig.Clear();
2967     routeConfig.SetPrefix(mPublishedPrefix);
2968     routeConfig.mPreference = mPublishedPreference;
2969     routeConfig.mStable     = true;
2970     routeConfig.mNat64      = true;
2971 
2972     SuccessOrAssert(
2973         Get<NetworkData::Publisher>().PublishExternalRoute(routeConfig, NetworkData::Publisher::kFromRoutingManager));
2974 }
2975 
HandleTimer(void)2976 void RoutingManager::Nat64PrefixManager::HandleTimer(void)
2977 {
2978     OT_ASSERT(mEnabled);
2979 
2980     Discover();
2981 
2982     mTimer.Start(TimeMilli::SecToMsec(kDefaultNat64PrefixLifetime));
2983     LogInfo("NAT64 prefix timer scheduled in %lu seconds", ToUlong(kDefaultNat64PrefixLifetime));
2984 }
2985 
Discover(void)2986 void RoutingManager::Nat64PrefixManager::Discover(void)
2987 {
2988     Error error = Get<RoutingManager>().mInfraIf.DiscoverNat64Prefix();
2989 
2990     if (error == kErrorNone)
2991     {
2992         LogInfo("Discovering infraif NAT64 prefix");
2993     }
2994     else
2995     {
2996         LogWarn("Failed to discover infraif NAT64 prefix: %s", ErrorToString(error));
2997     }
2998 }
2999 
HandleDiscoverDone(const Ip6::Prefix & aPrefix)3000 void RoutingManager::Nat64PrefixManager::HandleDiscoverDone(const Ip6::Prefix &aPrefix)
3001 {
3002     mInfraIfPrefix = aPrefix;
3003 
3004     LogInfo("Infraif NAT64 prefix: %s", mInfraIfPrefix.IsValidNat64() ? mInfraIfPrefix.ToString().AsCString() : "none");
3005 
3006     if (Get<RoutingManager>().mIsRunning)
3007     {
3008         Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
3009     }
3010 }
3011 
GetState(void) const3012 Nat64::State RoutingManager::Nat64PrefixManager::GetState(void) const
3013 {
3014     Nat64::State state = Nat64::kStateDisabled;
3015 
3016     VerifyOrExit(mEnabled);
3017     VerifyOrExit(Get<RoutingManager>().IsRunning(), state = Nat64::kStateNotRunning);
3018     VerifyOrExit(mPublishedPrefix.IsValidNat64(), state = Nat64::kStateIdle);
3019     state = Nat64::kStateActive;
3020 
3021 exit:
3022     return state;
3023 }
3024 
3025 #endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
3026 
3027 //---------------------------------------------------------------------------------------------------------------------
3028 // RsSender
3029 
RsSender(Instance & aInstance)3030 RoutingManager::RsSender::RsSender(Instance &aInstance)
3031     : InstanceLocator(aInstance)
3032     , mTxCount(0)
3033     , mTimer(aInstance)
3034 {
3035 }
3036 
Start(void)3037 void RoutingManager::RsSender::Start(void)
3038 {
3039     uint32_t delay;
3040 
3041     VerifyOrExit(!IsInProgress());
3042 
3043     delay = Random::NonCrypto::GetUint32InRange(0, kMaxStartDelay);
3044     LogInfo("Scheduled Router Solicitation in %lu milliseconds", ToUlong(delay));
3045 
3046     mTxCount   = 0;
3047     mStartTime = TimerMilli::GetNow();
3048     mTimer.Start(delay);
3049 
3050 exit:
3051     return;
3052 }
3053 
Stop(void)3054 void RoutingManager::RsSender::Stop(void) { mTimer.Stop(); }
3055 
SendRs(void)3056 Error RoutingManager::RsSender::SendRs(void)
3057 {
3058     Ip6::Address                  destAddress;
3059     Ip6::Nd::RouterSolicitMessage routerSolicit;
3060     InfraIf::Icmp6Packet          packet;
3061     Error                         error;
3062 
3063     packet.InitFrom(routerSolicit);
3064     destAddress.SetToLinkLocalAllRoutersMulticast();
3065 
3066     error = Get<RoutingManager>().mInfraIf.Send(packet, destAddress);
3067 
3068     if (error == kErrorNone)
3069     {
3070         Get<Ip6::Ip6>().GetBorderRoutingCounters().mRsTxSuccess++;
3071     }
3072     else
3073     {
3074         Get<Ip6::Ip6>().GetBorderRoutingCounters().mRsTxFailure++;
3075     }
3076     return error;
3077 }
3078 
HandleTimer(void)3079 void RoutingManager::RsSender::HandleTimer(void)
3080 {
3081     Error    error;
3082     uint32_t delay;
3083 
3084     if (mTxCount >= kMaxTxCount)
3085     {
3086         Get<RoutingManager>().HandleRsSenderFinished(mStartTime);
3087         ExitNow();
3088     }
3089 
3090     error = SendRs();
3091 
3092     if (error == kErrorNone)
3093     {
3094         mTxCount++;
3095         LogInfo("Successfully sent RS %u/%u", mTxCount, kMaxTxCount);
3096         delay = (mTxCount == kMaxTxCount) ? kWaitOnLastAttempt : kTxInterval;
3097     }
3098     else
3099     {
3100         LogCrit("Failed to send RS %u, error:%s", mTxCount + 1, ErrorToString(error));
3101 
3102         // Note that `mTxCount` is intentionally not incremented
3103         // if the tx fails.
3104         delay = kRetryDelay;
3105     }
3106 
3107     mTimer.Start(delay);
3108 
3109 exit:
3110     return;
3111 }
3112 
3113 } // namespace BorderRouter
3114 
3115 } // namespace ot
3116 
3117 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
3118