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