1 /*
2 * Copyright (c) 2020, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file includes implementation for the RA-based routing management.
32 *
33 */
34
35 #include "border_router/routing_manager.hpp"
36
37 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
38
39 #include <string.h>
40
41 #include <openthread/border_router.h>
42 #include <openthread/platform/infra_if.h>
43
44 #include "common/code_utils.hpp"
45 #include "common/debug.hpp"
46 #include "common/instance.hpp"
47 #include "common/locator_getters.hpp"
48 #include "common/log.hpp"
49 #include "common/num_utils.hpp"
50 #include "common/random.hpp"
51 #include "common/settings.hpp"
52 #include "meshcop/extended_panid.hpp"
53 #include "net/ip6.hpp"
54 #include "net/nat64_translator.hpp"
55 #include "thread/network_data_leader.hpp"
56 #include "thread/network_data_local.hpp"
57 #include "thread/network_data_notifier.hpp"
58
59 namespace ot {
60
61 namespace BorderRouter {
62
63 RegisterLogModule("BorderRouter");
64
RoutingManager(Instance & aInstance)65 RoutingManager::RoutingManager(Instance &aInstance)
66 : InstanceLocator(aInstance)
67 , mIsRunning(false)
68 , mIsEnabled(false)
69 , mInfraIf(aInstance)
70 , mOmrPrefixManager(aInstance)
71 , mRioPreference(NetworkData::kRoutePreferenceLow)
72 , mUserSetRioPreference(false)
73 , mOnLinkPrefixManager(aInstance)
74 , mDiscoveredPrefixTable(aInstance)
75 , mRoutePublisher(aInstance)
76 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
77 , mNat64PrefixManager(aInstance)
78 #endif
79 , mRsSender(aInstance)
80 , mDiscoveredPrefixStaleTimer(aInstance)
81 , mRoutingPolicyTimer(aInstance)
82 {
83 mBrUlaPrefix.Clear();
84 }
85
Init(uint32_t aInfraIfIndex,bool aInfraIfIsRunning)86 Error RoutingManager::Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning)
87 {
88 Error error;
89
90 SuccessOrExit(error = mInfraIf.Init(aInfraIfIndex));
91
92 SuccessOrExit(error = LoadOrGenerateRandomBrUlaPrefix());
93 mOmrPrefixManager.Init(mBrUlaPrefix);
94 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
95 mNat64PrefixManager.GenerateLocalPrefix(mBrUlaPrefix);
96 #endif
97 mOnLinkPrefixManager.Init();
98
99 error = mInfraIf.HandleStateChanged(mInfraIf.GetIfIndex(), aInfraIfIsRunning);
100
101 exit:
102 if (error != kErrorNone)
103 {
104 mInfraIf.Deinit();
105 }
106
107 return error;
108 }
109
SetEnabled(bool aEnabled)110 Error RoutingManager::SetEnabled(bool aEnabled)
111 {
112 Error error = kErrorNone;
113
114 VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
115
116 VerifyOrExit(aEnabled != mIsEnabled);
117
118 mIsEnabled = aEnabled;
119 EvaluateState();
120
121 exit:
122 return error;
123 }
124
GetState(void) const125 RoutingManager::State RoutingManager::GetState(void) const
126 {
127 State state = kStateUninitialized;
128
129 VerifyOrExit(IsInitialized());
130 VerifyOrExit(IsEnabled(), state = kStateDisabled);
131
132 state = IsRunning() ? kStateRunning : kStateStopped;
133
134 exit:
135 return state;
136 }
137
SetRouteInfoOptionPreference(RoutePreference aPreference)138 void RoutingManager::SetRouteInfoOptionPreference(RoutePreference aPreference)
139 {
140 LogInfo("User explicitly set RIO Preference to %s", RoutePreferenceToString(aPreference));
141 mUserSetRioPreference = true;
142 UpdateRioPreference(aPreference);
143 }
144
ClearRouteInfoOptionPreference(void)145 void RoutingManager::ClearRouteInfoOptionPreference(void)
146 {
147 VerifyOrExit(mUserSetRioPreference);
148
149 LogInfo("User cleared explicitly set RIO Preference");
150 mUserSetRioPreference = false;
151 SetRioPreferenceBasedOnRole();
152
153 exit:
154 return;
155 }
156
SetRioPreferenceBasedOnRole(void)157 void RoutingManager::SetRioPreferenceBasedOnRole(void)
158 {
159 UpdateRioPreference(Get<Mle::Mle>().IsRouterOrLeader() ? NetworkData::kRoutePreferenceMedium
160 : NetworkData::kRoutePreferenceLow);
161 }
162
UpdateRioPreference(RoutePreference aPreference)163 void RoutingManager::UpdateRioPreference(RoutePreference aPreference)
164 {
165 VerifyOrExit(mRioPreference != aPreference);
166
167 LogInfo("RIO Preference changed: %s -> %s", RoutePreferenceToString(mRioPreference),
168 RoutePreferenceToString(aPreference));
169 mRioPreference = aPreference;
170
171 VerifyOrExit(mIsRunning);
172 ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
173
174 exit:
175 return;
176 }
177
GetOmrPrefix(Ip6::Prefix & aPrefix) const178 Error RoutingManager::GetOmrPrefix(Ip6::Prefix &aPrefix) const
179 {
180 Error error = kErrorNone;
181
182 VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
183 aPrefix = mOmrPrefixManager.GetLocalPrefix().GetPrefix();
184
185 exit:
186 return error;
187 }
188
GetFavoredOmrPrefix(Ip6::Prefix & aPrefix,RoutePreference & aPreference) const189 Error RoutingManager::GetFavoredOmrPrefix(Ip6::Prefix &aPrefix, RoutePreference &aPreference) const
190 {
191 Error error = kErrorNone;
192
193 VerifyOrExit(IsRunning(), error = kErrorInvalidState);
194 aPrefix = mOmrPrefixManager.GetFavoredPrefix().GetPrefix();
195 aPreference = mOmrPrefixManager.GetFavoredPrefix().GetPreference();
196
197 exit:
198 return error;
199 }
200
GetOnLinkPrefix(Ip6::Prefix & aPrefix) const201 Error RoutingManager::GetOnLinkPrefix(Ip6::Prefix &aPrefix) const
202 {
203 Error error = kErrorNone;
204
205 VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
206 aPrefix = mOnLinkPrefixManager.GetLocalPrefix();
207
208 exit:
209 return error;
210 }
211
GetFavoredOnLinkPrefix(Ip6::Prefix & aPrefix) const212 Error RoutingManager::GetFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const
213 {
214 Error error = kErrorNone;
215
216 VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
217 aPrefix = mOnLinkPrefixManager.GetFavoredDiscoveredPrefix();
218
219 if (aPrefix.GetLength() == 0)
220 {
221 aPrefix = mOnLinkPrefixManager.GetLocalPrefix();
222 }
223
224 exit:
225 return error;
226 }
227
228 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
SetNat64PrefixManagerEnabled(bool aEnabled)229 void RoutingManager::SetNat64PrefixManagerEnabled(bool aEnabled)
230 {
231 // PrefixManager will start itself if routing manager is running.
232 mNat64PrefixManager.SetEnabled(aEnabled);
233 }
234
GetNat64Prefix(Ip6::Prefix & aPrefix)235 Error RoutingManager::GetNat64Prefix(Ip6::Prefix &aPrefix)
236 {
237 Error error = kErrorNone;
238
239 VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
240 aPrefix = mNat64PrefixManager.GetLocalPrefix();
241
242 exit:
243 return error;
244 }
245
GetFavoredNat64Prefix(Ip6::Prefix & aPrefix,RoutePreference & aRoutePreference)246 Error RoutingManager::GetFavoredNat64Prefix(Ip6::Prefix &aPrefix, RoutePreference &aRoutePreference)
247 {
248 Error error = kErrorNone;
249
250 VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
251 aPrefix = mNat64PrefixManager.GetFavoredPrefix(aRoutePreference);
252
253 exit:
254 return error;
255 }
256 #endif
257
LoadOrGenerateRandomBrUlaPrefix(void)258 Error RoutingManager::LoadOrGenerateRandomBrUlaPrefix(void)
259 {
260 Error error = kErrorNone;
261 bool generated = false;
262
263 if (Get<Settings>().Read<Settings::BrUlaPrefix>(mBrUlaPrefix) != kErrorNone || !IsValidBrUlaPrefix(mBrUlaPrefix))
264 {
265 Ip6::NetworkPrefix randomUlaPrefix;
266
267 LogNote("No valid /48 BR ULA prefix found in settings, generating new one");
268
269 SuccessOrExit(error = randomUlaPrefix.GenerateRandomUla());
270
271 mBrUlaPrefix.Set(randomUlaPrefix);
272 mBrUlaPrefix.SetSubnetId(0);
273 mBrUlaPrefix.SetLength(kBrUlaPrefixLength);
274
275 IgnoreError(Get<Settings>().Save<Settings::BrUlaPrefix>(mBrUlaPrefix));
276 generated = true;
277 }
278
279 OT_UNUSED_VARIABLE(generated);
280
281 LogNote("BR ULA prefix: %s (%s)", mBrUlaPrefix.ToString().AsCString(), generated ? "generated" : "loaded");
282
283 exit:
284 if (error != kErrorNone)
285 {
286 LogCrit("Failed to generate random /48 BR ULA prefix");
287 }
288 return error;
289 }
290
EvaluateState(void)291 void RoutingManager::EvaluateState(void)
292 {
293 if (mIsEnabled && Get<Mle::MleRouter>().IsAttached() && mInfraIf.IsRunning())
294 {
295 Start();
296 }
297 else
298 {
299 Stop();
300 }
301 }
302
Start(void)303 void RoutingManager::Start(void)
304 {
305 if (!mIsRunning)
306 {
307 LogInfo("Border Routing manager started");
308
309 mIsRunning = true;
310 UpdateDiscoveredPrefixTableOnNetDataChange();
311 mOnLinkPrefixManager.Start();
312 mOmrPrefixManager.Start();
313 mRoutePublisher.Start();
314 mRsSender.Start();
315 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
316 mNat64PrefixManager.Start();
317 #endif
318 }
319 }
320
Stop(void)321 void RoutingManager::Stop(void)
322 {
323 VerifyOrExit(mIsRunning);
324
325 mOmrPrefixManager.Stop();
326 mOnLinkPrefixManager.Stop();
327
328 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
329 mNat64PrefixManager.Stop();
330 #endif
331
332 SendRouterAdvertisement(kInvalidateAllPrevPrefixes);
333
334 mAdvertisedPrefixes.Clear();
335
336 mDiscoveredPrefixTable.RemoveAllEntries();
337 mDiscoveredPrefixStaleTimer.Stop();
338
339 mRaInfo.mTxCount = 0;
340
341 mRsSender.Stop();
342
343 mRoutingPolicyTimer.Stop();
344
345 mRoutePublisher.Stop();
346
347 LogInfo("Border Routing manager stopped");
348
349 mIsRunning = false;
350
351 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
352 if (Get<Srp::Server>().IsAutoEnableMode())
353 {
354 Get<Srp::Server>().Disable();
355 }
356 #endif
357
358 exit:
359 return;
360 }
361
362 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
HandleSrpServerAutoEnableMode(void)363 void RoutingManager::HandleSrpServerAutoEnableMode(void)
364 {
365 VerifyOrExit(Get<Srp::Server>().IsAutoEnableMode());
366
367 if (IsInitalPolicyEvaluationDone())
368 {
369 Get<Srp::Server>().Enable();
370 }
371 else
372 {
373 Get<Srp::Server>().Disable();
374 }
375
376 exit:
377 return;
378 }
379 #endif
380
HandleReceived(const InfraIf::Icmp6Packet & aPacket,const Ip6::Address & aSrcAddress)381 void RoutingManager::HandleReceived(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
382 {
383 const Ip6::Icmp::Header *icmp6Header;
384
385 VerifyOrExit(mIsRunning);
386
387 icmp6Header = reinterpret_cast<const Ip6::Icmp::Header *>(aPacket.GetBytes());
388
389 switch (icmp6Header->GetType())
390 {
391 case Ip6::Icmp::Header::kTypeRouterAdvert:
392 HandleRouterAdvertisement(aPacket, aSrcAddress);
393 break;
394 case Ip6::Icmp::Header::kTypeRouterSolicit:
395 HandleRouterSolicit(aPacket, aSrcAddress);
396 break;
397 case Ip6::Icmp::Header::kTypeNeighborAdvert:
398 HandleNeighborAdvertisement(aPacket);
399 break;
400 default:
401 break;
402 }
403
404 exit:
405 return;
406 }
407
HandleNotifierEvents(Events aEvents)408 void RoutingManager::HandleNotifierEvents(Events aEvents)
409 {
410 if (aEvents.Contains(kEventThreadRoleChanged) && !mUserSetRioPreference)
411 {
412 SetRioPreferenceBasedOnRole();
413 mRoutePublisher.HandleRoleChanged();
414 }
415
416 VerifyOrExit(IsInitialized() && IsEnabled());
417
418 if (aEvents.Contains(kEventThreadRoleChanged))
419 {
420 EvaluateState();
421 }
422
423 if (mIsRunning && aEvents.Contains(kEventThreadNetdataChanged))
424 {
425 UpdateDiscoveredPrefixTableOnNetDataChange();
426 mOnLinkPrefixManager.HandleNetDataChange();
427 ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
428 }
429
430 if (aEvents.Contains(kEventThreadExtPanIdChanged))
431 {
432 mOnLinkPrefixManager.HandleExtPanIdChange();
433 }
434
435 exit:
436 return;
437 }
438
UpdateDiscoveredPrefixTableOnNetDataChange(void)439 void RoutingManager::UpdateDiscoveredPrefixTableOnNetDataChange(void)
440 {
441 NetworkData::Iterator iterator = NetworkData::kIteratorInit;
442 NetworkData::OnMeshPrefixConfig prefixConfig;
443
444 // Remove all OMR prefixes in Network Data from the
445 // discovered prefix table. Also check if we have
446 // an OMR prefix with default route flag.
447
448 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
449 {
450 if (!IsValidOmrPrefix(prefixConfig))
451 {
452 continue;
453 }
454
455 mDiscoveredPrefixTable.RemoveRoutePrefix(prefixConfig.GetPrefix());
456 }
457 }
458
459 // This method evaluate the routing policy depends on prefix and route
460 // information on Thread Network and infra link. As a result, this
461 // method May send RA messages on infra link and publish/unpublish
462 // OMR and NAT64 prefix in the Thread network.
EvaluateRoutingPolicy(void)463 void RoutingManager::EvaluateRoutingPolicy(void)
464 {
465 OT_ASSERT(mIsRunning);
466
467 LogInfo("Evaluating routing policy");
468
469 mOnLinkPrefixManager.Evaluate();
470 mOmrPrefixManager.Evaluate();
471 mRoutePublisher.Evaluate();
472 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
473 mNat64PrefixManager.Evaluate();
474 #endif
475
476 SendRouterAdvertisement(kAdvPrefixesFromNetData);
477
478 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
479 if (Get<Srp::Server>().IsAutoEnableMode() && IsInitalPolicyEvaluationDone())
480 {
481 // If SRP server uses the auto-enable mode, we enable the SRP
482 // server on the first RA transmission after we are done with
483 // initial prefix/route configurations. Note that if SRP server
484 // is already enabled, calling `Enable()` again does nothing.
485
486 Get<Srp::Server>().Enable();
487 }
488 #endif
489
490 ScheduleRoutingPolicyEvaluation(kForNextRa);
491 }
492
IsInitalPolicyEvaluationDone(void) const493 bool RoutingManager::IsInitalPolicyEvaluationDone(void) const
494 {
495 // This method indicates whether or not we are done with the
496 // initial policy evaluation and prefix and route setup, i.e.,
497 // the OMR and on-link prefixes are determined, advertised in
498 // the emitted Router Advert message on infrastructure side
499 // and published in the Thread Network Data.
500
501 return mIsRunning && !mOmrPrefixManager.GetFavoredPrefix().IsEmpty() &&
502 mOnLinkPrefixManager.IsInitalEvaluationDone();
503 }
504
ScheduleRoutingPolicyEvaluation(ScheduleMode aMode)505 void RoutingManager::ScheduleRoutingPolicyEvaluation(ScheduleMode aMode)
506 {
507 TimeMilli now = TimerMilli::GetNow();
508 uint32_t delay = 0;
509 TimeMilli evaluateTime;
510
511 switch (aMode)
512 {
513 case kImmediately:
514 break;
515
516 case kForNextRa:
517 delay = Random::NonCrypto::GetUint32InRange(Time::SecToMsec(kMinRtrAdvInterval),
518 Time::SecToMsec(kMaxRtrAdvInterval));
519
520 if (mRaInfo.mTxCount <= kMaxInitRtrAdvertisements && delay > Time::SecToMsec(kMaxInitRtrAdvInterval))
521 {
522 delay = Time::SecToMsec(kMaxInitRtrAdvInterval);
523 }
524 break;
525
526 case kAfterRandomDelay:
527 delay = Random::NonCrypto::GetUint32InRange(kPolicyEvaluationMinDelay, kPolicyEvaluationMaxDelay);
528 break;
529
530 case kToReplyToRs:
531 delay = Random::NonCrypto::GetUint32InRange(0, kRaReplyJitter);
532 break;
533 }
534
535 // Ensure we wait a min delay after last RA tx
536 evaluateTime = Max(now + delay, mRaInfo.mLastTxTime + kMinDelayBetweenRtrAdvs);
537
538 LogInfo("Start evaluating routing policy, scheduled in %lu milliseconds", ToUlong(evaluateTime - now));
539
540 mRoutingPolicyTimer.FireAtIfEarlier(evaluateTime);
541 }
542
SendRouterAdvertisement(RouterAdvTxMode aRaTxMode)543 void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode)
544 {
545 // RA message max length is derived to accommodate:
546 //
547 // - The RA header.
548 // - One PIO for current local on-link prefix.
549 // - At most `kMaxOldPrefixes` for old deprecating on-link prefixes.
550 // - At most twice `kMaxOnMeshPrefixes` RIO for on-mesh prefixes.
551 // Factor two is used for RIO to account for entries invalidating
552 // previous prefixes while adding new ones.
553
554 static constexpr uint16_t kMaxRaLength =
555 sizeof(Ip6::Nd::RouterAdvertMessage::Header) + sizeof(Ip6::Nd::PrefixInfoOption) +
556 sizeof(Ip6::Nd::PrefixInfoOption) * OnLinkPrefixManager::kMaxOldPrefixes +
557 2 * kMaxOnMeshPrefixes * (sizeof(Ip6::Nd::RouteInfoOption) + sizeof(Ip6::Prefix));
558
559 uint8_t buffer[kMaxRaLength];
560 Ip6::Nd::RouterAdvertMessage raMsg(mRaInfo.mHeader, buffer);
561 NetworkData::Iterator iterator;
562 NetworkData::OnMeshPrefixConfig prefixConfig;
563
564 // Append PIO for local on-link prefix if is either being
565 // advertised or deprecated and for old prefix if is being
566 // deprecated.
567
568 mOnLinkPrefixManager.AppendAsPiosTo(raMsg);
569
570 // Determine which previously advertised prefixes need to be
571 // invalidated. Under `kInvalidateAllPrevPrefixes` mode we need
572 // to invalidate all. Under `kAdvPrefixesFromNetData` mode, we
573 // check Network Data entries and invalidate any previously
574 // advertised prefix that is no longer present in the Network
575 // Data. We go through all Network Data prefixes and mark the
576 // ones we find in `mAdvertisedPrefixes` as deleted by setting
577 // the prefix length to zero). By the end, the remaining entries
578 // in the array with a non-zero prefix length are invalidated.
579
580 if (aRaTxMode != kInvalidateAllPrevPrefixes)
581 {
582 iterator = NetworkData::kIteratorInit;
583
584 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
585 {
586 if (!prefixConfig.mOnMesh || prefixConfig.mDp ||
587 (prefixConfig.GetPrefix() == mOmrPrefixManager.GetLocalPrefix().GetPrefix()))
588 {
589 continue;
590 }
591
592 mAdvertisedPrefixes.MarkAsDeleted(prefixConfig.GetPrefix());
593 }
594
595 if (mOmrPrefixManager.IsLocalAddedInNetData())
596 {
597 mAdvertisedPrefixes.MarkAsDeleted(mOmrPrefixManager.GetLocalPrefix().GetPrefix());
598 }
599 }
600
601 for (const OnMeshPrefix &prefix : mAdvertisedPrefixes)
602 {
603 if (prefix.GetLength() != 0)
604 {
605 SuccessOrAssert(raMsg.AppendRouteInfoOption(prefix, /* aRouteLifetime */ 0, mRioPreference));
606 LogInfo("RouterAdvert: Added RIO for %s (lifetime=0)", prefix.ToString().AsCString());
607 }
608 }
609
610 // Discover and add prefixes from Network Data to advertise as
611 // RIO in the Router Advertisement message.
612
613 mAdvertisedPrefixes.Clear();
614
615 if (aRaTxMode == kAdvPrefixesFromNetData)
616 {
617 // `mAdvertisedPrefixes` array has a limited size. We add more
618 // important prefixes first in the array to ensure they are
619 // advertised in the RA message. Note that `Add()` method
620 // will ensure to add a prefix only once (will check if
621 // prefix is already present in the array).
622
623 // (1) Local OMR prefix.
624
625 if (mOmrPrefixManager.IsLocalAddedInNetData())
626 {
627 mAdvertisedPrefixes.Add(mOmrPrefixManager.GetLocalPrefix().GetPrefix());
628 }
629
630 // (2) Favored OMR prefix.
631
632 if (!mOmrPrefixManager.GetFavoredPrefix().IsEmpty() && !mOmrPrefixManager.GetFavoredPrefix().IsDomainPrefix())
633 {
634 mAdvertisedPrefixes.Add(mOmrPrefixManager.GetFavoredPrefix().GetPrefix());
635 }
636
637 // (3) All other OMR prefixes.
638
639 iterator = NetworkData::kIteratorInit;
640
641 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
642 {
643 // Local OMR prefix is added to the array depending on
644 // `mOmrPrefixManager.IsLocalAddedInNetData()` at step (1).
645 // As we iterate through the Network Data prefixes, we skip
646 // over entries matching the local OMR prefix. This
647 // ensures that we stop including it in emitted RA
648 // message as soon as we decide to remove it from Network
649 // Data. Note that upon requesting it to be removed from
650 // Network Data the change needs to be registered with
651 // leader and can take some time to be updated in Network
652 // Data.
653
654 if (prefixConfig.mDp)
655 {
656 continue;
657 }
658
659 if (IsValidOmrPrefix(prefixConfig) &&
660 (prefixConfig.GetPrefix() != mOmrPrefixManager.GetLocalPrefix().GetPrefix()))
661 {
662 mAdvertisedPrefixes.Add(prefixConfig.GetPrefix());
663 }
664 }
665
666 // (4) All other on-mesh prefixes (excluding Domain Prefix).
667
668 iterator = NetworkData::kIteratorInit;
669
670 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
671 {
672 if (prefixConfig.mOnMesh && !prefixConfig.mDp && !IsValidOmrPrefix(prefixConfig))
673 {
674 mAdvertisedPrefixes.Add(prefixConfig.GetPrefix());
675 }
676 }
677
678 for (const OnMeshPrefix &prefix : mAdvertisedPrefixes)
679 {
680 SuccessOrAssert(raMsg.AppendRouteInfoOption(prefix, kDefaultOmrPrefixLifetime, mRioPreference));
681 LogInfo("RouterAdvert: Added RIO for %s (lifetime=%lu)", prefix.ToString().AsCString(),
682 ToUlong(kDefaultOmrPrefixLifetime));
683 }
684 }
685
686 if (raMsg.ContainsAnyOptions())
687 {
688 Error error;
689 Ip6::Address destAddress;
690
691 ++mRaInfo.mTxCount;
692
693 destAddress.SetToLinkLocalAllNodesMulticast();
694
695 error = mInfraIf.Send(raMsg.GetAsPacket(), destAddress);
696
697 if (error == kErrorNone)
698 {
699 mRaInfo.mLastTxTime = TimerMilli::GetNow();
700 Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaTxSuccess++;
701 LogInfo("Sent Router Advertisement on %s", mInfraIf.ToString().AsCString());
702 DumpDebg("[BR-CERT] direction=send | type=RA |", raMsg.GetAsPacket().GetBytes(),
703 raMsg.GetAsPacket().GetLength());
704 }
705 else
706 {
707 Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaTxFailure++;
708 LogWarn("Failed to send Router Advertisement on %s: %s", mInfraIf.ToString().AsCString(),
709 ErrorToString(error));
710 }
711 }
712 }
713
IsReceivedRouterAdvertFromManager(const Ip6::Nd::RouterAdvertMessage & aRaMessage) const714 bool RoutingManager::IsReceivedRouterAdvertFromManager(const Ip6::Nd::RouterAdvertMessage &aRaMessage) const
715 {
716 // Determines whether or not a received RA message was prepared by
717 // by `RoutingManager` itself.
718
719 bool isFromManager = false;
720 uint16_t rioCount = 0;
721 Ip6::Prefix prefix;
722
723 VerifyOrExit(aRaMessage.ContainsAnyOptions());
724
725 for (const Ip6::Nd::Option &option : aRaMessage)
726 {
727 switch (option.GetType())
728 {
729 case Ip6::Nd::Option::kTypePrefixInfo:
730 {
731 const Ip6::Nd::PrefixInfoOption &pio = static_cast<const Ip6::Nd::PrefixInfoOption &>(option);
732
733 VerifyOrExit(pio.IsValid());
734 pio.GetPrefix(prefix);
735
736 // If it is a non-deprecated PIO, it should match the
737 // local on-link prefix.
738
739 if (pio.GetPreferredLifetime() > 0)
740 {
741 VerifyOrExit(prefix == mOnLinkPrefixManager.GetLocalPrefix());
742 }
743
744 break;
745 }
746
747 case Ip6::Nd::Option::kTypeRouteInfo:
748 {
749 // RIO (with non-zero lifetime) should match entries from
750 // `mAdvertisedPrefixes`. We keep track of the number
751 // of matched RIOs and check after the loop ends that all
752 // entries were seen.
753
754 const Ip6::Nd::RouteInfoOption &rio = static_cast<const Ip6::Nd::RouteInfoOption &>(option);
755
756 VerifyOrExit(rio.IsValid());
757 rio.GetPrefix(prefix);
758
759 if (rio.GetRouteLifetime() != 0)
760 {
761 VerifyOrExit(mAdvertisedPrefixes.Contains(prefix));
762 rioCount++;
763 }
764
765 break;
766 }
767
768 default:
769 ExitNow();
770 }
771 }
772
773 VerifyOrExit(rioCount == mAdvertisedPrefixes.GetLength());
774
775 isFromManager = true;
776
777 exit:
778 return isFromManager;
779 }
780
IsValidBrUlaPrefix(const Ip6::Prefix & aBrUlaPrefix)781 bool RoutingManager::IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix)
782 {
783 return aBrUlaPrefix.mLength == kBrUlaPrefixLength && aBrUlaPrefix.mPrefix.mFields.m8[0] == 0xfd;
784 }
785
IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig & aOnMeshPrefixConfig)786 bool RoutingManager::IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig)
787 {
788 return IsValidOmrPrefix(aOnMeshPrefixConfig.GetPrefix()) && aOnMeshPrefixConfig.mOnMesh &&
789 aOnMeshPrefixConfig.mSlaac && aOnMeshPrefixConfig.mStable;
790 }
791
IsValidOmrPrefix(const Ip6::Prefix & aPrefix)792 bool RoutingManager::IsValidOmrPrefix(const Ip6::Prefix &aPrefix)
793 {
794 // Accept ULA/GUA prefixes with 64-bit length.
795 return (aPrefix.GetLength() == kOmrPrefixLength) && !aPrefix.IsLinkLocal() && !aPrefix.IsMulticast();
796 }
797
IsValidOnLinkPrefix(const Ip6::Nd::PrefixInfoOption & aPio)798 bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Nd::PrefixInfoOption &aPio)
799 {
800 Ip6::Prefix prefix;
801
802 aPio.GetPrefix(prefix);
803
804 return IsValidOnLinkPrefix(prefix) && aPio.IsOnLinkFlagSet() && aPio.IsAutoAddrConfigFlagSet();
805 }
806
IsValidOnLinkPrefix(const Ip6::Prefix & aOnLinkPrefix)807 bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix)
808 {
809 return aOnLinkPrefix.IsValid() && (aOnLinkPrefix.GetLength() > 0) && !aOnLinkPrefix.IsLinkLocal() &&
810 !aOnLinkPrefix.IsMulticast();
811 }
812
HandleRsSenderFinished(TimeMilli aStartTime)813 void RoutingManager::HandleRsSenderFinished(TimeMilli aStartTime)
814 {
815 // This is a callback from `RsSender` and is invoked when it
816 // finishes a cycle of sending Router Solicitations. `aStartTime`
817 // specifies the start time of the RS transmission cycle.
818 //
819 // We remove or deprecate old entries in discovered table that are
820 // not refreshed during Router Solicitation. We also invalidate
821 // the learned RA header if it is not refreshed during Router
822 // Solicitation.
823
824 mDiscoveredPrefixTable.RemoveOrDeprecateOldEntries(aStartTime);
825
826 if (mRaInfo.mHeaderUpdateTime <= aStartTime)
827 {
828 UpdateRouterAdvertHeader(/* aRouterAdvertMessage */ nullptr);
829 }
830
831 ScheduleRoutingPolicyEvaluation(kImmediately);
832 }
833
HandleDiscoveredPrefixStaleTimer(void)834 void RoutingManager::HandleDiscoveredPrefixStaleTimer(void)
835 {
836 LogInfo("Stale On-Link or OMR Prefixes or RA messages are detected");
837 mRsSender.Start();
838 }
839
HandleRouterSolicit(const InfraIf::Icmp6Packet & aPacket,const Ip6::Address & aSrcAddress)840 void RoutingManager::HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
841 {
842 OT_UNUSED_VARIABLE(aPacket);
843 OT_UNUSED_VARIABLE(aSrcAddress);
844
845 Get<Ip6::Ip6>().GetBorderRoutingCounters().mRsRx++;
846 LogInfo("Received Router Solicitation from %s on %s", aSrcAddress.ToString().AsCString(),
847 mInfraIf.ToString().AsCString());
848
849 ScheduleRoutingPolicyEvaluation(kToReplyToRs);
850 }
851
HandleNeighborAdvertisement(const InfraIf::Icmp6Packet & aPacket)852 void RoutingManager::HandleNeighborAdvertisement(const InfraIf::Icmp6Packet &aPacket)
853 {
854 const Ip6::Nd::NeighborAdvertMessage *naMsg;
855
856 VerifyOrExit(aPacket.GetLength() >= sizeof(naMsg));
857 naMsg = reinterpret_cast<const Ip6::Nd::NeighborAdvertMessage *>(aPacket.GetBytes());
858
859 mDiscoveredPrefixTable.ProcessNeighborAdvertMessage(*naMsg);
860
861 exit:
862 return;
863 }
864
HandleRouterAdvertisement(const InfraIf::Icmp6Packet & aPacket,const Ip6::Address & aSrcAddress)865 void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
866 {
867 Ip6::Nd::RouterAdvertMessage routerAdvMessage(aPacket);
868
869 OT_ASSERT(mIsRunning);
870
871 VerifyOrExit(routerAdvMessage.IsValid());
872
873 Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaRx++;
874 LogInfo("Received Router Advertisement from %s on %s", aSrcAddress.ToString().AsCString(),
875 mInfraIf.ToString().AsCString());
876 DumpDebg("[BR-CERT] direction=recv | type=RA |", aPacket.GetBytes(), aPacket.GetLength());
877
878 mDiscoveredPrefixTable.ProcessRouterAdvertMessage(routerAdvMessage, aSrcAddress);
879
880 // Remember the header and parameters of RA messages which are
881 // initiated from the infra interface.
882 if (mInfraIf.HasAddress(aSrcAddress))
883 {
884 UpdateRouterAdvertHeader(&routerAdvMessage);
885 }
886
887 exit:
888 return;
889 }
890
ShouldProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption & aPio,const Ip6::Prefix & aPrefix)891 bool RoutingManager::ShouldProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, const Ip6::Prefix &aPrefix)
892 {
893 // Indicate whether to process or skip a given prefix
894 // from a PIO (from received RA message).
895
896 bool shouldProcess = false;
897
898 VerifyOrExit(mIsRunning);
899
900 if (!IsValidOnLinkPrefix(aPio))
901 {
902 LogInfo("Ignore invalid on-link prefix in PIO: %s", aPrefix.ToString().AsCString());
903 ExitNow();
904 }
905
906 if (mOnLinkPrefixManager.IsPublishingOrAdvertising())
907 {
908 VerifyOrExit(aPrefix != mOnLinkPrefixManager.GetLocalPrefix());
909 }
910
911 shouldProcess = true;
912
913 exit:
914 return shouldProcess;
915 }
916
ShouldProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption & aRio,const Ip6::Prefix & aPrefix)917 bool RoutingManager::ShouldProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, const Ip6::Prefix &aPrefix)
918 {
919 // Indicate whether to process or skip a given prefix
920 // from a RIO (from received RA message).
921
922 OT_UNUSED_VARIABLE(aRio);
923
924 bool shouldProcess = false;
925
926 VerifyOrExit(mIsRunning);
927
928 if (aPrefix.GetLength() == 0)
929 {
930 // Always process default route ::/0 prefix.
931 ExitNow(shouldProcess = true);
932 }
933
934 if (!IsValidOmrPrefix(aPrefix))
935 {
936 LogInfo("Ignore RIO prefix %s since not a valid OMR prefix", aPrefix.ToString().AsCString());
937 ExitNow();
938 }
939
940 VerifyOrExit(mOmrPrefixManager.GetLocalPrefix().GetPrefix() != aPrefix);
941
942 // Ignore OMR prefixes advertised by ourselves or in current Thread Network Data.
943 // The `mAdvertisedPrefixes` and the OMR prefix set in Network Data should eventually
944 // be equal, but there is time that they are not synchronized immediately:
945 // 1. Network Data could contain more OMR prefixes than `mAdvertisedPrefixes` because
946 // we added random delay before Evaluating routing policy when Network Data is changed.
947 // 2. `mAdvertisedPrefixes` could contain more OMR prefixes than Network Data because
948 // it takes time to sync a new OMR prefix into Network Data (multicast loopback RA
949 // messages are usually faster than Thread Network Data propagation).
950 // They are the reasons why we need both the checks.
951
952 VerifyOrExit(!mAdvertisedPrefixes.Contains(aPrefix));
953 VerifyOrExit(!Get<RoutingManager>().NetworkDataContainsOmrPrefix(aPrefix));
954
955 shouldProcess = true;
956
957 exit:
958 return shouldProcess;
959 }
960
HandleDiscoveredPrefixTableChanged(void)961 void RoutingManager::HandleDiscoveredPrefixTableChanged(void)
962 {
963 // This is a callback from `mDiscoveredPrefixTable` indicating that
964 // there has been a change in the table.
965
966 VerifyOrExit(mIsRunning);
967
968 ResetDiscoveredPrefixStaleTimer();
969 mOnLinkPrefixManager.HandleDiscoveredPrefixTableChanged();
970 mRoutePublisher.Evaluate();
971
972 exit:
973 return;
974 }
975
NetworkDataContainsOmrPrefix(const Ip6::Prefix & aPrefix) const976 bool RoutingManager::NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) const
977 {
978 NetworkData::Iterator iterator = NetworkData::kIteratorInit;
979 NetworkData::OnMeshPrefixConfig onMeshPrefixConfig;
980 bool contains = false;
981
982 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, onMeshPrefixConfig) == kErrorNone)
983 {
984 if (IsValidOmrPrefix(onMeshPrefixConfig) && onMeshPrefixConfig.GetPrefix() == aPrefix)
985 {
986 contains = true;
987 break;
988 }
989 }
990
991 return contains;
992 }
993
NetworkDataContainsUlaRoute(void) const994 bool RoutingManager::NetworkDataContainsUlaRoute(void) const
995 {
996 // Determine whether leader Network Data contains a route
997 // prefix which is either the ULA prefix `fc00::/7` or
998 // a sub-prefix of it (e.g., default route).
999
1000 NetworkData::Iterator iterator = NetworkData::kIteratorInit;
1001 NetworkData::ExternalRouteConfig routeConfig;
1002 bool contains = false;
1003
1004 while (Get<NetworkData::Leader>().GetNextExternalRoute(iterator, routeConfig) == kErrorNone)
1005 {
1006 if (routeConfig.mStable && RoutePublisher::GetUlaPrefix().ContainsPrefix(routeConfig.GetPrefix()))
1007 {
1008 contains = true;
1009 break;
1010 }
1011 }
1012
1013 return contains;
1014 }
1015
UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage * aRouterAdvertMessage)1016 void RoutingManager::UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage)
1017 {
1018 // Updates the `mRaInfo` from the given RA message.
1019
1020 Ip6::Nd::RouterAdvertMessage::Header oldHeader;
1021
1022 if (aRouterAdvertMessage != nullptr)
1023 {
1024 // We skip and do not update RA header if the received RA message
1025 // was not prepared and sent by `RoutingManager` itself.
1026
1027 VerifyOrExit(!IsReceivedRouterAdvertFromManager(*aRouterAdvertMessage));
1028 }
1029
1030 oldHeader = mRaInfo.mHeader;
1031 mRaInfo.mHeaderUpdateTime = TimerMilli::GetNow();
1032
1033 if (aRouterAdvertMessage == nullptr || aRouterAdvertMessage->GetHeader().GetRouterLifetime() == 0)
1034 {
1035 mRaInfo.mHeader.SetToDefault();
1036 mRaInfo.mIsHeaderFromHost = false;
1037 }
1038 else
1039 {
1040 // The checksum is set to zero in `mRaInfo.mHeader`
1041 // which indicates to platform that it needs to do the
1042 // calculation and update it.
1043
1044 mRaInfo.mHeader = aRouterAdvertMessage->GetHeader();
1045 mRaInfo.mHeader.SetChecksum(0);
1046 mRaInfo.mIsHeaderFromHost = true;
1047 }
1048
1049 ResetDiscoveredPrefixStaleTimer();
1050
1051 if (mRaInfo.mHeader != oldHeader)
1052 {
1053 // If there was a change to the header, start timer to
1054 // reevaluate routing policy and send RA message with new
1055 // header.
1056
1057 ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
1058 }
1059
1060 exit:
1061 return;
1062 }
1063
ResetDiscoveredPrefixStaleTimer(void)1064 void RoutingManager::ResetDiscoveredPrefixStaleTimer(void)
1065 {
1066 TimeMilli now = TimerMilli::GetNow();
1067 TimeMilli nextStaleTime;
1068
1069 OT_ASSERT(mIsRunning);
1070
1071 // The stale timer triggers sending RS to check the state of
1072 // discovered prefixes and host RA messages.
1073
1074 nextStaleTime = mDiscoveredPrefixTable.CalculateNextStaleTime(now);
1075
1076 // Check for stale Router Advertisement Message if learnt from Host.
1077 if (mRaInfo.mIsHeaderFromHost)
1078 {
1079 TimeMilli raStaleTime = Max(now, mRaInfo.mHeaderUpdateTime + Time::SecToMsec(kRtrAdvStaleTime));
1080
1081 nextStaleTime = Min(nextStaleTime, raStaleTime);
1082 }
1083
1084 if (nextStaleTime == now.GetDistantFuture())
1085 {
1086 if (mDiscoveredPrefixStaleTimer.IsRunning())
1087 {
1088 LogDebg("Prefix stale timer stopped");
1089 }
1090
1091 mDiscoveredPrefixStaleTimer.Stop();
1092 }
1093 else
1094 {
1095 mDiscoveredPrefixStaleTimer.FireAt(nextStaleTime);
1096 LogDebg("Prefix stale timer scheduled in %lu ms", ToUlong(nextStaleTime - now));
1097 }
1098 }
1099
1100 //---------------------------------------------------------------------------------------------------------------------
1101 // DiscoveredPrefixTable
1102
DiscoveredPrefixTable(Instance & aInstance)1103 RoutingManager::DiscoveredPrefixTable::DiscoveredPrefixTable(Instance &aInstance)
1104 : InstanceLocator(aInstance)
1105 , mEntryTimer(aInstance)
1106 , mRouterTimer(aInstance)
1107 , mSignalTask(aInstance)
1108 {
1109 }
1110
ProcessRouterAdvertMessage(const Ip6::Nd::RouterAdvertMessage & aRaMessage,const Ip6::Address & aSrcAddress)1111 void RoutingManager::DiscoveredPrefixTable::ProcessRouterAdvertMessage(const Ip6::Nd::RouterAdvertMessage &aRaMessage,
1112 const Ip6::Address &aSrcAddress)
1113 {
1114 // Process a received RA message and update the prefix table.
1115
1116 Router *router = mRouters.FindMatching(aSrcAddress);
1117
1118 if (router == nullptr)
1119 {
1120 router = mRouters.PushBack();
1121
1122 if (router == nullptr)
1123 {
1124 LogWarn("Received RA from too many routers, ignore RA from %s", aSrcAddress.ToString().AsCString());
1125 ExitNow();
1126 }
1127
1128 router->mAddress = aSrcAddress;
1129 router->mEntries.Clear();
1130 }
1131
1132 // RA message can indicate router provides default route in the RA
1133 // message header and can also include an RIO for `::/0`. When
1134 // processing an RA message, the preference and lifetime values
1135 // in a `::/0` RIO override the preference and lifetime values in
1136 // the RA header (per RFC 4191 section 3.1).
1137
1138 ProcessDefaultRoute(aRaMessage.GetHeader(), *router);
1139
1140 for (const Ip6::Nd::Option &option : aRaMessage)
1141 {
1142 switch (option.GetType())
1143 {
1144 case Ip6::Nd::Option::kTypePrefixInfo:
1145 ProcessPrefixInfoOption(static_cast<const Ip6::Nd::PrefixInfoOption &>(option), *router);
1146 break;
1147
1148 case Ip6::Nd::Option::kTypeRouteInfo:
1149 ProcessRouteInfoOption(static_cast<const Ip6::Nd::RouteInfoOption &>(option), *router);
1150 break;
1151
1152 default:
1153 break;
1154 }
1155 }
1156
1157 UpdateRouterOnRx(*router);
1158
1159 RemoveRoutersWithNoEntries();
1160
1161 exit:
1162 return;
1163 }
1164
ProcessDefaultRoute(const Ip6::Nd::RouterAdvertMessage::Header & aRaHeader,Router & aRouter)1165 void RoutingManager::DiscoveredPrefixTable::ProcessDefaultRoute(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader,
1166 Router &aRouter)
1167 {
1168 Entry *entry;
1169 Ip6::Prefix prefix;
1170
1171 prefix.Clear();
1172 entry = aRouter.mEntries.FindMatching(Entry::Matcher(prefix, Entry::kTypeRoute));
1173
1174 if (entry == nullptr)
1175 {
1176 VerifyOrExit(aRaHeader.GetRouterLifetime() != 0);
1177
1178 entry = AllocateEntry();
1179
1180 if (entry == nullptr)
1181 {
1182 LogWarn("Discovered too many prefixes, ignore default route from RA header");
1183 ExitNow();
1184 }
1185
1186 entry->SetFrom(aRaHeader);
1187 aRouter.mEntries.Push(*entry);
1188 }
1189 else
1190 {
1191 entry->SetFrom(aRaHeader);
1192 }
1193
1194 mEntryTimer.FireAtIfEarlier(entry->GetExpireTime());
1195
1196 SignalTableChanged();
1197
1198 exit:
1199 return;
1200 }
1201
ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption & aPio,Router & aRouter)1202 void RoutingManager::DiscoveredPrefixTable::ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio,
1203 Router &aRouter)
1204 {
1205 Ip6::Prefix prefix;
1206 Entry *entry;
1207
1208 VerifyOrExit(aPio.IsValid());
1209 aPio.GetPrefix(prefix);
1210
1211 VerifyOrExit(Get<RoutingManager>().ShouldProcessPrefixInfoOption(aPio, prefix));
1212
1213 LogInfo("Processing PIO (%s, %lu seconds)", prefix.ToString().AsCString(), ToUlong(aPio.GetValidLifetime()));
1214
1215 entry = aRouter.mEntries.FindMatching(Entry::Matcher(prefix, Entry::kTypeOnLink));
1216
1217 if (entry == nullptr)
1218 {
1219 VerifyOrExit(aPio.GetValidLifetime() != 0);
1220
1221 entry = AllocateEntry();
1222
1223 if (entry == nullptr)
1224 {
1225 LogWarn("Discovered too many prefixes, ignore on-link prefix %s", prefix.ToString().AsCString());
1226 ExitNow();
1227 }
1228
1229 entry->SetFrom(aPio);
1230 aRouter.mEntries.Push(*entry);
1231 }
1232 else
1233 {
1234 Entry newEntry;
1235
1236 newEntry.SetFrom(aPio);
1237 entry->AdoptValidAndPreferredLifetimesFrom(newEntry);
1238 }
1239
1240 mEntryTimer.FireAtIfEarlier(entry->GetExpireTime());
1241
1242 SignalTableChanged();
1243
1244 exit:
1245 return;
1246 }
1247
ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption & aRio,Router & aRouter)1248 void RoutingManager::DiscoveredPrefixTable::ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio,
1249 Router &aRouter)
1250 {
1251 Ip6::Prefix prefix;
1252 Entry *entry;
1253
1254 VerifyOrExit(aRio.IsValid());
1255 aRio.GetPrefix(prefix);
1256
1257 VerifyOrExit(Get<RoutingManager>().ShouldProcessRouteInfoOption(aRio, prefix));
1258
1259 LogInfo("Processing RIO (%s, %lu seconds)", prefix.ToString().AsCString(), ToUlong(aRio.GetRouteLifetime()));
1260
1261 entry = aRouter.mEntries.FindMatching(Entry::Matcher(prefix, Entry::kTypeRoute));
1262
1263 if (entry == nullptr)
1264 {
1265 VerifyOrExit(aRio.GetRouteLifetime() != 0);
1266
1267 entry = AllocateEntry();
1268
1269 if (entry == nullptr)
1270 {
1271 LogWarn("Discovered too many prefixes, ignore route prefix %s", prefix.ToString().AsCString());
1272 ExitNow();
1273 }
1274
1275 entry->SetFrom(aRio);
1276 aRouter.mEntries.Push(*entry);
1277 }
1278 else
1279 {
1280 entry->SetFrom(aRio);
1281 }
1282
1283 mEntryTimer.FireAtIfEarlier(entry->GetExpireTime());
1284
1285 SignalTableChanged();
1286
1287 exit:
1288 return;
1289 }
1290
Contains(const Entry::Checker & aChecker) const1291 bool RoutingManager::DiscoveredPrefixTable::Contains(const Entry::Checker &aChecker) const
1292 {
1293 bool contains = false;
1294
1295 for (const Router &router : mRouters)
1296 {
1297 if (router.mEntries.ContainsMatching(aChecker))
1298 {
1299 contains = true;
1300 break;
1301 }
1302 }
1303
1304 return contains;
1305 }
1306
ContainsDefaultOrNonUlaRoutePrefix(void) const1307 bool RoutingManager::DiscoveredPrefixTable::ContainsDefaultOrNonUlaRoutePrefix(void) const
1308 {
1309 return Contains(Entry::Checker(Entry::Checker::kIsNotUla, Entry::kTypeRoute));
1310 }
1311
ContainsNonUlaOnLinkPrefix(void) const1312 bool RoutingManager::DiscoveredPrefixTable::ContainsNonUlaOnLinkPrefix(void) const
1313 {
1314 return Contains(Entry::Checker(Entry::Checker::kIsNotUla, Entry::kTypeOnLink));
1315 }
1316
ContainsUlaOnLinkPrefix(void) const1317 bool RoutingManager::DiscoveredPrefixTable::ContainsUlaOnLinkPrefix(void) const
1318 {
1319 return Contains(Entry::Checker(Entry::Checker::kIsUla, Entry::kTypeOnLink));
1320 }
1321
FindFavoredOnLinkPrefix(Ip6::Prefix & aPrefix) const1322 void RoutingManager::DiscoveredPrefixTable::FindFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const
1323 {
1324 // Find the smallest preferred on-link prefix entry in the table
1325 // and return it in `aPrefix`. If there is none, `aPrefix` is
1326 // cleared (prefix length is set to zero).
1327
1328 aPrefix.Clear();
1329
1330 for (const Router &router : mRouters)
1331 {
1332 for (const Entry &entry : router.mEntries)
1333 {
1334 if (!entry.IsOnLinkPrefix() || entry.IsDeprecated())
1335 {
1336 continue;
1337 }
1338
1339 if ((aPrefix.GetLength() == 0) || (entry.GetPrefix() < aPrefix))
1340 {
1341 aPrefix = entry.GetPrefix();
1342 }
1343 }
1344 }
1345 }
1346
RemoveOnLinkPrefix(const Ip6::Prefix & aPrefix)1347 void RoutingManager::DiscoveredPrefixTable::RemoveOnLinkPrefix(const Ip6::Prefix &aPrefix)
1348 {
1349 RemovePrefix(Entry::Matcher(aPrefix, Entry::kTypeOnLink));
1350 }
1351
RemoveRoutePrefix(const Ip6::Prefix & aPrefix)1352 void RoutingManager::DiscoveredPrefixTable::RemoveRoutePrefix(const Ip6::Prefix &aPrefix)
1353 {
1354 RemovePrefix(Entry::Matcher(aPrefix, Entry::kTypeRoute));
1355 }
1356
RemovePrefix(const Entry::Matcher & aMatcher)1357 void RoutingManager::DiscoveredPrefixTable::RemovePrefix(const Entry::Matcher &aMatcher)
1358 {
1359 // Removes all entries matching a given prefix from the table.
1360
1361 LinkedList<Entry> removedEntries;
1362
1363 for (Router &router : mRouters)
1364 {
1365 router.mEntries.RemoveAllMatching(aMatcher, removedEntries);
1366 }
1367
1368 VerifyOrExit(!removedEntries.IsEmpty());
1369
1370 FreeEntries(removedEntries);
1371 RemoveRoutersWithNoEntries();
1372
1373 SignalTableChanged();
1374
1375 exit:
1376 return;
1377 }
1378
RemoveAllEntries(void)1379 void RoutingManager::DiscoveredPrefixTable::RemoveAllEntries(void)
1380 {
1381 // Remove all entries from the table and unpublish them
1382 // from Network Data.
1383
1384 for (Router &router : mRouters)
1385 {
1386 Entry *entry;
1387
1388 while ((entry = router.mEntries.Pop()) != nullptr)
1389 {
1390 FreeEntry(*entry);
1391 SignalTableChanged();
1392 }
1393 }
1394
1395 RemoveRoutersWithNoEntries();
1396 mEntryTimer.Stop();
1397 }
1398
RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold)1399 void RoutingManager::DiscoveredPrefixTable::RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold)
1400 {
1401 // Remove route prefix entries and deprecate on-link entries in
1402 // the table that are old (not updated since `aTimeThreshold`).
1403
1404 for (Router &router : mRouters)
1405 {
1406 for (Entry &entry : router.mEntries)
1407 {
1408 if (entry.GetLastUpdateTime() <= aTimeThreshold)
1409 {
1410 if (entry.IsOnLinkPrefix())
1411 {
1412 entry.ClearPreferredLifetime();
1413 }
1414 else
1415 {
1416 entry.ClearValidLifetime();
1417 }
1418
1419 SignalTableChanged();
1420 }
1421 }
1422 }
1423
1424 RemoveExpiredEntries();
1425 }
1426
RemoveOrDeprecateEntriesFromInactiveRouters(void)1427 void RoutingManager::DiscoveredPrefixTable::RemoveOrDeprecateEntriesFromInactiveRouters(void)
1428 {
1429 // Remove route prefix entries and deprecate on-link prefix entries
1430 // in the table for routers that have reached the max NS probe
1431 // attempts and considered as inactive.
1432
1433 for (Router &router : mRouters)
1434 {
1435 if (router.mNsProbeCount <= Router::kMaxNsProbes)
1436 {
1437 continue;
1438 }
1439
1440 for (Entry &entry : router.mEntries)
1441 {
1442 if (entry.IsOnLinkPrefix() && !entry.IsDeprecated())
1443 {
1444 entry.ClearPreferredLifetime();
1445 SignalTableChanged();
1446 }
1447 else
1448 {
1449 entry.ClearValidLifetime();
1450 }
1451 }
1452 }
1453
1454 RemoveExpiredEntries();
1455 }
1456
CalculateNextStaleTime(TimeMilli aNow) const1457 TimeMilli RoutingManager::DiscoveredPrefixTable::CalculateNextStaleTime(TimeMilli aNow) const
1458 {
1459 TimeMilli onLinkStaleTime = aNow;
1460 TimeMilli routeStaleTime = aNow.GetDistantFuture();
1461 bool foundOnLink = false;
1462
1463 // For on-link prefixes, we consider stale time as when all on-link
1464 // prefixes become stale (the latest stale time) but for route
1465 // prefixes we consider the earliest stale time.
1466
1467 for (const Router &router : mRouters)
1468 {
1469 for (const Entry &entry : router.mEntries)
1470 {
1471 TimeMilli entryStaleTime = Max(aNow, entry.GetStaleTime());
1472
1473 if (entry.IsOnLinkPrefix() && !entry.IsDeprecated())
1474 {
1475 onLinkStaleTime = Max(onLinkStaleTime, entryStaleTime);
1476 foundOnLink = true;
1477 }
1478
1479 if (!entry.IsOnLinkPrefix())
1480 {
1481 routeStaleTime = Min(routeStaleTime, entryStaleTime);
1482 }
1483 }
1484 }
1485
1486 return foundOnLink ? Min(onLinkStaleTime, routeStaleTime) : routeStaleTime;
1487 }
1488
RemoveRoutersWithNoEntries(void)1489 void RoutingManager::DiscoveredPrefixTable::RemoveRoutersWithNoEntries(void)
1490 {
1491 mRouters.RemoveAllMatching(Router::kContainsNoEntries);
1492 }
1493
FreeEntries(LinkedList<Entry> & aEntries)1494 void RoutingManager::DiscoveredPrefixTable::FreeEntries(LinkedList<Entry> &aEntries)
1495 {
1496 // Frees all entries in the given list `aEntries` (put them back
1497 // in the entry pool).
1498
1499 Entry *entry;
1500
1501 while ((entry = aEntries.Pop()) != nullptr)
1502 {
1503 FreeEntry(*entry);
1504 }
1505 }
1506
FindFavoredEntryToPublish(const Ip6::Prefix & aPrefix) const1507 const RoutingManager::DiscoveredPrefixTable::Entry *RoutingManager::DiscoveredPrefixTable::FindFavoredEntryToPublish(
1508 const Ip6::Prefix &aPrefix) const
1509 {
1510 // Finds the favored entry matching a given `aPrefix` in the table
1511 // to publish in the Network Data. We can have multiple entries
1512 // in the table matching the same `aPrefix` from different
1513 // routers and potentially with different preference values. We
1514 // select the one with the highest preference as the favored
1515 // entry to publish.
1516
1517 const Entry *favoredEntry = nullptr;
1518
1519 for (const Router &router : mRouters)
1520 {
1521 for (const Entry &entry : router.mEntries)
1522 {
1523 if (entry.GetPrefix() != aPrefix)
1524 {
1525 continue;
1526 }
1527
1528 if ((favoredEntry == nullptr) || (entry.GetPreference() > favoredEntry->GetPreference()))
1529 {
1530 favoredEntry = &entry;
1531 }
1532 }
1533 }
1534
1535 return favoredEntry;
1536 }
1537
HandleEntryTimer(void)1538 void RoutingManager::DiscoveredPrefixTable::HandleEntryTimer(void) { RemoveExpiredEntries(); }
1539
RemoveExpiredEntries(void)1540 void RoutingManager::DiscoveredPrefixTable::RemoveExpiredEntries(void)
1541 {
1542 TimeMilli now = TimerMilli::GetNow();
1543 TimeMilli nextExpireTime = now.GetDistantFuture();
1544 LinkedList<Entry> expiredEntries;
1545
1546 for (Router &router : mRouters)
1547 {
1548 router.mEntries.RemoveAllMatching(Entry::ExpirationChecker(now), expiredEntries);
1549 }
1550
1551 RemoveRoutersWithNoEntries();
1552
1553 if (!expiredEntries.IsEmpty())
1554 {
1555 SignalTableChanged();
1556 }
1557
1558 FreeEntries(expiredEntries);
1559
1560 // Determine the next expire time and schedule timer.
1561
1562 for (const Router &router : mRouters)
1563 {
1564 for (const Entry &entry : router.mEntries)
1565 {
1566 nextExpireTime = Min(nextExpireTime, entry.GetExpireTime());
1567 }
1568 }
1569
1570 if (nextExpireTime != now.GetDistantFuture())
1571 {
1572 mEntryTimer.FireAt(nextExpireTime);
1573 }
1574 }
1575
SignalTableChanged(void)1576 void RoutingManager::DiscoveredPrefixTable::SignalTableChanged(void) { mSignalTask.Post(); }
1577
ProcessNeighborAdvertMessage(const Ip6::Nd::NeighborAdvertMessage & aNaMessage)1578 void RoutingManager::DiscoveredPrefixTable::ProcessNeighborAdvertMessage(
1579 const Ip6::Nd::NeighborAdvertMessage &aNaMessage)
1580 {
1581 Router *router;
1582
1583 VerifyOrExit(aNaMessage.IsValid());
1584
1585 router = mRouters.FindMatching(aNaMessage.GetTargetAddress());
1586 VerifyOrExit(router != nullptr);
1587
1588 LogInfo("Received NA from router %s", router->mAddress.ToString().AsCString());
1589
1590 UpdateRouterOnRx(*router);
1591
1592 exit:
1593 return;
1594 }
1595
UpdateRouterOnRx(Router & aRouter)1596 void RoutingManager::DiscoveredPrefixTable::UpdateRouterOnRx(Router &aRouter)
1597 {
1598 aRouter.mNsProbeCount = 0;
1599 aRouter.mTimeout = TimerMilli::GetNow() + Random::NonCrypto::AddJitter(Router::kActiveTimeout, Router::kJitter);
1600
1601 mRouterTimer.FireAtIfEarlier(aRouter.mTimeout);
1602 }
1603
HandleRouterTimer(void)1604 void RoutingManager::DiscoveredPrefixTable::HandleRouterTimer(void)
1605 {
1606 TimeMilli now = TimerMilli::GetNow();
1607 TimeMilli nextTime = now.GetDistantFuture();
1608
1609 for (Router &router : mRouters)
1610 {
1611 if (router.mNsProbeCount > Router::kMaxNsProbes)
1612 {
1613 continue;
1614 }
1615
1616 // If the `router` emitting RA has an address belonging to
1617 // infra interface, it indicates that the RAs are from
1618 // same device. In this case we skip performing NS probes.
1619 // This addresses situation where platform may not be
1620 // be able to receive and pass the NA message response
1621 // from device itself.
1622
1623 if (Get<RoutingManager>().mInfraIf.HasAddress(router.mAddress))
1624 {
1625 continue;
1626 }
1627
1628 if (router.mTimeout <= now)
1629 {
1630 router.mNsProbeCount++;
1631
1632 if (router.mNsProbeCount > Router::kMaxNsProbes)
1633 {
1634 LogInfo("No response to all Neighbor Solicitations attempts from router %s",
1635 router.mAddress.ToString().AsCString());
1636 continue;
1637 }
1638
1639 router.mTimeout = now + ((router.mNsProbeCount < Router::kMaxNsProbes) ? Router::kNsProbeRetryInterval
1640 : Router::kNsProbeTimeout);
1641
1642 SendNeighborSolicitToRouter(router);
1643 }
1644
1645 nextTime = Min(nextTime, router.mTimeout);
1646 }
1647
1648 RemoveOrDeprecateEntriesFromInactiveRouters();
1649
1650 if (nextTime != now.GetDistantFuture())
1651 {
1652 mRouterTimer.FireAtIfEarlier(nextTime);
1653 }
1654 }
1655
SendNeighborSolicitToRouter(const Router & aRouter)1656 void RoutingManager::DiscoveredPrefixTable::SendNeighborSolicitToRouter(const Router &aRouter)
1657 {
1658 InfraIf::Icmp6Packet packet;
1659 Ip6::Nd::NeighborSolicitMessage neighborSolicitMsg;
1660
1661 VerifyOrExit(!Get<RoutingManager>().mRsSender.IsInProgress());
1662
1663 neighborSolicitMsg.SetTargetAddress(aRouter.mAddress);
1664 packet.InitFrom(neighborSolicitMsg);
1665
1666 IgnoreError(Get<RoutingManager>().mInfraIf.Send(packet, aRouter.mAddress));
1667
1668 LogInfo("Sent Neighbor Solicitation to %s - attempt:%u/%u", aRouter.mAddress.ToString().AsCString(),
1669 aRouter.mNsProbeCount, Router::kMaxNsProbes);
1670
1671 exit:
1672 return;
1673 }
1674
InitIterator(PrefixTableIterator & aIterator) const1675 void RoutingManager::DiscoveredPrefixTable::InitIterator(PrefixTableIterator &aIterator) const
1676 {
1677 Iterator &iterator = static_cast<Iterator &>(aIterator);
1678
1679 iterator.SetInitTime();
1680 iterator.SetRouter(mRouters.Front());
1681 iterator.SetEntry(mRouters.IsEmpty() ? nullptr : mRouters[0].mEntries.GetHead());
1682 }
1683
GetNextEntry(PrefixTableIterator & aIterator,PrefixTableEntry & aEntry) const1684 Error RoutingManager::DiscoveredPrefixTable::GetNextEntry(PrefixTableIterator &aIterator,
1685 PrefixTableEntry &aEntry) const
1686 {
1687 Error error = kErrorNone;
1688 Iterator &iterator = static_cast<Iterator &>(aIterator);
1689
1690 VerifyOrExit(iterator.GetRouter() != nullptr, error = kErrorNotFound);
1691 OT_ASSERT(iterator.GetEntry() != nullptr);
1692
1693 aEntry.mRouterAddress = iterator.GetRouter()->mAddress;
1694 aEntry.mPrefix = iterator.GetEntry()->GetPrefix();
1695 aEntry.mIsOnLink = iterator.GetEntry()->IsOnLinkPrefix();
1696 aEntry.mMsecSinceLastUpdate = iterator.GetInitTime() - iterator.GetEntry()->GetLastUpdateTime();
1697 aEntry.mValidLifetime = iterator.GetEntry()->GetValidLifetime();
1698 aEntry.mPreferredLifetime = aEntry.mIsOnLink ? iterator.GetEntry()->GetPreferredLifetime() : 0;
1699 aEntry.mRoutePreference =
1700 static_cast<otRoutePreference>(aEntry.mIsOnLink ? 0 : iterator.GetEntry()->GetRoutePreference());
1701
1702 // Advance the iterator
1703 iterator.SetEntry(iterator.GetEntry()->GetNext());
1704
1705 if (iterator.GetEntry() == nullptr)
1706 {
1707 if (iterator.GetRouter() != mRouters.Back())
1708 {
1709 iterator.SetRouter(iterator.GetRouter() + 1);
1710 iterator.SetEntry(iterator.GetRouter()->mEntries.GetHead());
1711 }
1712 else
1713 {
1714 iterator.SetRouter(nullptr);
1715 }
1716 }
1717
1718 exit:
1719 return error;
1720 }
1721
1722 //---------------------------------------------------------------------------------------------------------------------
1723 // DiscoveredPrefixTable::Entry
1724
SetFrom(const Ip6::Nd::RouterAdvertMessage::Header & aRaHeader)1725 void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader)
1726 {
1727 mPrefix.Clear();
1728 mType = kTypeRoute;
1729 mValidLifetime = aRaHeader.GetRouterLifetime();
1730 mShared.mRoutePreference = aRaHeader.GetDefaultRouterPreference();
1731 mLastUpdateTime = TimerMilli::GetNow();
1732 }
1733
SetFrom(const Ip6::Nd::PrefixInfoOption & aPio)1734 void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::PrefixInfoOption &aPio)
1735 {
1736 aPio.GetPrefix(mPrefix);
1737 mType = kTypeOnLink;
1738 mValidLifetime = aPio.GetValidLifetime();
1739 mShared.mPreferredLifetime = aPio.GetPreferredLifetime();
1740 mLastUpdateTime = TimerMilli::GetNow();
1741 }
1742
SetFrom(const Ip6::Nd::RouteInfoOption & aRio)1743 void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::RouteInfoOption &aRio)
1744 {
1745 aRio.GetPrefix(mPrefix);
1746 mType = kTypeRoute;
1747 mValidLifetime = aRio.GetRouteLifetime();
1748 mShared.mRoutePreference = aRio.GetPreference();
1749 mLastUpdateTime = TimerMilli::GetNow();
1750 }
1751
operator ==(const Entry & aOther) const1752 bool RoutingManager::DiscoveredPrefixTable::Entry::operator==(const Entry &aOther) const
1753 {
1754 return (mType == aOther.mType) && (mPrefix == aOther.mPrefix);
1755 }
1756
Matches(const Matcher & aMatcher) const1757 bool RoutingManager::DiscoveredPrefixTable::Entry::Matches(const Matcher &aMatcher) const
1758 {
1759 return (mType == aMatcher.mType) && (mPrefix == aMatcher.mPrefix);
1760 }
1761
Matches(const Checker & aChecker) const1762 bool RoutingManager::DiscoveredPrefixTable::Entry::Matches(const Checker &aChecker) const
1763 {
1764 return (mType == aChecker.mType) && (mPrefix.IsUniqueLocal() == (aChecker.mMode == Checker::kIsUla));
1765 }
1766
Matches(const ExpirationChecker & aChecker) const1767 bool RoutingManager::DiscoveredPrefixTable::Entry::Matches(const ExpirationChecker &aChecker) const
1768 {
1769 return GetExpireTime() <= aChecker.mNow;
1770 }
1771
GetExpireTime(void) const1772 TimeMilli RoutingManager::DiscoveredPrefixTable::Entry::GetExpireTime(void) const
1773 {
1774 return mLastUpdateTime + CalculateExpireDelay(mValidLifetime);
1775 }
1776
GetStaleTime(void) const1777 TimeMilli RoutingManager::DiscoveredPrefixTable::Entry::GetStaleTime(void) const
1778 {
1779 uint32_t delay = Min(kRtrAdvStaleTime, IsOnLinkPrefix() ? GetPreferredLifetime() : mValidLifetime);
1780
1781 return mLastUpdateTime + TimeMilli::SecToMsec(delay);
1782 }
1783
IsDeprecated(void) const1784 bool RoutingManager::DiscoveredPrefixTable::Entry::IsDeprecated(void) const
1785 {
1786 OT_ASSERT(IsOnLinkPrefix());
1787
1788 return mLastUpdateTime + TimeMilli::SecToMsec(GetPreferredLifetime()) <= TimerMilli::GetNow();
1789 }
1790
GetPreference(void) const1791 RoutingManager::RoutePreference RoutingManager::DiscoveredPrefixTable::Entry::GetPreference(void) const
1792 {
1793 // Returns the preference level to use when we publish
1794 // the prefix entry in Network Data.
1795
1796 return IsOnLinkPrefix() ? NetworkData::kRoutePreferenceMedium : GetRoutePreference();
1797 }
1798
AdoptValidAndPreferredLifetimesFrom(const Entry & aEntry)1799 void RoutingManager::DiscoveredPrefixTable::Entry::AdoptValidAndPreferredLifetimesFrom(const Entry &aEntry)
1800 {
1801 constexpr uint32_t kTwoHoursInSeconds = 2 * 3600;
1802
1803 // Per RFC 4862 section 5.5.3.e:
1804 //
1805 // 1. If the received Valid Lifetime is greater than 2 hours or
1806 // greater than RemainingLifetime, set the valid lifetime of the
1807 // corresponding address to the advertised Valid Lifetime.
1808 // 2. If RemainingLifetime is less than or equal to 2 hours, ignore
1809 // the Prefix Information option with regards to the valid
1810 // lifetime, unless ...
1811 // 3. Otherwise, reset the valid lifetime of the corresponding
1812 // address to 2 hours.
1813
1814 if (aEntry.mValidLifetime > kTwoHoursInSeconds || aEntry.GetExpireTime() > GetExpireTime())
1815 {
1816 mValidLifetime = aEntry.mValidLifetime;
1817 }
1818 else if (GetExpireTime() > TimerMilli::GetNow() + TimeMilli::SecToMsec(kTwoHoursInSeconds))
1819 {
1820 mValidLifetime = kTwoHoursInSeconds;
1821 }
1822
1823 mShared.mPreferredLifetime = aEntry.GetPreferredLifetime();
1824 mLastUpdateTime = aEntry.GetLastUpdateTime();
1825 }
1826
CalculateExpireDelay(uint32_t aValidLifetime)1827 uint32_t RoutingManager::DiscoveredPrefixTable::Entry::CalculateExpireDelay(uint32_t aValidLifetime)
1828 {
1829 uint32_t delay;
1830
1831 if (aValidLifetime * static_cast<uint64_t>(1000) > Timer::kMaxDelay)
1832 {
1833 delay = Timer::kMaxDelay;
1834 }
1835 else
1836 {
1837 delay = aValidLifetime * 1000;
1838 }
1839
1840 return delay;
1841 }
1842
1843 //---------------------------------------------------------------------------------------------------------------------
1844 // FavoredOmrPrefix
1845
IsInfrastructureDerived(void) const1846 bool RoutingManager::FavoredOmrPrefix::IsInfrastructureDerived(void) const
1847 {
1848 // Indicate whether the OMR prefix is infrastructure-derived which
1849 // can be identified as a valid OMR prefix with preference of
1850 // medium or higher.
1851
1852 return !IsEmpty() && (mPreference >= NetworkData::kRoutePreferenceMedium);
1853 }
1854
SetFrom(const NetworkData::OnMeshPrefixConfig & aOnMeshPrefixConfig)1855 void RoutingManager::FavoredOmrPrefix::SetFrom(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig)
1856 {
1857 mPrefix = aOnMeshPrefixConfig.GetPrefix();
1858 mPreference = aOnMeshPrefixConfig.GetPreference();
1859 mIsDomainPrefix = aOnMeshPrefixConfig.mDp;
1860 }
1861
SetFrom(const OmrPrefix & aOmrPrefix)1862 void RoutingManager::FavoredOmrPrefix::SetFrom(const OmrPrefix &aOmrPrefix)
1863 {
1864 mPrefix = aOmrPrefix.GetPrefix();
1865 mPreference = aOmrPrefix.GetPreference();
1866 mIsDomainPrefix = aOmrPrefix.IsDomainPrefix();
1867 }
1868
IsFavoredOver(const NetworkData::OnMeshPrefixConfig & aOmrPrefixConfig) const1869 bool RoutingManager::FavoredOmrPrefix::IsFavoredOver(const NetworkData::OnMeshPrefixConfig &aOmrPrefixConfig) const
1870 {
1871 // This method determines whether this OMR prefix is favored
1872 // over another prefix. A prefix with higher preference is
1873 // favored. If the preference is the same, then the smaller
1874 // prefix (in the sense defined by `Ip6::Prefix`) is favored.
1875
1876 bool isFavored = (mPreference > aOmrPrefixConfig.GetPreference());
1877
1878 OT_ASSERT(IsValidOmrPrefix(aOmrPrefixConfig));
1879
1880 if (mPreference == aOmrPrefixConfig.GetPreference())
1881 {
1882 isFavored = (mPrefix < aOmrPrefixConfig.GetPrefix());
1883 }
1884
1885 return isFavored;
1886 }
1887
1888 //---------------------------------------------------------------------------------------------------------------------
1889 // OmrPrefixManager
1890
OmrPrefixManager(Instance & aInstance)1891 RoutingManager::OmrPrefixManager::OmrPrefixManager(Instance &aInstance)
1892 : InstanceLocator(aInstance)
1893 , mIsLocalAddedInNetData(false)
1894 , mDefaultRoute(false)
1895 {
1896 }
1897
Init(const Ip6::Prefix & aBrUlaPrefix)1898 void RoutingManager::OmrPrefixManager::Init(const Ip6::Prefix &aBrUlaPrefix)
1899 {
1900 mLocalPrefix.mPrefix = aBrUlaPrefix;
1901 mLocalPrefix.mPrefix.SetSubnetId(kOmrPrefixSubnetId);
1902 mLocalPrefix.mPrefix.SetLength(kOmrPrefixLength);
1903 mLocalPrefix.mPreference = NetworkData::kRoutePreferenceLow;
1904 mLocalPrefix.mIsDomainPrefix = false;
1905
1906 LogInfo("Generated local OMR prefix: %s", mLocalPrefix.mPrefix.ToString().AsCString());
1907 }
1908
Start(void)1909 void RoutingManager::OmrPrefixManager::Start(void) { DetermineFavoredPrefix(); }
1910
Stop(void)1911 void RoutingManager::OmrPrefixManager::Stop(void)
1912 {
1913 RemoveLocalFromNetData();
1914 mFavoredPrefix.Clear();
1915 }
1916
DetermineFavoredPrefix(void)1917 void RoutingManager::OmrPrefixManager::DetermineFavoredPrefix(void)
1918 {
1919 // Determine the favored OMR prefix present in Network Data.
1920
1921 NetworkData::Iterator iterator = NetworkData::kIteratorInit;
1922 NetworkData::OnMeshPrefixConfig prefixConfig;
1923
1924 mFavoredPrefix.Clear();
1925
1926 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
1927 {
1928 if (!IsValidOmrPrefix(prefixConfig) || !prefixConfig.mPreferred)
1929 {
1930 continue;
1931 }
1932
1933 if (mFavoredPrefix.IsEmpty() || !mFavoredPrefix.IsFavoredOver(prefixConfig))
1934 {
1935 mFavoredPrefix.SetFrom(prefixConfig);
1936 }
1937 }
1938 }
1939
Evaluate(void)1940 void RoutingManager::OmrPrefixManager::Evaluate(void)
1941 {
1942 OT_ASSERT(Get<RoutingManager>().IsRunning());
1943
1944 DetermineFavoredPrefix();
1945
1946 // Decide if we need to add or remove our local OMR prefix.
1947
1948 if (mFavoredPrefix.IsEmpty())
1949 {
1950 LogInfo("No favored OMR prefix found in Thread network");
1951
1952 // The `mFavoredPrefix` remains empty if we fail to publish
1953 // the local OMR prefix.
1954 SuccessOrExit(AddLocalToNetData());
1955
1956 mFavoredPrefix.SetFrom(mLocalPrefix);
1957 }
1958 else if (mFavoredPrefix.GetPrefix() == mLocalPrefix.GetPrefix())
1959 {
1960 IgnoreError(AddLocalToNetData());
1961 }
1962 else if (mIsLocalAddedInNetData)
1963 {
1964 LogInfo("There is already a favored OMR prefix %s in the Thread network",
1965 mFavoredPrefix.GetPrefix().ToString().AsCString());
1966
1967 RemoveLocalFromNetData();
1968 }
1969
1970 exit:
1971 return;
1972 }
1973
AddLocalToNetData(void)1974 Error RoutingManager::OmrPrefixManager::AddLocalToNetData(void)
1975 {
1976 Error error = kErrorNone;
1977
1978 VerifyOrExit(!mIsLocalAddedInNetData);
1979 SuccessOrExit(error = AddOrUpdateLocalInNetData());
1980 mIsLocalAddedInNetData = true;
1981
1982 exit:
1983 return error;
1984 }
1985
AddOrUpdateLocalInNetData(void)1986 Error RoutingManager::OmrPrefixManager::AddOrUpdateLocalInNetData(void)
1987 {
1988 // Add the local OMR prefix in Thread Network Data or update it
1989 // (e.g., change default route flag) if it is already added.
1990
1991 Error error;
1992 NetworkData::OnMeshPrefixConfig config;
1993
1994 config.Clear();
1995 config.mPrefix = mLocalPrefix.GetPrefix();
1996 config.mStable = true;
1997 config.mSlaac = true;
1998 config.mPreferred = true;
1999 config.mOnMesh = true;
2000 config.mDefaultRoute = mDefaultRoute;
2001 config.mPreference = mLocalPrefix.GetPreference();
2002
2003 error = Get<NetworkData::Local>().AddOnMeshPrefix(config);
2004
2005 if (error != kErrorNone)
2006 {
2007 LogWarn("Failed to %s %s in Thread Network Data: %s", !mIsLocalAddedInNetData ? "add" : "update",
2008 LocalToString().AsCString(), ErrorToString(error));
2009 ExitNow();
2010 }
2011
2012 Get<NetworkData::Notifier>().HandleServerDataUpdated();
2013
2014 LogInfo("%s %s in Thread Network Data", !mIsLocalAddedInNetData ? "Added" : "Updated", LocalToString().AsCString());
2015
2016 exit:
2017 return error;
2018 }
2019
RemoveLocalFromNetData(void)2020 void RoutingManager::OmrPrefixManager::RemoveLocalFromNetData(void)
2021 {
2022 Error error = kErrorNone;
2023
2024 VerifyOrExit(mIsLocalAddedInNetData);
2025
2026 error = Get<NetworkData::Local>().RemoveOnMeshPrefix(mLocalPrefix.GetPrefix());
2027
2028 if (error != kErrorNone)
2029 {
2030 LogWarn("Failed to remove %s from Thread Network Data: %s", LocalToString().AsCString(), ErrorToString(error));
2031 ExitNow();
2032 }
2033
2034 mIsLocalAddedInNetData = false;
2035 Get<NetworkData::Notifier>().HandleServerDataUpdated();
2036 LogInfo("Removed %s from Thread Network Data", LocalToString().AsCString());
2037
2038 exit:
2039 return;
2040 }
2041
UpdateDefaultRouteFlag(bool aDefaultRoute)2042 void RoutingManager::OmrPrefixManager::UpdateDefaultRouteFlag(bool aDefaultRoute)
2043 {
2044 VerifyOrExit(aDefaultRoute != mDefaultRoute);
2045
2046 mDefaultRoute = aDefaultRoute;
2047
2048 VerifyOrExit(mIsLocalAddedInNetData);
2049 IgnoreError(AddOrUpdateLocalInNetData());
2050
2051 exit:
2052 return;
2053 }
2054
LocalToString(void) const2055 RoutingManager::OmrPrefixManager::InfoString RoutingManager::OmrPrefixManager::LocalToString(void) const
2056 {
2057 InfoString string;
2058
2059 string.Append("local OMR prefix %s (def-route:%s)", mLocalPrefix.GetPrefix().ToString().AsCString(),
2060 ToYesNo(mDefaultRoute));
2061 return string;
2062 }
2063
2064 //---------------------------------------------------------------------------------------------------------------------
2065 // OnLinkPrefixManager
2066
OnLinkPrefixManager(Instance & aInstance)2067 RoutingManager::OnLinkPrefixManager::OnLinkPrefixManager(Instance &aInstance)
2068 : InstanceLocator(aInstance)
2069 , mState(kIdle)
2070 , mTimer(aInstance)
2071 {
2072 mLocalPrefix.Clear();
2073 mFavoredDiscoveredPrefix.Clear();
2074 mOldLocalPrefixes.Clear();
2075 }
2076
Init(void)2077 void RoutingManager::OnLinkPrefixManager::Init(void)
2078 {
2079 TimeMilli now = TimerMilli::GetNow();
2080 Settings::BrOnLinkPrefix savedPrefix;
2081 bool refreshStoredPrefixes = false;
2082
2083 // Restore old prefixes from `Settings`
2084
2085 for (int index = 0; Get<Settings>().ReadBrOnLinkPrefix(index, savedPrefix) == kErrorNone; index++)
2086 {
2087 uint32_t lifetime;
2088 OldPrefix *entry;
2089
2090 if (mOldLocalPrefixes.ContainsMatching(savedPrefix.GetPrefix()))
2091 {
2092 // We should not see duplicate entries in `Settings`
2093 // but if we do we refresh the stored prefixes to make
2094 // it consistent.
2095 refreshStoredPrefixes = true;
2096 continue;
2097 }
2098
2099 entry = mOldLocalPrefixes.PushBack();
2100
2101 if (entry == nullptr)
2102 {
2103 // If there are more stored prefixes, we refresh the
2104 // prefixes in `Settings` to remove the ones we cannot
2105 // handle.
2106
2107 refreshStoredPrefixes = true;
2108 break;
2109 }
2110
2111 lifetime = Min(savedPrefix.GetLifetime(), Time::MsecToSec(TimerMilli::kMaxDelay));
2112
2113 entry->mPrefix = savedPrefix.GetPrefix();
2114 entry->mExpireTime = now + Time::SecToMsec(lifetime);
2115
2116 LogInfo("Restored old prefix %s, lifetime:%lu", entry->mPrefix.ToString().AsCString(), ToUlong(lifetime));
2117
2118 mTimer.FireAtIfEarlier(entry->mExpireTime);
2119 }
2120
2121 if (refreshStoredPrefixes)
2122 {
2123 // We clear the entries in `Settings` and re-write the entries
2124 // from `mOldLocalPrefixes` array.
2125
2126 IgnoreError(Get<Settings>().DeleteAllBrOnLinkPrefixes());
2127
2128 for (OldPrefix &oldPrefix : mOldLocalPrefixes)
2129 {
2130 SavePrefix(oldPrefix.mPrefix, oldPrefix.mExpireTime);
2131 }
2132 }
2133
2134 GenerateLocalPrefix();
2135 }
2136
GenerateLocalPrefix(void)2137 void RoutingManager::OnLinkPrefixManager::GenerateLocalPrefix(void)
2138 {
2139 const MeshCoP::ExtendedPanId &extPanId = Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId();
2140 OldPrefix *entry;
2141 Ip6::Prefix oldLocalPrefix = mLocalPrefix;
2142
2143 // Global ID: 40 most significant bits of Extended PAN ID
2144 // Subnet ID: 16 least significant bits of Extended PAN ID
2145
2146 mLocalPrefix.mPrefix.mFields.m8[0] = 0xfd;
2147 memcpy(mLocalPrefix.mPrefix.mFields.m8 + 1, extPanId.m8, 5);
2148 memcpy(mLocalPrefix.mPrefix.mFields.m8 + 6, extPanId.m8 + 6, 2);
2149
2150 mLocalPrefix.SetLength(kOnLinkPrefixLength);
2151
2152 // We ensure that the local prefix did change, since not all the
2153 // bytes in Extended PAN ID are used in derivation of the local prefix.
2154
2155 VerifyOrExit(mLocalPrefix != oldLocalPrefix);
2156
2157 LogNote("Local on-link prefix: %s", mLocalPrefix.ToString().AsCString());
2158
2159 // Check if the new local prefix happens to be in `mOldLocalPrefixes` array.
2160 // If so, we remove it from the array and set `mState` accordingly.
2161
2162 entry = mOldLocalPrefixes.FindMatching(mLocalPrefix);
2163
2164 if (entry != nullptr)
2165 {
2166 mState = kDeprecating;
2167 mExpireTime = entry->mExpireTime;
2168 mOldLocalPrefixes.Remove(*entry);
2169 }
2170 else
2171 {
2172 mState = kIdle;
2173 }
2174
2175 exit:
2176 return;
2177 }
2178
Start(void)2179 void RoutingManager::OnLinkPrefixManager::Start(void) {}
2180
Stop(void)2181 void RoutingManager::OnLinkPrefixManager::Stop(void)
2182 {
2183 mFavoredDiscoveredPrefix.Clear();
2184
2185 switch (mState)
2186 {
2187 case kIdle:
2188 break;
2189
2190 case kPublishing:
2191 case kAdvertising:
2192 case kDeprecating:
2193 mState = kDeprecating;
2194 break;
2195 }
2196 }
2197
Evaluate(void)2198 void RoutingManager::OnLinkPrefixManager::Evaluate(void)
2199 {
2200 VerifyOrExit(!Get<RoutingManager>().mRsSender.IsInProgress());
2201
2202 Get<RoutingManager>().mDiscoveredPrefixTable.FindFavoredOnLinkPrefix(mFavoredDiscoveredPrefix);
2203
2204 if ((mFavoredDiscoveredPrefix.GetLength() == 0) || (mFavoredDiscoveredPrefix == mLocalPrefix))
2205 {
2206 // We need to advertise our local on-link prefix when there is
2207 // no discovered on-link prefix. If the favored discovered
2208 // prefix is the same as our local on-link prefix we also
2209 // start advertising the local prefix to add redundancy. Note
2210 // that local on-link prefix is derived from extended PAN ID
2211 // and therefore is the same for all BRs on the same Thread
2212 // mesh.
2213
2214 PublishAndAdvertise();
2215
2216 // We remove the local on-link prefix from discovered prefix
2217 // table, in case it was previously discovered and included in
2218 // the table (now as a deprecating entry). We remove it with
2219 // `kKeepInNetData` flag to ensure that the prefix is not
2220 // unpublished from network data.
2221 //
2222 // Note that `ShouldProcessPrefixInfoOption()` will also check
2223 // not allow the local on-link prefix to be added in the prefix
2224 // table while we are advertising it.
2225
2226 Get<RoutingManager>().mDiscoveredPrefixTable.RemoveOnLinkPrefix(mLocalPrefix);
2227
2228 mFavoredDiscoveredPrefix.Clear();
2229 }
2230 else if (IsPublishingOrAdvertising())
2231 {
2232 // When an application-specific on-link prefix is received and
2233 // it is larger than the local prefix, we will not remove the
2234 // advertised local prefix. In this case, there will be two
2235 // on-link prefixes on the infra link. But all BRs will still
2236 // converge to the same smallest/favored on-link prefix and the
2237 // application-specific prefix is not used.
2238
2239 if (!(mLocalPrefix < mFavoredDiscoveredPrefix))
2240 {
2241 LogInfo("EvaluateOnLinkPrefix: There is already favored on-link prefix %s",
2242 mFavoredDiscoveredPrefix.ToString().AsCString());
2243 Deprecate();
2244 }
2245 }
2246
2247 exit:
2248 return;
2249 }
2250
IsInitalEvaluationDone(void) const2251 bool RoutingManager::OnLinkPrefixManager::IsInitalEvaluationDone(void) const
2252 {
2253 // This method indicates whether or not we are done with the
2254 // initial policy evaluation of the on-link prefixes, i.e., either
2255 // we have discovered a favored on-link prefix (being advertised by
2256 // another router on infra link) or we are advertising our local
2257 // on-link prefix.
2258
2259 return (mFavoredDiscoveredPrefix.GetLength() != 0 || IsPublishingOrAdvertising());
2260 }
2261
HandleDiscoveredPrefixTableChanged(void)2262 void RoutingManager::OnLinkPrefixManager::HandleDiscoveredPrefixTableChanged(void)
2263 {
2264 // This is a callback from `mDiscoveredPrefixTable` indicating that
2265 // there has been a change in the table. If the favored on-link
2266 // prefix has changed, we trigger a re-evaluation of the routing
2267 // policy.
2268
2269 Ip6::Prefix newFavoredPrefix;
2270
2271 Get<RoutingManager>().mDiscoveredPrefixTable.FindFavoredOnLinkPrefix(newFavoredPrefix);
2272
2273 if (newFavoredPrefix != mFavoredDiscoveredPrefix)
2274 {
2275 Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
2276 }
2277 }
2278
PublishAndAdvertise(void)2279 void RoutingManager::OnLinkPrefixManager::PublishAndAdvertise(void)
2280 {
2281 // Start publishing and advertising the local on-link prefix if
2282 // not already.
2283
2284 switch (mState)
2285 {
2286 case kIdle:
2287 case kDeprecating:
2288 break;
2289
2290 case kPublishing:
2291 case kAdvertising:
2292 ExitNow();
2293 }
2294
2295 mState = kPublishing;
2296 ResetExpireTime(TimerMilli::GetNow());
2297
2298 // We wait for the ULA `fc00::/7` route or a sub-prefix of it (e.g.,
2299 // default route) to be added in Network Data before
2300 // starting to advertise the local on-link prefix in RAs.
2301 // However, if it is already present in Network Data (e.g.,
2302 // added by another BR on the same Thread mesh), we can
2303 // immediately start advertising it.
2304
2305 if (Get<RoutingManager>().NetworkDataContainsUlaRoute())
2306 {
2307 EnterAdvertisingState();
2308 }
2309
2310 exit:
2311 return;
2312 }
2313
Deprecate(void)2314 void RoutingManager::OnLinkPrefixManager::Deprecate(void)
2315 {
2316 // Deprecate the local on-link prefix if it was being advertised
2317 // before. While depreciating the prefix, we wait for the lifetime
2318 // timer to expire before unpublishing the prefix from the Network
2319 // Data. We also continue to include it as a PIO in the RA message
2320 // with zero preferred lifetime and the remaining valid lifetime
2321 // until the timer expires.
2322
2323 switch (mState)
2324 {
2325 case kPublishing:
2326 case kAdvertising:
2327 mState = kDeprecating;
2328 LogInfo("Deprecate local on-link prefix %s", mLocalPrefix.ToString().AsCString());
2329 break;
2330
2331 case kIdle:
2332 case kDeprecating:
2333 break;
2334 }
2335 }
2336
ShouldPublishUlaRoute(void) const2337 bool RoutingManager::OnLinkPrefixManager::ShouldPublishUlaRoute(void) const
2338 {
2339 // Determine whether or not we should publish ULA prefix. We need
2340 // to publish if we are in any of `kPublishing`, `kAdvertising`,
2341 // or `kDeprecating` states, or if there is at least one old local
2342 // prefix being deprecated.
2343
2344 return (mState != kIdle) || !mOldLocalPrefixes.IsEmpty();
2345 }
2346
ResetExpireTime(TimeMilli aNow)2347 void RoutingManager::OnLinkPrefixManager::ResetExpireTime(TimeMilli aNow)
2348 {
2349 mExpireTime = aNow + TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime);
2350 mTimer.FireAtIfEarlier(mExpireTime);
2351 SavePrefix(mLocalPrefix, mExpireTime);
2352 }
2353
EnterAdvertisingState(void)2354 void RoutingManager::OnLinkPrefixManager::EnterAdvertisingState(void)
2355 {
2356 mState = kAdvertising;
2357 LogInfo("Start advertising local on-link prefix %s", mLocalPrefix.ToString().AsCString());
2358 }
2359
IsPublishingOrAdvertising(void) const2360 bool RoutingManager::OnLinkPrefixManager::IsPublishingOrAdvertising(void) const
2361 {
2362 return (mState == kPublishing) || (mState == kAdvertising);
2363 }
2364
AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage & aRaMessage)2365 void RoutingManager::OnLinkPrefixManager::AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage &aRaMessage)
2366 {
2367 AppendCurPrefix(aRaMessage);
2368 AppendOldPrefixes(aRaMessage);
2369 }
2370
AppendCurPrefix(Ip6::Nd::RouterAdvertMessage & aRaMessage)2371 void RoutingManager::OnLinkPrefixManager::AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage)
2372 {
2373 // Append the local on-link prefix to the `aRaMessage` as a PIO
2374 // only if it is being advertised or deprecated.
2375 //
2376 // If in `kAdvertising` state, we reset the expire time.
2377 // If in `kDeprecating` state, we include it as PIO with zero
2378 // preferred lifetime and the remaining valid lifetime.
2379
2380 uint32_t validLifetime = kDefaultOnLinkPrefixLifetime;
2381 uint32_t preferredLifetime = kDefaultOnLinkPrefixLifetime;
2382 TimeMilli now = TimerMilli::GetNow();
2383
2384 switch (mState)
2385 {
2386 case kAdvertising:
2387 ResetExpireTime(now);
2388 break;
2389
2390 case kDeprecating:
2391 VerifyOrExit(mExpireTime > now);
2392 validLifetime = TimeMilli::MsecToSec(mExpireTime - now);
2393 preferredLifetime = 0;
2394 break;
2395
2396 case kIdle:
2397 case kPublishing:
2398 ExitNow();
2399 }
2400
2401 SuccessOrAssert(aRaMessage.AppendPrefixInfoOption(mLocalPrefix, validLifetime, preferredLifetime));
2402
2403 LogInfo("RouterAdvert: Added PIO for %s (valid=%lu, preferred=%lu)", mLocalPrefix.ToString().AsCString(),
2404 ToUlong(validLifetime), ToUlong(preferredLifetime));
2405
2406 exit:
2407 return;
2408 }
2409
AppendOldPrefixes(Ip6::Nd::RouterAdvertMessage & aRaMessage)2410 void RoutingManager::OnLinkPrefixManager::AppendOldPrefixes(Ip6::Nd::RouterAdvertMessage &aRaMessage)
2411 {
2412 TimeMilli now = TimerMilli::GetNow();
2413 uint32_t validLifetime;
2414
2415 for (const OldPrefix &oldPrefix : mOldLocalPrefixes)
2416 {
2417 if (oldPrefix.mExpireTime < now)
2418 {
2419 continue;
2420 }
2421
2422 validLifetime = TimeMilli::MsecToSec(oldPrefix.mExpireTime - now);
2423 SuccessOrAssert(aRaMessage.AppendPrefixInfoOption(oldPrefix.mPrefix, validLifetime, 0));
2424
2425 LogInfo("RouterAdvert: Added PIO for %s (valid=%lu, preferred=0)", oldPrefix.mPrefix.ToString().AsCString(),
2426 ToUlong(validLifetime));
2427 }
2428 }
2429
HandleNetDataChange(void)2430 void RoutingManager::OnLinkPrefixManager::HandleNetDataChange(void)
2431 {
2432 VerifyOrExit(mState == kPublishing);
2433
2434 if (Get<RoutingManager>().NetworkDataContainsUlaRoute())
2435 {
2436 EnterAdvertisingState();
2437 Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
2438 }
2439
2440 exit:
2441 return;
2442 }
2443
HandleExtPanIdChange(void)2444 void RoutingManager::OnLinkPrefixManager::HandleExtPanIdChange(void)
2445 {
2446 // If the current local prefix is being advertised or deprecated,
2447 // we save it in `mOldLocalPrefixes` and keep deprecating it. It will
2448 // be included in emitted RAs as PIO with zero preferred lifetime.
2449 // It will still be present in Network Data until its expire time
2450 // so to allow Thread nodes to continue to communicate with `InfraIf`
2451 // device using addresses based on this prefix.
2452
2453 uint16_t oldState = mState;
2454 Ip6::Prefix oldPrefix = mLocalPrefix;
2455
2456 GenerateLocalPrefix();
2457
2458 VerifyOrExit(oldPrefix != mLocalPrefix);
2459
2460 switch (oldState)
2461 {
2462 case kIdle:
2463 case kPublishing:
2464 break;
2465
2466 case kAdvertising:
2467 case kDeprecating:
2468 DeprecateOldPrefix(oldPrefix, mExpireTime);
2469 break;
2470 }
2471
2472 if (Get<RoutingManager>().mIsRunning)
2473 {
2474 Get<RoutingManager>().mRoutePublisher.Evaluate();
2475 Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
2476 }
2477
2478 exit:
2479 return;
2480 }
2481
DeprecateOldPrefix(const Ip6::Prefix & aPrefix,TimeMilli aExpireTime)2482 void RoutingManager::OnLinkPrefixManager::DeprecateOldPrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime)
2483 {
2484 OldPrefix *entry = nullptr;
2485 Ip6::Prefix removedPrefix;
2486
2487 removedPrefix.Clear();
2488
2489 VerifyOrExit(!mOldLocalPrefixes.ContainsMatching(aPrefix));
2490
2491 LogInfo("Deprecating old on-link prefix %s", aPrefix.ToString().AsCString());
2492
2493 if (!mOldLocalPrefixes.IsFull())
2494 {
2495 entry = mOldLocalPrefixes.PushBack();
2496 }
2497 else
2498 {
2499 // If there is no more room in `mOldLocalPrefixes` array
2500 // we evict the entry with the earliest expiration time.
2501
2502 entry = &mOldLocalPrefixes[0];
2503
2504 for (OldPrefix &oldPrefix : mOldLocalPrefixes)
2505 {
2506 if ((oldPrefix.mExpireTime < entry->mExpireTime))
2507 {
2508 entry = &oldPrefix;
2509 }
2510 }
2511
2512 removedPrefix = entry->mPrefix;
2513
2514 IgnoreError(Get<Settings>().RemoveBrOnLinkPrefix(removedPrefix));
2515 }
2516
2517 entry->mPrefix = aPrefix;
2518 entry->mExpireTime = aExpireTime;
2519 mTimer.FireAtIfEarlier(aExpireTime);
2520
2521 SavePrefix(aPrefix, aExpireTime);
2522
2523 exit:
2524 return;
2525 }
2526
SavePrefix(const Ip6::Prefix & aPrefix,TimeMilli aExpireTime)2527 void RoutingManager::OnLinkPrefixManager::SavePrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime)
2528 {
2529 Settings::BrOnLinkPrefix savedPrefix;
2530
2531 savedPrefix.SetPrefix(aPrefix);
2532 savedPrefix.SetLifetime(TimeMilli::MsecToSec(aExpireTime - TimerMilli::GetNow()));
2533 IgnoreError(Get<Settings>().AddOrUpdateBrOnLinkPrefix(savedPrefix));
2534 }
2535
HandleTimer(void)2536 void RoutingManager::OnLinkPrefixManager::HandleTimer(void)
2537 {
2538 TimeMilli now = TimerMilli::GetNow();
2539 TimeMilli nextExpireTime = now.GetDistantFuture();
2540 Array<Ip6::Prefix, kMaxOldPrefixes> expiredPrefixes;
2541
2542 switch (mState)
2543 {
2544 case kIdle:
2545 break;
2546 case kPublishing:
2547 case kAdvertising:
2548 case kDeprecating:
2549 if (now >= mExpireTime)
2550 {
2551 LogInfo("Local on-link prefix %s expired", mLocalPrefix.ToString().AsCString());
2552 IgnoreError(Get<Settings>().RemoveBrOnLinkPrefix(mLocalPrefix));
2553 mState = kIdle;
2554 }
2555 else
2556 {
2557 nextExpireTime = mExpireTime;
2558 }
2559 break;
2560 }
2561
2562 for (OldPrefix &entry : mOldLocalPrefixes)
2563 {
2564 if (now >= entry.mExpireTime)
2565 {
2566 SuccessOrAssert(expiredPrefixes.PushBack(entry.mPrefix));
2567 }
2568 else
2569 {
2570 nextExpireTime = Min(nextExpireTime, entry.mExpireTime);
2571 }
2572 }
2573
2574 for (const Ip6::Prefix &prefix : expiredPrefixes)
2575 {
2576 LogInfo("Old local on-link prefix %s expired", prefix.ToString().AsCString());
2577 IgnoreError(Get<Settings>().RemoveBrOnLinkPrefix(prefix));
2578 mOldLocalPrefixes.RemoveMatching(prefix);
2579 }
2580
2581 if (nextExpireTime != now.GetDistantFuture())
2582 {
2583 mTimer.FireAtIfEarlier(nextExpireTime);
2584 }
2585
2586 Get<RoutingManager>().mRoutePublisher.Evaluate();
2587 }
2588
2589 //---------------------------------------------------------------------------------------------------------------------
2590 // OnMeshPrefixArray
2591
Add(const OnMeshPrefix & aPrefix)2592 void RoutingManager::OnMeshPrefixArray::Add(const OnMeshPrefix &aPrefix)
2593 {
2594 // Checks if `aPrefix` is already present in the array and if not
2595 // adds it as new entry.
2596
2597 Error error;
2598
2599 VerifyOrExit(!Contains(aPrefix));
2600
2601 error = PushBack(aPrefix);
2602
2603 if (error != kErrorNone)
2604 {
2605 LogWarn("Too many on-mesh prefixes in net data, ignoring prefix %s", aPrefix.ToString().AsCString());
2606 }
2607
2608 exit:
2609 return;
2610 }
2611
MarkAsDeleted(const OnMeshPrefix & aPrefix)2612 void RoutingManager::OnMeshPrefixArray::MarkAsDeleted(const OnMeshPrefix &aPrefix)
2613 {
2614 // Searches for a matching entry to `aPrefix` and if found marks
2615 // it as deleted by setting prefix length to zero.
2616
2617 OnMeshPrefix *entry = Find(aPrefix);
2618
2619 if (entry != nullptr)
2620 {
2621 entry->SetLength(0);
2622 }
2623 }
2624
2625 //---------------------------------------------------------------------------------------------------------------------
2626 // RoutePublisher
2627
2628 const otIp6Prefix RoutingManager::RoutePublisher::kUlaPrefix = {
2629 {{{0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
2630 7,
2631 };
2632
RoutePublisher(Instance & aInstance)2633 RoutingManager::RoutePublisher::RoutePublisher(Instance &aInstance)
2634 : InstanceLocator(aInstance)
2635 , mState(kDoNotPublish)
2636 , mPreference(NetworkData::kRoutePreferenceMedium)
2637 , mUserSetPreference(false)
2638 {
2639 }
2640
Evaluate(void)2641 void RoutingManager::RoutePublisher::Evaluate(void)
2642 {
2643 State newState = kDoNotPublish;
2644
2645 VerifyOrExit(Get<RoutingManager>().IsRunning());
2646
2647 if (Get<RoutingManager>().mOmrPrefixManager.GetFavoredPrefix().IsInfrastructureDerived() &&
2648 Get<RoutingManager>().mDiscoveredPrefixTable.ContainsDefaultOrNonUlaRoutePrefix())
2649 {
2650 newState = kPublishDefault;
2651 }
2652 else if (Get<RoutingManager>().mDiscoveredPrefixTable.ContainsNonUlaOnLinkPrefix())
2653 {
2654 newState = kPublishDefault;
2655 }
2656 else if (Get<RoutingManager>().mDiscoveredPrefixTable.ContainsUlaOnLinkPrefix() ||
2657 Get<RoutingManager>().mOnLinkPrefixManager.ShouldPublishUlaRoute())
2658 {
2659 newState = kPublishUla;
2660 }
2661
2662 exit:
2663 if (newState != mState)
2664 {
2665 LogInfo("RoutePublisher state: %s -> %s", StateToString(mState), StateToString(newState));
2666 UpdatePublishedRoute(newState);
2667 Get<RoutingManager>().mOmrPrefixManager.UpdateDefaultRouteFlag(newState == kPublishDefault);
2668 }
2669 }
2670
DeterminePrefixFor(State aState,Ip6::Prefix & aPrefix) const2671 void RoutingManager::RoutePublisher::DeterminePrefixFor(State aState, Ip6::Prefix &aPrefix) const
2672 {
2673 aPrefix.Clear();
2674
2675 switch (aState)
2676 {
2677 case kDoNotPublish:
2678 case kPublishDefault:
2679 // `Clear()` will set the prefix `::/0`.
2680 break;
2681 case kPublishUla:
2682 aPrefix = GetUlaPrefix();
2683 break;
2684 }
2685 }
2686
UpdatePublishedRoute(State aNewState)2687 void RoutingManager::RoutePublisher::UpdatePublishedRoute(State aNewState)
2688 {
2689 // Updates the published route entry in Network Data, transitioning
2690 // from current `mState` to new `aNewState`. This method can be used
2691 // when there is no change to `mState` but a change to `mPreference`.
2692
2693 Ip6::Prefix oldPrefix;
2694 NetworkData::ExternalRouteConfig routeConfig;
2695
2696 DeterminePrefixFor(mState, oldPrefix);
2697
2698 if (aNewState == kDoNotPublish)
2699 {
2700 VerifyOrExit(mState != kDoNotPublish);
2701 IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(oldPrefix));
2702 ExitNow();
2703 }
2704
2705 routeConfig.Clear();
2706 routeConfig.mPreference = mPreference;
2707 routeConfig.mStable = true;
2708 DeterminePrefixFor(aNewState, routeConfig.GetPrefix());
2709
2710 // If we were not publishing a route prefix before, publish the new
2711 // `routeConfig`. Otherwise, use `ReplacePublishedExternalRoute()` to
2712 // replace the previously published prefix entry. This ensures that we do
2713 // not have a situation where the previous route is removed while the new
2714 // one is not yet added in the Network Data.
2715
2716 if (mState == kDoNotPublish)
2717 {
2718 SuccessOrAssert(Get<NetworkData::Publisher>().PublishExternalRoute(
2719 routeConfig, NetworkData::Publisher::kFromRoutingManager));
2720 }
2721 else
2722 {
2723 SuccessOrAssert(Get<NetworkData::Publisher>().ReplacePublishedExternalRoute(
2724 oldPrefix, routeConfig, NetworkData::Publisher::kFromRoutingManager));
2725 }
2726
2727 exit:
2728 mState = aNewState;
2729 }
2730
Unpublish(void)2731 void RoutingManager::RoutePublisher::Unpublish(void)
2732 {
2733 // Unpublish the previously published route based on `mState`
2734 // and update `mState`.
2735
2736 Ip6::Prefix prefix;
2737
2738 VerifyOrExit(mState != kDoNotPublish);
2739 DeterminePrefixFor(mState, prefix);
2740 IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(prefix));
2741 mState = kDoNotPublish;
2742
2743 exit:
2744 return;
2745 }
2746
SetPreference(RoutePreference aPreference)2747 void RoutingManager::RoutePublisher::SetPreference(RoutePreference aPreference)
2748 {
2749 LogInfo("User explicitly set published route preference to %s", RoutePreferenceToString(aPreference));
2750 mUserSetPreference = true;
2751 UpdatePreference(aPreference);
2752 }
2753
ClearPreference(void)2754 void RoutingManager::RoutePublisher::ClearPreference(void)
2755 {
2756 VerifyOrExit(mUserSetPreference);
2757
2758 LogInfo("User cleared explicitly set published route preference - set based on role");
2759 mUserSetPreference = false;
2760 SetPreferenceBasedOnRole();
2761
2762 exit:
2763 return;
2764 }
2765
SetPreferenceBasedOnRole(void)2766 void RoutingManager::RoutePublisher::SetPreferenceBasedOnRole(void)
2767 {
2768 UpdatePreference(Get<Mle::Mle>().IsRouterOrLeader() ? NetworkData::kRoutePreferenceMedium
2769 : NetworkData::kRoutePreferenceLow);
2770 }
2771
HandleRoleChanged(void)2772 void RoutingManager::RoutePublisher::HandleRoleChanged(void)
2773 {
2774 if (!mUserSetPreference)
2775 {
2776 SetPreferenceBasedOnRole();
2777 }
2778 }
2779
UpdatePreference(RoutePreference aPreference)2780 void RoutingManager::RoutePublisher::UpdatePreference(RoutePreference aPreference)
2781 {
2782 VerifyOrExit(mPreference != aPreference);
2783
2784 LogInfo("Published route preference changed: %s -> %s", RoutePreferenceToString(mPreference),
2785 RoutePreferenceToString(aPreference));
2786 mPreference = aPreference;
2787 UpdatePublishedRoute(mState);
2788
2789 exit:
2790 return;
2791 }
2792
StateToString(State aState)2793 const char *RoutingManager::RoutePublisher::StateToString(State aState)
2794 {
2795 static const char *const kStateStrings[] = {
2796 "none", // (0) kDoNotPublish
2797 "def-route", // (1) kPublishDefault
2798 "ula", // (2) kPublishUla
2799 };
2800
2801 static_assert(0 == kDoNotPublish, "kDoNotPublish value is incorrect");
2802 static_assert(1 == kPublishDefault, "kPublishDefault value is incorrect");
2803 static_assert(2 == kPublishUla, "kPublishUla value is incorrect");
2804
2805 return kStateStrings[aState];
2806 }
2807
2808 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
2809
2810 //---------------------------------------------------------------------------------------------------------------------
2811 // Nat64PrefixManager
2812
Nat64PrefixManager(Instance & aInstance)2813 RoutingManager::Nat64PrefixManager::Nat64PrefixManager(Instance &aInstance)
2814 : InstanceLocator(aInstance)
2815 , mEnabled(false)
2816 , mTimer(aInstance)
2817 {
2818 mInfraIfPrefix.Clear();
2819 mLocalPrefix.Clear();
2820 mPublishedPrefix.Clear();
2821 }
2822
SetEnabled(bool aEnabled)2823 void RoutingManager::Nat64PrefixManager::SetEnabled(bool aEnabled)
2824 {
2825 VerifyOrExit(mEnabled != aEnabled);
2826 mEnabled = aEnabled;
2827
2828 if (aEnabled)
2829 {
2830 if (Get<RoutingManager>().IsRunning())
2831 {
2832 Start();
2833 }
2834 }
2835 else
2836 {
2837 Stop();
2838 }
2839
2840 exit:
2841 return;
2842 }
2843
Start(void)2844 void RoutingManager::Nat64PrefixManager::Start(void)
2845 {
2846 VerifyOrExit(mEnabled);
2847 LogInfo("Starting Nat64PrefixManager");
2848 mTimer.Start(0);
2849
2850 exit:
2851 return;
2852 }
2853
Stop(void)2854 void RoutingManager::Nat64PrefixManager::Stop(void)
2855 {
2856 LogInfo("Stopping Nat64PrefixManager");
2857
2858 if (mPublishedPrefix.IsValidNat64())
2859 {
2860 IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(mPublishedPrefix));
2861 }
2862
2863 mPublishedPrefix.Clear();
2864 mInfraIfPrefix.Clear();
2865 mTimer.Stop();
2866
2867 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
2868 Get<Nat64::Translator>().ClearNat64Prefix();
2869 #endif
2870 }
2871
GenerateLocalPrefix(const Ip6::Prefix & aBrUlaPrefix)2872 void RoutingManager::Nat64PrefixManager::GenerateLocalPrefix(const Ip6::Prefix &aBrUlaPrefix)
2873 {
2874 mLocalPrefix = aBrUlaPrefix;
2875 mLocalPrefix.SetSubnetId(kNat64PrefixSubnetId);
2876 mLocalPrefix.mPrefix.mFields.m32[2] = 0;
2877 mLocalPrefix.SetLength(kNat64PrefixLength);
2878
2879 LogInfo("Generated local NAT64 prefix: %s", mLocalPrefix.ToString().AsCString());
2880 }
2881
GetFavoredPrefix(RoutePreference & aPreference) const2882 const Ip6::Prefix &RoutingManager::Nat64PrefixManager::GetFavoredPrefix(RoutePreference &aPreference) const
2883 {
2884 const Ip6::Prefix *favoredPrefix = &mLocalPrefix;
2885
2886 aPreference = NetworkData::kRoutePreferenceLow;
2887
2888 if (mInfraIfPrefix.IsValidNat64() &&
2889 Get<RoutingManager>().mOmrPrefixManager.GetFavoredPrefix().IsInfrastructureDerived())
2890 {
2891 favoredPrefix = &mInfraIfPrefix;
2892 aPreference = NetworkData::kRoutePreferenceMedium;
2893 }
2894
2895 return *favoredPrefix;
2896 }
2897
Evaluate(void)2898 void RoutingManager::Nat64PrefixManager::Evaluate(void)
2899 {
2900 Error error;
2901 Ip6::Prefix prefix;
2902 RoutePreference preference;
2903 NetworkData::ExternalRouteConfig netdataPrefixConfig;
2904 bool shouldPublish;
2905
2906 VerifyOrExit(mEnabled);
2907
2908 LogInfo("Evaluating NAT64 prefix");
2909
2910 prefix = GetFavoredPrefix(preference);
2911
2912 error = Get<NetworkData::Leader>().GetPreferredNat64Prefix(netdataPrefixConfig);
2913
2914 // NAT64 prefix is expected to be published from this BR
2915 // when one of the following is true:
2916 //
2917 // - No NAT64 prefix in Network Data.
2918 // - The preferred NAT64 prefix in Network Data has lower
2919 // preference than this BR's prefix.
2920 // - The preferred NAT64 prefix in Network Data was published
2921 // by this BR.
2922 // - The preferred NAT64 prefix in Network Data is same as the
2923 // discovered infrastructure prefix.
2924 //
2925 // TODO: change to check RLOC16 to determine if the NAT64 prefix
2926 // was published by this BR.
2927
2928 shouldPublish =
2929 ((error == kErrorNotFound) || (netdataPrefixConfig.mPreference < preference) ||
2930 (netdataPrefixConfig.GetPrefix() == mPublishedPrefix) || (netdataPrefixConfig.GetPrefix() == mInfraIfPrefix));
2931
2932 if (mPublishedPrefix.IsValidNat64() && (!shouldPublish || (prefix != mPublishedPrefix)))
2933 {
2934 IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(mPublishedPrefix));
2935 mPublishedPrefix.Clear();
2936 }
2937
2938 if (shouldPublish && ((prefix != mPublishedPrefix) || (preference != mPublishedPreference)))
2939 {
2940 mPublishedPrefix = prefix;
2941 mPublishedPreference = preference;
2942 Publish();
2943 }
2944
2945 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
2946 // When there is an prefix other than mLocalPrefix, means there is an external translator available. So we bypass
2947 // the NAT64 translator by clearing the NAT64 prefix in the translator.
2948 if (mPublishedPrefix == mLocalPrefix)
2949 {
2950 Get<Nat64::Translator>().SetNat64Prefix(mLocalPrefix);
2951 }
2952 else
2953 {
2954 Get<Nat64::Translator>().ClearNat64Prefix();
2955 }
2956 #endif
2957
2958 exit:
2959 return;
2960 }
2961
Publish(void)2962 void RoutingManager::Nat64PrefixManager::Publish(void)
2963 {
2964 NetworkData::ExternalRouteConfig routeConfig;
2965
2966 routeConfig.Clear();
2967 routeConfig.SetPrefix(mPublishedPrefix);
2968 routeConfig.mPreference = mPublishedPreference;
2969 routeConfig.mStable = true;
2970 routeConfig.mNat64 = true;
2971
2972 SuccessOrAssert(
2973 Get<NetworkData::Publisher>().PublishExternalRoute(routeConfig, NetworkData::Publisher::kFromRoutingManager));
2974 }
2975
HandleTimer(void)2976 void RoutingManager::Nat64PrefixManager::HandleTimer(void)
2977 {
2978 OT_ASSERT(mEnabled);
2979
2980 Discover();
2981
2982 mTimer.Start(TimeMilli::SecToMsec(kDefaultNat64PrefixLifetime));
2983 LogInfo("NAT64 prefix timer scheduled in %lu seconds", ToUlong(kDefaultNat64PrefixLifetime));
2984 }
2985
Discover(void)2986 void RoutingManager::Nat64PrefixManager::Discover(void)
2987 {
2988 Error error = Get<RoutingManager>().mInfraIf.DiscoverNat64Prefix();
2989
2990 if (error == kErrorNone)
2991 {
2992 LogInfo("Discovering infraif NAT64 prefix");
2993 }
2994 else
2995 {
2996 LogWarn("Failed to discover infraif NAT64 prefix: %s", ErrorToString(error));
2997 }
2998 }
2999
HandleDiscoverDone(const Ip6::Prefix & aPrefix)3000 void RoutingManager::Nat64PrefixManager::HandleDiscoverDone(const Ip6::Prefix &aPrefix)
3001 {
3002 mInfraIfPrefix = aPrefix;
3003
3004 LogInfo("Infraif NAT64 prefix: %s", mInfraIfPrefix.IsValidNat64() ? mInfraIfPrefix.ToString().AsCString() : "none");
3005
3006 if (Get<RoutingManager>().mIsRunning)
3007 {
3008 Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
3009 }
3010 }
3011
GetState(void) const3012 Nat64::State RoutingManager::Nat64PrefixManager::GetState(void) const
3013 {
3014 Nat64::State state = Nat64::kStateDisabled;
3015
3016 VerifyOrExit(mEnabled);
3017 VerifyOrExit(Get<RoutingManager>().IsRunning(), state = Nat64::kStateNotRunning);
3018 VerifyOrExit(mPublishedPrefix.IsValidNat64(), state = Nat64::kStateIdle);
3019 state = Nat64::kStateActive;
3020
3021 exit:
3022 return state;
3023 }
3024
3025 #endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
3026
3027 //---------------------------------------------------------------------------------------------------------------------
3028 // RsSender
3029
RsSender(Instance & aInstance)3030 RoutingManager::RsSender::RsSender(Instance &aInstance)
3031 : InstanceLocator(aInstance)
3032 , mTxCount(0)
3033 , mTimer(aInstance)
3034 {
3035 }
3036
Start(void)3037 void RoutingManager::RsSender::Start(void)
3038 {
3039 uint32_t delay;
3040
3041 VerifyOrExit(!IsInProgress());
3042
3043 delay = Random::NonCrypto::GetUint32InRange(0, kMaxStartDelay);
3044 LogInfo("Scheduled Router Solicitation in %lu milliseconds", ToUlong(delay));
3045
3046 mTxCount = 0;
3047 mStartTime = TimerMilli::GetNow();
3048 mTimer.Start(delay);
3049
3050 exit:
3051 return;
3052 }
3053
Stop(void)3054 void RoutingManager::RsSender::Stop(void) { mTimer.Stop(); }
3055
SendRs(void)3056 Error RoutingManager::RsSender::SendRs(void)
3057 {
3058 Ip6::Address destAddress;
3059 Ip6::Nd::RouterSolicitMessage routerSolicit;
3060 InfraIf::Icmp6Packet packet;
3061 Error error;
3062
3063 packet.InitFrom(routerSolicit);
3064 destAddress.SetToLinkLocalAllRoutersMulticast();
3065
3066 error = Get<RoutingManager>().mInfraIf.Send(packet, destAddress);
3067
3068 if (error == kErrorNone)
3069 {
3070 Get<Ip6::Ip6>().GetBorderRoutingCounters().mRsTxSuccess++;
3071 }
3072 else
3073 {
3074 Get<Ip6::Ip6>().GetBorderRoutingCounters().mRsTxFailure++;
3075 }
3076 return error;
3077 }
3078
HandleTimer(void)3079 void RoutingManager::RsSender::HandleTimer(void)
3080 {
3081 Error error;
3082 uint32_t delay;
3083
3084 if (mTxCount >= kMaxTxCount)
3085 {
3086 Get<RoutingManager>().HandleRsSenderFinished(mStartTime);
3087 ExitNow();
3088 }
3089
3090 error = SendRs();
3091
3092 if (error == kErrorNone)
3093 {
3094 mTxCount++;
3095 LogInfo("Successfully sent RS %u/%u", mTxCount, kMaxTxCount);
3096 delay = (mTxCount == kMaxTxCount) ? kWaitOnLastAttempt : kTxInterval;
3097 }
3098 else
3099 {
3100 LogCrit("Failed to send RS %u, error:%s", mTxCount + 1, ErrorToString(error));
3101
3102 // Note that `mTxCount` is intentionally not incremented
3103 // if the tx fails.
3104 delay = kRetryDelay;
3105 }
3106
3107 mTimer.Start(delay);
3108
3109 exit:
3110 return;
3111 }
3112
3113 } // namespace BorderRouter
3114
3115 } // namespace ot
3116
3117 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
3118