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