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