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/logging.hpp"
48 #include "common/random.hpp"
49 #include "common/settings.hpp"
50 #include "net/ip6.hpp"
51 #include "thread/network_data_leader.hpp"
52 #include "thread/network_data_local.hpp"
53 #include "thread/network_data_notifier.hpp"
54 
55 namespace ot {
56 
57 namespace BorderRouter {
58 
RoutingManager(Instance & aInstance)59 RoutingManager::RoutingManager(Instance &aInstance)
60     : InstanceLocator(aInstance)
61     , mIsRunning(false)
62     , mIsEnabled(false)
63     , mInfraIfIsRunning(false)
64     , mInfraIfIndex(0)
65     , mIsAdvertisingLocalOnLinkPrefix(false)
66     , mOnLinkPrefixDeprecateTimer(aInstance, HandleOnLinkPrefixDeprecateTimer)
67     , mTimeRouterAdvMessageLastUpdate(TimerMilli::GetNow())
68     , mDiscoveredPrefixInvalidTimer(aInstance, HandleDiscoveredPrefixInvalidTimer)
69     , mDiscoveredPrefixStaleTimer(aInstance, HandleDiscoveredPrefixStaleTimer)
70     , mRouterAdvertisementTimer(aInstance, HandleRouterAdvertisementTimer)
71     , mRouterAdvertisementCount(0)
72     , mVicariousRouterSolicitTimer(aInstance, HandleVicariousRouterSolicitTimer)
73     , mRouterSolicitTimer(aInstance, HandleRouterSolicitTimer)
74     , mRouterSolicitCount(0)
75     , mRoutingPolicyTimer(aInstance, HandleRoutingPolicyTimer)
76 {
77     mLocalOmrPrefix.Clear();
78 
79     mLocalOnLinkPrefix.Clear();
80 }
81 
Init(uint32_t aInfraIfIndex,bool aInfraIfIsRunning)82 Error RoutingManager::Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning)
83 {
84     Error error;
85 
86     VerifyOrExit(!IsInitialized(), error = kErrorInvalidState);
87     VerifyOrExit(aInfraIfIndex > 0, error = kErrorInvalidArgs);
88 
89     SuccessOrExit(error = LoadOrGenerateRandomOmrPrefix());
90     SuccessOrExit(error = LoadOrGenerateRandomOnLinkPrefix());
91 
92     mInfraIfIndex = aInfraIfIndex;
93 
94     // Initialize the infra interface status.
95     SuccessOrExit(error = HandleInfraIfStateChanged(mInfraIfIndex, aInfraIfIsRunning));
96 
97 exit:
98     if (error != kErrorNone)
99     {
100         mInfraIfIndex = 0;
101     }
102     return error;
103 }
104 
SetEnabled(bool aEnabled)105 Error RoutingManager::SetEnabled(bool aEnabled)
106 {
107     Error error = kErrorNone;
108 
109     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
110 
111     VerifyOrExit(aEnabled != mIsEnabled);
112 
113     mIsEnabled = aEnabled;
114     EvaluateState();
115 
116 exit:
117     return error;
118 }
119 
GetOmrPrefix(Ip6::Prefix & aPrefix)120 Error RoutingManager::GetOmrPrefix(Ip6::Prefix &aPrefix)
121 {
122     Error error = kErrorNone;
123 
124     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
125     aPrefix = mLocalOmrPrefix;
126 
127 exit:
128     return error;
129 }
130 
GetOnLinkPrefix(Ip6::Prefix & aPrefix)131 Error RoutingManager::GetOnLinkPrefix(Ip6::Prefix &aPrefix)
132 {
133     Error error = kErrorNone;
134 
135     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
136     aPrefix = mLocalOnLinkPrefix;
137 
138 exit:
139     return error;
140 }
141 
LoadOrGenerateRandomOmrPrefix(void)142 Error RoutingManager::LoadOrGenerateRandomOmrPrefix(void)
143 {
144     Error error = kErrorNone;
145 
146     if (Get<Settings>().Read<Settings::OmrPrefix>(mLocalOmrPrefix) != kErrorNone || !IsValidOmrPrefix(mLocalOmrPrefix))
147     {
148         Ip6::NetworkPrefix randomOmrPrefix;
149 
150         otLogNoteBr("No valid OMR prefix found in settings, generating new one");
151 
152         error = randomOmrPrefix.GenerateRandomUla();
153         if (error != kErrorNone)
154         {
155             otLogCritBr("Failed to generate random OMR prefix");
156             ExitNow();
157         }
158 
159         mLocalOmrPrefix.Set(randomOmrPrefix);
160         IgnoreError(Get<Settings>().Save<Settings::OmrPrefix>(mLocalOmrPrefix));
161     }
162 
163 exit:
164     return error;
165 }
166 
LoadOrGenerateRandomOnLinkPrefix(void)167 Error RoutingManager::LoadOrGenerateRandomOnLinkPrefix(void)
168 {
169     Error error = kErrorNone;
170 
171     if (Get<Settings>().Read<Settings::OnLinkPrefix>(mLocalOnLinkPrefix) != kErrorNone ||
172         !mLocalOnLinkPrefix.IsUniqueLocal())
173     {
174         Ip6::NetworkPrefix randomOnLinkPrefix;
175 
176         otLogNoteBr("No valid on-link prefix found in settings, generating new one");
177 
178         error = randomOnLinkPrefix.GenerateRandomUla();
179         if (error != kErrorNone)
180         {
181             otLogCritBr("Failed to generate random on-link prefix");
182             ExitNow();
183         }
184 
185         randomOnLinkPrefix.m8[6] = 0;
186         randomOnLinkPrefix.m8[7] = 0;
187         mLocalOnLinkPrefix.Set(randomOnLinkPrefix);
188 
189         IgnoreError(Get<Settings>().Save<Settings::OnLinkPrefix>(mLocalOnLinkPrefix));
190     }
191 
192 exit:
193     return error;
194 }
195 
EvaluateState(void)196 void RoutingManager::EvaluateState(void)
197 {
198     if (mIsEnabled && Get<Mle::MleRouter>().IsAttached() && mInfraIfIsRunning)
199     {
200         Start();
201     }
202     else
203     {
204         Stop();
205     }
206 }
207 
Start(void)208 void RoutingManager::Start(void)
209 {
210     if (!mIsRunning)
211     {
212         otLogInfoBr("Border Routing manager started");
213 
214         mIsRunning = true;
215         StartRouterSolicitationDelay();
216     }
217 }
218 
Stop(void)219 void RoutingManager::Stop(void)
220 {
221     VerifyOrExit(mIsRunning);
222 
223     UnpublishLocalOmrPrefix();
224 
225     if (mIsAdvertisingLocalOnLinkPrefix)
226     {
227         RemoveExternalRoute(mLocalOnLinkPrefix);
228 
229         // Start deprecating the local on-link prefix to send a PIO
230         // with zero preferred lifetime in `SendRouterAdvertisement`.
231         DeprecateOnLinkPrefix();
232     }
233 
234     // Use empty OMR & on-link prefixes to invalidate possible advertised prefixes.
235     SendRouterAdvertisement(OmrPrefixArray(), nullptr);
236 
237     mAdvertisedOmrPrefixes.Clear();
238     mIsAdvertisingLocalOnLinkPrefix = false;
239     mOnLinkPrefixDeprecateTimer.Stop();
240 
241     InvalidateAllDiscoveredPrefixes();
242     mDiscoveredPrefixes.Clear();
243     mDiscoveredPrefixInvalidTimer.Stop();
244     mDiscoveredPrefixStaleTimer.Stop();
245 
246     mRouterAdvertisementTimer.Stop();
247     mRouterAdvertisementCount = 0;
248 
249     mVicariousRouterSolicitTimer.Stop();
250     mRouterSolicitTimer.Stop();
251     mRouterSolicitCount = 0;
252 
253     mRoutingPolicyTimer.Stop();
254 
255     otLogInfoBr("Border Routing manager stopped");
256 
257     mIsRunning = false;
258 
259 exit:
260     return;
261 }
262 
RecvIcmp6Message(uint32_t aInfraIfIndex,const Ip6::Address & aSrcAddress,const uint8_t * aBuffer,uint16_t aBufferLength)263 void RoutingManager::RecvIcmp6Message(uint32_t            aInfraIfIndex,
264                                       const Ip6::Address &aSrcAddress,
265                                       const uint8_t *     aBuffer,
266                                       uint16_t            aBufferLength)
267 {
268     Error                    error = kErrorNone;
269     const Ip6::Icmp::Header *icmp6Header;
270 
271     VerifyOrExit(IsInitialized() && mIsRunning, error = kErrorDrop);
272     VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = kErrorDrop);
273     VerifyOrExit(aBuffer != nullptr && aBufferLength >= sizeof(*icmp6Header), error = kErrorParse);
274 
275     icmp6Header = reinterpret_cast<const Ip6::Icmp::Header *>(aBuffer);
276 
277     switch (icmp6Header->GetType())
278     {
279     case Ip6::Icmp::Header::kTypeRouterAdvert:
280         HandleRouterAdvertisement(aSrcAddress, aBuffer, aBufferLength);
281         break;
282     case Ip6::Icmp::Header::kTypeRouterSolicit:
283         HandleRouterSolicit(aSrcAddress, aBuffer, aBufferLength);
284         break;
285     default:
286         break;
287     }
288 
289 exit:
290     if (error != kErrorNone)
291     {
292         otLogDebgBr("Dropped ICMPv6 message: %s", ErrorToString(error));
293     }
294 }
295 
HandleInfraIfStateChanged(uint32_t aInfraIfIndex,bool aIsRunning)296 Error RoutingManager::HandleInfraIfStateChanged(uint32_t aInfraIfIndex, bool aIsRunning)
297 {
298     Error error = kErrorNone;
299 
300     VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
301     VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = kErrorInvalidArgs);
302     VerifyOrExit(aIsRunning != mInfraIfIsRunning);
303 
304     otLogInfoBr("Infra interface (%u) state changed: %sRUNNING -> %sRUNNING", aInfraIfIndex,
305                 (mInfraIfIsRunning ? "" : "NOT "), (aIsRunning ? "" : "NOT "));
306 
307     mInfraIfIsRunning = aIsRunning;
308     EvaluateState();
309 
310 exit:
311     return error;
312 }
313 
HandleNotifierEvents(Events aEvents)314 void RoutingManager::HandleNotifierEvents(Events aEvents)
315 {
316     VerifyOrExit(IsInitialized() && IsEnabled());
317 
318     if (aEvents.Contains(kEventThreadRoleChanged))
319     {
320         EvaluateState();
321     }
322 
323     if (mIsRunning && aEvents.Contains(kEventThreadNetdataChanged))
324     {
325         StartRoutingPolicyEvaluationDelay();
326     }
327 
328 exit:
329     return;
330 }
331 
EvaluateOmrPrefix(OmrPrefixArray & aNewOmrPrefixes)332 void RoutingManager::EvaluateOmrPrefix(OmrPrefixArray &aNewOmrPrefixes)
333 {
334     NetworkData::Iterator           iterator = NetworkData::kIteratorInit;
335     NetworkData::OnMeshPrefixConfig onMeshPrefixConfig;
336     Ip6::Prefix *                   smallestOmrPrefix       = nullptr;
337     Ip6::Prefix *                   publishedLocalOmrPrefix = nullptr;
338 
339     OT_ASSERT(mIsRunning);
340 
341     while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, onMeshPrefixConfig) == kErrorNone)
342     {
343         const Ip6::Prefix &prefix = onMeshPrefixConfig.GetPrefix();
344 
345         if (!IsValidOmrPrefix(onMeshPrefixConfig))
346         {
347             continue;
348         }
349 
350         if (aNewOmrPrefixes.Contains(prefix))
351         {
352             // Ignore duplicate prefixes.
353             continue;
354         }
355 
356         if (aNewOmrPrefixes.PushBack(prefix) != kErrorNone)
357         {
358             otLogWarnBr("EvaluateOmrPrefix: Too many OMR prefixes, ignoring prefix %s", prefix.ToString().AsCString());
359             continue;
360         }
361 
362         if (smallestOmrPrefix == nullptr || (prefix < *smallestOmrPrefix))
363         {
364             smallestOmrPrefix = aNewOmrPrefixes.Back();
365         }
366 
367         if (prefix == mLocalOmrPrefix)
368         {
369             publishedLocalOmrPrefix = aNewOmrPrefixes.Back();
370         }
371     }
372 
373     // Decide if we need to add or remove our local OMR prefix.
374 
375     if (aNewOmrPrefixes.IsEmpty())
376     {
377         otLogInfoBr("EvaluateOmrPrefix: No valid OMR prefixes found in Thread network");
378 
379         if (PublishLocalOmrPrefix() == kErrorNone)
380         {
381             IgnoreError(aNewOmrPrefixes.PushBack(mLocalOmrPrefix));
382         }
383 
384         // The `aNewOmrPrefixes` remains empty if we fail to publish
385         // the local OMR prefix.
386     }
387     else if (publishedLocalOmrPrefix != nullptr && smallestOmrPrefix != publishedLocalOmrPrefix)
388     {
389         otLogInfoBr("EvaluateOmrPrefix: There is already a smaller OMR prefix %s in the Thread network",
390                     smallestOmrPrefix->ToString().AsCString());
391 
392         UnpublishLocalOmrPrefix();
393 
394         // Remove the local OMR prefix from the list by overwriting it
395         // with the last element and then popping it from the list.
396 
397         *publishedLocalOmrPrefix = *aNewOmrPrefixes.Back();
398         aNewOmrPrefixes.PopBack();
399     }
400 }
401 
PublishLocalOmrPrefix(void)402 Error RoutingManager::PublishLocalOmrPrefix(void)
403 {
404     Error                           error = kErrorNone;
405     NetworkData::OnMeshPrefixConfig omrPrefixConfig;
406 
407     OT_ASSERT(mIsRunning);
408 
409     omrPrefixConfig.Clear();
410     omrPrefixConfig.mPrefix       = mLocalOmrPrefix;
411     omrPrefixConfig.mStable       = true;
412     omrPrefixConfig.mSlaac        = true;
413     omrPrefixConfig.mPreferred    = true;
414     omrPrefixConfig.mOnMesh       = true;
415     omrPrefixConfig.mDefaultRoute = false;
416     omrPrefixConfig.mPreference   = NetworkData::kRoutePreferenceMedium;
417 
418     error = Get<NetworkData::Local>().AddOnMeshPrefix(omrPrefixConfig);
419     if (error != kErrorNone)
420     {
421         otLogWarnBr("Failed to publish local OMR prefix %s in Thread network: %s",
422                     mLocalOmrPrefix.ToString().AsCString(), ErrorToString(error));
423     }
424     else
425     {
426         Get<NetworkData::Notifier>().HandleServerDataUpdated();
427         otLogInfoBr("Published local OMR prefix %s in Thread network", mLocalOmrPrefix.ToString().AsCString());
428     }
429 
430     return error;
431 }
432 
UnpublishLocalOmrPrefix(void)433 void RoutingManager::UnpublishLocalOmrPrefix(void)
434 {
435     Error error = kErrorNone;
436 
437     VerifyOrExit(mIsRunning);
438 
439     SuccessOrExit(error = Get<NetworkData::Local>().RemoveOnMeshPrefix(mLocalOmrPrefix));
440 
441     Get<NetworkData::Notifier>().HandleServerDataUpdated();
442     otLogInfoBr("Unpublished local OMR prefix %s from Thread network", mLocalOmrPrefix.ToString().AsCString());
443 
444 exit:
445     if (error != kErrorNone)
446     {
447         otLogWarnBr("Failed to unpublish local OMR prefix %s from Thread network: %s",
448                     mLocalOmrPrefix.ToString().AsCString(), ErrorToString(error));
449     }
450 }
451 
AddExternalRoute(const Ip6::Prefix & aPrefix,RoutePreference aRoutePreference)452 Error RoutingManager::AddExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference)
453 {
454     Error                            error;
455     NetworkData::ExternalRouteConfig routeConfig;
456 
457     OT_ASSERT(mIsRunning);
458 
459     routeConfig.Clear();
460     routeConfig.SetPrefix(aPrefix);
461     routeConfig.mStable     = true;
462     routeConfig.mPreference = aRoutePreference;
463 
464     error = Get<NetworkData::Local>().AddHasRoutePrefix(routeConfig);
465     if (error != kErrorNone)
466     {
467         otLogWarnBr("Failed to add external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error));
468     }
469     else
470     {
471         Get<NetworkData::Notifier>().HandleServerDataUpdated();
472         otLogInfoBr("Added external route %s", aPrefix.ToString().AsCString());
473     }
474 
475     return error;
476 }
477 
RemoveExternalRoute(const Ip6::Prefix & aPrefix)478 void RoutingManager::RemoveExternalRoute(const Ip6::Prefix &aPrefix)
479 {
480     Error error = kErrorNone;
481 
482     VerifyOrExit(mIsRunning);
483 
484     SuccessOrExit(error = Get<NetworkData::Local>().RemoveHasRoutePrefix(aPrefix));
485 
486     Get<NetworkData::Notifier>().HandleServerDataUpdated();
487     otLogInfoBr("Removed external route %s", aPrefix.ToString().AsCString());
488 
489 exit:
490     if (error != kErrorNone)
491     {
492         otLogWarnBr("Failed to remove external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error));
493     }
494 }
495 
EvaluateOnLinkPrefix(void)496 const Ip6::Prefix *RoutingManager::EvaluateOnLinkPrefix(void)
497 {
498     const Ip6::Prefix *newOnLinkPrefix      = nullptr;
499     const Ip6::Prefix *smallestOnLinkPrefix = nullptr;
500 
501     // We don't evaluate on-link prefix if we are doing Router Solicitation.
502     VerifyOrExit(!mRouterSolicitTimer.IsRunning(),
503                  newOnLinkPrefix = (mIsAdvertisingLocalOnLinkPrefix ? &mLocalOnLinkPrefix : nullptr));
504 
505     for (const ExternalPrefix &prefix : mDiscoveredPrefixes)
506     {
507         if (!prefix.mIsOnLinkPrefix || prefix.IsDeprecated())
508         {
509             continue;
510         }
511 
512         if (smallestOnLinkPrefix == nullptr || (prefix.mPrefix < *smallestOnLinkPrefix))
513         {
514             smallestOnLinkPrefix = &prefix.mPrefix;
515         }
516     }
517 
518     // We start advertising our local on-link prefix if there is no existing one.
519     if (smallestOnLinkPrefix == nullptr)
520     {
521         if (mIsAdvertisingLocalOnLinkPrefix ||
522             (AddExternalRoute(mLocalOnLinkPrefix, NetworkData::kRoutePreferenceMedium) == kErrorNone))
523         {
524             newOnLinkPrefix = &mLocalOnLinkPrefix;
525         }
526 
527         mOnLinkPrefixDeprecateTimer.Stop();
528     }
529     // When an application-specific on-link prefix is received and it is bigger than the
530     // advertised prefix, we will not remove the advertised prefix. In this case, there
531     // will be two on-link prefixes on the infra link. But all BRs will still converge to
532     // the same smallest on-link prefix and the application-specific prefix is not used.
533     else if (mIsAdvertisingLocalOnLinkPrefix)
534     {
535         if (mLocalOnLinkPrefix < *smallestOnLinkPrefix)
536         {
537             newOnLinkPrefix = &mLocalOnLinkPrefix;
538         }
539         else
540         {
541             otLogInfoBr("EvaluateOnLinkPrefix: There is already smaller on-link prefix %s on interface %u",
542                         smallestOnLinkPrefix->ToString().AsCString(), mInfraIfIndex);
543             DeprecateOnLinkPrefix();
544         }
545     }
546 
547 exit:
548     return newOnLinkPrefix;
549 }
550 
HandleOnLinkPrefixDeprecateTimer(Timer & aTimer)551 void RoutingManager::HandleOnLinkPrefixDeprecateTimer(Timer &aTimer)
552 {
553     aTimer.Get<RoutingManager>().HandleOnLinkPrefixDeprecateTimer();
554 }
555 
HandleOnLinkPrefixDeprecateTimer(void)556 void RoutingManager::HandleOnLinkPrefixDeprecateTimer(void)
557 {
558     otLogInfoBr("Local on-link prefix %s expired", mLocalOnLinkPrefix.ToString().AsCString());
559     RemoveExternalRoute(mLocalOnLinkPrefix);
560 }
561 
DeprecateOnLinkPrefix(void)562 void RoutingManager::DeprecateOnLinkPrefix(void)
563 {
564     OT_ASSERT(mIsAdvertisingLocalOnLinkPrefix);
565 
566     otLogInfoBr("Deprecate local on-link prefix %s", mLocalOnLinkPrefix.ToString().AsCString());
567     mOnLinkPrefixDeprecateTimer.StartAt(mTimeAdvertisedOnLinkPrefix,
568                                         TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime));
569 }
570 
571 // This method evaluate the routing policy depends on prefix and route
572 // information on Thread Network and infra link. As a result, this
573 // method May send RA messages on infra link and publish/unpublish
574 // OMR prefix in the Thread network.
EvaluateRoutingPolicy(void)575 void RoutingManager::EvaluateRoutingPolicy(void)
576 {
577     OT_ASSERT(mIsRunning);
578 
579     const Ip6::Prefix *newOnLinkPrefix = nullptr;
580     OmrPrefixArray     newOmrPrefixes;
581 
582     otLogInfoBr("Evaluating routing policy");
583 
584     // 0. Evaluate on-link & OMR prefixes.
585     newOnLinkPrefix = EvaluateOnLinkPrefix();
586     EvaluateOmrPrefix(newOmrPrefixes);
587 
588     // 1. Send Router Advertisement message if necessary.
589     SendRouterAdvertisement(newOmrPrefixes, newOnLinkPrefix);
590 
591     if (newOmrPrefixes.IsEmpty())
592     {
593         // This is the very exceptional case and happens only when we failed to publish
594         // our local OMR prefix to the Thread network. We schedule the Router Advertisement
595         // timer to re-evaluate our routing policy in the future.
596 
597         otLogWarnBr("No OMR prefix advertised! Start Router Advertisement timer for future evaluation");
598     }
599 
600     // 2. Schedule Router Advertisement timer with random interval.
601     {
602         uint32_t nextSendTime;
603 
604         nextSendTime = Random::NonCrypto::GetUint32InRange(kMinRtrAdvInterval, kMaxRtrAdvInterval);
605 
606         if (mRouterAdvertisementCount <= kMaxInitRtrAdvertisements && nextSendTime > kMaxInitRtrAdvInterval)
607         {
608             nextSendTime = kMaxInitRtrAdvInterval;
609         }
610 
611         otLogInfoBr("Router advertisement scheduled in %u seconds", nextSendTime);
612         mRouterAdvertisementTimer.Start(Time::SecToMsec(nextSendTime));
613     }
614 
615     // 3. Update advertised on-link & OMR prefixes information.
616     mIsAdvertisingLocalOnLinkPrefix = (newOnLinkPrefix == &mLocalOnLinkPrefix);
617     mAdvertisedOmrPrefixes          = newOmrPrefixes;
618 }
619 
StartRoutingPolicyEvaluationDelay(void)620 void RoutingManager::StartRoutingPolicyEvaluationDelay(void)
621 {
622     OT_ASSERT(mIsRunning);
623 
624     uint32_t randomDelay;
625 
626     static_assert(kMaxRoutingPolicyDelay > 0, "invalid maximum routing policy evaluation delay");
627     randomDelay = Random::NonCrypto::GetUint32InRange(0, Time::SecToMsec(kMaxRoutingPolicyDelay));
628 
629     otLogInfoBr("Start evaluating routing policy, scheduled in %u milliseconds", randomDelay);
630     mRoutingPolicyTimer.Start(randomDelay);
631 }
632 
633 // starts sending Router Solicitations in random delay
634 // between 0 and kMaxRtrSolicitationDelay.
StartRouterSolicitationDelay(void)635 void RoutingManager::StartRouterSolicitationDelay(void)
636 {
637     uint32_t randomDelay;
638 
639     VerifyOrExit(!mRouterSolicitTimer.IsRunning() && mRouterSolicitCount == 0);
640 
641     mVicariousRouterSolicitTimer.Stop();
642 
643     static_assert(kMaxRtrSolicitationDelay > 0, "invalid maximum Router Solicitation delay");
644     randomDelay = Random::NonCrypto::GetUint32InRange(0, Time::SecToMsec(kMaxRtrSolicitationDelay));
645 
646     otLogInfoBr("Start Router Solicitation, scheduled in %u milliseconds", randomDelay);
647     mTimeRouterSolicitStart = TimerMilli::GetNow();
648     mRouterSolicitTimer.Start(randomDelay);
649 
650 exit:
651     return;
652 }
653 
SendRouterSolicitation(void)654 Error RoutingManager::SendRouterSolicitation(void)
655 {
656     Ip6::Address                    destAddress;
657     RouterAdv::RouterSolicitMessage routerSolicit;
658 
659     OT_ASSERT(IsInitialized());
660 
661     destAddress.SetToLinkLocalAllRoutersMulticast();
662     return otPlatInfraIfSendIcmp6Nd(mInfraIfIndex, &destAddress, reinterpret_cast<const uint8_t *>(&routerSolicit),
663                                     sizeof(routerSolicit));
664 }
665 
666 // This method sends Router Advertisement messages to advertise on-link prefix and route for OMR prefix.
667 // @param[in]  aNewOmrPrefixes   An array of the new OMR prefixes to be advertised.
668 //                               Empty array means we should stop advertising OMR prefixes.
669 // @param[in]  aOnLinkPrefix     A pointer to the new on-link prefix to be advertised.
670 //                               nullptr means we should stop advertising on-link prefix.
SendRouterAdvertisement(const OmrPrefixArray & aNewOmrPrefixes,const Ip6::Prefix * aNewOnLinkPrefix)671 void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefixes, const Ip6::Prefix *aNewOnLinkPrefix)
672 {
673     uint8_t  buffer[kMaxRouterAdvMessageLength];
674     uint16_t bufferLength = 0;
675 
676     static_assert(sizeof(mRouterAdvMessage) <= sizeof(buffer), "RA buffer too small");
677     memcpy(buffer, &mRouterAdvMessage, sizeof(mRouterAdvMessage));
678     bufferLength += sizeof(mRouterAdvMessage);
679 
680     if (aNewOnLinkPrefix != nullptr)
681     {
682         OT_ASSERT(aNewOnLinkPrefix == &mLocalOnLinkPrefix);
683 
684         RouterAdv::PrefixInfoOption pio;
685 
686         pio.SetOnLink(true);
687         pio.SetAutoAddrConfig(true);
688         pio.SetValidLifetime(kDefaultOnLinkPrefixLifetime);
689         pio.SetPreferredLifetime(kDefaultOnLinkPrefixLifetime);
690         pio.SetPrefix(*aNewOnLinkPrefix);
691 
692         OT_ASSERT(bufferLength + pio.GetSize() <= sizeof(buffer));
693         memcpy(buffer + bufferLength, &pio, pio.GetSize());
694         bufferLength += pio.GetSize();
695 
696         if (!mIsAdvertisingLocalOnLinkPrefix)
697         {
698             otLogInfoBr("Start advertising new on-link prefix %s on interface %u",
699                         aNewOnLinkPrefix->ToString().AsCString(), mInfraIfIndex);
700         }
701 
702         otLogInfoBr("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)",
703                     aNewOnLinkPrefix->ToString().AsCString(), pio.GetPreferredLifetime(), pio.GetValidLifetime());
704 
705         mTimeAdvertisedOnLinkPrefix = TimerMilli::GetNow();
706     }
707     else if (mOnLinkPrefixDeprecateTimer.IsRunning())
708     {
709         RouterAdv::PrefixInfoOption pio;
710 
711         pio.SetOnLink(true);
712         pio.SetAutoAddrConfig(true);
713         pio.SetValidLifetime(TimeMilli::MsecToSec(mOnLinkPrefixDeprecateTimer.GetFireTime() - TimerMilli::GetNow()));
714 
715         // Set zero preferred lifetime to immediately deprecate the advertised on-link prefix.
716         pio.SetPreferredLifetime(0);
717         pio.SetPrefix(mLocalOnLinkPrefix);
718 
719         OT_ASSERT(bufferLength + pio.GetSize() <= sizeof(buffer));
720         memcpy(buffer + bufferLength, &pio, pio.GetSize());
721         bufferLength += pio.GetSize();
722 
723         otLogInfoBr("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)",
724                     mLocalOnLinkPrefix.ToString().AsCString(), pio.GetPreferredLifetime(), pio.GetValidLifetime());
725     }
726 
727     // Invalidate the advertised OMR prefixes if they are no longer in the new OMR prefix array.
728 
729     for (const Ip6::Prefix &advertisedOmrPrefix : mAdvertisedOmrPrefixes)
730     {
731         if (!aNewOmrPrefixes.Contains(advertisedOmrPrefix))
732         {
733             RouterAdv::RouteInfoOption rio;
734 
735             // Set zero route lifetime to immediately invalidate the advertised OMR prefix.
736             rio.SetRouteLifetime(0);
737             rio.SetPrefix(advertisedOmrPrefix);
738 
739             OT_ASSERT(bufferLength + rio.GetSize() <= sizeof(buffer));
740             memcpy(buffer + bufferLength, &rio, rio.GetSize());
741             bufferLength += rio.GetSize();
742 
743             otLogInfoBr("Stop advertising OMR prefix %s on interface %u", advertisedOmrPrefix.ToString().AsCString(),
744                         mInfraIfIndex);
745         }
746     }
747 
748     for (const Ip6::Prefix &newOmrPrefix : aNewOmrPrefixes)
749     {
750         RouterAdv::RouteInfoOption rio;
751 
752         rio.SetRouteLifetime(kDefaultOmrPrefixLifetime);
753         rio.SetPrefix(newOmrPrefix);
754 
755         OT_ASSERT(bufferLength + rio.GetSize() <= sizeof(buffer));
756         memcpy(buffer + bufferLength, &rio, rio.GetSize());
757         bufferLength += rio.GetSize();
758 
759         otLogInfoBr("Send OMR prefix %s in RIO (valid lifetime = %u seconds)", newOmrPrefix.ToString().AsCString(),
760                     kDefaultOmrPrefixLifetime);
761     }
762 
763     // Send the message only when there are options.
764     if (bufferLength > sizeof(mRouterAdvMessage))
765     {
766         Error        error;
767         Ip6::Address destAddress;
768 
769         ++mRouterAdvertisementCount;
770 
771         destAddress.SetToLinkLocalAllNodesMulticast();
772         error = otPlatInfraIfSendIcmp6Nd(mInfraIfIndex, &destAddress, buffer, bufferLength);
773 
774         if (error == kErrorNone)
775         {
776             otLogInfoBr("Sent Router Advertisement on interface %u", mInfraIfIndex);
777             otDumpDebgBr("[BR-CERT] direction=send | type=RA |", buffer, bufferLength);
778         }
779         else
780         {
781             otLogWarnBr("Failed to send Router Advertisement on interface %u: %s", mInfraIfIndex, ErrorToString(error));
782         }
783     }
784 }
785 
IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig & aOnMeshPrefixConfig)786 bool RoutingManager::IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig)
787 {
788     return IsValidOmrPrefix(aOnMeshPrefixConfig.GetPrefix()) && aOnMeshPrefixConfig.mSlaac && !aOnMeshPrefixConfig.mDp;
789 }
790 
IsValidOmrPrefix(const Ip6::Prefix & aOmrPrefix)791 bool RoutingManager::IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix)
792 {
793     // Accept ULA prefix with length of 64 bits and GUA prefix.
794     return (aOmrPrefix.mLength == kOmrPrefixLength && aOmrPrefix.mPrefix.mFields.m8[0] == 0xfd) ||
795            (aOmrPrefix.mLength >= 3 && (aOmrPrefix.GetBytes()[0] & 0xE0) == 0x20);
796 }
797 
IsValidOnLinkPrefix(const RouterAdv::PrefixInfoOption & aPio,bool aManagedAddrConfig)798 bool RoutingManager::IsValidOnLinkPrefix(const RouterAdv::PrefixInfoOption &aPio, bool aManagedAddrConfig)
799 {
800     return IsValidOnLinkPrefix(aPio.GetPrefix()) && aPio.GetOnLink() &&
801            (aPio.GetAutoAddrConfig() || aManagedAddrConfig);
802 }
803 
IsValidOnLinkPrefix(const Ip6::Prefix & aOnLinkPrefix)804 bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix)
805 {
806     return !aOnLinkPrefix.IsLinkLocal() && !aOnLinkPrefix.IsMulticast();
807 }
808 
HandleRouterAdvertisementTimer(Timer & aTimer)809 void RoutingManager::HandleRouterAdvertisementTimer(Timer &aTimer)
810 {
811     aTimer.Get<RoutingManager>().HandleRouterAdvertisementTimer();
812 }
813 
HandleRouterAdvertisementTimer(void)814 void RoutingManager::HandleRouterAdvertisementTimer(void)
815 {
816     otLogInfoBr("Router advertisement timer triggered");
817 
818     EvaluateRoutingPolicy();
819 }
820 
HandleVicariousRouterSolicitTimer(Timer & aTimer)821 void RoutingManager::HandleVicariousRouterSolicitTimer(Timer &aTimer)
822 {
823     aTimer.Get<RoutingManager>().HandleVicariousRouterSolicitTimer();
824 }
825 
HandleVicariousRouterSolicitTimer(void)826 void RoutingManager::HandleVicariousRouterSolicitTimer(void)
827 {
828     otLogInfoBr("Vicarious router solicitation time out");
829 
830     for (const ExternalPrefix &prefix : mDiscoveredPrefixes)
831     {
832         if (prefix.mTimeLastUpdate <= mTimeVicariousRouterSolicitStart)
833         {
834             StartRouterSolicitationDelay();
835             break;
836         }
837     }
838 }
839 
HandleRouterSolicitTimer(Timer & aTimer)840 void RoutingManager::HandleRouterSolicitTimer(Timer &aTimer)
841 {
842     aTimer.Get<RoutingManager>().HandleRouterSolicitTimer();
843 }
844 
HandleRouterSolicitTimer(void)845 void RoutingManager::HandleRouterSolicitTimer(void)
846 {
847     otLogInfoBr("Router solicitation times out");
848 
849     if (mRouterSolicitCount < kMaxRtrSolicitations)
850     {
851         uint32_t nextSolicitationDelay;
852         Error    error;
853 
854         error = SendRouterSolicitation();
855         ++mRouterSolicitCount;
856 
857         if (error == kErrorNone)
858         {
859             otLogDebgBr("Successfully sent %uth Router Solicitation", mRouterSolicitCount);
860         }
861         else
862         {
863             otLogCritBr("Failed to send %uth Router Solicitation: %s", mRouterSolicitCount, ErrorToString(error));
864         }
865 
866         nextSolicitationDelay =
867             (mRouterSolicitCount == kMaxRtrSolicitations) ? kMaxRtrSolicitationDelay : kRtrSolicitationInterval;
868 
869         otLogDebgBr("Router solicitation timer scheduled in %u seconds", nextSolicitationDelay);
870         mRouterSolicitTimer.Start(Time::SecToMsec(nextSolicitationDelay));
871     }
872     else
873     {
874         // Invalidate/deprecate all OMR/on-link prefixes that are not refreshed during Router Solicitation.
875         for (ExternalPrefix &prefix : mDiscoveredPrefixes)
876         {
877             if (prefix.mTimeLastUpdate <= mTimeRouterSolicitStart)
878             {
879                 if (prefix.mIsOnLinkPrefix)
880                 {
881                     prefix.mPreferredLifetime = 0;
882                 }
883                 else
884                 {
885                     InvalidateDiscoveredPrefixes(&prefix.mPrefix, prefix.mIsOnLinkPrefix);
886                 }
887             }
888         }
889 
890         // Invalidate the learned RA message if it is not refreshed during Router Solicitation.
891         if (mTimeRouterAdvMessageLastUpdate <= mTimeRouterSolicitStart)
892         {
893             UpdateRouterAdvMessage(/* aRouterAdvMessage */ nullptr);
894         }
895 
896         // Re-evaluate our routing policy and send Router Advertisement if necessary.
897         EvaluateRoutingPolicy();
898         mRouterSolicitCount = 0;
899     }
900 }
901 
HandleDiscoveredPrefixStaleTimer(Timer & aTimer)902 void RoutingManager::HandleDiscoveredPrefixStaleTimer(Timer &aTimer)
903 {
904     aTimer.Get<RoutingManager>().HandleDiscoveredPrefixStaleTimer();
905 }
906 
HandleDiscoveredPrefixStaleTimer(void)907 void RoutingManager::HandleDiscoveredPrefixStaleTimer(void)
908 {
909     otLogInfoBr("All on-link prefixes are stale");
910     StartRouterSolicitationDelay();
911 }
912 
HandleDiscoveredPrefixInvalidTimer(Timer & aTimer)913 void RoutingManager::HandleDiscoveredPrefixInvalidTimer(Timer &aTimer)
914 {
915     aTimer.Get<RoutingManager>().HandleDiscoveredPrefixInvalidTimer();
916 }
917 
HandleDiscoveredPrefixInvalidTimer(void)918 void RoutingManager::HandleDiscoveredPrefixInvalidTimer(void)
919 {
920     InvalidateDiscoveredPrefixes();
921 }
922 
HandleRoutingPolicyTimer(Timer & aTimer)923 void RoutingManager::HandleRoutingPolicyTimer(Timer &aTimer)
924 {
925     aTimer.Get<RoutingManager>().EvaluateRoutingPolicy();
926 }
927 
HandleRouterSolicit(const Ip6::Address & aSrcAddress,const uint8_t * aBuffer,uint16_t aBufferLength)928 void RoutingManager::HandleRouterSolicit(const Ip6::Address &aSrcAddress,
929                                          const uint8_t *     aBuffer,
930                                          uint16_t            aBufferLength)
931 {
932     uint32_t randomDelay;
933 
934     OT_UNUSED_VARIABLE(aSrcAddress);
935     OT_UNUSED_VARIABLE(aBuffer);
936     OT_UNUSED_VARIABLE(aBufferLength);
937 
938     VerifyOrExit(!mRouterSolicitTimer.IsRunning());
939     otLogInfoBr("Received Router Solicitation from %s on interface %u", aSrcAddress.ToString().AsCString(),
940                 mInfraIfIndex);
941 
942     if (!mVicariousRouterSolicitTimer.IsRunning())
943     {
944         mTimeVicariousRouterSolicitStart = TimerMilli::GetNow();
945         mVicariousRouterSolicitTimer.Start(Time::SecToMsec(kVicariousSolicitationTime));
946     }
947 
948     // Schedule Router Advertisements with random delay.
949     randomDelay = Random::NonCrypto::GetUint32InRange(0, kMaxRaDelayTime);
950     otLogInfoBr("Router Advertisement scheduled in %u milliseconds", randomDelay);
951     mRouterAdvertisementTimer.Start(randomDelay);
952 
953 exit:
954     return;
955 }
956 
GetPrefixExpireDelay(uint32_t aValidLifetime)957 uint32_t RoutingManager::ExternalPrefix::GetPrefixExpireDelay(uint32_t aValidLifetime)
958 {
959     uint32_t delay;
960 
961     if (aValidLifetime * static_cast<uint64_t>(1000) > Timer::kMaxDelay)
962     {
963         delay = Timer::kMaxDelay;
964     }
965     else
966     {
967         delay = aValidLifetime * 1000;
968     }
969 
970     return delay;
971 }
972 
HandleRouterAdvertisement(const Ip6::Address & aSrcAddress,const uint8_t * aBuffer,uint16_t aBufferLength)973 void RoutingManager::HandleRouterAdvertisement(const Ip6::Address &aSrcAddress,
974                                                const uint8_t *     aBuffer,
975                                                uint16_t            aBufferLength)
976 {
977     OT_ASSERT(mIsRunning);
978     OT_UNUSED_VARIABLE(aSrcAddress);
979 
980     using RouterAdv::Option;
981     using RouterAdv::PrefixInfoOption;
982     using RouterAdv::RouteInfoOption;
983     using RouterAdv::RouterAdvMessage;
984 
985     bool                    needReevaluate = false;
986     const uint8_t *         optionsBegin;
987     uint16_t                optionsLength;
988     const Option *          option;
989     const RouterAdvMessage *routerAdvMessage;
990 
991     VerifyOrExit(aBufferLength >= sizeof(RouterAdvMessage));
992 
993     otLogInfoBr("Received Router Advertisement from %s on interface %u", aSrcAddress.ToString().AsCString(),
994                 mInfraIfIndex);
995     otDumpDebgBr("[BR-CERT] direction=recv | type=RA |", aBuffer, aBufferLength);
996 
997     routerAdvMessage = reinterpret_cast<const RouterAdvMessage *>(aBuffer);
998     optionsBegin     = aBuffer + sizeof(RouterAdvMessage);
999     optionsLength    = aBufferLength - sizeof(RouterAdvMessage);
1000 
1001     option = nullptr;
1002     while ((option = Option::GetNextOption(option, optionsBegin, optionsLength)) != nullptr)
1003     {
1004         switch (option->GetType())
1005         {
1006         case Option::Type::kPrefixInfo:
1007         {
1008             const PrefixInfoOption *pio = static_cast<const PrefixInfoOption *>(option);
1009 
1010             if (pio->IsValid())
1011             {
1012                 needReevaluate |= UpdateDiscoveredPrefixes(*pio, routerAdvMessage->GetManagedAddrConfig());
1013             }
1014         }
1015         break;
1016 
1017         case Option::Type::kRouteInfo:
1018         {
1019             const RouteInfoOption *rio = static_cast<const RouteInfoOption *>(option);
1020 
1021             if (rio->IsValid())
1022             {
1023                 needReevaluate |= UpdateDiscoveredPrefixes(*rio);
1024             }
1025         }
1026         break;
1027 
1028         default:
1029             break;
1030         }
1031     }
1032 
1033     // Remember the header and parameters of RA messages which are
1034     // initiated from the infra interface.
1035     if (otPlatInfraIfHasAddress(mInfraIfIndex, &aSrcAddress))
1036     {
1037         needReevaluate |= UpdateRouterAdvMessage(routerAdvMessage);
1038     }
1039 
1040     if (needReevaluate)
1041     {
1042         StartRoutingPolicyEvaluationDelay();
1043     }
1044 
1045 exit:
1046     return;
1047 }
1048 
UpdateDiscoveredPrefixes(const RouterAdv::PrefixInfoOption & aPio,bool aManagedAddrConfig)1049 bool RoutingManager::UpdateDiscoveredPrefixes(const RouterAdv::PrefixInfoOption &aPio, bool aManagedAddrConfig)
1050 {
1051     Ip6::Prefix     prefix         = aPio.GetPrefix();
1052     bool            needReevaluate = false;
1053     TimeMilli       staleTime;
1054     ExternalPrefix  onLinkPrefix;
1055     ExternalPrefix *existingPrefix = nullptr;
1056 
1057     if (!IsValidOnLinkPrefix(aPio, aManagedAddrConfig))
1058     {
1059         otLogInfoBr("Ignore invalid on-link prefix in PIO: %s", prefix.ToString().AsCString());
1060         ExitNow();
1061     }
1062 
1063     VerifyOrExit(!mIsAdvertisingLocalOnLinkPrefix || prefix != mLocalOnLinkPrefix);
1064 
1065     otLogInfoBr("Discovered on-link prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(),
1066                 aPio.GetValidLifetime(), mInfraIfIndex);
1067 
1068     onLinkPrefix.mIsOnLinkPrefix    = true;
1069     onLinkPrefix.mPrefix            = prefix;
1070     onLinkPrefix.mValidLifetime     = aPio.GetValidLifetime();
1071     onLinkPrefix.mPreferredLifetime = aPio.GetPreferredLifetime();
1072     onLinkPrefix.mTimeLastUpdate    = TimerMilli::GetNow();
1073 
1074     staleTime = TimerMilli::GetNow();
1075 
1076     for (ExternalPrefix &externalPrefix : mDiscoveredPrefixes)
1077     {
1078         if (externalPrefix == onLinkPrefix)
1079         {
1080             existingPrefix = &externalPrefix;
1081         }
1082 
1083         staleTime = OT_MAX(staleTime, externalPrefix.GetStaleTime());
1084     }
1085 
1086     if (existingPrefix == nullptr)
1087     {
1088         if (onLinkPrefix.mValidLifetime == 0)
1089         {
1090             ExitNow();
1091         }
1092 
1093         if (!mDiscoveredPrefixes.IsFull())
1094         {
1095             SuccessOrExit(AddExternalRoute(prefix, NetworkData::kRoutePreferenceMedium));
1096             existingPrefix  = mDiscoveredPrefixes.PushBack();
1097             *existingPrefix = onLinkPrefix;
1098             needReevaluate  = true;
1099         }
1100         else
1101         {
1102             otLogWarnBr("Discovered too many prefixes, ignore new on-link prefix %s", prefix.ToString().AsCString());
1103             ExitNow();
1104         }
1105     }
1106     else
1107     {
1108         constexpr uint32_t kTwoHoursInSeconds = 2 * 3600;
1109 
1110         // Per RFC 4862 section 5.5.3.e:
1111         // 1.  If the received Valid Lifetime is greater than 2 hours or
1112         //     greater than RemainingLifetime, set the valid lifetime of the
1113         //     corresponding address to the advertised Valid Lifetime.
1114         // 2.  If RemainingLifetime is less than or equal to 2 hours, ignore
1115         //     the Prefix Information option with regards to the valid
1116         //     lifetime, unless ...
1117         // 3.  Otherwise, reset the valid lifetime of the corresponding
1118         //     address to 2 hours.
1119 
1120         if (onLinkPrefix.mValidLifetime > kTwoHoursInSeconds ||
1121             onLinkPrefix.GetExpireTime() > existingPrefix->GetExpireTime())
1122         {
1123             existingPrefix->mValidLifetime = onLinkPrefix.mValidLifetime;
1124         }
1125         else if (existingPrefix->GetExpireTime() > TimerMilli::GetNow() + TimeMilli::SecToMsec(kTwoHoursInSeconds))
1126         {
1127             existingPrefix->mValidLifetime = kTwoHoursInSeconds;
1128         }
1129 
1130         existingPrefix->mPreferredLifetime = onLinkPrefix.mPreferredLifetime;
1131         existingPrefix->mTimeLastUpdate    = onLinkPrefix.mTimeLastUpdate;
1132         needReevaluate                     = (existingPrefix->mPreferredLifetime == 0);
1133     }
1134 
1135     mDiscoveredPrefixInvalidTimer.FireAtIfEarlier(existingPrefix->GetExpireTime());
1136     staleTime = OT_MAX(staleTime, existingPrefix->GetStaleTime());
1137     mDiscoveredPrefixStaleTimer.FireAtIfEarlier(staleTime);
1138 
1139 exit:
1140     return needReevaluate;
1141 }
1142 
UpdateDiscoveredPrefixes(const RouterAdv::RouteInfoOption & aRio)1143 bool RoutingManager::UpdateDiscoveredPrefixes(const RouterAdv::RouteInfoOption &aRio)
1144 {
1145     Ip6::Prefix     prefix         = aRio.GetPrefix();
1146     bool            needReevaluate = false;
1147     ExternalPrefix  omrPrefix;
1148     ExternalPrefix *existingPrefix = nullptr;
1149 
1150     if (!IsValidOmrPrefix(prefix))
1151     {
1152         otLogInfoBr("Ignore invalid OMR prefix in RIO: %s", prefix.ToString().AsCString());
1153         ExitNow();
1154     }
1155 
1156     // Ignore OMR prefixes advertised by ourselves or in current Thread Network Data.
1157     // The `mAdvertisedOmrPrefixes` and the OMR prefix set in Network Data should eventually
1158     // be equal, but there is time that they are not synchronized immediately:
1159     // 1. Network Data could contain more OMR prefixes than `mAdvertisedOmrPrefixes` because
1160     //    we added random delay before Evaluating routing policy when Network Data is changed.
1161     // 2. `mAdvertisedOmrPrefixes` could contain more OMR prefixes than Network Data because
1162     //    it takes time to sync a new OMR prefix into Network Data (multicast loopback RA
1163     //    messages are usually faster than Thread Network Data propagation).
1164     // They are the reasons why we need both the checks.
1165 
1166     VerifyOrExit(!mAdvertisedOmrPrefixes.Contains(prefix));
1167     VerifyOrExit(!NetworkDataContainsOmrPrefix(prefix));
1168 
1169     otLogInfoBr("Discovered OMR prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(),
1170                 aRio.GetRouteLifetime(), mInfraIfIndex);
1171 
1172     if (aRio.GetRouteLifetime() == 0)
1173     {
1174         needReevaluate = InvalidateDiscoveredPrefixes(&prefix, /* aIsOnLinkPrefix */ false);
1175         ExitNow();
1176     }
1177 
1178     omrPrefix.mIsOnLinkPrefix  = false;
1179     omrPrefix.mPrefix          = prefix;
1180     omrPrefix.mValidLifetime   = aRio.GetRouteLifetime();
1181     omrPrefix.mRoutePreference = aRio.GetPreference();
1182     omrPrefix.mTimeLastUpdate  = TimerMilli::GetNow();
1183 
1184     for (ExternalPrefix &externalPrefix : mDiscoveredPrefixes)
1185     {
1186         if (externalPrefix == omrPrefix)
1187         {
1188             existingPrefix = &externalPrefix;
1189         }
1190 
1191         mDiscoveredPrefixStaleTimer.FireAtIfEarlier(externalPrefix.GetStaleTime());
1192     }
1193 
1194     if (existingPrefix == nullptr)
1195     {
1196         if (omrPrefix.mValidLifetime == 0)
1197         {
1198             ExitNow();
1199         }
1200 
1201         if (!mDiscoveredPrefixes.IsFull())
1202         {
1203             SuccessOrExit(AddExternalRoute(prefix, omrPrefix.mRoutePreference));
1204             existingPrefix = mDiscoveredPrefixes.PushBack();
1205             needReevaluate = true;
1206         }
1207         else
1208         {
1209             otLogWarnBr("Discovered too many prefixes, ignore new prefix %s", prefix.ToString().AsCString());
1210             ExitNow();
1211         }
1212     }
1213 
1214     *existingPrefix = omrPrefix;
1215 
1216     mDiscoveredPrefixInvalidTimer.FireAtIfEarlier(existingPrefix->GetExpireTime());
1217     mDiscoveredPrefixStaleTimer.FireAtIfEarlier(existingPrefix->GetStaleTime());
1218 
1219 exit:
1220     return needReevaluate;
1221 }
1222 
InvalidateDiscoveredPrefixes(const Ip6::Prefix * aPrefix,bool aIsOnLinkPrefix)1223 bool RoutingManager::InvalidateDiscoveredPrefixes(const Ip6::Prefix *aPrefix, bool aIsOnLinkPrefix)
1224 {
1225     bool                didRemove                = false;
1226     TimeMilli           now                      = TimerMilli::GetNow();
1227     uint8_t             remainingOnLinkPrefixNum = 0;
1228     ExternalPrefixArray remainingPrefixes;
1229 
1230     mDiscoveredPrefixInvalidTimer.Stop();
1231 
1232     for (const ExternalPrefix &prefix : mDiscoveredPrefixes)
1233     {
1234         if ((aPrefix != nullptr && prefix.mPrefix == *aPrefix && prefix.mIsOnLinkPrefix == aIsOnLinkPrefix) ||
1235             (prefix.GetExpireTime() <= now))
1236         {
1237             RemoveExternalRoute(prefix.mPrefix);
1238             didRemove = true;
1239         }
1240         else
1241         {
1242             mDiscoveredPrefixInvalidTimer.FireAtIfEarlier(prefix.GetExpireTime());
1243 
1244             IgnoreError(remainingPrefixes.PushBack(prefix));
1245 
1246             if (prefix.mIsOnLinkPrefix)
1247             {
1248                 ++remainingOnLinkPrefixNum;
1249             }
1250         }
1251     }
1252 
1253     mDiscoveredPrefixes = remainingPrefixes;
1254 
1255     if (remainingOnLinkPrefixNum == 0 && !mIsAdvertisingLocalOnLinkPrefix)
1256     {
1257         // There are no valid on-link prefixes on infra link now, start Router Solicitation
1258         // To discover more on-link prefixes or timeout to advertise my local on-link prefix.
1259         StartRouterSolicitationDelay();
1260     }
1261 
1262     return didRemove; // If anything was removed we need to reevaluate.
1263 }
1264 
InvalidateAllDiscoveredPrefixes(void)1265 void RoutingManager::InvalidateAllDiscoveredPrefixes(void)
1266 {
1267     for (ExternalPrefix &prefix : mDiscoveredPrefixes)
1268     {
1269         prefix.mValidLifetime = 0;
1270     }
1271 
1272     InvalidateDiscoveredPrefixes();
1273 
1274     OT_ASSERT(mDiscoveredPrefixes.IsEmpty());
1275 }
1276 
NetworkDataContainsOmrPrefix(const Ip6::Prefix & aPrefix) const1277 bool RoutingManager::NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) const
1278 {
1279     NetworkData::Iterator           iterator = NetworkData::kIteratorInit;
1280     NetworkData::OnMeshPrefixConfig onMeshPrefixConfig;
1281     bool                            contain = false;
1282 
1283     while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, onMeshPrefixConfig) == OT_ERROR_NONE)
1284     {
1285         if (IsValidOmrPrefix(onMeshPrefixConfig) && onMeshPrefixConfig.GetPrefix() == aPrefix)
1286         {
1287             contain = true;
1288             break;
1289         }
1290     }
1291 
1292     return contain;
1293 }
1294 
1295 // Update the `mRouterAdvMessage` with given Router Advertisement message.
1296 // Returns a boolean which indicates whether there are changes of `mRouterAdvMessage`.
UpdateRouterAdvMessage(const RouterAdv::RouterAdvMessage * aRouterAdvMessage)1297 bool RoutingManager::UpdateRouterAdvMessage(const RouterAdv::RouterAdvMessage *aRouterAdvMessage)
1298 {
1299     RouterAdv::RouterAdvMessage oldRouterAdvMessage;
1300 
1301     oldRouterAdvMessage = mRouterAdvMessage;
1302 
1303     mTimeRouterAdvMessageLastUpdate = TimerMilli::GetNow();
1304     if (aRouterAdvMessage == nullptr || aRouterAdvMessage->GetRouterLifetime() == 0)
1305     {
1306         mRouterAdvMessage.SetToDefault();
1307     }
1308     else
1309     {
1310         mRouterAdvMessage = *aRouterAdvMessage;
1311         mDiscoveredPrefixStaleTimer.FireAtIfEarlier(mTimeRouterAdvMessageLastUpdate +
1312                                                     Time::SecToMsec(kRtrAdvStaleTime));
1313     }
1314 
1315     return (mRouterAdvMessage != oldRouterAdvMessage);
1316 }
1317 
1318 } // namespace BorderRouter
1319 
1320 } // namespace ot
1321 
1322 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
1323