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