• 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/platform/infra_if.h>
42 
43 #include "common/code_utils.hpp"
44 #include "common/debug.hpp"
45 #include "common/instance.hpp"
46 #include "common/locator_getters.hpp"
47 #include "common/log.hpp"
48 #include "common/random.hpp"
49 #include "common/settings.hpp"
50 #include "meshcop/extended_panid.hpp"
51 #include "net/ip6.hpp"
52 #include "thread/network_data_leader.hpp"
53 #include "thread/network_data_local.hpp"
54 #include "thread/network_data_notifier.hpp"
55 
56 namespace ot {
57 
58 namespace BorderRouter {
59 
60 RegisterLogModule("BorderRouter");
61 
RoutingManager(Instance & aInstance)62 RoutingManager::RoutingManager(Instance &aInstance)
63     : InstanceLocator(aInstance)
64     , mIsRunning(false)
65     , mIsEnabled(false)
66     , mInfraIf(aInstance)
67     , mLocalOmrPrefix(aInstance)
68     , mRouteInfoOptionPreference(NetworkData::kRoutePreferenceMedium)
69     , mIsAdvertisingLocalOnLinkPrefix(false)
70     , mOnLinkPrefixDeprecateTimer(aInstance, HandleOnLinkPrefixDeprecateTimer)
71     , mIsAdvertisingLocalNat64Prefix(false)
72     , mDiscoveredPrefixTable(aInstance)
73     , mTimeRouterAdvMessageLastUpdate(TimerMilli::GetNow())
74     , mLearntRouterAdvMessageFromHost(false)
75     , mDiscoveredPrefixStaleTimer(aInstance, HandleDiscoveredPrefixStaleTimer)
76     , mRouterAdvertisementCount(0)
77     , mLastRouterAdvertisementSendTime(TimerMilli::GetNow() - kMinDelayBetweenRtrAdvs)
78     , mRouterSolicitTimer(aInstance, HandleRouterSolicitTimer)
79     , mRouterSolicitCount(0)
80     , mRoutingPolicyTimer(aInstance, HandleRoutingPolicyTimer)
81 {
82     mFavoredDiscoveredOnLinkPrefix.Clear();
83 
84     mBrUlaPrefix.Clear();
85 
86     mLocalOnLinkPrefix.Clear();
87 
88     mLocalNat64Prefix.Clear();
89 }
90 
Init(uint32_t aInfraIfIndex,bool aInfraIfIsRunning)91 Error RoutingManager::Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning)
92 {
93     Error error;
94 
95     SuccessOrExit(error = mInfraIf.Init(aInfraIfIndex));
96 
97     SuccessOrExit(error = LoadOrGenerateRandomBrUlaPrefix());
98     mLocalOmrPrefix.GenerateFrom(mBrUlaPrefix);
99 #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
100     GenerateNat64Prefix();
101 #endif
102     GenerateOnLinkPrefix();
103 
104     error = mInfraIf.HandleStateChanged(mInfraIf.GetIfIndex(), aInfraIfIsRunning);
105 
106 exit:
107     if (error != kErrorNone)
108     {
109         mInfraIf.Deinit();
110     }
111 
112     return error;
113 }
114 
SetEnabled(bool aEnabled)115 Error RoutingManager::SetEnabled(bool aEnabled)
116 {
117     Error error = kErrorNone;
118 
119     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
120 
121     VerifyOrExit(aEnabled != mIsEnabled);
122 
123     mIsEnabled = aEnabled;
124     EvaluateState();
125 
126 exit:
127     return error;
128 }
129 
SetRouteInfoOptionPreference(RoutePreference aPreference)130 void RoutingManager::SetRouteInfoOptionPreference(RoutePreference aPreference)
131 {
132     VerifyOrExit(mRouteInfoOptionPreference != aPreference);
133 
134     mRouteInfoOptionPreference = aPreference;
135 
136     VerifyOrExit(mIsRunning);
137     StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter);
138 
139 exit:
140     return;
141 }
142 
GetOmrPrefix(Ip6::Prefix & aPrefix)143 Error RoutingManager::GetOmrPrefix(Ip6::Prefix &aPrefix)
144 {
145     Error error = kErrorNone;
146 
147     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
148     aPrefix = mLocalOmrPrefix.GetPrefix();
149 
150 exit:
151     return error;
152 }
153 
GetFavoredOmrPrefix(Ip6::Prefix & aPrefix,RoutePreference & aPreference)154 Error RoutingManager::GetFavoredOmrPrefix(Ip6::Prefix &aPrefix, RoutePreference &aPreference)
155 {
156     Error error = kErrorNone;
157 
158     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
159     aPrefix     = mFavoredOmrPrefix.GetPrefix();
160     aPreference = mFavoredOmrPrefix.GetPreference();
161 
162 exit:
163     return error;
164 }
165 
GetOnLinkPrefix(Ip6::Prefix & aPrefix)166 Error RoutingManager::GetOnLinkPrefix(Ip6::Prefix &aPrefix)
167 {
168     Error error = kErrorNone;
169 
170     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
171     aPrefix = mLocalOnLinkPrefix;
172 
173 exit:
174     return error;
175 }
176 
177 #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
GetNat64Prefix(Ip6::Prefix & aPrefix)178 Error RoutingManager::GetNat64Prefix(Ip6::Prefix &aPrefix)
179 {
180     Error error = kErrorNone;
181 
182     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
183     aPrefix = mLocalNat64Prefix;
184 
185 exit:
186     return error;
187 }
188 #endif
189 
LoadOrGenerateRandomBrUlaPrefix(void)190 Error RoutingManager::LoadOrGenerateRandomBrUlaPrefix(void)
191 {
192     Error error     = kErrorNone;
193     bool  generated = false;
194 
195     if (Get<Settings>().Read<Settings::BrUlaPrefix>(mBrUlaPrefix) != kErrorNone || !IsValidBrUlaPrefix(mBrUlaPrefix))
196     {
197         Ip6::NetworkPrefix randomUlaPrefix;
198 
199         LogNote("No valid /48 BR ULA prefix found in settings, generating new one");
200 
201         SuccessOrExit(error = randomUlaPrefix.GenerateRandomUla());
202 
203         mBrUlaPrefix.Set(randomUlaPrefix);
204         mBrUlaPrefix.SetSubnetId(0);
205         mBrUlaPrefix.SetLength(kBrUlaPrefixLength);
206 
207         IgnoreError(Get<Settings>().Save<Settings::BrUlaPrefix>(mBrUlaPrefix));
208         generated = true;
209     }
210 
211     OT_UNUSED_VARIABLE(generated);
212 
213     LogNote("BR ULA prefix: %s (%s)", mBrUlaPrefix.ToString().AsCString(), generated ? "generated" : "loaded");
214 
215 exit:
216     if (error != kErrorNone)
217     {
218         LogCrit("Failed to generate random /48 BR ULA prefix");
219     }
220     return error;
221 }
222 
223 #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
GenerateNat64Prefix(void)224 void RoutingManager::GenerateNat64Prefix(void)
225 {
226     mLocalNat64Prefix = mBrUlaPrefix;
227     mLocalNat64Prefix.SetSubnetId(kNat64PrefixSubnetId);
228     mLocalNat64Prefix.mPrefix.mFields.m32[2] = 0;
229     mLocalNat64Prefix.SetLength(kNat64PrefixLength);
230 
231     LogInfo("Generated NAT64 prefix: %s", mLocalNat64Prefix.ToString().AsCString());
232 }
233 #endif
234 
GenerateOnLinkPrefix(void)235 void RoutingManager::GenerateOnLinkPrefix(void)
236 {
237     MeshCoP::ExtendedPanId extPanId = Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId();
238 
239     mLocalOnLinkPrefix.mPrefix.mFields.m8[0] = 0xfd;
240     // Global ID: 40 most significant bits of Extended PAN ID
241     memcpy(mLocalOnLinkPrefix.mPrefix.mFields.m8 + 1, extPanId.m8, 5);
242     // Subnet ID: 16 least significant bits of Extended PAN ID
243     memcpy(mLocalOnLinkPrefix.mPrefix.mFields.m8 + 6, extPanId.m8 + 6, 2);
244     mLocalOnLinkPrefix.SetLength(kOnLinkPrefixLength);
245 
246     LogNote("Local on-link prefix: %s", mLocalOnLinkPrefix.ToString().AsCString());
247 }
248 
EvaluateState(void)249 void RoutingManager::EvaluateState(void)
250 {
251     if (mIsEnabled && Get<Mle::MleRouter>().IsAttached() && mInfraIf.IsRunning())
252     {
253         Start();
254     }
255     else
256     {
257         Stop();
258     }
259 }
260 
Start(void)261 void RoutingManager::Start(void)
262 {
263     if (!mIsRunning)
264     {
265         LogInfo("Border Routing manager started");
266 
267         mIsRunning = true;
268         UpdateDiscoveredPrefixTableOnNetDataChange();
269         StartRouterSolicitationDelay();
270     }
271 }
272 
Stop(void)273 void RoutingManager::Stop(void)
274 {
275     VerifyOrExit(mIsRunning);
276 
277     mLocalOmrPrefix.RemoveFromNetData();
278     mFavoredOmrPrefix.Clear();
279 
280     mFavoredDiscoveredOnLinkPrefix.Clear();
281 
282     if (mIsAdvertisingLocalOnLinkPrefix)
283     {
284         UnpublishExternalRoute(mLocalOnLinkPrefix);
285 
286         // Start deprecating the local on-link prefix to send a PIO
287         // with zero preferred lifetime in `SendRouterAdvertisement`.
288         DeprecateOnLinkPrefix();
289     }
290 
291 #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
292     if (mIsAdvertisingLocalNat64Prefix)
293     {
294         UnpublishExternalRoute(mLocalNat64Prefix);
295         mIsAdvertisingLocalNat64Prefix = false;
296     }
297 #endif
298     SendRouterAdvertisement(kInvalidateAllPrevPrefixes);
299 
300     mAdvertisedPrefixes.Clear();
301     mOnLinkPrefixDeprecateTimer.Stop();
302 
303     mDiscoveredPrefixTable.RemoveAllEntries();
304     mDiscoveredPrefixStaleTimer.Stop();
305 
306     mRouterAdvertisementCount = 0;
307 
308     mRouterSolicitTimer.Stop();
309     mRouterSolicitCount = 0;
310 
311     mRoutingPolicyTimer.Stop();
312 
313     LogInfo("Border Routing manager stopped");
314 
315     mIsRunning = false;
316 
317 exit:
318     return;
319 }
320 
HandleReceived(const InfraIf::Icmp6Packet & aPacket,const Ip6::Address & aSrcAddress)321 void RoutingManager::HandleReceived(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
322 {
323     const Ip6::Icmp::Header *icmp6Header;
324 
325     VerifyOrExit(mIsRunning);
326 
327     icmp6Header = reinterpret_cast<const Ip6::Icmp::Header *>(aPacket.GetBytes());
328 
329     switch (icmp6Header->GetType())
330     {
331     case Ip6::Icmp::Header::kTypeRouterAdvert:
332         HandleRouterAdvertisement(aPacket, aSrcAddress);
333         break;
334     case Ip6::Icmp::Header::kTypeRouterSolicit:
335         HandleRouterSolicit(aPacket, aSrcAddress);
336         break;
337     default:
338         break;
339     }
340 
341 exit:
342     return;
343 }
344 
HandleNotifierEvents(Events aEvents)345 void RoutingManager::HandleNotifierEvents(Events aEvents)
346 {
347     VerifyOrExit(IsInitialized() && IsEnabled());
348 
349     if (aEvents.Contains(kEventThreadRoleChanged))
350     {
351         EvaluateState();
352     }
353 
354     if (mIsRunning && aEvents.Contains(kEventThreadNetdataChanged))
355     {
356         UpdateDiscoveredPrefixTableOnNetDataChange();
357         StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter);
358     }
359 
360     if (aEvents.Contains(kEventThreadExtPanIdChanged))
361     {
362         if (mIsAdvertisingLocalOnLinkPrefix)
363         {
364             UnpublishExternalRoute(mLocalOnLinkPrefix);
365             // TODO: consider deprecating/invalidating existing
366             // on-link prefix
367             mIsAdvertisingLocalOnLinkPrefix = false;
368         }
369 
370         GenerateOnLinkPrefix();
371 
372         if (mIsRunning)
373         {
374             StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter);
375         }
376     }
377 
378 exit:
379     return;
380 }
381 
UpdateDiscoveredPrefixTableOnNetDataChange(void)382 void RoutingManager::UpdateDiscoveredPrefixTableOnNetDataChange(void)
383 {
384     NetworkData::Iterator           iterator = NetworkData::kIteratorInit;
385     NetworkData::OnMeshPrefixConfig prefixConfig;
386     bool                            foundDefRouteOmrPrefix = false;
387 
388     // Remove all OMR prefixes in Network Data from the
389     // discovered prefix table. Also check if we have
390     // an OMR prefix with default route flag.
391 
392     while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
393     {
394         if (!IsValidOmrPrefix(prefixConfig))
395         {
396             continue;
397         }
398 
399         mDiscoveredPrefixTable.RemoveRoutePrefix(prefixConfig.GetPrefix(),
400                                                  DiscoveredPrefixTable::kUnpublishFromNetData);
401 
402         if (prefixConfig.mDefaultRoute)
403         {
404             foundDefRouteOmrPrefix = true;
405         }
406     }
407 
408     // If we find an OMR prefix with default route flag, it indicates
409     // that this prefix can be used with default route (routable beyond
410     // infra link).
411     //
412     // `DiscoveredPrefixTable` will always track which routers provide
413     // default route when processing received RA messages, but only
414     // if we see an OMR prefix with default route flag, we allow it
415     // to publish the discovered default route (as ::/0 external
416     // route) in Network Data.
417 
418     mDiscoveredPrefixTable.SetAllowDefaultRouteInNetData(foundDefRouteOmrPrefix);
419 }
420 
EvaluateOmrPrefix(void)421 void RoutingManager::EvaluateOmrPrefix(void)
422 {
423     NetworkData::Iterator           iterator = NetworkData::kIteratorInit;
424     NetworkData::OnMeshPrefixConfig onMeshPrefixConfig;
425 
426     OT_ASSERT(mIsRunning);
427 
428     mFavoredOmrPrefix.Clear();
429 
430     while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, onMeshPrefixConfig) == kErrorNone)
431     {
432         if (!IsValidOmrPrefix(onMeshPrefixConfig) || !onMeshPrefixConfig.mPreferred)
433         {
434             continue;
435         }
436 
437         if (mFavoredOmrPrefix.IsEmpty() || !mFavoredOmrPrefix.IsFavoredOver(onMeshPrefixConfig))
438         {
439             mFavoredOmrPrefix.SetFrom(onMeshPrefixConfig);
440         }
441     }
442 
443     // Decide if we need to add or remove our local OMR prefix.
444 
445     if (mFavoredOmrPrefix.IsEmpty())
446     {
447         LogInfo("EvaluateOmrPrefix: No preferred OMR prefix found in Thread network");
448 
449         // The `aNewPrefixes` remains empty if we fail to publish
450         // the local OMR prefix.
451         SuccessOrExit(mLocalOmrPrefix.AddToNetData());
452 
453         mFavoredOmrPrefix.SetFrom(mLocalOmrPrefix);
454     }
455     else if (mFavoredOmrPrefix.GetPrefix() == mLocalOmrPrefix.GetPrefix())
456     {
457         IgnoreError(mLocalOmrPrefix.AddToNetData());
458     }
459     else if (mLocalOmrPrefix.IsAddedInNetData())
460     {
461         LogInfo("EvaluateOmrPrefix: There is already a preferred OMR prefix %s in the Thread network",
462                 mFavoredOmrPrefix.GetPrefix().ToString().AsCString());
463 
464         mLocalOmrPrefix.RemoveFromNetData();
465     }
466 
467 exit:
468     return;
469 }
470 
PublishExternalRoute(const Ip6::Prefix & aPrefix,RoutePreference aRoutePreference,bool aNat64)471 Error RoutingManager::PublishExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64)
472 {
473     Error                            error;
474     NetworkData::ExternalRouteConfig routeConfig;
475 
476     OT_ASSERT(mIsRunning);
477 
478     routeConfig.Clear();
479     routeConfig.SetPrefix(aPrefix);
480     routeConfig.mStable     = true;
481     routeConfig.mNat64      = aNat64;
482     routeConfig.mPreference = aRoutePreference;
483 
484     error = Get<NetworkData::Publisher>().PublishExternalRoute(routeConfig);
485 
486     if (error != kErrorNone)
487     {
488         LogWarn("Failed to publish external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error));
489     }
490 
491     return error;
492 }
493 
UnpublishExternalRoute(const Ip6::Prefix & aPrefix)494 void RoutingManager::UnpublishExternalRoute(const Ip6::Prefix &aPrefix)
495 {
496     VerifyOrExit(mIsRunning);
497     IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(aPrefix));
498 
499 exit:
500     return;
501 }
502 
EvaluateOnLinkPrefix(void)503 void RoutingManager::EvaluateOnLinkPrefix(void)
504 {
505     VerifyOrExit(!IsRouterSolicitationInProgress());
506 
507     mDiscoveredPrefixTable.FindFavoredOnLinkPrefix(mFavoredDiscoveredOnLinkPrefix);
508 
509     if (mFavoredDiscoveredOnLinkPrefix.GetLength() == 0)
510     {
511         // We need to advertise our local on-link prefix since there is
512         // no discovered on-link prefix.
513 
514         mOnLinkPrefixDeprecateTimer.Stop();
515         VerifyOrExit(!mIsAdvertisingLocalOnLinkPrefix);
516 
517         SuccessOrExit(PublishExternalRoute(mLocalOnLinkPrefix, NetworkData::kRoutePreferenceMedium));
518 
519         mIsAdvertisingLocalOnLinkPrefix = true;
520         LogInfo("Start advertising on-link prefix %s on %s", mLocalOnLinkPrefix.ToString().AsCString(),
521                 mInfraIf.ToString().AsCString());
522 
523         // We remove the local on-link prefix from discovered prefix
524         // table, in case it was previously discovered and included in
525         // the table (now as a deprecating entry). We remove it with
526         // `kKeepInNetData` flag to ensure that the prefix is not
527         // unpublished from network data.
528         //
529         // Note that `ShouldProcessPrefixInfoOption()` will also check
530         // not allow the local on-link prefix to be added in the prefix
531         // table while we are advertising it.
532 
533         mDiscoveredPrefixTable.RemoveOnLinkPrefix(mLocalOnLinkPrefix, DiscoveredPrefixTable::kKeepInNetData);
534     }
535     else
536     {
537         VerifyOrExit(mIsAdvertisingLocalOnLinkPrefix);
538 
539         // When an application-specific on-link prefix is received and
540         // it is larger than the local prefix, we will not remove the
541         // advertised local prefix. In this case, there will be two
542         // on-link prefixes on the infra link. But all BRs will still
543         // converge to the same smallest/favored on-link prefix and the
544         // application-specific prefix is not used.
545 
546         if (!(mLocalOnLinkPrefix < mFavoredDiscoveredOnLinkPrefix))
547         {
548             LogInfo("EvaluateOnLinkPrefix: There is already favored on-link prefix %s on %s",
549                     mFavoredDiscoveredOnLinkPrefix.ToString().AsCString(), mInfraIf.ToString().AsCString());
550             DeprecateOnLinkPrefix();
551         }
552     }
553 
554 exit:
555     return;
556 }
557 
HandleOnLinkPrefixDeprecateTimer(Timer & aTimer)558 void RoutingManager::HandleOnLinkPrefixDeprecateTimer(Timer &aTimer)
559 {
560     aTimer.Get<RoutingManager>().HandleOnLinkPrefixDeprecateTimer();
561 }
562 
HandleOnLinkPrefixDeprecateTimer(void)563 void RoutingManager::HandleOnLinkPrefixDeprecateTimer(void)
564 {
565     OT_ASSERT(!mIsAdvertisingLocalOnLinkPrefix);
566 
567     LogInfo("Local on-link prefix %s expired", mLocalOnLinkPrefix.ToString().AsCString());
568 
569     if (!mDiscoveredPrefixTable.ContainsOnLinkPrefix(mLocalOnLinkPrefix))
570     {
571         UnpublishExternalRoute(mLocalOnLinkPrefix);
572     }
573 }
574 
DeprecateOnLinkPrefix(void)575 void RoutingManager::DeprecateOnLinkPrefix(void)
576 {
577     OT_ASSERT(mIsAdvertisingLocalOnLinkPrefix);
578 
579     mIsAdvertisingLocalOnLinkPrefix = false;
580 
581     LogInfo("Deprecate local on-link prefix %s", mLocalOnLinkPrefix.ToString().AsCString());
582     mOnLinkPrefixDeprecateTimer.StartAt(mTimeAdvertisedOnLinkPrefix,
583                                         TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime));
584 }
585 
586 #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
EvaluateNat64Prefix(void)587 void RoutingManager::EvaluateNat64Prefix(void)
588 {
589     OT_ASSERT(mIsRunning);
590 
591     NetworkData::Iterator            iterator = NetworkData::kIteratorInit;
592     NetworkData::ExternalRouteConfig config;
593     Ip6::Prefix                      smallestNat64Prefix;
594 
595     LogInfo("Evaluating NAT64 prefix");
596 
597     smallestNat64Prefix.Clear();
598     while (Get<NetworkData::Leader>().GetNextExternalRoute(iterator, config) == kErrorNone)
599     {
600         const Ip6::Prefix &prefix = config.GetPrefix();
601 
602         if (config.mNat64 && prefix.IsValidNat64())
603         {
604             if (smallestNat64Prefix.GetLength() == 0 || prefix < smallestNat64Prefix)
605             {
606                 smallestNat64Prefix = prefix;
607             }
608         }
609     }
610 
611     if (smallestNat64Prefix.GetLength() == 0 || smallestNat64Prefix == mLocalNat64Prefix)
612     {
613         LogInfo("No NAT64 prefix in Network Data is smaller than the local NAT64 prefix %s",
614                 mLocalNat64Prefix.ToString().AsCString());
615 
616         // Advertise local NAT64 prefix.
617         if (!mIsAdvertisingLocalNat64Prefix &&
618             PublishExternalRoute(mLocalNat64Prefix, NetworkData::kRoutePreferenceLow, /* aNat64= */ true) == kErrorNone)
619         {
620             mIsAdvertisingLocalNat64Prefix = true;
621         }
622     }
623     else if (mIsAdvertisingLocalNat64Prefix && smallestNat64Prefix < mLocalNat64Prefix)
624     {
625         // Withdraw local NAT64 prefix if it's not the smallest one in Network Data.
626         // TODO: remove the prefix with lower preference after discovering upstream NAT64 prefix is supported
627         LogNote("Withdrawing local NAT64 prefix since a smaller one %s exists.",
628                 smallestNat64Prefix.ToString().AsCString());
629 
630         UnpublishExternalRoute(mLocalNat64Prefix);
631         mIsAdvertisingLocalNat64Prefix = false;
632     }
633 }
634 #endif
635 
636 // This method evaluate the routing policy depends on prefix and route
637 // information on Thread Network and infra link. As a result, this
638 // method May send RA messages on infra link and publish/unpublish
639 // OMR and NAT64 prefix in the Thread network.
EvaluateRoutingPolicy(void)640 void RoutingManager::EvaluateRoutingPolicy(void)
641 {
642     OT_ASSERT(mIsRunning);
643 
644     LogInfo("Evaluating routing policy");
645 
646     // 0. Evaluate on-link, OMR and NAT64 prefixes.
647     EvaluateOnLinkPrefix();
648     EvaluateOmrPrefix();
649 #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
650     EvaluateNat64Prefix();
651 #endif
652 
653     // 1. Send Router Advertisement message if necessary.
654     SendRouterAdvertisement(kAdvPrefixesFromNetData);
655 
656     // 2. Schedule routing policy timer with random interval for the next Router Advertisement.
657     {
658         uint32_t nextSendDelay;
659 
660         nextSendDelay = Random::NonCrypto::GetUint32InRange(kMinRtrAdvInterval, kMaxRtrAdvInterval);
661 
662         if (mRouterAdvertisementCount <= kMaxInitRtrAdvertisements && nextSendDelay > kMaxInitRtrAdvInterval)
663         {
664             nextSendDelay = kMaxInitRtrAdvInterval;
665         }
666 
667         StartRoutingPolicyEvaluationDelay(Time::SecToMsec(nextSendDelay));
668     }
669 }
670 
StartRoutingPolicyEvaluationJitter(uint32_t aJitterMilli)671 void RoutingManager::StartRoutingPolicyEvaluationJitter(uint32_t aJitterMilli)
672 {
673     OT_ASSERT(mIsRunning);
674 
675     StartRoutingPolicyEvaluationDelay(Random::NonCrypto::GetUint32InRange(0, aJitterMilli));
676 }
677 
StartRoutingPolicyEvaluationDelay(uint32_t aDelayMilli)678 void RoutingManager::StartRoutingPolicyEvaluationDelay(uint32_t aDelayMilli)
679 {
680     TimeMilli now          = TimerMilli::GetNow();
681     TimeMilli evaluateTime = now + aDelayMilli;
682     TimeMilli earliestTime = mLastRouterAdvertisementSendTime + kMinDelayBetweenRtrAdvs;
683 
684     evaluateTime = OT_MAX(evaluateTime, earliestTime);
685 
686     LogInfo("Start evaluating routing policy, scheduled in %u milliseconds", evaluateTime - now);
687 
688     mRoutingPolicyTimer.FireAtIfEarlier(evaluateTime);
689 }
690 
691 // starts sending Router Solicitations in random delay
692 // between 0 and kMaxRtrSolicitationDelay.
StartRouterSolicitationDelay(void)693 void RoutingManager::StartRouterSolicitationDelay(void)
694 {
695     uint32_t randomDelay;
696 
697     VerifyOrExit(!IsRouterSolicitationInProgress());
698 
699     OT_ASSERT(mRouterSolicitCount == 0);
700 
701     static_assert(kMaxRtrSolicitationDelay > 0, "invalid maximum Router Solicitation delay");
702     randomDelay = Random::NonCrypto::GetUint32InRange(0, Time::SecToMsec(kMaxRtrSolicitationDelay));
703 
704     LogInfo("Start Router Solicitation, scheduled in %u milliseconds", randomDelay);
705     mTimeRouterSolicitStart = TimerMilli::GetNow();
706     mRouterSolicitTimer.Start(randomDelay);
707 
708 exit:
709     return;
710 }
711 
IsRouterSolicitationInProgress(void) const712 bool RoutingManager::IsRouterSolicitationInProgress(void) const
713 {
714     return mRouterSolicitTimer.IsRunning() || mRouterSolicitCount > 0;
715 }
716 
SendRouterSolicitation(void)717 Error RoutingManager::SendRouterSolicitation(void)
718 {
719     Ip6::Address                  destAddress;
720     Ip6::Nd::RouterSolicitMessage routerSolicit;
721     InfraIf::Icmp6Packet          packet;
722 
723     OT_ASSERT(IsInitialized());
724 
725     packet.InitFrom(routerSolicit);
726     destAddress.SetToLinkLocalAllRoutersMulticast();
727 
728     return mInfraIf.Send(packet, destAddress);
729 }
730 
SendRouterAdvertisement(RouterAdvTxMode aRaTxMode)731 void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode)
732 {
733     // RA message max length is derived to accommodate:
734     //
735     // - The RA header,
736     // - At most one PIO (for local on-link prefix),
737     // - At most twice `kMaxOnMeshPrefixes` RIO for on-mesh prefixes.
738     //   Factor two is used for RIO to account for entries invalidating
739     //   previous prefixes while adding new ones.
740 
741     static constexpr uint16_t kMaxRaLength =
742         sizeof(Ip6::Nd::RouterAdvertMessage::Header) + sizeof(Ip6::Nd::PrefixInfoOption) +
743         2 * kMaxOnMeshPrefixes * (sizeof(Ip6::Nd::RouteInfoOption) + sizeof(Ip6::Prefix));
744 
745     uint8_t                         buffer[kMaxRaLength];
746     Ip6::Nd::RouterAdvertMessage    raMsg(mRouterAdvertHeader, buffer);
747     NetworkData::Iterator           iterator;
748     NetworkData::OnMeshPrefixConfig prefixConfig;
749 
750     // Append PIO for local on-link prefix. Ensure it is either being
751     // advertised or deprecated.
752 
753     if (mIsAdvertisingLocalOnLinkPrefix || mOnLinkPrefixDeprecateTimer.IsRunning())
754     {
755         uint32_t validLifetime     = kDefaultOnLinkPrefixLifetime;
756         uint32_t preferredLifetime = kDefaultOnLinkPrefixLifetime;
757 
758         if (mOnLinkPrefixDeprecateTimer.IsRunning())
759         {
760             validLifetime     = TimeMilli::MsecToSec(mOnLinkPrefixDeprecateTimer.GetFireTime() - TimerMilli::GetNow());
761             preferredLifetime = 0;
762         }
763 
764         SuccessOrAssert(raMsg.AppendPrefixInfoOption(mLocalOnLinkPrefix, validLifetime, preferredLifetime));
765 
766         if (mIsAdvertisingLocalOnLinkPrefix)
767         {
768             mTimeAdvertisedOnLinkPrefix = TimerMilli::GetNow();
769         }
770 
771         LogInfo("RouterAdvert: Added PIO for %s (valid=%u, preferred=%u)", mLocalOnLinkPrefix.ToString().AsCString(),
772                 validLifetime, preferredLifetime);
773     }
774 
775     // Determine which previously advertised prefixes need to be
776     // invalidated. Under `kInvalidateAllPrevPrefixes` mode we need
777     // to invalidate all. Under `kAdvPrefixesFromNetData` mode, we
778     // check Network Data entries and invalidate any previously
779     // advertised prefix that is no longer present in the Network
780     // Data. We go through all Network Data prefixes and mark the
781     // ones we find in `mAdvertisedPrefixes` as deleted by setting
782     // the prefix length to zero). By the end, the remaining entries
783     // in the array with a non-zero prefix length are invalidated.
784 
785     if (aRaTxMode != kInvalidateAllPrevPrefixes)
786     {
787         iterator = NetworkData::kIteratorInit;
788 
789         while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
790         {
791             if (!prefixConfig.mOnMesh || prefixConfig.mDp || (prefixConfig.GetPrefix() == mLocalOmrPrefix.GetPrefix()))
792             {
793                 continue;
794             }
795 
796             mAdvertisedPrefixes.MarkAsDeleted(prefixConfig.GetPrefix());
797         }
798 
799         if (mLocalOmrPrefix.IsAddedInNetData())
800         {
801             mAdvertisedPrefixes.MarkAsDeleted(mLocalOmrPrefix.GetPrefix());
802         }
803     }
804 
805     for (const OnMeshPrefix &prefix : mAdvertisedPrefixes)
806     {
807         if (prefix.GetLength() != 0)
808         {
809             SuccessOrAssert(raMsg.AppendRouteInfoOption(prefix, /* aRouteLifetime */ 0, mRouteInfoOptionPreference));
810             LogInfo("RouterAdvert: Added RIO for %s (lifetime=0)", prefix.ToString().AsCString());
811         }
812     }
813 
814     // Discover and add prefixes from Network Data to advertise as
815     // RIO in the Router Advertisement message.
816 
817     mAdvertisedPrefixes.Clear();
818 
819     if (aRaTxMode == kAdvPrefixesFromNetData)
820     {
821         // `mAdvertisedPrefixes` array has a limited size. We add more
822         // important prefixes first in the array to ensure they are
823         // advertised in the RA message. Note that `Add()` method
824         // will ensure to add a prefix only once (will check if
825         // prefix is already present in the array).
826 
827         // (1) Local OMR prefix.
828 
829         if (mLocalOmrPrefix.IsAddedInNetData())
830         {
831             mAdvertisedPrefixes.Add(mLocalOmrPrefix.GetPrefix());
832         }
833 
834         // (2) Favored OMR prefix.
835 
836         if (!mFavoredOmrPrefix.IsEmpty())
837         {
838             mAdvertisedPrefixes.Add(mFavoredOmrPrefix.GetPrefix());
839         }
840 
841         // (3) All other OMR prefixes.
842 
843         iterator = NetworkData::kIteratorInit;
844 
845         while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
846         {
847             // Local OMR prefix is added to the array depending on
848             // `mLocalOmrPrefix.IsAddedInNetData()` at step (1). As
849             // we iterate through the Network Data prefixes, we skip
850             // over entries matching the local OMR prefix. This
851             // ensures that we stop including it in emitted RA
852             // message as soon as we decide to remove it from Network
853             // Data. Note that upon requesting it to be removed from
854             // Network Data the change needs to be registered with
855             // leader and can take some time to be updated in Network
856             // Data.
857 
858             if (IsValidOmrPrefix(prefixConfig) && (prefixConfig.GetPrefix() != mLocalOmrPrefix.GetPrefix()))
859             {
860                 mAdvertisedPrefixes.Add(prefixConfig.GetPrefix());
861             }
862         }
863 
864         // (4) All other on-mesh prefixes (excluding Domain Prefix).
865 
866         iterator = NetworkData::kIteratorInit;
867 
868         while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
869         {
870             if (prefixConfig.mOnMesh && !prefixConfig.mDp && !IsValidOmrPrefix(prefixConfig))
871             {
872                 mAdvertisedPrefixes.Add(prefixConfig.GetPrefix());
873             }
874         }
875 
876         for (const OnMeshPrefix &prefix : mAdvertisedPrefixes)
877         {
878             SuccessOrAssert(raMsg.AppendRouteInfoOption(prefix, kDefaultOmrPrefixLifetime, mRouteInfoOptionPreference));
879             LogInfo("RouterAdvert: Added RIO for %s (lifetime=%u)", prefix.ToString().AsCString(),
880                     kDefaultOmrPrefixLifetime);
881         }
882     }
883 
884     if (raMsg.ContainsAnyOptions())
885     {
886         Error        error;
887         Ip6::Address destAddress;
888 
889         ++mRouterAdvertisementCount;
890 
891         destAddress.SetToLinkLocalAllNodesMulticast();
892 
893         error = mInfraIf.Send(raMsg.GetAsPacket(), destAddress);
894 
895         if (error == kErrorNone)
896         {
897             mLastRouterAdvertisementSendTime = TimerMilli::GetNow();
898             LogInfo("Sent Router Advertisement on %s", mInfraIf.ToString().AsCString());
899             DumpDebg("[BR-CERT] direction=send | type=RA |", raMsg.GetAsPacket().GetBytes(),
900                      raMsg.GetAsPacket().GetLength());
901         }
902         else
903         {
904             LogWarn("Failed to send Router Advertisement on %s: %s", mInfraIf.ToString().AsCString(),
905                     ErrorToString(error));
906         }
907     }
908 }
909 
IsReceivedRouterAdvertFromManager(const Ip6::Nd::RouterAdvertMessage & aRaMessage) const910 bool RoutingManager::IsReceivedRouterAdvertFromManager(const Ip6::Nd::RouterAdvertMessage &aRaMessage) const
911 {
912     // Determines whether or not a received RA message was prepared by
913     // by `RoutingManager` itself.
914 
915     bool        isFromManager = false;
916     uint16_t    rioCount      = 0;
917     Ip6::Prefix prefix;
918 
919     VerifyOrExit(aRaMessage.ContainsAnyOptions());
920 
921     for (const Ip6::Nd::Option &option : aRaMessage)
922     {
923         switch (option.GetType())
924         {
925         case Ip6::Nd::Option::kTypePrefixInfo:
926         {
927             // PIO should match `mLocalOnLinkPrefix`.
928 
929             const Ip6::Nd::PrefixInfoOption &pio = static_cast<const Ip6::Nd::PrefixInfoOption &>(option);
930 
931             VerifyOrExit(pio.IsValid());
932             pio.GetPrefix(prefix);
933 
934             VerifyOrExit(prefix == mLocalOnLinkPrefix);
935             break;
936         }
937 
938         case Ip6::Nd::Option::kTypeRouteInfo:
939         {
940             // RIO (with non-zero lifetime) should match entries from
941             // `mAdvertisedPrefixes`. We keep track of the number
942             // of matched RIOs and check after the loop ends that all
943             // entries were seen.
944 
945             const Ip6::Nd::RouteInfoOption &rio = static_cast<const Ip6::Nd::RouteInfoOption &>(option);
946 
947             VerifyOrExit(rio.IsValid());
948             rio.GetPrefix(prefix);
949 
950             if (rio.GetRouteLifetime() != 0)
951             {
952                 VerifyOrExit(mAdvertisedPrefixes.Contains(prefix));
953                 rioCount++;
954             }
955 
956             break;
957         }
958 
959         default:
960             ExitNow();
961         }
962     }
963 
964     VerifyOrExit(rioCount == mAdvertisedPrefixes.GetLength());
965 
966     isFromManager = true;
967 
968 exit:
969     return isFromManager;
970 }
971 
IsValidBrUlaPrefix(const Ip6::Prefix & aBrUlaPrefix)972 bool RoutingManager::IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix)
973 {
974     return aBrUlaPrefix.mLength == kBrUlaPrefixLength && aBrUlaPrefix.mPrefix.mFields.m8[0] == 0xfd;
975 }
976 
IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig & aOnMeshPrefixConfig)977 bool RoutingManager::IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig)
978 {
979     return IsValidOmrPrefix(aOnMeshPrefixConfig.GetPrefix()) && aOnMeshPrefixConfig.mOnMesh &&
980            aOnMeshPrefixConfig.mSlaac && aOnMeshPrefixConfig.mStable && !aOnMeshPrefixConfig.mDp;
981 }
982 
IsValidOmrPrefix(const Ip6::Prefix & aOmrPrefix)983 bool RoutingManager::IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix)
984 {
985     // Accept ULA prefix with length of 64 bits and GUA prefix.
986     return (aOmrPrefix.mLength == kOmrPrefixLength && aOmrPrefix.mPrefix.mFields.m8[0] == 0xfd) ||
987            (aOmrPrefix.mLength >= 3 && (aOmrPrefix.GetBytes()[0] & 0xE0) == 0x20);
988 }
989 
IsValidOnLinkPrefix(const Ip6::Nd::PrefixInfoOption & aPio)990 bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Nd::PrefixInfoOption &aPio)
991 {
992     Ip6::Prefix prefix;
993 
994     aPio.GetPrefix(prefix);
995 
996     return IsValidOnLinkPrefix(prefix) && aPio.IsOnLinkFlagSet() && aPio.IsAutoAddrConfigFlagSet();
997 }
998 
IsValidOnLinkPrefix(const Ip6::Prefix & aOnLinkPrefix)999 bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix)
1000 {
1001     return aOnLinkPrefix.IsValid() && (aOnLinkPrefix.GetLength() > 0) && !aOnLinkPrefix.IsLinkLocal() &&
1002            !aOnLinkPrefix.IsMulticast();
1003 }
1004 
HandleRouterSolicitTimer(Timer & aTimer)1005 void RoutingManager::HandleRouterSolicitTimer(Timer &aTimer)
1006 {
1007     aTimer.Get<RoutingManager>().HandleRouterSolicitTimer();
1008 }
1009 
HandleRouterSolicitTimer(void)1010 void RoutingManager::HandleRouterSolicitTimer(void)
1011 {
1012     LogInfo("Router solicitation times out");
1013 
1014     if (mRouterSolicitCount < kMaxRtrSolicitations)
1015     {
1016         uint32_t nextSolicitationDelay;
1017         Error    error;
1018 
1019         error = SendRouterSolicitation();
1020 
1021         if (error == kErrorNone)
1022         {
1023             LogDebg("Successfully sent %uth Router Solicitation", mRouterSolicitCount);
1024             ++mRouterSolicitCount;
1025             nextSolicitationDelay =
1026                 (mRouterSolicitCount == kMaxRtrSolicitations) ? kMaxRtrSolicitationDelay : kRtrSolicitationInterval;
1027         }
1028         else
1029         {
1030             LogCrit("Failed to send %uth Router Solicitation: %s", mRouterSolicitCount, ErrorToString(error));
1031 
1032             // It's unexpected that RS will fail and we will retry sending RS messages in 60 seconds.
1033             // Notice that `mRouterSolicitCount` is not incremented for failed RS and thus we will
1034             // not start configuring on-link prefixes before `kMaxRtrSolicitations` successful RS
1035             // messages have been sent.
1036             nextSolicitationDelay = kRtrSolicitationRetryDelay;
1037             mRouterSolicitCount   = 0;
1038         }
1039 
1040         LogDebg("Router solicitation timer scheduled in %u seconds", nextSolicitationDelay);
1041         mRouterSolicitTimer.Start(Time::SecToMsec(nextSolicitationDelay));
1042     }
1043     else
1044     {
1045         // Remove route prefixes and deprecate on-link prefixes that
1046         // are not refreshed during Router Solicitation.
1047         mDiscoveredPrefixTable.RemoveOrDeprecateOldEntries(mTimeRouterSolicitStart);
1048 
1049         // Invalidate the learned RA message if it is not refreshed during Router Solicitation.
1050         if (mTimeRouterAdvMessageLastUpdate <= mTimeRouterSolicitStart)
1051         {
1052             UpdateRouterAdvertHeader(/* aRouterAdvertMessage */ nullptr);
1053         }
1054 
1055         mRouterSolicitCount = 0;
1056 
1057         // Re-evaluate our routing policy and send Router Advertisement if necessary.
1058         StartRoutingPolicyEvaluationDelay(/* aDelayJitter */ 0);
1059     }
1060 }
1061 
HandleDiscoveredPrefixStaleTimer(Timer & aTimer)1062 void RoutingManager::HandleDiscoveredPrefixStaleTimer(Timer &aTimer)
1063 {
1064     aTimer.Get<RoutingManager>().HandleDiscoveredPrefixStaleTimer();
1065 }
1066 
HandleDiscoveredPrefixStaleTimer(void)1067 void RoutingManager::HandleDiscoveredPrefixStaleTimer(void)
1068 {
1069     LogInfo("Stale On-Link or OMR Prefixes or RA messages are detected");
1070     StartRouterSolicitationDelay();
1071 }
1072 
HandleRoutingPolicyTimer(Timer & aTimer)1073 void RoutingManager::HandleRoutingPolicyTimer(Timer &aTimer)
1074 {
1075     aTimer.Get<RoutingManager>().EvaluateRoutingPolicy();
1076 }
1077 
HandleRouterSolicit(const InfraIf::Icmp6Packet & aPacket,const Ip6::Address & aSrcAddress)1078 void RoutingManager::HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
1079 {
1080     OT_UNUSED_VARIABLE(aPacket);
1081     OT_UNUSED_VARIABLE(aSrcAddress);
1082 
1083     LogInfo("Received Router Solicitation from %s on %s", aSrcAddress.ToString().AsCString(),
1084             mInfraIf.ToString().AsCString());
1085 
1086     // Schedule routing policy evaluation with random jitter to respond with Router Advertisement.
1087     StartRoutingPolicyEvaluationJitter(kRaReplyJitter);
1088 }
1089 
HandleRouterAdvertisement(const InfraIf::Icmp6Packet & aPacket,const Ip6::Address & aSrcAddress)1090 void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
1091 {
1092     Ip6::Nd::RouterAdvertMessage routerAdvMessage(aPacket);
1093 
1094     OT_ASSERT(mIsRunning);
1095 
1096     VerifyOrExit(routerAdvMessage.IsValid());
1097 
1098     LogInfo("Received Router Advertisement from %s on %s", aSrcAddress.ToString().AsCString(),
1099             mInfraIf.ToString().AsCString());
1100     DumpDebg("[BR-CERT] direction=recv | type=RA |", aPacket.GetBytes(), aPacket.GetLength());
1101 
1102     mDiscoveredPrefixTable.ProcessRouterAdvertMessage(routerAdvMessage, aSrcAddress);
1103 
1104     // Remember the header and parameters of RA messages which are
1105     // initiated from the infra interface.
1106     if (mInfraIf.HasAddress(aSrcAddress))
1107     {
1108         UpdateRouterAdvertHeader(&routerAdvMessage);
1109     }
1110 
1111 exit:
1112     return;
1113 }
1114 
ShouldProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption & aPio,const Ip6::Prefix & aPrefix)1115 bool RoutingManager::ShouldProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, const Ip6::Prefix &aPrefix)
1116 {
1117     // Indicate whether to process or skip a given prefix
1118     // from a PIO (from received RA message).
1119 
1120     bool shouldProcess = false;
1121 
1122     VerifyOrExit(mIsRunning);
1123 
1124     if (!IsValidOnLinkPrefix(aPio))
1125     {
1126         LogInfo("Ignore invalid on-link prefix in PIO: %s", aPrefix.ToString().AsCString());
1127         ExitNow();
1128     }
1129 
1130     if (mIsAdvertisingLocalOnLinkPrefix)
1131     {
1132         VerifyOrExit(aPrefix != mLocalOnLinkPrefix);
1133     }
1134 
1135     shouldProcess = true;
1136 
1137 exit:
1138     return shouldProcess;
1139 }
1140 
ShouldProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption & aRio,const Ip6::Prefix & aPrefix)1141 bool RoutingManager::ShouldProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, const Ip6::Prefix &aPrefix)
1142 {
1143     // Indicate whether to process or skip a given prefix
1144     // from a RIO (from received RA message).
1145 
1146     OT_UNUSED_VARIABLE(aRio);
1147 
1148     bool shouldProcess = false;
1149 
1150     VerifyOrExit(mIsRunning);
1151 
1152     if (aPrefix.GetLength() == 0)
1153     {
1154         // Always process default route ::/0 prefix.
1155         ExitNow(shouldProcess = true);
1156     }
1157 
1158     if (!IsValidOmrPrefix(aPrefix))
1159     {
1160         LogInfo("Ignore RIO prefix %s since not a valid OMR prefix", aPrefix.ToString().AsCString());
1161         ExitNow();
1162     }
1163 
1164     VerifyOrExit(mLocalOmrPrefix.GetPrefix() != aPrefix);
1165 
1166     // Ignore OMR prefixes advertised by ourselves or in current Thread Network Data.
1167     // The `mAdvertisedPrefixes` and the OMR prefix set in Network Data should eventually
1168     // be equal, but there is time that they are not synchronized immediately:
1169     // 1. Network Data could contain more OMR prefixes than `mAdvertisedPrefixes` because
1170     //    we added random delay before Evaluating routing policy when Network Data is changed.
1171     // 2. `mAdvertisedPrefixes` could contain more OMR prefixes than Network Data because
1172     //    it takes time to sync a new OMR prefix into Network Data (multicast loopback RA
1173     //    messages are usually faster than Thread Network Data propagation).
1174     // They are the reasons why we need both the checks.
1175 
1176     VerifyOrExit(!mAdvertisedPrefixes.Contains(aPrefix));
1177     VerifyOrExit(!Get<RoutingManager>().NetworkDataContainsOmrPrefix(aPrefix));
1178 
1179     shouldProcess = true;
1180 
1181 exit:
1182     return shouldProcess;
1183 }
1184 
HandleDiscoveredPrefixTableChanged(void)1185 void RoutingManager::HandleDiscoveredPrefixTableChanged(void)
1186 {
1187     // This is a callback from `mDiscoveredPrefixTable` indicating that
1188     // there has been a change in the table. If the favored on-link
1189     // prefix has changed, we trigger a re-evaluation of the routing
1190     // policy.
1191 
1192     Ip6::Prefix newFavoredPrefix;
1193 
1194     VerifyOrExit(mIsRunning);
1195 
1196     ResetDiscoveredPrefixStaleTimer();
1197 
1198     mDiscoveredPrefixTable.FindFavoredOnLinkPrefix(newFavoredPrefix);
1199 
1200     if (newFavoredPrefix != mFavoredDiscoveredOnLinkPrefix)
1201     {
1202         StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter);
1203     }
1204 
1205 exit:
1206     return;
1207 }
1208 
NetworkDataContainsOmrPrefix(const Ip6::Prefix & aPrefix) const1209 bool RoutingManager::NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) const
1210 {
1211     NetworkData::Iterator           iterator = NetworkData::kIteratorInit;
1212     NetworkData::OnMeshPrefixConfig onMeshPrefixConfig;
1213     bool                            contain = false;
1214 
1215     while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, onMeshPrefixConfig) == kErrorNone)
1216     {
1217         if (IsValidOmrPrefix(onMeshPrefixConfig) && onMeshPrefixConfig.GetPrefix() == aPrefix)
1218         {
1219             contain = true;
1220             break;
1221         }
1222     }
1223 
1224     return contain;
1225 }
1226 
UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage * aRouterAdvertMessage)1227 void RoutingManager::UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage)
1228 {
1229     // Updates the `mRouterAdvertHeader` from the given RA message.
1230 
1231     Ip6::Nd::RouterAdvertMessage::Header oldHeader;
1232 
1233     if (aRouterAdvertMessage != nullptr)
1234     {
1235         // We skip and do not update RA header if the received RA message
1236         // was not prepared and sent by `RoutingManager` itself.
1237 
1238         VerifyOrExit(!IsReceivedRouterAdvertFromManager(*aRouterAdvertMessage));
1239     }
1240 
1241     oldHeader                       = mRouterAdvertHeader;
1242     mTimeRouterAdvMessageLastUpdate = TimerMilli::GetNow();
1243 
1244     if (aRouterAdvertMessage == nullptr || aRouterAdvertMessage->GetHeader().GetRouterLifetime() == 0)
1245     {
1246         mRouterAdvertHeader.SetToDefault();
1247         mLearntRouterAdvMessageFromHost = false;
1248     }
1249     else
1250     {
1251         // The checksum is set to zero in `mRouterAdvertHeader`
1252         // which indicates to platform that it needs to do the
1253         // calculation and update it.
1254 
1255         mRouterAdvertHeader = aRouterAdvertMessage->GetHeader();
1256         mRouterAdvertHeader.SetChecksum(0);
1257         mLearntRouterAdvMessageFromHost = true;
1258     }
1259 
1260     ResetDiscoveredPrefixStaleTimer();
1261 
1262     if (mRouterAdvertHeader != oldHeader)
1263     {
1264         // If there was a change to the header, start timer to
1265         // reevaluate routing policy and send RA message with new
1266         // header.
1267 
1268         StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter);
1269     }
1270 
1271 exit:
1272     return;
1273 }
1274 
ResetDiscoveredPrefixStaleTimer(void)1275 void RoutingManager::ResetDiscoveredPrefixStaleTimer(void)
1276 {
1277     TimeMilli now = TimerMilli::GetNow();
1278     TimeMilli nextStaleTime;
1279 
1280     OT_ASSERT(mIsRunning);
1281 
1282     // The stale timer triggers sending RS to check the state of
1283     // discovered prefixes and host RA messages.
1284 
1285     nextStaleTime = mDiscoveredPrefixTable.CalculateNextStaleTime(now);
1286 
1287     // Check for stale Router Advertisement Message if learnt from Host.
1288     if (mLearntRouterAdvMessageFromHost)
1289     {
1290         TimeMilli raStaleTime = OT_MAX(now, mTimeRouterAdvMessageLastUpdate + Time::SecToMsec(kRtrAdvStaleTime));
1291 
1292         nextStaleTime = OT_MIN(nextStaleTime, raStaleTime);
1293     }
1294 
1295     if (nextStaleTime == now.GetDistantFuture())
1296     {
1297         if (mDiscoveredPrefixStaleTimer.IsRunning())
1298         {
1299             LogDebg("Prefix stale timer stopped");
1300         }
1301 
1302         mDiscoveredPrefixStaleTimer.Stop();
1303     }
1304     else
1305     {
1306         mDiscoveredPrefixStaleTimer.FireAt(nextStaleTime);
1307         LogDebg("Prefix stale timer scheduled in %lu ms", nextStaleTime - now);
1308     }
1309 }
1310 
1311 //---------------------------------------------------------------------------------------------------------------------
1312 // DiscoveredPrefixTable
1313 
DiscoveredPrefixTable(Instance & aInstance)1314 RoutingManager::DiscoveredPrefixTable::DiscoveredPrefixTable(Instance &aInstance)
1315     : InstanceLocator(aInstance)
1316     , mTimer(aInstance, HandleTimer)
1317     , mSignalTask(aInstance, HandleSignalTask)
1318     , mAllowDefaultRouteInNetData(false)
1319 {
1320 }
1321 
ProcessRouterAdvertMessage(const Ip6::Nd::RouterAdvertMessage & aRaMessage,const Ip6::Address & aSrcAddress)1322 void RoutingManager::DiscoveredPrefixTable::ProcessRouterAdvertMessage(const Ip6::Nd::RouterAdvertMessage &aRaMessage,
1323                                                                        const Ip6::Address &                aSrcAddress)
1324 {
1325     // Process a received RA message and update the prefix table.
1326 
1327     Router *router = mRouters.FindMatching(aSrcAddress);
1328 
1329     if (router == nullptr)
1330     {
1331         router = mRouters.PushBack();
1332 
1333         if (router == nullptr)
1334         {
1335             LogWarn("Received RA from too many routers, ignore RA from %s", aSrcAddress.ToString().AsCString());
1336             ExitNow();
1337         }
1338 
1339         router->mAddress = aSrcAddress;
1340         router->mEntries.Clear();
1341     }
1342 
1343     // RA message can indicate router provides default route in the RA
1344     // message header and can also include an RIO for `::/0`. When
1345     // processing an RA message, the preference and lifetime values
1346     // in a `::/0` RIO override the preference and lifetime values in
1347     // the RA header (per RFC 4191 section 3.1).
1348 
1349     ProcessDefaultRoute(aRaMessage.GetHeader(), *router);
1350 
1351     for (const Ip6::Nd::Option &option : aRaMessage)
1352     {
1353         switch (option.GetType())
1354         {
1355         case Ip6::Nd::Option::kTypePrefixInfo:
1356             ProcessPrefixInfoOption(static_cast<const Ip6::Nd::PrefixInfoOption &>(option), *router);
1357             break;
1358 
1359         case Ip6::Nd::Option::kTypeRouteInfo:
1360             ProcessRouteInfoOption(static_cast<const Ip6::Nd::RouteInfoOption &>(option), *router);
1361             break;
1362 
1363         default:
1364             break;
1365         }
1366     }
1367 
1368     RemoveRoutersWithNoEntries();
1369 
1370 exit:
1371     return;
1372 }
1373 
ProcessDefaultRoute(const Ip6::Nd::RouterAdvertMessage::Header & aRaHeader,Router & aRouter)1374 void RoutingManager::DiscoveredPrefixTable::ProcessDefaultRoute(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader,
1375                                                                 Router &                                    aRouter)
1376 {
1377     Entry *     entry;
1378     Ip6::Prefix prefix;
1379 
1380     prefix.Clear();
1381     entry = aRouter.mEntries.FindMatching(Entry::Matcher(prefix, Entry::kTypeRoute));
1382 
1383     if (entry == nullptr)
1384     {
1385         VerifyOrExit(aRaHeader.GetRouterLifetime() != 0);
1386 
1387         entry = AllocateEntry();
1388 
1389         if (entry == nullptr)
1390         {
1391             LogWarn("Discovered too many prefixes, ignore default route from RA header");
1392             ExitNow();
1393         }
1394 
1395         entry->SetFrom(aRaHeader);
1396         aRouter.mEntries.Push(*entry);
1397     }
1398     else
1399     {
1400         entry->SetFrom(aRaHeader);
1401     }
1402 
1403     UpdateNetworkDataOnChangeTo(*entry);
1404     mTimer.FireAtIfEarlier(entry->GetExpireTime());
1405     SignalTableChanged();
1406 
1407 exit:
1408     return;
1409 }
1410 
ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption & aPio,Router & aRouter)1411 void RoutingManager::DiscoveredPrefixTable::ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio,
1412                                                                     Router &                         aRouter)
1413 {
1414     Ip6::Prefix prefix;
1415     Entry *     entry;
1416 
1417     VerifyOrExit(aPio.IsValid());
1418     aPio.GetPrefix(prefix);
1419 
1420     VerifyOrExit(Get<RoutingManager>().ShouldProcessPrefixInfoOption(aPio, prefix));
1421 
1422     LogInfo("Processing PIO (%s, %u seconds)", prefix.ToString().AsCString(), aPio.GetValidLifetime());
1423 
1424     entry = aRouter.mEntries.FindMatching(Entry::Matcher(prefix, Entry::kTypeOnLink));
1425 
1426     if (entry == nullptr)
1427     {
1428         VerifyOrExit(aPio.GetValidLifetime() != 0);
1429 
1430         entry = AllocateEntry();
1431 
1432         if (entry == nullptr)
1433         {
1434             LogWarn("Discovered too many prefixes, ignore on-link prefix %s", prefix.ToString().AsCString());
1435             ExitNow();
1436         }
1437 
1438         entry->SetFrom(aPio);
1439         aRouter.mEntries.Push(*entry);
1440     }
1441     else
1442     {
1443         Entry newEntry;
1444 
1445         newEntry.SetFrom(aPio);
1446         entry->AdoptValidAndPreferredLiftimesFrom(newEntry);
1447     }
1448 
1449     UpdateNetworkDataOnChangeTo(*entry);
1450     mTimer.FireAtIfEarlier(entry->GetExpireTime());
1451     SignalTableChanged();
1452 
1453 exit:
1454     return;
1455 }
1456 
ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption & aRio,Router & aRouter)1457 void RoutingManager::DiscoveredPrefixTable::ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio,
1458                                                                    Router &                        aRouter)
1459 {
1460     Ip6::Prefix prefix;
1461     Entry *     entry;
1462 
1463     VerifyOrExit(aRio.IsValid());
1464     aRio.GetPrefix(prefix);
1465 
1466     VerifyOrExit(Get<RoutingManager>().ShouldProcessRouteInfoOption(aRio, prefix));
1467 
1468     LogInfo("Processing RIO (%s, %u seconds)", prefix.ToString().AsCString(), aRio.GetRouteLifetime());
1469 
1470     entry = aRouter.mEntries.FindMatching(Entry::Matcher(prefix, Entry::kTypeRoute));
1471 
1472     if (entry == nullptr)
1473     {
1474         VerifyOrExit(aRio.GetRouteLifetime() != 0);
1475 
1476         entry = AllocateEntry();
1477 
1478         if (entry == nullptr)
1479         {
1480             LogWarn("Discovered too many prefixes, ignore route prefix %s", prefix.ToString().AsCString());
1481             ExitNow();
1482         }
1483 
1484         entry->SetFrom(aRio);
1485         aRouter.mEntries.Push(*entry);
1486     }
1487     else
1488     {
1489         entry->SetFrom(aRio);
1490     }
1491 
1492     UpdateNetworkDataOnChangeTo(*entry);
1493     mTimer.FireAtIfEarlier(entry->GetExpireTime());
1494     SignalTableChanged();
1495 
1496 exit:
1497     return;
1498 }
1499 
SetAllowDefaultRouteInNetData(bool aAllow)1500 void RoutingManager::DiscoveredPrefixTable::SetAllowDefaultRouteInNetData(bool aAllow)
1501 {
1502     Entry *     favoredEntry;
1503     Ip6::Prefix prefix;
1504 
1505     VerifyOrExit(aAllow != mAllowDefaultRouteInNetData);
1506 
1507     LogInfo("Allow default route in netdata: %s -> %s", ToYesNo(mAllowDefaultRouteInNetData), ToYesNo(aAllow));
1508 
1509     mAllowDefaultRouteInNetData = aAllow;
1510 
1511     prefix.Clear();
1512     favoredEntry = FindFavoredEntryToPublish(prefix);
1513     VerifyOrExit(favoredEntry != nullptr);
1514 
1515     if (mAllowDefaultRouteInNetData)
1516     {
1517         PublishEntry(*favoredEntry);
1518     }
1519     else
1520     {
1521         UnpublishEntry(*favoredEntry);
1522     }
1523 
1524 exit:
1525     return;
1526 }
1527 
FindFavoredOnLinkPrefix(Ip6::Prefix & aPrefix) const1528 void RoutingManager::DiscoveredPrefixTable::FindFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const
1529 {
1530     // Find the smallest preferred on-link prefix entry in the table
1531     // and return it in `aPrefix`. If there is none, `aPrefix` is
1532     // cleared (prefix length is set to zero).
1533 
1534     aPrefix.Clear();
1535 
1536     for (const Router &router : mRouters)
1537     {
1538         for (const Entry &entry : router.mEntries)
1539         {
1540             if (!entry.IsOnLinkPrefix() || entry.IsDeprecated())
1541             {
1542                 continue;
1543             }
1544 
1545             if ((aPrefix.GetLength() == 0) || (entry.GetPrefix() < aPrefix))
1546             {
1547                 aPrefix = entry.GetPrefix();
1548             }
1549         }
1550     }
1551 }
1552 
ContainsOnLinkPrefix(const Ip6::Prefix & aPrefix) const1553 bool RoutingManager::DiscoveredPrefixTable::ContainsOnLinkPrefix(const Ip6::Prefix &aPrefix) const
1554 {
1555     return ContainsPrefix(Entry::Matcher(aPrefix, Entry::kTypeOnLink));
1556 }
1557 
ContainsRoutePrefix(const Ip6::Prefix & aPrefix) const1558 bool RoutingManager::DiscoveredPrefixTable::ContainsRoutePrefix(const Ip6::Prefix &aPrefix) const
1559 {
1560     return ContainsPrefix(Entry::Matcher(aPrefix, Entry::kTypeRoute));
1561 }
1562 
ContainsPrefix(const Entry::Matcher & aMatcher) const1563 bool RoutingManager::DiscoveredPrefixTable::ContainsPrefix(const Entry::Matcher &aMatcher) const
1564 {
1565     bool contains = false;
1566 
1567     for (const Router &router : mRouters)
1568     {
1569         if (router.mEntries.ContainsMatching(aMatcher))
1570         {
1571             contains = true;
1572             break;
1573         }
1574     }
1575 
1576     return contains;
1577 }
1578 
RemoveOnLinkPrefix(const Ip6::Prefix & aPrefix,NetDataMode aNetDataMode)1579 void RoutingManager::DiscoveredPrefixTable::RemoveOnLinkPrefix(const Ip6::Prefix &aPrefix, NetDataMode aNetDataMode)
1580 {
1581     RemovePrefix(Entry::Matcher(aPrefix, Entry::kTypeOnLink), aNetDataMode);
1582 }
1583 
RemoveRoutePrefix(const Ip6::Prefix & aPrefix,NetDataMode aNetDataMode)1584 void RoutingManager::DiscoveredPrefixTable::RemoveRoutePrefix(const Ip6::Prefix &aPrefix, NetDataMode aNetDataMode)
1585 {
1586     RemovePrefix(Entry::Matcher(aPrefix, Entry::kTypeRoute), aNetDataMode);
1587 }
1588 
RemovePrefix(const Entry::Matcher & aMatcher,NetDataMode aNetDataMode)1589 void RoutingManager::DiscoveredPrefixTable::RemovePrefix(const Entry::Matcher &aMatcher, NetDataMode aNetDataMode)
1590 {
1591     // Removes all entries matching a given prefix from the table.
1592     // `aNetDataMode` specifies behavior when a match is found and
1593     // removed. It indicates whether or not to unpublish it from
1594     // Network Data.
1595 
1596     LinkedList<Entry> removedEntries;
1597 
1598     for (Router &router : mRouters)
1599     {
1600         router.mEntries.RemoveAllMatching(aMatcher, removedEntries);
1601     }
1602 
1603     VerifyOrExit(!removedEntries.IsEmpty());
1604 
1605     if (aNetDataMode == kUnpublishFromNetData)
1606     {
1607         UnpublishEntry(*removedEntries.GetHead());
1608     }
1609 
1610     FreeEntries(removedEntries);
1611     RemoveRoutersWithNoEntries();
1612 
1613     SignalTableChanged();
1614 
1615 exit:
1616     return;
1617 }
1618 
RemoveAllEntries(void)1619 void RoutingManager::DiscoveredPrefixTable::RemoveAllEntries(void)
1620 {
1621     // Remove all entries from the table and unpublish them
1622     // from Network Data.
1623 
1624     for (Router &router : mRouters)
1625     {
1626         Entry *entry;
1627 
1628         while ((entry = router.mEntries.Pop()) != nullptr)
1629         {
1630             UnpublishEntry(*entry);
1631             FreeEntry(*entry);
1632             SignalTableChanged();
1633         }
1634     }
1635 
1636     RemoveRoutersWithNoEntries();
1637     mTimer.Stop();
1638 }
1639 
RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold)1640 void RoutingManager::DiscoveredPrefixTable::RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold)
1641 {
1642     // Remove route prefix entries and deprecate on-link entries in
1643     // the table that are old (not updated since `aTimeThreshold`).
1644 
1645     for (Router &router : mRouters)
1646     {
1647         for (Entry &entry : router.mEntries)
1648         {
1649             if (entry.GetLastUpdateTime() <= aTimeThreshold)
1650             {
1651                 if (entry.IsOnLinkPrefix())
1652                 {
1653                     entry.ClearPreferredLifetime();
1654                 }
1655                 else
1656                 {
1657                     entry.ClearValidLifetime();
1658                 }
1659 
1660                 SignalTableChanged();
1661             }
1662         }
1663     }
1664 
1665     RemoveExpiredEntries();
1666 }
1667 
CalculateNextStaleTime(TimeMilli aNow) const1668 TimeMilli RoutingManager::DiscoveredPrefixTable::CalculateNextStaleTime(TimeMilli aNow) const
1669 {
1670     TimeMilli onLinkStaleTime = aNow;
1671     TimeMilli routeStaleTime  = aNow.GetDistantFuture();
1672     bool      foundOnLink     = false;
1673 
1674     // For on-link prefixes, we consider stale time as when all on-link
1675     // prefixes become stale (the latest stale time) but for route
1676     // prefixes we consider the earliest stale time.
1677 
1678     for (const Router &router : mRouters)
1679     {
1680         for (const Entry &entry : router.mEntries)
1681         {
1682             TimeMilli entryStaleTime = OT_MAX(aNow, entry.GetStaleTime());
1683 
1684             if (entry.IsOnLinkPrefix() && !entry.IsDeprecated())
1685             {
1686                 onLinkStaleTime = OT_MAX(onLinkStaleTime, entryStaleTime);
1687                 foundOnLink     = true;
1688             }
1689 
1690             if (!entry.IsOnLinkPrefix())
1691             {
1692                 routeStaleTime = OT_MIN(routeStaleTime, entryStaleTime);
1693             }
1694         }
1695     }
1696 
1697     return foundOnLink ? OT_MIN(onLinkStaleTime, routeStaleTime) : routeStaleTime;
1698 }
1699 
RemoveRoutersWithNoEntries(void)1700 void RoutingManager::DiscoveredPrefixTable::RemoveRoutersWithNoEntries(void)
1701 {
1702     mRouters.RemoveAllMatching(Router::kContainsNoEntries);
1703 }
1704 
FreeEntries(LinkedList<Entry> & aEntries)1705 void RoutingManager::DiscoveredPrefixTable::FreeEntries(LinkedList<Entry> &aEntries)
1706 {
1707     // Frees all entries in the given list `aEntries` (put them back
1708     // in the entry pool).
1709 
1710     Entry *entry;
1711 
1712     while ((entry = aEntries.Pop()) != nullptr)
1713     {
1714         FreeEntry(*entry);
1715     }
1716 }
1717 
FindFavoredEntryToPublish(const Ip6::Prefix & aPrefix)1718 RoutingManager::DiscoveredPrefixTable::Entry *RoutingManager::DiscoveredPrefixTable::FindFavoredEntryToPublish(
1719     const Ip6::Prefix &aPrefix)
1720 {
1721     // Finds the favored entry matching a given `aPrefix` in the table
1722     // to publish in the Network Data. We can have multiple entries
1723     // in the table matching the same `aPrefix` from different
1724     // routers and potentially with different preference values. We
1725     // select the one with the highest preference as the favored
1726     // entry to publish.
1727 
1728     Entry *favoredEntry = nullptr;
1729 
1730     for (Router &router : mRouters)
1731     {
1732         for (Entry &entry : router.mEntries)
1733         {
1734             if (entry.GetPrefix() != aPrefix)
1735             {
1736                 continue;
1737             }
1738 
1739             if ((favoredEntry == nullptr) || (entry.GetPreference() > favoredEntry->GetPreference()))
1740             {
1741                 favoredEntry = &entry;
1742             }
1743         }
1744     }
1745 
1746     return favoredEntry;
1747 }
1748 
UpdateNetworkDataOnChangeTo(Entry & aEntry)1749 void RoutingManager::DiscoveredPrefixTable::UpdateNetworkDataOnChangeTo(Entry &aEntry)
1750 {
1751     // Updates Network Data when there is a change to `aEntry` which
1752     // can be a newly added entry or an existing entry that is
1753     // modified due to processing of a received RA message.
1754 
1755     Entry *favoredEntry;
1756 
1757     if (aEntry.GetPrefix().GetLength() == 0)
1758     {
1759         // If the change is to default route ::/0 prefix, make sure we
1760         // are allowed to publish default route in Network Data.
1761 
1762         VerifyOrExit(mAllowDefaultRouteInNetData);
1763     }
1764 
1765     favoredEntry = FindFavoredEntryToPublish(aEntry.GetPrefix());
1766 
1767     OT_ASSERT(favoredEntry != nullptr);
1768     PublishEntry(*favoredEntry);
1769 
1770 exit:
1771     return;
1772 }
1773 
PublishEntry(const Entry & aEntry)1774 void RoutingManager::DiscoveredPrefixTable::PublishEntry(const Entry &aEntry)
1775 {
1776     IgnoreError(Get<RoutingManager>().PublishExternalRoute(aEntry.GetPrefix(), aEntry.GetPreference()));
1777 }
1778 
UnpublishEntry(const Entry & aEntry)1779 void RoutingManager::DiscoveredPrefixTable::UnpublishEntry(const Entry &aEntry)
1780 {
1781     Get<RoutingManager>().UnpublishExternalRoute(aEntry.GetPrefix());
1782 }
1783 
HandleTimer(Timer & aTimer)1784 void RoutingManager::DiscoveredPrefixTable::HandleTimer(Timer &aTimer)
1785 {
1786     aTimer.Get<RoutingManager>().mDiscoveredPrefixTable.HandleTimer();
1787 }
1788 
HandleTimer(void)1789 void RoutingManager::DiscoveredPrefixTable::HandleTimer(void)
1790 {
1791     RemoveExpiredEntries();
1792 }
1793 
RemoveExpiredEntries(void)1794 void RoutingManager::DiscoveredPrefixTable::RemoveExpiredEntries(void)
1795 {
1796     TimeMilli         now            = TimerMilli::GetNow();
1797     TimeMilli         nextExpireTime = now.GetDistantFuture();
1798     LinkedList<Entry> expiredEntries;
1799 
1800     for (Router &router : mRouters)
1801     {
1802         router.mEntries.RemoveAllMatching(Entry::ExpirationChecker(now), expiredEntries);
1803     }
1804 
1805     RemoveRoutersWithNoEntries();
1806 
1807     // Determine if we need to publish/unpublish any prefixes in
1808     // the Network Data.
1809 
1810     for (const Entry &expiredEntry : expiredEntries)
1811     {
1812         Entry *favoredEntry = FindFavoredEntryToPublish(expiredEntry.GetPrefix());
1813 
1814         if (favoredEntry == nullptr)
1815         {
1816             UnpublishEntry(expiredEntry);
1817         }
1818         else
1819         {
1820             PublishEntry(*favoredEntry);
1821         }
1822     }
1823 
1824     if (!expiredEntries.IsEmpty())
1825     {
1826         SignalTableChanged();
1827     }
1828 
1829     FreeEntries(expiredEntries);
1830 
1831     // Determine the next expire time and schedule timer.
1832 
1833     for (const Router &router : mRouters)
1834     {
1835         for (const Entry &entry : router.mEntries)
1836         {
1837             nextExpireTime = OT_MIN(nextExpireTime, entry.GetExpireTime());
1838         }
1839     }
1840 
1841     if (nextExpireTime != now.GetDistantFuture())
1842     {
1843         mTimer.FireAt(nextExpireTime);
1844     }
1845 }
1846 
SignalTableChanged(void)1847 void RoutingManager::DiscoveredPrefixTable::SignalTableChanged(void)
1848 {
1849     mSignalTask.Post();
1850 }
1851 
HandleSignalTask(Tasklet & aTasklet)1852 void RoutingManager::DiscoveredPrefixTable::HandleSignalTask(Tasklet &aTasklet)
1853 {
1854     aTasklet.Get<RoutingManager>().HandleDiscoveredPrefixTableChanged();
1855 }
1856 
InitIterator(PrefixTableIterator & aIterator) const1857 void RoutingManager::DiscoveredPrefixTable::InitIterator(PrefixTableIterator &aIterator) const
1858 {
1859     Iterator &iterator = static_cast<Iterator &>(aIterator);
1860 
1861     iterator.SetInitTime();
1862     iterator.SetRouter(mRouters.Front());
1863     iterator.SetEntry(mRouters.IsEmpty() ? nullptr : mRouters[0].mEntries.GetHead());
1864 }
1865 
GetNextEntry(PrefixTableIterator & aIterator,PrefixTableEntry & aEntry) const1866 Error RoutingManager::DiscoveredPrefixTable::GetNextEntry(PrefixTableIterator &aIterator,
1867                                                           PrefixTableEntry &   aEntry) const
1868 {
1869     Error     error    = kErrorNone;
1870     Iterator &iterator = static_cast<Iterator &>(aIterator);
1871 
1872     VerifyOrExit(iterator.GetRouter() != nullptr, error = kErrorNotFound);
1873     OT_ASSERT(iterator.GetEntry() != nullptr);
1874 
1875     aEntry.mRouterAddress       = iterator.GetRouter()->mAddress;
1876     aEntry.mPrefix              = iterator.GetEntry()->GetPrefix();
1877     aEntry.mIsOnLink            = iterator.GetEntry()->IsOnLinkPrefix();
1878     aEntry.mMsecSinceLastUpdate = iterator.GetInitTime() - iterator.GetEntry()->GetLastUpdateTime();
1879     aEntry.mValidLifetime       = iterator.GetEntry()->GetValidLifetime();
1880     aEntry.mPreferredLifetime   = aEntry.mIsOnLink ? iterator.GetEntry()->GetPreferredLifetime() : 0;
1881     aEntry.mRoutePreference =
1882         static_cast<otRoutePreference>(aEntry.mIsOnLink ? 0 : iterator.GetEntry()->GetRoutePreference());
1883 
1884     // Advance the iterator
1885     iterator.SetEntry(iterator.GetEntry()->GetNext());
1886 
1887     if (iterator.GetEntry() == nullptr)
1888     {
1889         if (iterator.GetRouter() != mRouters.Back())
1890         {
1891             iterator.SetRouter(iterator.GetRouter() + 1);
1892             iterator.SetEntry(iterator.GetRouter()->mEntries.GetHead());
1893         }
1894         else
1895         {
1896             iterator.SetRouter(nullptr);
1897         }
1898     }
1899 
1900 exit:
1901     return error;
1902 }
1903 
1904 //---------------------------------------------------------------------------------------------------------------------
1905 // DiscoveredPrefixTable::Entry
1906 
SetFrom(const Ip6::Nd::RouterAdvertMessage::Header & aRaHeader)1907 void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader)
1908 {
1909     mPrefix.Clear();
1910     mType                    = kTypeRoute;
1911     mValidLifetime           = aRaHeader.GetRouterLifetime();
1912     mShared.mRoutePreference = aRaHeader.GetDefaultRouterPreference();
1913     mLastUpdateTime          = TimerMilli::GetNow();
1914 }
1915 
SetFrom(const Ip6::Nd::PrefixInfoOption & aPio)1916 void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::PrefixInfoOption &aPio)
1917 {
1918     aPio.GetPrefix(mPrefix);
1919     mType                      = kTypeOnLink;
1920     mValidLifetime             = aPio.GetValidLifetime();
1921     mShared.mPreferredLifetime = aPio.GetPreferredLifetime();
1922     mLastUpdateTime            = TimerMilli::GetNow();
1923 }
1924 
SetFrom(const Ip6::Nd::RouteInfoOption & aRio)1925 void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::RouteInfoOption &aRio)
1926 {
1927     aRio.GetPrefix(mPrefix);
1928     mType                    = kTypeRoute;
1929     mValidLifetime           = aRio.GetRouteLifetime();
1930     mShared.mRoutePreference = aRio.GetPreference();
1931     mLastUpdateTime          = TimerMilli::GetNow();
1932 }
1933 
operator ==(const Entry & aOther) const1934 bool RoutingManager::DiscoveredPrefixTable::Entry::operator==(const Entry &aOther) const
1935 {
1936     return (mType == aOther.mType) && (mPrefix == aOther.mPrefix);
1937 }
1938 
Matches(const Matcher & aMatcher) const1939 bool RoutingManager::DiscoveredPrefixTable::Entry::Matches(const Matcher &aMatcher) const
1940 {
1941     return (mType == aMatcher.mType) && (mPrefix == aMatcher.mPrefix);
1942 }
1943 
Matches(const ExpirationChecker & aCheker) const1944 bool RoutingManager::DiscoveredPrefixTable::Entry::Matches(const ExpirationChecker &aCheker) const
1945 {
1946     return GetExpireTime() <= aCheker.mNow;
1947 }
1948 
GetExpireTime(void) const1949 TimeMilli RoutingManager::DiscoveredPrefixTable::Entry::GetExpireTime(void) const
1950 {
1951     return mLastUpdateTime + CalculateExpireDelay(mValidLifetime);
1952 }
1953 
GetStaleTime(void) const1954 TimeMilli RoutingManager::DiscoveredPrefixTable::Entry::GetStaleTime(void) const
1955 {
1956     uint32_t delay = OT_MIN(kRtrAdvStaleTime, IsOnLinkPrefix() ? GetPreferredLifetime() : mValidLifetime);
1957 
1958     return mLastUpdateTime + TimeMilli::SecToMsec(delay);
1959 }
1960 
IsDeprecated(void) const1961 bool RoutingManager::DiscoveredPrefixTable::Entry::IsDeprecated(void) const
1962 {
1963     OT_ASSERT(IsOnLinkPrefix());
1964 
1965     return mLastUpdateTime + TimeMilli::SecToMsec(GetPreferredLifetime()) <= TimerMilli::GetNow();
1966 }
1967 
GetPreference(void) const1968 RoutingManager::RoutePreference RoutingManager::DiscoveredPrefixTable::Entry::GetPreference(void) const
1969 {
1970     // Returns the preference level to use when we publish
1971     // the prefix entry in Network Data.
1972 
1973     return IsOnLinkPrefix() ? NetworkData::kRoutePreferenceMedium : GetRoutePreference();
1974 }
1975 
AdoptValidAndPreferredLiftimesFrom(const Entry & aEntry)1976 void RoutingManager::DiscoveredPrefixTable::Entry::AdoptValidAndPreferredLiftimesFrom(const Entry &aEntry)
1977 {
1978     constexpr uint32_t kTwoHoursInSeconds = 2 * 3600;
1979 
1980     // Per RFC 4862 section 5.5.3.e:
1981     //
1982     // 1.  If the received Valid Lifetime is greater than 2 hours or
1983     //     greater than RemainingLifetime, set the valid lifetime of the
1984     //     corresponding address to the advertised Valid Lifetime.
1985     // 2.  If RemainingLifetime is less than or equal to 2 hours, ignore
1986     //     the Prefix Information option with regards to the valid
1987     //     lifetime, unless ...
1988     // 3.  Otherwise, reset the valid lifetime of the corresponding
1989     //     address to 2 hours.
1990 
1991     if (aEntry.mValidLifetime > kTwoHoursInSeconds || aEntry.GetExpireTime() > GetExpireTime())
1992     {
1993         mValidLifetime = aEntry.mValidLifetime;
1994     }
1995     else if (GetExpireTime() > TimerMilli::GetNow() + TimeMilli::SecToMsec(kTwoHoursInSeconds))
1996     {
1997         mValidLifetime = kTwoHoursInSeconds;
1998     }
1999 
2000     mShared.mPreferredLifetime = aEntry.GetPreferredLifetime();
2001     mLastUpdateTime            = aEntry.GetLastUpdateTime();
2002 }
2003 
CalculateExpireDelay(uint32_t aValidLifetime)2004 uint32_t RoutingManager::DiscoveredPrefixTable::Entry::CalculateExpireDelay(uint32_t aValidLifetime)
2005 {
2006     uint32_t delay;
2007 
2008     if (aValidLifetime * static_cast<uint64_t>(1000) > Timer::kMaxDelay)
2009     {
2010         delay = Timer::kMaxDelay;
2011     }
2012     else
2013     {
2014         delay = aValidLifetime * 1000;
2015     }
2016 
2017     return delay;
2018 }
2019 
2020 //---------------------------------------------------------------------------------------------------------------------
2021 // OmrPrefix
2022 
SetFrom(const NetworkData::OnMeshPrefixConfig & aOnMeshPrefixConfig)2023 void RoutingManager::OmrPrefix::SetFrom(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig)
2024 {
2025     mPrefix     = aOnMeshPrefixConfig.GetPrefix();
2026     mPreference = aOnMeshPrefixConfig.GetPreference();
2027 }
2028 
SetFrom(const LocalOmrPrefix & aLocalOmrPrefix)2029 void RoutingManager::OmrPrefix::SetFrom(const LocalOmrPrefix &aLocalOmrPrefix)
2030 {
2031     mPrefix     = aLocalOmrPrefix.GetPrefix();
2032     mPreference = aLocalOmrPrefix.GetPreference();
2033 }
2034 
IsFavoredOver(const NetworkData::OnMeshPrefixConfig & aOmrPrefixConfig) const2035 bool RoutingManager::OmrPrefix::IsFavoredOver(const NetworkData::OnMeshPrefixConfig &aOmrPrefixConfig) const
2036 {
2037     // This method determines whether this OMR prefix is favored
2038     // over another prefix. A prefix with higher preference is
2039     // favored. If the preference is the same, then the smaller
2040     // prefix (in the sense defined by `Ip6::Prefix`) is favored.
2041 
2042     bool isFavored = (mPreference > aOmrPrefixConfig.GetPreference());
2043 
2044     OT_ASSERT(IsValidOmrPrefix(aOmrPrefixConfig));
2045 
2046     if (mPreference == aOmrPrefixConfig.GetPreference())
2047     {
2048         isFavored = (mPrefix < aOmrPrefixConfig.GetPrefix());
2049     }
2050 
2051     return isFavored;
2052 }
2053 
2054 //---------------------------------------------------------------------------------------------------------------------
2055 // LocalOmrPrefix
2056 
LocalOmrPrefix(Instance & aInstance)2057 RoutingManager::LocalOmrPrefix::LocalOmrPrefix(Instance &aInstance)
2058     : InstanceLocator(aInstance)
2059     , mIsAddedInNetData(false)
2060 {
2061 }
2062 
GenerateFrom(const Ip6::Prefix & aBrUlaPrefix)2063 void RoutingManager::LocalOmrPrefix::GenerateFrom(const Ip6::Prefix &aBrUlaPrefix)
2064 {
2065     mPrefix = aBrUlaPrefix;
2066     mPrefix.SetSubnetId(kOmrPrefixSubnetId);
2067     mPrefix.SetLength(kOmrPrefixLength);
2068 
2069     LogInfo("Generated OMR prefix: %s", mPrefix.ToString().AsCString());
2070 }
2071 
AddToNetData(void)2072 Error RoutingManager::LocalOmrPrefix::AddToNetData(void)
2073 {
2074     Error                           error = kErrorNone;
2075     NetworkData::OnMeshPrefixConfig config;
2076 
2077     VerifyOrExit(!mIsAddedInNetData);
2078 
2079     config.Clear();
2080     config.mPrefix       = mPrefix;
2081     config.mStable       = true;
2082     config.mSlaac        = true;
2083     config.mPreferred    = true;
2084     config.mOnMesh       = true;
2085     config.mDefaultRoute = false;
2086     config.mPreference   = GetPreference();
2087 
2088     error = Get<NetworkData::Local>().AddOnMeshPrefix(config);
2089 
2090     if (error != kErrorNone)
2091     {
2092         LogWarn("Failed to add local OMR prefix %s in Thread Network Data: %s", mPrefix.ToString().AsCString(),
2093                 ErrorToString(error));
2094         ExitNow();
2095     }
2096 
2097     mIsAddedInNetData = true;
2098     Get<NetworkData::Notifier>().HandleServerDataUpdated();
2099     LogInfo("Added local OMR prefix %s in Thread Network Data", mPrefix.ToString().AsCString());
2100 
2101 exit:
2102     return error;
2103 }
2104 
RemoveFromNetData(void)2105 void RoutingManager::LocalOmrPrefix::RemoveFromNetData(void)
2106 {
2107     Error error = kErrorNone;
2108 
2109     VerifyOrExit(mIsAddedInNetData);
2110 
2111     error = Get<NetworkData::Local>().RemoveOnMeshPrefix(mPrefix);
2112 
2113     if (error != kErrorNone)
2114     {
2115         LogWarn("Failed to remove local OMR prefix %s from Thread Network Data: %s", mPrefix.ToString().AsCString(),
2116                 ErrorToString(error));
2117         ExitNow();
2118     }
2119 
2120     mIsAddedInNetData = false;
2121     Get<NetworkData::Notifier>().HandleServerDataUpdated();
2122     LogInfo("Removed local OMR prefix %s from Thread Network Data", mPrefix.ToString().AsCString());
2123 
2124 exit:
2125     return;
2126 }
2127 
2128 //---------------------------------------------------------------------------------------------------------------------
2129 // OnMeshPrefixArray
2130 
Add(const OnMeshPrefix & aPrefix)2131 void RoutingManager::OnMeshPrefixArray::Add(const OnMeshPrefix &aPrefix)
2132 {
2133     // Checks if `aPrefix` is already present in the array and if not
2134     // adds it as new entry.
2135 
2136     Error error;
2137 
2138     VerifyOrExit(!Contains(aPrefix));
2139 
2140     error = PushBack(aPrefix);
2141 
2142     if (error != kErrorNone)
2143     {
2144         LogWarn("Too many on-mesh prefixes in net data, ignoring prefix %s", aPrefix.ToString().AsCString());
2145     }
2146 
2147 exit:
2148     return;
2149 }
2150 
MarkAsDeleted(const OnMeshPrefix & aPrefix)2151 void RoutingManager::OnMeshPrefixArray::MarkAsDeleted(const OnMeshPrefix &aPrefix)
2152 {
2153     // Searches for a matching entry to `aPrefix` and if found marks
2154     // it as deleted by setting prefix length to zero.
2155 
2156     OnMeshPrefix *entry = Find(aPrefix);
2157 
2158     if (entry != nullptr)
2159     {
2160         entry->SetLength(0);
2161     }
2162 }
2163 
2164 } // namespace BorderRouter
2165 
2166 } // namespace ot
2167 
2168 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
2169