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