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/locator_getters.hpp"
48 #include "common/log.hpp"
49 #include "common/num_utils.hpp"
50 #include "common/numeric_limits.hpp"
51 #include "common/random.hpp"
52 #include "common/settings.hpp"
53 #include "common/type_traits.hpp"
54 #include "instance/instance.hpp"
55 #include "meshcop/extended_panid.hpp"
56 #include "net/ip6.hpp"
57 #include "net/nat64_translator.hpp"
58 #include "net/nd6.hpp"
59 #include "thread/mle_router.hpp"
60 #include "thread/network_data_leader.hpp"
61 #include "thread/network_data_local.hpp"
62 #include "thread/network_data_notifier.hpp"
63
64 namespace ot {
65
66 namespace BorderRouter {
67
68 RegisterLogModule("RoutingManager");
69
RoutingManager(Instance & aInstance)70 RoutingManager::RoutingManager(Instance &aInstance)
71 : InstanceLocator(aInstance)
72 , mIsRunning(false)
73 , mIsEnabled(false)
74 , mInfraIf(aInstance)
75 , mOmrPrefixManager(aInstance)
76 , mRioAdvertiser(aInstance)
77 , mOnLinkPrefixManager(aInstance)
78 , mRxRaTracker(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 , mRoutingPolicyTimer(aInstance)
88 {
89 mBrUlaPrefix.Clear();
90 }
91
Init(uint32_t aInfraIfIndex,bool aInfraIfIsRunning)92 Error RoutingManager::Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning)
93 {
94 Error error;
95
96 VerifyOrExit(GetState() == kStateUninitialized || GetState() == kStateDisabled, error = kErrorInvalidState);
97
98 if (!mInfraIf.IsInitialized())
99 {
100 LogInfo("Initializing - InfraIfIndex:%lu", ToUlong(aInfraIfIndex));
101 SuccessOrExit(error = mInfraIf.Init(aInfraIfIndex));
102 SuccessOrExit(error = LoadOrGenerateRandomBrUlaPrefix());
103 mOmrPrefixManager.Init(mBrUlaPrefix);
104 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
105 mNat64PrefixManager.GenerateLocalPrefix(mBrUlaPrefix);
106 #endif
107 mOnLinkPrefixManager.Init();
108 }
109 else if (aInfraIfIndex != mInfraIf.GetIfIndex())
110 {
111 LogInfo("Reinitializing - InfraIfIndex:%lu -> %lu", ToUlong(mInfraIf.GetIfIndex()), ToUlong(aInfraIfIndex));
112
113 #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_AUTO_ENABLE_ON_INFRA_IF
114 IgnoreError(Get<Dns::Multicast::Core>().SetEnabled(false, mInfraIf.GetIfIndex()));
115 #endif
116
117 mInfraIf.SetIfIndex(aInfraIfIndex);
118 }
119
120 error = mInfraIf.HandleStateChanged(mInfraIf.GetIfIndex(), aInfraIfIsRunning);
121
122 exit:
123 if (error != kErrorNone)
124 {
125 mInfraIf.Deinit();
126 }
127
128 return error;
129 }
130
SetEnabled(bool aEnabled)131 Error RoutingManager::SetEnabled(bool aEnabled)
132 {
133 Error error = kErrorNone;
134
135 VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
136
137 VerifyOrExit(aEnabled != mIsEnabled);
138
139 mIsEnabled = aEnabled;
140 LogInfo("%s", mIsEnabled ? "Enabling" : "Disabling");
141 EvaluateState();
142
143 exit:
144 return error;
145 }
146
GetState(void) const147 RoutingManager::State RoutingManager::GetState(void) const
148 {
149 State state = kStateUninitialized;
150
151 VerifyOrExit(IsInitialized());
152 VerifyOrExit(IsEnabled(), state = kStateDisabled);
153
154 state = IsRunning() ? kStateRunning : kStateStopped;
155
156 exit:
157 return state;
158 }
159
GetOmrPrefix(Ip6::Prefix & aPrefix) const160 Error RoutingManager::GetOmrPrefix(Ip6::Prefix &aPrefix) const
161 {
162 Error error = kErrorNone;
163
164 VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
165 aPrefix = mOmrPrefixManager.GetGeneratedPrefix();
166
167 exit:
168 return error;
169 }
170
171 #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
GetPdOmrPrefix(PrefixTableEntry & aPrefixInfo) const172 Error RoutingManager::GetPdOmrPrefix(PrefixTableEntry &aPrefixInfo) const
173 {
174 Error error = kErrorNone;
175
176 VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
177 error = mPdPrefixManager.GetPrefixInfo(aPrefixInfo);
178
179 exit:
180 return error;
181 }
182
GetPdProcessedRaInfo(PdProcessedRaInfo & aPdProcessedRaInfo)183 Error RoutingManager::GetPdProcessedRaInfo(PdProcessedRaInfo &aPdProcessedRaInfo)
184 {
185 Error error = kErrorNone;
186
187 VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
188 error = mPdPrefixManager.GetProcessedRaInfo(aPdProcessedRaInfo);
189
190 exit:
191 return error;
192 }
193 #endif
194
GetFavoredOmrPrefix(Ip6::Prefix & aPrefix,RoutePreference & aPreference) const195 Error RoutingManager::GetFavoredOmrPrefix(Ip6::Prefix &aPrefix, RoutePreference &aPreference) const
196 {
197 Error error = kErrorNone;
198
199 VerifyOrExit(IsRunning(), error = kErrorInvalidState);
200 aPrefix = mOmrPrefixManager.GetFavoredPrefix().GetPrefix();
201 aPreference = mOmrPrefixManager.GetFavoredPrefix().GetPreference();
202
203 exit:
204 return error;
205 }
206
GetOnLinkPrefix(Ip6::Prefix & aPrefix) const207 Error RoutingManager::GetOnLinkPrefix(Ip6::Prefix &aPrefix) const
208 {
209 Error error = kErrorNone;
210
211 VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
212 aPrefix = mOnLinkPrefixManager.GetLocalPrefix();
213
214 exit:
215 return error;
216 }
217
GetFavoredOnLinkPrefix(Ip6::Prefix & aPrefix) const218 Error RoutingManager::GetFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const
219 {
220 Error error = kErrorNone;
221
222 VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
223 aPrefix = mOnLinkPrefixManager.GetFavoredDiscoveredPrefix();
224
225 if (aPrefix.GetLength() == 0)
226 {
227 aPrefix = mOnLinkPrefixManager.GetLocalPrefix();
228 }
229
230 exit:
231 return error;
232 }
233
234 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
SetNat64PrefixManagerEnabled(bool aEnabled)235 void RoutingManager::SetNat64PrefixManagerEnabled(bool aEnabled)
236 {
237 // PrefixManager will start itself if routing manager is running.
238 mNat64PrefixManager.SetEnabled(aEnabled);
239 }
240
GetNat64Prefix(Ip6::Prefix & aPrefix)241 Error RoutingManager::GetNat64Prefix(Ip6::Prefix &aPrefix)
242 {
243 Error error = kErrorNone;
244
245 VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
246 aPrefix = mNat64PrefixManager.GetLocalPrefix();
247
248 exit:
249 return error;
250 }
251
GetFavoredNat64Prefix(Ip6::Prefix & aPrefix,RoutePreference & aRoutePreference)252 Error RoutingManager::GetFavoredNat64Prefix(Ip6::Prefix &aPrefix, RoutePreference &aRoutePreference)
253 {
254 Error error = kErrorNone;
255
256 VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
257 aPrefix = mNat64PrefixManager.GetFavoredPrefix(aRoutePreference);
258
259 exit:
260 return error;
261 }
262 #endif
263
LoadOrGenerateRandomBrUlaPrefix(void)264 Error RoutingManager::LoadOrGenerateRandomBrUlaPrefix(void)
265 {
266 Error error = kErrorNone;
267 bool generated = false;
268
269 if (Get<Settings>().Read<Settings::BrUlaPrefix>(mBrUlaPrefix) != kErrorNone || !IsValidBrUlaPrefix(mBrUlaPrefix))
270 {
271 Ip6::NetworkPrefix randomUlaPrefix;
272
273 LogNote("No valid /48 BR ULA prefix found in settings, generating new one");
274
275 SuccessOrExit(error = randomUlaPrefix.GenerateRandomUla());
276
277 mBrUlaPrefix.Set(randomUlaPrefix);
278 mBrUlaPrefix.SetSubnetId(0);
279 mBrUlaPrefix.SetLength(kBrUlaPrefixLength);
280
281 IgnoreError(Get<Settings>().Save<Settings::BrUlaPrefix>(mBrUlaPrefix));
282 generated = true;
283 }
284
285 OT_UNUSED_VARIABLE(generated);
286
287 LogNote("BR ULA prefix: %s (%s)", mBrUlaPrefix.ToString().AsCString(), generated ? "generated" : "loaded");
288
289 exit:
290 if (error != kErrorNone)
291 {
292 LogCrit("Failed to generate random /48 BR ULA prefix");
293 }
294 return error;
295 }
296
EvaluateState(void)297 void RoutingManager::EvaluateState(void)
298 {
299 if (mIsEnabled && Get<Mle::MleRouter>().IsAttached() && mInfraIf.IsRunning())
300 {
301 Start();
302 }
303 else
304 {
305 Stop();
306 }
307 }
308
Start(void)309 void RoutingManager::Start(void)
310 {
311 if (!mIsRunning)
312 {
313 LogInfo("Starting");
314
315 mIsRunning = true;
316 mRxRaTracker.Start();
317 mOnLinkPrefixManager.Start();
318 mOmrPrefixManager.Start();
319 mRoutePublisher.Start();
320 mRsSender.Start();
321 #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
322 mPdPrefixManager.Start();
323 #endif
324 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
325 mNat64PrefixManager.Start();
326 #endif
327 }
328 }
329
Stop(void)330 void RoutingManager::Stop(void)
331 {
332 VerifyOrExit(mIsRunning);
333
334 mOmrPrefixManager.Stop();
335 mOnLinkPrefixManager.Stop();
336 #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
337 mPdPrefixManager.Stop();
338 #endif
339 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
340 mNat64PrefixManager.Stop();
341 #endif
342
343 SendRouterAdvertisement(kInvalidateAllPrevPrefixes);
344
345 mRxRaTracker.Stop();
346
347 mTxRaInfo.mTxCount = 0;
348
349 mRsSender.Stop();
350
351 mRoutingPolicyTimer.Stop();
352
353 mRoutePublisher.Stop();
354
355 LogInfo("Stopped");
356
357 mIsRunning = false;
358
359 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
360 if (Get<Srp::Server>().IsAutoEnableMode())
361 {
362 Get<Srp::Server>().Disable();
363 }
364 #endif
365
366 exit:
367 return;
368 }
369
SetExtraRouterAdvertOptions(const uint8_t * aOptions,uint16_t aLength)370 Error RoutingManager::SetExtraRouterAdvertOptions(const uint8_t *aOptions, uint16_t aLength)
371 {
372 Error error = kErrorNone;
373
374 if (aOptions == nullptr)
375 {
376 mExtraRaOptions.Free();
377 }
378 else
379 {
380 error = mExtraRaOptions.SetFrom(aOptions, aLength);
381 }
382
383 return error;
384 }
385
386 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
HandleSrpServerAutoEnableMode(void)387 void RoutingManager::HandleSrpServerAutoEnableMode(void)
388 {
389 VerifyOrExit(Get<Srp::Server>().IsAutoEnableMode());
390
391 if (IsInitalPolicyEvaluationDone())
392 {
393 Get<Srp::Server>().Enable();
394 }
395 else
396 {
397 Get<Srp::Server>().Disable();
398 }
399
400 exit:
401 return;
402 }
403 #endif
404
HandleReceived(const InfraIf::Icmp6Packet & aPacket,const Ip6::Address & aSrcAddress)405 void RoutingManager::HandleReceived(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
406 {
407 const Ip6::Icmp::Header *icmp6Header;
408
409 VerifyOrExit(mIsRunning);
410
411 icmp6Header = reinterpret_cast<const Ip6::Icmp::Header *>(aPacket.GetBytes());
412
413 switch (icmp6Header->GetType())
414 {
415 case Ip6::Icmp::Header::kTypeRouterAdvert:
416 HandleRouterAdvertisement(aPacket, aSrcAddress);
417 break;
418 case Ip6::Icmp::Header::kTypeRouterSolicit:
419 HandleRouterSolicit(aPacket, aSrcAddress);
420 break;
421 case Ip6::Icmp::Header::kTypeNeighborAdvert:
422 HandleNeighborAdvertisement(aPacket);
423 break;
424 default:
425 break;
426 }
427
428 exit:
429 return;
430 }
431
HandleNotifierEvents(Events aEvents)432 void RoutingManager::HandleNotifierEvents(Events aEvents)
433 {
434 if (aEvents.Contains(kEventThreadRoleChanged))
435 {
436 mRioAdvertiser.HandleRoleChanged();
437 }
438
439 mRoutePublisher.HandleNotifierEvents(aEvents);
440
441 VerifyOrExit(IsInitialized() && IsEnabled());
442
443 if (aEvents.Contains(kEventThreadRoleChanged))
444 {
445 EvaluateState();
446 }
447
448 if (mIsRunning && aEvents.Contains(kEventThreadNetdataChanged))
449 {
450 mRxRaTracker.HandleNetDataChange();
451 mOnLinkPrefixManager.HandleNetDataChange();
452 ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
453 }
454
455 if (aEvents.Contains(kEventThreadExtPanIdChanged))
456 {
457 mOnLinkPrefixManager.HandleExtPanIdChange();
458 }
459
460 exit:
461 return;
462 }
463
EvaluateRoutingPolicy(void)464 void RoutingManager::EvaluateRoutingPolicy(void)
465 {
466 OT_ASSERT(mIsRunning);
467
468 LogInfo("Evaluating routing policy");
469
470 mOnLinkPrefixManager.Evaluate();
471 mOmrPrefixManager.Evaluate();
472 mRoutePublisher.Evaluate();
473 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
474 mNat64PrefixManager.Evaluate();
475 #endif
476
477 if (IsInitalPolicyEvaluationDone())
478 {
479 SendRouterAdvertisement(kAdvPrefixesFromNetData);
480 }
481
482 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
483 if (Get<Srp::Server>().IsAutoEnableMode() && IsInitalPolicyEvaluationDone())
484 {
485 // If SRP server uses the auto-enable mode, we enable the SRP
486 // server on the first RA transmission after we are done with
487 // initial prefix/route configurations. Note that if SRP server
488 // is already enabled, calling `Enable()` again does nothing.
489
490 Get<Srp::Server>().Enable();
491 }
492 #endif
493
494 ScheduleRoutingPolicyEvaluation(kForNextRa);
495 }
496
IsInitalPolicyEvaluationDone(void) const497 bool RoutingManager::IsInitalPolicyEvaluationDone(void) const
498 {
499 // This method indicates whether or not we are done with the
500 // initial policy evaluation and prefix and route setup, i.e.,
501 // the OMR and on-link prefixes are determined, advertised in
502 // the emitted Router Advert message on infrastructure side
503 // and published in the Thread Network Data.
504
505 return mIsRunning && !mOmrPrefixManager.GetFavoredPrefix().IsEmpty() &&
506 mOnLinkPrefixManager.IsInitalEvaluationDone();
507 }
508
ScheduleRoutingPolicyEvaluation(ScheduleMode aMode)509 void RoutingManager::ScheduleRoutingPolicyEvaluation(ScheduleMode aMode)
510 {
511 TimeMilli now = TimerMilli::GetNow();
512 uint32_t delay = 0;
513 uint32_t interval = 0;
514 uint16_t jitter = 0;
515 TimeMilli evaluateTime;
516
517 VerifyOrExit(mIsRunning);
518
519 switch (aMode)
520 {
521 case kImmediately:
522 break;
523
524 case kForNextRa:
525 if (mTxRaInfo.mTxCount <= kInitalRaTxCount)
526 {
527 interval = kInitalRaInterval;
528 jitter = kInitialRaJitter;
529 }
530 else
531 {
532 interval = kRaBeaconInterval;
533 jitter = kRaBeaconJitter;
534 }
535
536 break;
537
538 case kAfterRandomDelay:
539 interval = kEvaluationInterval;
540 jitter = kEvaluationJitter;
541 break;
542
543 case kToReplyToRs:
544 interval = kRsReplyInterval;
545 jitter = kRsReplyJitter;
546 break;
547 }
548
549 delay = Random::NonCrypto::AddJitter(interval, jitter);
550
551 // Ensure we wait a min delay after last RA tx
552 evaluateTime = Max(now + delay, mTxRaInfo.mLastTxTime + kMinDelayBetweenRas);
553
554 mRoutingPolicyTimer.FireAtIfEarlier(evaluateTime);
555
556 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
557 {
558 uint32_t duration = evaluateTime - now;
559
560 if (duration == 0)
561 {
562 LogInfo("Will evaluate routing policy immediately");
563 }
564 else
565 {
566 String<Uptime::kStringSize> string;
567
568 Uptime::UptimeToString(duration, string, /* aIncludeMsec */ true);
569 LogInfo("Will evaluate routing policy in %s (%lu msec)", string.AsCString() + 3, ToUlong(duration));
570 }
571 }
572 #endif
573
574 exit:
575 return;
576 }
577
SendRouterAdvertisement(RouterAdvTxMode aRaTxMode)578 void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode)
579 {
580 Error error = kErrorNone;
581 RouterAdvert::TxMessage raMsg;
582 RouterAdvert::Header header;
583 Ip6::Address destAddress;
584 InfraIf::Icmp6Packet packet;
585
586 LogInfo("Preparing RA");
587
588 if (mRxRaTracker.GetLocalRaHeaderToMirror().IsValid())
589 {
590 header = mRxRaTracker.GetLocalRaHeaderToMirror();
591 }
592 else
593 {
594 header.SetToDefault();
595 }
596
597 mRxRaTracker.DetermineAndSetFlags(header);
598
599 SuccessOrExit(error = raMsg.AppendHeader(header));
600
601 LogInfo("- RA Header - flags - M:%u O:%u", header.IsManagedAddressConfigFlagSet(), header.IsOtherConfigFlagSet());
602 LogInfo("- RA Header - default route - lifetime:%u", header.GetRouterLifetime());
603
604 #if OPENTHREAD_CONFIG_BORDER_ROUTING_STUB_ROUTER_FLAG_IN_EMITTED_RA_ENABLE
605 SuccessOrExit(error = raMsg.AppendFlagsExtensionOption(/* aStubRouterFlag */ true));
606 LogInfo("- FlagsExt - StubRouter:1");
607 #endif
608
609 // Append PIO for local on-link prefix if is either being
610 // advertised or deprecated and for old prefix if is being
611 // deprecated.
612
613 SuccessOrExit(error = mOnLinkPrefixManager.AppendAsPiosTo(raMsg));
614
615 if (aRaTxMode == kInvalidateAllPrevPrefixes)
616 {
617 SuccessOrExit(error = mRioAdvertiser.InvalidatPrevRios(raMsg));
618 }
619 else
620 {
621 SuccessOrExit(error = mRioAdvertiser.AppendRios(raMsg));
622 }
623
624 if (mExtraRaOptions.GetLength() > 0)
625 {
626 SuccessOrExit(error = raMsg.AppendBytes(mExtraRaOptions.GetBytes(), mExtraRaOptions.GetLength()));
627 }
628
629 VerifyOrExit(raMsg.ContainsAnyOptions());
630
631 destAddress.SetToLinkLocalAllNodesMulticast();
632 raMsg.GetAsPacket(packet);
633
634 mTxRaInfo.IncrementTxCountAndSaveHash(packet);
635
636 SuccessOrExit(error = mInfraIf.Send(packet, destAddress));
637
638 mTxRaInfo.mLastTxTime = TimerMilli::GetNow();
639 Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaTxSuccess++;
640 LogInfo("Sent RA on %s", mInfraIf.ToString().AsCString());
641 DumpDebg("[BR-CERT] direction=send | type=RA |", packet.GetBytes(), packet.GetLength());
642
643 exit:
644 if (error != kErrorNone)
645 {
646 Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaTxFailure++;
647 LogWarn("Failed to send RA on %s: %s", mInfraIf.ToString().AsCString(), ErrorToString(error));
648 }
649 }
650
CalculateExpirationTime(TimeMilli aUpdateTime,uint32_t aLifetime)651 TimeMilli RoutingManager::CalculateExpirationTime(TimeMilli aUpdateTime, uint32_t aLifetime)
652 {
653 // `aLifetime` is in unit of seconds. We clamp the lifetime to max
654 // interval supported by `Timer` (`2^31` msec or ~24.8 days).
655 // This ensures that the time calculation fits within `TimeMilli`
656 // range.
657
658 static constexpr uint32_t kMaxLifetime = Time::MsecToSec(Timer::kMaxDelay);
659
660 return aUpdateTime + Time::SecToMsec(Min(aLifetime, kMaxLifetime));
661 }
662
IsValidBrUlaPrefix(const Ip6::Prefix & aBrUlaPrefix)663 bool RoutingManager::IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix)
664 {
665 return aBrUlaPrefix.mLength == kBrUlaPrefixLength && aBrUlaPrefix.mPrefix.mFields.m8[0] == 0xfd;
666 }
667
IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig & aOnMeshPrefixConfig)668 bool RoutingManager::IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig)
669 {
670 return IsValidOmrPrefix(aOnMeshPrefixConfig.GetPrefix()) && aOnMeshPrefixConfig.mOnMesh &&
671 aOnMeshPrefixConfig.mSlaac && aOnMeshPrefixConfig.mStable;
672 }
673
IsValidOmrPrefix(const Ip6::Prefix & aPrefix)674 bool RoutingManager::IsValidOmrPrefix(const Ip6::Prefix &aPrefix)
675 {
676 // Accept ULA/GUA prefixes with 64-bit length.
677 return (aPrefix.GetLength() == kOmrPrefixLength) && !aPrefix.IsLinkLocal() && !aPrefix.IsMulticast();
678 }
679
IsValidOnLinkPrefix(const PrefixInfoOption & aPio)680 bool RoutingManager::IsValidOnLinkPrefix(const PrefixInfoOption &aPio)
681 {
682 Ip6::Prefix prefix;
683
684 aPio.GetPrefix(prefix);
685
686 return IsValidOnLinkPrefix(prefix) && aPio.IsOnLinkFlagSet() && aPio.IsAutoAddrConfigFlagSet();
687 }
688
IsValidOnLinkPrefix(const Ip6::Prefix & aOnLinkPrefix)689 bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix)
690 {
691 return (aOnLinkPrefix.GetLength() == kOnLinkPrefixLength) && !aOnLinkPrefix.IsLinkLocal() &&
692 !aOnLinkPrefix.IsMulticast();
693 }
694
HandleRsSenderFinished(TimeMilli aStartTime)695 void RoutingManager::HandleRsSenderFinished(TimeMilli aStartTime)
696 {
697 // This is a callback from `RsSender` and is invoked when it
698 // finishes a cycle of sending Router Solicitations. `aStartTime`
699 // specifies the start time of the RS transmission cycle.
700 //
701 // We remove or deprecate old entries in discovered table that are
702 // not refreshed during Router Solicitation. We also invalidate
703 // the learned RA header if it is not refreshed during Router
704 // Solicitation.
705
706 mRxRaTracker.RemoveOrDeprecateOldEntries(aStartTime);
707 ScheduleRoutingPolicyEvaluation(kImmediately);
708 }
709
HandleRouterSolicit(const InfraIf::Icmp6Packet & aPacket,const Ip6::Address & aSrcAddress)710 void RoutingManager::HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
711 {
712 OT_UNUSED_VARIABLE(aPacket);
713 OT_UNUSED_VARIABLE(aSrcAddress);
714
715 Get<Ip6::Ip6>().GetBorderRoutingCounters().mRsRx++;
716 LogInfo("Received RS from %s on %s", aSrcAddress.ToString().AsCString(), mInfraIf.ToString().AsCString());
717
718 ScheduleRoutingPolicyEvaluation(kToReplyToRs);
719 }
720
HandleNeighborAdvertisement(const InfraIf::Icmp6Packet & aPacket)721 void RoutingManager::HandleNeighborAdvertisement(const InfraIf::Icmp6Packet &aPacket)
722 {
723 const NeighborAdvertMessage *naMsg;
724
725 VerifyOrExit(aPacket.GetLength() >= sizeof(NeighborAdvertMessage));
726 naMsg = reinterpret_cast<const NeighborAdvertMessage *>(aPacket.GetBytes());
727
728 mRxRaTracker.ProcessNeighborAdvertMessage(*naMsg);
729
730 exit:
731 return;
732 }
733
HandleRouterAdvertisement(const InfraIf::Icmp6Packet & aPacket,const Ip6::Address & aSrcAddress)734 void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
735 {
736 RouterAdvert::RxMessage raMsg(aPacket);
737 RouterAdvOrigin raOrigin = kAnotherRouter;
738
739 OT_ASSERT(mIsRunning);
740
741 VerifyOrExit(raMsg.IsValid());
742
743 Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaRx++;
744
745 if (mInfraIf.HasAddress(aSrcAddress))
746 {
747 raOrigin = mTxRaInfo.IsRaFromManager(raMsg) ? kThisBrRoutingManager : kThisBrOtherEntity;
748 }
749
750 LogInfo("Received RA from %s on %s %s", aSrcAddress.ToString().AsCString(), mInfraIf.ToString().AsCString(),
751 RouterAdvOriginToString(raOrigin));
752
753 DumpDebg("[BR-CERT] direction=recv | type=RA |", aPacket.GetBytes(), aPacket.GetLength());
754
755 mRxRaTracker.ProcessRouterAdvertMessage(raMsg, aSrcAddress, raOrigin);
756
757 exit:
758 return;
759 }
760
HandleRaPrefixTableChanged(void)761 void RoutingManager::HandleRaPrefixTableChanged(void)
762 {
763 // This is a callback from `mRxRaTracker` indicating that
764 // there has been a change in the table.
765
766 VerifyOrExit(mIsRunning);
767
768 mOnLinkPrefixManager.HandleRaPrefixTableChanged();
769 mRoutePublisher.Evaluate();
770
771 exit:
772 return;
773 }
774
HandleLocalOnLinkPrefixChanged(void)775 void RoutingManager::HandleLocalOnLinkPrefixChanged(void)
776 {
777 // This is a callback from `OnLinkPrefixManager` indicating
778 // that the local on-link prefix is changed. The local on-link
779 // prefix is derived from extended PAN ID.
780
781 VerifyOrExit(mIsRunning);
782
783 mRoutePublisher.Evaluate();
784 mRxRaTracker.HandleLocalOnLinkPrefixChanged();
785 ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
786
787 exit:
788 return;
789 }
790
NetworkDataContainsUlaRoute(void) const791 bool RoutingManager::NetworkDataContainsUlaRoute(void) const
792 {
793 // Determine whether leader Network Data contains a route
794 // prefix which is either the ULA prefix `fc00::/7` or
795 // a sub-prefix of it (e.g., default route).
796
797 NetworkData::Iterator iterator = NetworkData::kIteratorInit;
798 NetworkData::ExternalRouteConfig routeConfig;
799 bool contains = false;
800
801 while (Get<NetworkData::Leader>().GetNextExternalRoute(iterator, routeConfig) == kErrorNone)
802 {
803 if (routeConfig.mStable && RoutePublisher::GetUlaPrefix().ContainsPrefix(routeConfig.GetPrefix()))
804 {
805 contains = true;
806 break;
807 }
808 }
809
810 return contains;
811 }
812
813 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
814
LogPrefixInfoOption(const Ip6::Prefix & aPrefix,uint32_t aValidLifetime,uint32_t aPreferredLifetime)815 void RoutingManager::LogPrefixInfoOption(const Ip6::Prefix &aPrefix,
816 uint32_t aValidLifetime,
817 uint32_t aPreferredLifetime)
818 {
819 LogInfo("- PIO %s (valid:%lu, preferred:%lu)", aPrefix.ToString().AsCString(), ToUlong(aValidLifetime),
820 ToUlong(aPreferredLifetime));
821 }
822
LogRouteInfoOption(const Ip6::Prefix & aPrefix,uint32_t aLifetime,RoutePreference aPreference)823 void RoutingManager::LogRouteInfoOption(const Ip6::Prefix &aPrefix, uint32_t aLifetime, RoutePreference aPreference)
824 {
825 LogInfo("- RIO %s (lifetime:%lu, prf:%s)", aPrefix.ToString().AsCString(), ToUlong(aLifetime),
826 RoutePreferenceToString(aPreference));
827 }
828
RouterAdvOriginToString(RouterAdvOrigin aRaOrigin)829 const char *RoutingManager::RouterAdvOriginToString(RouterAdvOrigin aRaOrigin)
830 {
831 static const char *const kOriginStrings[] = {
832 "", // (0) kAnotherRouter
833 "(this BR routing-manager)", // (1) kThisBrRoutingManager
834 "(this BR other sw entity)", // (2) kThisBrOtherEntity
835 };
836
837 static_assert(0 == kAnotherRouter, "kAnotherRouter value is incorrect");
838 static_assert(1 == kThisBrRoutingManager, "kThisBrRoutingManager value is incorrect");
839 static_assert(2 == kThisBrOtherEntity, "kThisBrOtherEntity value is incorrect");
840
841 return kOriginStrings[aRaOrigin];
842 }
843
844 #else
845
LogPrefixInfoOption(const Ip6::Prefix &,uint32_t,uint32_t)846 void RoutingManager::LogPrefixInfoOption(const Ip6::Prefix &, uint32_t, uint32_t) {}
LogRouteInfoOption(const Ip6::Prefix &,uint32_t,RoutePreference)847 void RoutingManager::LogRouteInfoOption(const Ip6::Prefix &, uint32_t, RoutePreference) {}
848
849 #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
850
851 //---------------------------------------------------------------------------------------------------------------------
852 // LifetimedPrefix
853
CalculateExpirationTime(uint32_t aLifetime) const854 TimeMilli RoutingManager::LifetimedPrefix::CalculateExpirationTime(uint32_t aLifetime) const
855 {
856 // `aLifetime` is in unit of seconds. This method ensures
857 // that the time calculation fits with `TimeMilli` range.
858
859 return RoutingManager::CalculateExpirationTime(mLastUpdateTime, aLifetime);
860 }
861
862 //---------------------------------------------------------------------------------------------------------------------
863 // OnLinkPrefix
864
SetFrom(const PrefixInfoOption & aPio)865 void RoutingManager::OnLinkPrefix::SetFrom(const PrefixInfoOption &aPio)
866 {
867 aPio.GetPrefix(mPrefix);
868 mValidLifetime = aPio.GetValidLifetime();
869 mPreferredLifetime = aPio.GetPreferredLifetime();
870 mLastUpdateTime = TimerMilli::GetNow();
871 }
872
SetFrom(const PrefixTableEntry & aPrefixTableEntry)873 void RoutingManager::OnLinkPrefix::SetFrom(const PrefixTableEntry &aPrefixTableEntry)
874 {
875 mPrefix = AsCoreType(&aPrefixTableEntry.mPrefix);
876 mValidLifetime = aPrefixTableEntry.mValidLifetime;
877 mPreferredLifetime = aPrefixTableEntry.mPreferredLifetime;
878 mLastUpdateTime = TimerMilli::GetNow();
879 }
880
IsDeprecated(void) const881 bool RoutingManager::OnLinkPrefix::IsDeprecated(void) const { return GetDeprecationTime() <= TimerMilli::GetNow(); }
882
GetDeprecationTime(void) const883 TimeMilli RoutingManager::OnLinkPrefix::GetDeprecationTime(void) const
884 {
885 return CalculateExpirationTime(mPreferredLifetime);
886 }
887
GetStaleTime(void) const888 TimeMilli RoutingManager::OnLinkPrefix::GetStaleTime(void) const
889 {
890 return CalculateExpirationTime(Min(kStaleTime, mPreferredLifetime));
891 }
892
AdoptValidAndPreferredLifetimesFrom(const OnLinkPrefix & aPrefix)893 void RoutingManager::OnLinkPrefix::AdoptValidAndPreferredLifetimesFrom(const OnLinkPrefix &aPrefix)
894 {
895 constexpr uint32_t kTwoHoursInSeconds = 2 * 3600;
896
897 // Per RFC 4862 section 5.5.3.e:
898 //
899 // 1. If the received Valid Lifetime is greater than 2 hours or
900 // greater than RemainingLifetime, set the valid lifetime of the
901 // corresponding address to the advertised Valid Lifetime.
902 // 2. If RemainingLifetime is less than or equal to 2 hours, ignore
903 // the Prefix Information option with regards to the valid
904 // lifetime, unless ...
905 // 3. Otherwise, reset the valid lifetime of the corresponding
906 // address to 2 hours.
907
908 if (aPrefix.mValidLifetime > kTwoHoursInSeconds || aPrefix.GetExpireTime() > GetExpireTime())
909 {
910 mValidLifetime = aPrefix.mValidLifetime;
911 }
912 else if (GetExpireTime() > TimerMilli::GetNow() + TimeMilli::SecToMsec(kTwoHoursInSeconds))
913 {
914 mValidLifetime = kTwoHoursInSeconds;
915 }
916
917 mPreferredLifetime = aPrefix.GetPreferredLifetime();
918 mLastUpdateTime = aPrefix.GetLastUpdateTime();
919 }
920
CopyInfoTo(PrefixTableEntry & aEntry,TimeMilli aNow) const921 void RoutingManager::OnLinkPrefix::CopyInfoTo(PrefixTableEntry &aEntry, TimeMilli aNow) const
922 {
923 aEntry.mPrefix = GetPrefix();
924 aEntry.mIsOnLink = true;
925 aEntry.mMsecSinceLastUpdate = aNow - GetLastUpdateTime();
926 aEntry.mValidLifetime = GetValidLifetime();
927 aEntry.mPreferredLifetime = GetPreferredLifetime();
928 }
929
930 //---------------------------------------------------------------------------------------------------------------------
931 // RoutePrefix
932
SetFrom(const RouteInfoOption & aRio)933 void RoutingManager::RoutePrefix::SetFrom(const RouteInfoOption &aRio)
934 {
935 aRio.GetPrefix(mPrefix);
936 mValidLifetime = aRio.GetRouteLifetime();
937 mRoutePreference = aRio.GetPreference();
938 mLastUpdateTime = TimerMilli::GetNow();
939 }
940
SetFrom(const RouterAdvert::Header & aRaHeader)941 void RoutingManager::RoutePrefix::SetFrom(const RouterAdvert::Header &aRaHeader)
942 {
943 mPrefix.Clear();
944 mValidLifetime = aRaHeader.GetRouterLifetime();
945 mRoutePreference = aRaHeader.GetDefaultRouterPreference();
946 mLastUpdateTime = TimerMilli::GetNow();
947 }
948
GetStaleTime(void) const949 TimeMilli RoutingManager::RoutePrefix::GetStaleTime(void) const
950 {
951 return CalculateExpirationTime(Min(kStaleTime, mValidLifetime));
952 }
953
CopyInfoTo(PrefixTableEntry & aEntry,TimeMilli aNow) const954 void RoutingManager::RoutePrefix::CopyInfoTo(PrefixTableEntry &aEntry, TimeMilli aNow) const
955 {
956 aEntry.mPrefix = GetPrefix();
957 aEntry.mIsOnLink = false;
958 aEntry.mMsecSinceLastUpdate = aNow - GetLastUpdateTime();
959 aEntry.mValidLifetime = GetValidLifetime();
960 aEntry.mPreferredLifetime = 0;
961 aEntry.mRoutePreference = static_cast<otRoutePreference>(GetRoutePreference());
962 }
963
964 //---------------------------------------------------------------------------------------------------------------------
965 // RxRaTracker
966
RxRaTracker(Instance & aInstance)967 RoutingManager::RxRaTracker::RxRaTracker(Instance &aInstance)
968 : InstanceLocator(aInstance)
969 , mExpirationTimer(aInstance)
970 , mStaleTimer(aInstance)
971 , mRouterTimer(aInstance)
972 , mSignalTask(aInstance)
973 {
974 mLocalRaHeader.Clear();
975 }
976
Start(void)977 void RoutingManager::RxRaTracker::Start(void) { HandleNetDataChange(); }
978
Stop(void)979 void RoutingManager::RxRaTracker::Stop(void)
980 {
981 mRouters.Free();
982 mLocalRaHeader.Clear();
983
984 mExpirationTimer.Stop();
985 mStaleTimer.Stop();
986 mRouterTimer.Stop();
987
988 SignalTableChanged();
989 }
990
ProcessRouterAdvertMessage(const RouterAdvert::RxMessage & aRaMessage,const Ip6::Address & aSrcAddress,RouterAdvOrigin aRaOrigin)991 void RoutingManager::RxRaTracker::ProcessRouterAdvertMessage(const RouterAdvert::RxMessage &aRaMessage,
992 const Ip6::Address &aSrcAddress,
993 RouterAdvOrigin aRaOrigin)
994 {
995 // Process a received RA message and update the prefix table.
996
997 Router *router;
998
999 VerifyOrExit(aRaOrigin != kThisBrRoutingManager);
1000
1001 router = mRouters.FindMatching(aSrcAddress);
1002
1003 if (router == nullptr)
1004 {
1005 Entry<Router> *newEntry = AllocateEntry<Router>();
1006
1007 if (newEntry == nullptr)
1008 {
1009 LogWarn("Received RA from too many routers, ignore RA from %s", aSrcAddress.ToString().AsCString());
1010 ExitNow();
1011 }
1012
1013 router = newEntry;
1014 router->Clear();
1015 router->mAddress = aSrcAddress;
1016
1017 mRouters.Push(*newEntry);
1018 }
1019
1020 // RA message can indicate router provides default route in the RA
1021 // message header and can also include an RIO for `::/0`. When
1022 // processing an RA message, the preference and lifetime values
1023 // in a `::/0` RIO override the preference and lifetime values in
1024 // the RA header (per RFC 4191 section 3.1).
1025
1026 ProcessRaHeader(aRaMessage.GetHeader(), *router, aRaOrigin);
1027
1028 for (const Option &option : aRaMessage)
1029 {
1030 switch (option.GetType())
1031 {
1032 case Option::kTypePrefixInfo:
1033 ProcessPrefixInfoOption(static_cast<const PrefixInfoOption &>(option), *router);
1034 break;
1035
1036 case Option::kTypeRouteInfo:
1037 ProcessRouteInfoOption(static_cast<const RouteInfoOption &>(option), *router);
1038 break;
1039
1040 case Option::kTypeRaFlagsExtension:
1041 ProcessRaFlagsExtOption(static_cast<const RaFlagsExtOption &>(option), *router);
1042 break;
1043
1044 default:
1045 break;
1046 }
1047 }
1048
1049 router->mIsLocalDevice = (aRaOrigin == kThisBrOtherEntity);
1050
1051 UpdateRouterOnRx(*router);
1052
1053 RemoveRoutersWithNoEntriesOrFlags();
1054
1055 exit:
1056 return;
1057 }
1058
ProcessRaHeader(const RouterAdvert::Header & aRaHeader,Router & aRouter,RouterAdvOrigin aRaOrigin)1059 void RoutingManager::RxRaTracker::ProcessRaHeader(const RouterAdvert::Header &aRaHeader,
1060 Router &aRouter,
1061 RouterAdvOrigin aRaOrigin)
1062 {
1063 bool managedFlag = aRaHeader.IsManagedAddressConfigFlagSet();
1064 bool otherFlag = aRaHeader.IsOtherConfigFlagSet();
1065 Entry<RoutePrefix> *entry;
1066 Ip6::Prefix prefix;
1067
1068 LogInfo("- RA Header - flags - M:%u O:%u", managedFlag, otherFlag);
1069
1070 if (aRouter.mManagedAddressConfigFlag != managedFlag)
1071 {
1072 aRouter.mManagedAddressConfigFlag = managedFlag;
1073 SignalTableChanged();
1074 }
1075
1076 if (aRouter.mOtherConfigFlag != otherFlag)
1077 {
1078 aRouter.mOtherConfigFlag = otherFlag;
1079 SignalTableChanged();
1080 }
1081
1082 if (aRaOrigin == kThisBrOtherEntity)
1083 {
1084 // Update `mLocalRaHeader`, which tracks the RA header of
1085 // locally generated RA by another sw entity running on this
1086 // device.
1087
1088 RouterAdvert::Header oldHeader = mLocalRaHeader;
1089
1090 if (aRaHeader.GetRouterLifetime() == 0)
1091 {
1092 mLocalRaHeader.Clear();
1093 }
1094 else
1095 {
1096 mLocalRaHeader = aRaHeader;
1097 mLocalRaHeaderUpdateTime = TimerMilli::GetNow();
1098
1099 // The checksum is set to zero which indicates to platform
1100 // that it needs to do the calculation and update it.
1101
1102 mLocalRaHeader.SetChecksum(0);
1103 }
1104
1105 if (mLocalRaHeader != oldHeader)
1106 {
1107 SignalTableChanged();
1108 Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
1109 }
1110 }
1111
1112 prefix.Clear();
1113 entry = aRouter.mRoutePrefixes.FindMatching(prefix);
1114
1115 LogInfo("- RA Header - default route - lifetime:%u", aRaHeader.GetRouterLifetime());
1116
1117 if (entry == nullptr)
1118 {
1119 VerifyOrExit(aRaHeader.GetRouterLifetime() != 0);
1120
1121 entry = AllocateEntry<RoutePrefix>();
1122
1123 if (entry == nullptr)
1124 {
1125 LogWarn("Discovered too many prefixes, ignore default route from RA header");
1126 ExitNow();
1127 }
1128
1129 entry->SetFrom(aRaHeader);
1130 aRouter.mRoutePrefixes.Push(*entry);
1131 }
1132 else
1133 {
1134 entry->SetFrom(aRaHeader);
1135 }
1136
1137 mExpirationTimer.FireAtIfEarlier(entry->GetExpireTime());
1138
1139 SignalTableChanged();
1140
1141 exit:
1142 return;
1143 }
1144
ProcessPrefixInfoOption(const PrefixInfoOption & aPio,Router & aRouter)1145 void RoutingManager::RxRaTracker::ProcessPrefixInfoOption(const PrefixInfoOption &aPio, Router &aRouter)
1146 {
1147 Ip6::Prefix prefix;
1148 Entry<OnLinkPrefix> *entry;
1149
1150 VerifyOrExit(aPio.IsValid());
1151 aPio.GetPrefix(prefix);
1152
1153 if (!IsValidOnLinkPrefix(aPio))
1154 {
1155 LogInfo("- PIO %s - ignore since not a valid on-link prefix", prefix.ToString().AsCString());
1156 ExitNow();
1157 }
1158
1159 VerifyOrExit(prefix != Get<RoutingManager>().mOnLinkPrefixManager.GetLocalPrefix());
1160
1161 LogPrefixInfoOption(prefix, aPio.GetValidLifetime(), aPio.GetPreferredLifetime());
1162
1163 entry = aRouter.mOnLinkPrefixes.FindMatching(prefix);
1164
1165 if (entry == nullptr)
1166 {
1167 VerifyOrExit(aPio.GetValidLifetime() != 0);
1168
1169 entry = AllocateEntry<OnLinkPrefix>();
1170
1171 if (entry == nullptr)
1172 {
1173 LogWarn("Discovered too many prefixes, ignore on-link prefix %s", prefix.ToString().AsCString());
1174 ExitNow();
1175 }
1176
1177 entry->SetFrom(aPio);
1178 aRouter.mOnLinkPrefixes.Push(*entry);
1179 }
1180 else
1181 {
1182 OnLinkPrefix newPrefix;
1183
1184 newPrefix.SetFrom(aPio);
1185 entry->AdoptValidAndPreferredLifetimesFrom(newPrefix);
1186 }
1187
1188 mExpirationTimer.FireAtIfEarlier(entry->GetExpireTime());
1189
1190 SignalTableChanged();
1191
1192 exit:
1193 return;
1194 }
1195
ProcessRouteInfoOption(const RouteInfoOption & aRio,Router & aRouter)1196 void RoutingManager::RxRaTracker::ProcessRouteInfoOption(const RouteInfoOption &aRio, Router &aRouter)
1197 {
1198 Ip6::Prefix prefix;
1199 Entry<RoutePrefix> *entry;
1200
1201 VerifyOrExit(aRio.IsValid());
1202 aRio.GetPrefix(prefix);
1203
1204 VerifyOrExit(!prefix.IsLinkLocal() && !prefix.IsMulticast());
1205
1206 VerifyOrExit(Get<RoutingManager>().mOmrPrefixManager.GetLocalPrefix().GetPrefix() != prefix);
1207
1208 // Disregard our own advertised OMR prefixes and those currently
1209 // present in the Thread Network Data.
1210 //
1211 // There should be eventual parity between the `RioAdvertiser`
1212 // prefixes and the OMR prefixes in Network Data, but temporary
1213 // discrepancies can occur due to the tx timing of RAs and time
1214 // required to update Network Data (registering with leader). So
1215 // both checks are necessary.
1216
1217 VerifyOrExit(!Get<RoutingManager>().mRioAdvertiser.HasAdvertised(prefix));
1218 VerifyOrExit(!Get<NetworkData::Leader>().ContainsOmrPrefix(prefix));
1219
1220 LogRouteInfoOption(prefix, aRio.GetRouteLifetime(), aRio.GetPreference());
1221
1222 entry = aRouter.mRoutePrefixes.FindMatching(prefix);
1223
1224 if (entry == nullptr)
1225 {
1226 VerifyOrExit(aRio.GetRouteLifetime() != 0);
1227
1228 entry = AllocateEntry<RoutePrefix>();
1229
1230 if (entry == nullptr)
1231 {
1232 LogWarn("Discovered too many prefixes, ignore route prefix %s", prefix.ToString().AsCString());
1233 ExitNow();
1234 }
1235
1236 entry->SetFrom(aRio);
1237 aRouter.mRoutePrefixes.Push(*entry);
1238 }
1239 else
1240 {
1241 entry->SetFrom(aRio);
1242 }
1243
1244 mExpirationTimer.FireAtIfEarlier(entry->GetExpireTime());
1245
1246 SignalTableChanged();
1247
1248 exit:
1249 return;
1250 }
1251
ProcessRaFlagsExtOption(const RaFlagsExtOption & aRaFlagsOption,Router & aRouter)1252 void RoutingManager::RxRaTracker::ProcessRaFlagsExtOption(const RaFlagsExtOption &aRaFlagsOption, Router &aRouter)
1253 {
1254 VerifyOrExit(aRaFlagsOption.IsValid());
1255 aRouter.mStubRouterFlag = aRaFlagsOption.IsStubRouterFlagSet();
1256
1257 LogInfo("- FlagsExt - StubRouter:%u", aRouter.mStubRouterFlag);
1258
1259 exit:
1260 return;
1261 }
1262
1263 #if !OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE
1264
1265 template <>
AllocateEntry(void)1266 RoutingManager::RxRaTracker::Entry<RoutingManager::RxRaTracker::Router> *RoutingManager::RxRaTracker::AllocateEntry(
1267 void)
1268 {
1269 Entry<Router> *router = mRouterPool.Allocate();
1270
1271 VerifyOrExit(router != nullptr);
1272 router->Init(GetInstance());
1273
1274 exit:
1275 return router;
1276 }
1277
1278 template <class PrefixType>
AllocateEntry(void)1279 RoutingManager::RxRaTracker::Entry<PrefixType> *RoutingManager::RxRaTracker::AllocateEntry(void)
1280 {
1281 static_assert(TypeTraits::IsSame<PrefixType, OnLinkPrefix>::kValue ||
1282 TypeTraits::IsSame<PrefixType, RoutePrefix>::kValue,
1283 "PrefixType MSUT be either RoutePrefix or OnLinkPrefix");
1284
1285 Entry<PrefixType> *entry = nullptr;
1286 SharedEntry *sharedEntry = mEntryPool.Allocate();
1287
1288 VerifyOrExit(sharedEntry != nullptr);
1289 entry = &sharedEntry->GetEntry<PrefixType>();
1290 entry->Init(GetInstance());
1291
1292 exit:
1293 return entry;
1294 }
1295
Free(void)1296 template <> void RoutingManager::RxRaTracker::Entry<RoutingManager::RxRaTracker::Router>::Free(void)
1297 {
1298 mOnLinkPrefixes.Free();
1299 mRoutePrefixes.Free();
1300 Get<RoutingManager>().mRxRaTracker.mRouterPool.Free(*this);
1301 }
1302
Free(void)1303 template <class PrefixType> void RoutingManager::RxRaTracker::Entry<PrefixType>::Free(void)
1304 {
1305 static_assert(TypeTraits::IsSame<PrefixType, OnLinkPrefix>::kValue ||
1306 TypeTraits::IsSame<PrefixType, RoutePrefix>::kValue,
1307 "PrefixType MSUT be either RoutePrefix or OnLinkPrefix");
1308
1309 Get<RoutingManager>().mRxRaTracker.mEntryPool.Free(*reinterpret_cast<SharedEntry *>(this));
1310 }
1311
1312 #endif // !OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE
1313
ContainsDefaultOrNonUlaRoutePrefix(void) const1314 bool RoutingManager::RxRaTracker::ContainsDefaultOrNonUlaRoutePrefix(void) const
1315 {
1316 bool contains = false;
1317
1318 for (const Router &router : mRouters)
1319 {
1320 if (router.mRoutePrefixes.ContainsMatching(RoutePrefix::kIsNotUla))
1321 {
1322 contains = true;
1323 break;
1324 }
1325 }
1326
1327 return contains;
1328 }
1329
ContainsOnLinkPrefix(OnLinkPrefix::UlaChecker aUlaChecker) const1330 bool RoutingManager::RxRaTracker::ContainsOnLinkPrefix(OnLinkPrefix::UlaChecker aUlaChecker) const
1331 {
1332 bool contains = false;
1333
1334 for (const Router &router : mRouters)
1335 {
1336 if (router.mOnLinkPrefixes.ContainsMatching(aUlaChecker))
1337 {
1338 contains = true;
1339 break;
1340 }
1341 }
1342
1343 return contains;
1344 }
1345
ContainsNonUlaOnLinkPrefix(void) const1346 bool RoutingManager::RxRaTracker::ContainsNonUlaOnLinkPrefix(void) const
1347 {
1348 return ContainsOnLinkPrefix(OnLinkPrefix::kIsNotUla);
1349 }
1350
ContainsUlaOnLinkPrefix(void) const1351 bool RoutingManager::RxRaTracker::ContainsUlaOnLinkPrefix(void) const
1352 {
1353 return ContainsOnLinkPrefix(OnLinkPrefix::kIsUla);
1354 }
1355
FindFavoredOnLinkPrefix(Ip6::Prefix & aPrefix) const1356 void RoutingManager::RxRaTracker::FindFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const
1357 {
1358 // Find the smallest preferred on-link prefix entry in the table
1359 // and return it in `aPrefix`. If there is none, `aPrefix` is
1360 // cleared (prefix length is set to zero).
1361
1362 aPrefix.Clear();
1363
1364 for (const Router &router : mRouters)
1365 {
1366 for (const OnLinkPrefix &entry : router.mOnLinkPrefixes)
1367 {
1368 if (entry.IsDeprecated() || (entry.GetPreferredLifetime() < kFavoredOnLinkPrefixMinPreferredLifetime))
1369 {
1370 continue;
1371 }
1372
1373 if ((aPrefix.GetLength() == 0) || (entry.GetPrefix() < aPrefix))
1374 {
1375 aPrefix = entry.GetPrefix();
1376 }
1377 }
1378 }
1379 }
1380
HandleLocalOnLinkPrefixChanged(void)1381 void RoutingManager::RxRaTracker::HandleLocalOnLinkPrefixChanged(void)
1382 {
1383 const Ip6::Prefix &prefix = Get<RoutingManager>().mOnLinkPrefixManager.GetLocalPrefix();
1384 bool didRemove = false;
1385
1386 for (Router &router : mRouters)
1387 {
1388 didRemove |= router.mOnLinkPrefixes.RemoveAndFreeAllMatching(prefix);
1389 }
1390
1391 VerifyOrExit(didRemove);
1392
1393 RemoveRoutersWithNoEntriesOrFlags();
1394
1395 SignalTableChanged();
1396
1397 exit:
1398 return;
1399 }
1400
HandleNetDataChange(void)1401 void RoutingManager::RxRaTracker::HandleNetDataChange(void)
1402 {
1403 NetworkData::Iterator iterator = NetworkData::kIteratorInit;
1404 NetworkData::OnMeshPrefixConfig prefixConfig;
1405 bool didRemove = false;
1406
1407 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
1408 {
1409 if (!IsValidOmrPrefix(prefixConfig))
1410 {
1411 continue;
1412 }
1413
1414 for (Router &router : mRouters)
1415 {
1416 didRemove |= router.mRoutePrefixes.RemoveAndFreeAllMatching(prefixConfig.GetPrefix());
1417 }
1418 }
1419
1420 if (didRemove)
1421 {
1422 RemoveRoutersWithNoEntriesOrFlags();
1423 SignalTableChanged();
1424 }
1425 }
1426
RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold)1427 void RoutingManager::RxRaTracker::RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold)
1428 {
1429 // Remove route prefix entries and deprecate on-link entries in
1430 // the table that are old (not updated since `aTimeThreshold`).
1431
1432 for (Router &router : mRouters)
1433 {
1434 for (OnLinkPrefix &entry : router.mOnLinkPrefixes)
1435 {
1436 if (entry.GetLastUpdateTime() <= aTimeThreshold)
1437 {
1438 entry.ClearPreferredLifetime();
1439 SignalTableChanged();
1440 }
1441 }
1442
1443 for (RoutePrefix &entry : router.mRoutePrefixes)
1444 {
1445 if (entry.GetLastUpdateTime() <= aTimeThreshold)
1446 {
1447 entry.ClearValidLifetime();
1448 SignalTableChanged();
1449 }
1450 }
1451 }
1452
1453 if (mLocalRaHeader.IsValid() && (mLocalRaHeaderUpdateTime <= aTimeThreshold))
1454 {
1455 mLocalRaHeader.Clear();
1456 SignalTableChanged();
1457 }
1458
1459 RemoveExpiredEntries();
1460 }
1461
RemoveOrDeprecateEntriesFromInactiveRouters(void)1462 void RoutingManager::RxRaTracker::RemoveOrDeprecateEntriesFromInactiveRouters(void)
1463 {
1464 // Remove route prefix entries and deprecate on-link prefix entries
1465 // in the table for routers that have reached the max NS probe
1466 // attempts and considered as inactive.
1467
1468 for (Router &router : mRouters)
1469 {
1470 if (router.mNsProbeCount <= Router::kMaxNsProbes)
1471 {
1472 continue;
1473 }
1474
1475 for (OnLinkPrefix &entry : router.mOnLinkPrefixes)
1476 {
1477 if (!entry.IsDeprecated())
1478 {
1479 entry.ClearPreferredLifetime();
1480 SignalTableChanged();
1481 }
1482 }
1483
1484 for (RoutePrefix &entry : router.mRoutePrefixes)
1485 {
1486 entry.ClearValidLifetime();
1487 }
1488 }
1489
1490 RemoveExpiredEntries();
1491 }
1492
ScheduleStaleTimer(void)1493 void RoutingManager::RxRaTracker::ScheduleStaleTimer(void)
1494 {
1495 // If multiple routers advertise the same on-link or route prefix,
1496 // the stale time for the prefix is determined by the latest stale
1497 // time among all corresponding entries.
1498 //
1499 // The "StaleTimeCalculated" flag is used to ensure stale time is
1500 // calculated only once for each unique prefix. Initially, this
1501 // flag is cleared on all entries. As we iterate over routers and
1502 // their entries, `DetermineStaleTimeFor()` will consider all
1503 // matching entries and mark "StaleTimeCalculated" flag on them.
1504
1505 NextFireTime staleTime;
1506
1507 for (Router &router : mRouters)
1508 {
1509 for (OnLinkPrefix &entry : router.mOnLinkPrefixes)
1510 {
1511 entry.SetStaleTimeCalculated(false);
1512 }
1513
1514 for (RoutePrefix &entry : router.mRoutePrefixes)
1515 {
1516 entry.SetStaleTimeCalculated(false);
1517 }
1518 }
1519
1520 for (const Router &router : mRouters)
1521 {
1522 for (const OnLinkPrefix &entry : router.mOnLinkPrefixes)
1523 {
1524 if (!entry.IsStaleTimeCalculated())
1525 {
1526 DetermineStaleTimeFor(entry, staleTime);
1527 }
1528 }
1529
1530 for (const RoutePrefix &entry : router.mRoutePrefixes)
1531 {
1532 if (!entry.IsStaleTimeCalculated())
1533 {
1534 DetermineStaleTimeFor(entry, staleTime);
1535 }
1536 }
1537 }
1538
1539 if (mLocalRaHeader.IsValid())
1540 {
1541 uint16_t interval = kStaleTime;
1542
1543 if (mLocalRaHeader.GetRouterLifetime() > 0)
1544 {
1545 interval = Min(interval, mLocalRaHeader.GetRouterLifetime());
1546 }
1547
1548 staleTime.UpdateIfEarlier(CalculateExpirationTime(mLocalRaHeaderUpdateTime, interval));
1549 }
1550
1551 mStaleTimer.FireAt(staleTime);
1552 }
1553
DetermineStaleTimeFor(const OnLinkPrefix & aPrefix,NextFireTime & aStaleTime)1554 void RoutingManager::RxRaTracker::DetermineStaleTimeFor(const OnLinkPrefix &aPrefix, NextFireTime &aStaleTime)
1555 {
1556 TimeMilli prefixStaleTime = aStaleTime.GetNow();
1557 bool found = false;
1558
1559 for (Router &router : mRouters)
1560 {
1561 for (OnLinkPrefix &entry : router.mOnLinkPrefixes)
1562 {
1563 if (!entry.Matches(aPrefix.GetPrefix()))
1564 {
1565 continue;
1566 }
1567
1568 entry.SetStaleTimeCalculated(true);
1569
1570 if (entry.IsDeprecated())
1571 {
1572 continue;
1573 }
1574
1575 prefixStaleTime = Max(prefixStaleTime, Max(aStaleTime.GetNow(), entry.GetStaleTime()));
1576 found = true;
1577 }
1578 }
1579
1580 if (found)
1581 {
1582 aStaleTime.UpdateIfEarlier(prefixStaleTime);
1583 }
1584 }
1585
DetermineStaleTimeFor(const RoutePrefix & aPrefix,NextFireTime & aStaleTime)1586 void RoutingManager::RxRaTracker::DetermineStaleTimeFor(const RoutePrefix &aPrefix, NextFireTime &aStaleTime)
1587 {
1588 TimeMilli prefixStaleTime = aStaleTime.GetNow();
1589 bool found = false;
1590
1591 for (Router &router : mRouters)
1592 {
1593 for (RoutePrefix &entry : router.mRoutePrefixes)
1594 {
1595 if (!entry.Matches(aPrefix.GetPrefix()))
1596 {
1597 continue;
1598 }
1599
1600 entry.SetStaleTimeCalculated(true);
1601
1602 prefixStaleTime = Max(prefixStaleTime, Max(aStaleTime.GetNow(), entry.GetStaleTime()));
1603 found = true;
1604 }
1605 }
1606
1607 if (found)
1608 {
1609 aStaleTime.UpdateIfEarlier(prefixStaleTime);
1610 }
1611 }
1612
HandleStaleTimer(void)1613 void RoutingManager::RxRaTracker::HandleStaleTimer(void)
1614 {
1615 VerifyOrExit(Get<RoutingManager>().IsRunning());
1616
1617 LogInfo("Stale timer expired");
1618 Get<RoutingManager>().mRsSender.Start();
1619
1620 exit:
1621 return;
1622 }
1623
RemoveRoutersWithNoEntriesOrFlags(void)1624 void RoutingManager::RxRaTracker::RemoveRoutersWithNoEntriesOrFlags(void)
1625 {
1626 mRouters.RemoveAndFreeAllMatching(Router::kContainsNoEntriesOrFlags);
1627 }
1628
HandleExpirationTimer(void)1629 void RoutingManager::RxRaTracker::HandleExpirationTimer(void) { RemoveExpiredEntries(); }
1630
RemoveExpiredEntries(void)1631 void RoutingManager::RxRaTracker::RemoveExpiredEntries(void)
1632 {
1633 NextFireTime nextExpireTime;
1634 bool didRemove = false;
1635
1636 for (Router &router : mRouters)
1637 {
1638 LifetimedPrefix::ExpirationChecker expirationChecker(nextExpireTime.GetNow());
1639
1640 didRemove |= router.mOnLinkPrefixes.RemoveAndFreeAllMatching(expirationChecker);
1641 didRemove |= router.mRoutePrefixes.RemoveAndFreeAllMatching(expirationChecker);
1642 }
1643
1644 RemoveRoutersWithNoEntriesOrFlags();
1645
1646 if (didRemove)
1647 {
1648 SignalTableChanged();
1649 }
1650
1651 // Determine the next expire time and schedule timer.
1652
1653 for (const Router &router : mRouters)
1654 {
1655 for (const OnLinkPrefix &entry : router.mOnLinkPrefixes)
1656 {
1657 nextExpireTime.UpdateIfEarlier(entry.GetExpireTime());
1658 }
1659
1660 for (const RoutePrefix &entry : router.mRoutePrefixes)
1661 {
1662 nextExpireTime.UpdateIfEarlier(entry.GetExpireTime());
1663 }
1664 }
1665
1666 mExpirationTimer.FireAt(nextExpireTime);
1667 }
1668
SignalTableChanged(void)1669 void RoutingManager::RxRaTracker::SignalTableChanged(void) { mSignalTask.Post(); }
1670
HandleSignalTask(void)1671 void RoutingManager::RxRaTracker::HandleSignalTask(void)
1672 {
1673 ScheduleStaleTimer();
1674 Get<RoutingManager>().HandleRaPrefixTableChanged();
1675 }
1676
ProcessNeighborAdvertMessage(const NeighborAdvertMessage & aNaMessage)1677 void RoutingManager::RxRaTracker::ProcessNeighborAdvertMessage(const NeighborAdvertMessage &aNaMessage)
1678 {
1679 Router *router;
1680
1681 VerifyOrExit(aNaMessage.IsValid());
1682
1683 router = mRouters.FindMatching(aNaMessage.GetTargetAddress());
1684 VerifyOrExit(router != nullptr);
1685
1686 LogInfo("Received NA from router %s", router->mAddress.ToString().AsCString());
1687
1688 UpdateRouterOnRx(*router);
1689
1690 exit:
1691 return;
1692 }
1693
UpdateRouterOnRx(Router & aRouter)1694 void RoutingManager::RxRaTracker::UpdateRouterOnRx(Router &aRouter)
1695 {
1696 aRouter.mNsProbeCount = 0;
1697 aRouter.mLastUpdateTime = TimerMilli::GetNow();
1698
1699 aRouter.mTimeout = aRouter.mLastUpdateTime + Random::NonCrypto::AddJitter(Router::kActiveTimeout, Router::kJitter);
1700 mRouterTimer.FireAtIfEarlier(aRouter.mTimeout);
1701 }
1702
HandleRouterTimer(void)1703 void RoutingManager::RxRaTracker::HandleRouterTimer(void)
1704 {
1705 NextFireTime nextTime;
1706
1707 for (Router &router : mRouters)
1708 {
1709 if (router.mNsProbeCount > Router::kMaxNsProbes)
1710 {
1711 continue;
1712 }
1713
1714 // Skip NS probes if the router is this device. This prevents
1715 // issues where the platform might not be able to receive and
1716 // process the NA messages from the local device itself.
1717
1718 if (router.mIsLocalDevice)
1719 {
1720 continue;
1721 }
1722
1723 if (router.mTimeout <= nextTime.GetNow())
1724 {
1725 router.mNsProbeCount++;
1726
1727 if (router.mNsProbeCount > Router::kMaxNsProbes)
1728 {
1729 LogInfo("No response to all Neighbor Solicitations attempts from router %s",
1730 router.mAddress.ToString().AsCString());
1731 continue;
1732 }
1733
1734 router.mTimeout =
1735 nextTime.GetNow() + ((router.mNsProbeCount < Router::kMaxNsProbes) ? Router::kNsProbeRetryInterval
1736 : Router::kNsProbeTimeout);
1737
1738 SendNeighborSolicitToRouter(router);
1739 }
1740
1741 nextTime.UpdateIfEarlier(router.mTimeout);
1742 }
1743
1744 RemoveOrDeprecateEntriesFromInactiveRouters();
1745
1746 mRouterTimer.FireAt(nextTime);
1747 }
1748
SendNeighborSolicitToRouter(const Router & aRouter)1749 void RoutingManager::RxRaTracker::SendNeighborSolicitToRouter(const Router &aRouter)
1750 {
1751 InfraIf::Icmp6Packet packet;
1752 NeighborSolicitMessage neighborSolicitMsg;
1753
1754 VerifyOrExit(!Get<RoutingManager>().mRsSender.IsInProgress());
1755
1756 neighborSolicitMsg.SetTargetAddress(aRouter.mAddress);
1757 packet.InitFrom(neighborSolicitMsg);
1758
1759 IgnoreError(Get<RoutingManager>().mInfraIf.Send(packet, aRouter.mAddress));
1760
1761 LogInfo("Sent Neighbor Solicitation to %s - attempt:%u/%u", aRouter.mAddress.ToString().AsCString(),
1762 aRouter.mNsProbeCount, Router::kMaxNsProbes);
1763
1764 exit:
1765 return;
1766 }
1767
DetermineAndSetFlags(RouterAdvert::Header & aHeader) const1768 void RoutingManager::RxRaTracker::DetermineAndSetFlags(RouterAdvert::Header &aHeader) const
1769 {
1770 // Determine the `M` and `O` flags to include in the RA message
1771 // header to be emitted.
1772 //
1773 // If any discovered router on infrastructure which is not itself a
1774 // stub router (e.g., another Thread BR) includes the `M` or `O`
1775 // flag, we also include the same flag.
1776 //
1777 // If a router has failed to respond to max number of NS probe
1778 // attempts, we consider it as offline and ignore its flags.
1779
1780 for (const Router &router : mRouters)
1781 {
1782 if (router.mStubRouterFlag)
1783 {
1784 continue;
1785 }
1786
1787 if (router.mNsProbeCount > Router::kMaxNsProbes)
1788 {
1789 continue;
1790 }
1791
1792 if (router.mManagedAddressConfigFlag)
1793 {
1794 aHeader.SetManagedAddressConfigFlag();
1795 }
1796
1797 if (router.mOtherConfigFlag)
1798 {
1799 aHeader.SetOtherConfigFlag();
1800 }
1801 }
1802 }
1803
InitIterator(PrefixTableIterator & aIterator) const1804 void RoutingManager::RxRaTracker::InitIterator(PrefixTableIterator &aIterator) const
1805 {
1806 static_cast<Iterator &>(aIterator).Init(mRouters.GetHead());
1807 }
1808
GetNextEntry(PrefixTableIterator & aIterator,PrefixTableEntry & aEntry) const1809 Error RoutingManager::RxRaTracker::GetNextEntry(PrefixTableIterator &aIterator, PrefixTableEntry &aEntry) const
1810 {
1811 Error error = kErrorNone;
1812 Iterator &iterator = static_cast<Iterator &>(aIterator);
1813
1814 ClearAllBytes(aEntry);
1815
1816 SuccessOrExit(error = iterator.AdvanceToNextEntry());
1817
1818 iterator.GetRouter()->CopyInfoTo(aEntry.mRouter, iterator.GetInitTime());
1819
1820 switch (iterator.GetEntryType())
1821 {
1822 case Iterator::kOnLinkPrefix:
1823 iterator.GetEntry<OnLinkPrefix>()->CopyInfoTo(aEntry, iterator.GetInitTime());
1824 break;
1825 case Iterator::kRoutePrefix:
1826 iterator.GetEntry<RoutePrefix>()->CopyInfoTo(aEntry, iterator.GetInitTime());
1827 break;
1828 }
1829
1830 exit:
1831 return error;
1832 }
1833
GetNextRouter(PrefixTableIterator & aIterator,RouterEntry & aEntry) const1834 Error RoutingManager::RxRaTracker::GetNextRouter(PrefixTableIterator &aIterator, RouterEntry &aEntry) const
1835 {
1836 Error error = kErrorNone;
1837 Iterator &iterator = static_cast<Iterator &>(aIterator);
1838
1839 ClearAllBytes(aEntry);
1840
1841 SuccessOrExit(error = iterator.AdvanceToNextRouter(Iterator::kRouterIterator));
1842 iterator.GetRouter()->CopyInfoTo(aEntry, iterator.GetInitTime());
1843
1844 exit:
1845 return error;
1846 }
1847
1848 //---------------------------------------------------------------------------------------------------------------------
1849 // RxRaTracker::Iterator
1850
Init(const Entry<Router> * aRoutersHead)1851 void RoutingManager::RxRaTracker::Iterator::Init(const Entry<Router> *aRoutersHead)
1852 {
1853 SetInitTime();
1854 SetType(kUnspecified);
1855 SetRouter(aRoutersHead);
1856 SetEntry(nullptr);
1857 SetEntryType(kRoutePrefix);
1858 }
1859
AdvanceToNextRouter(Type aType)1860 Error RoutingManager::RxRaTracker::Iterator::AdvanceToNextRouter(Type aType)
1861 {
1862 Error error = kErrorNone;
1863
1864 if (GetType() == kUnspecified)
1865 {
1866 // On the first call, when iterator type is `kUnspecified`, we
1867 // set the type, and keep the `GetRouter()` as is so to start
1868 // from the first router in the list.
1869
1870 SetType(aType);
1871 }
1872 else
1873 {
1874 // On subsequent call, we ensure that the iterator type
1875 // matches what we expect and advance to the next router on
1876 // the list.
1877
1878 VerifyOrExit(GetType() == aType, error = kErrorInvalidArgs);
1879 VerifyOrExit(GetRouter() != nullptr, error = kErrorNone);
1880 SetRouter(GetRouter()->GetNext());
1881 }
1882
1883 VerifyOrExit(GetRouter() != nullptr, error = kErrorNotFound);
1884
1885 exit:
1886 return error;
1887 }
1888
AdvanceToNextEntry(void)1889 Error RoutingManager::RxRaTracker::Iterator::AdvanceToNextEntry(void)
1890 {
1891 Error error = kErrorNone;
1892
1893 VerifyOrExit(GetRouter() != nullptr, error = kErrorNotFound);
1894
1895 if (HasEntry())
1896 {
1897 switch (GetEntryType())
1898 {
1899 case kOnLinkPrefix:
1900 SetEntry(GetEntry<OnLinkPrefix>()->GetNext());
1901 break;
1902 case kRoutePrefix:
1903 SetEntry(GetEntry<RoutePrefix>()->GetNext());
1904 break;
1905 }
1906 }
1907
1908 while (!HasEntry())
1909 {
1910 switch (GetEntryType())
1911 {
1912 case kOnLinkPrefix:
1913
1914 // Transition from on-link prefixes to route prefixes of
1915 // the current router.
1916
1917 SetEntry(GetRouter()->mRoutePrefixes.GetHead());
1918 SetEntryType(kRoutePrefix);
1919 break;
1920
1921 case kRoutePrefix:
1922
1923 // Transition to the next router and start with its on-link
1924 // prefixes.
1925 //
1926 // On the first call when iterator type is `kUnspecified`,
1927 // `AdvanceToNextRouter()` sets the type and starts from
1928 // the first router.
1929
1930 SuccessOrExit(error = AdvanceToNextRouter(kPrefixIterator));
1931 SetEntry(GetRouter()->mOnLinkPrefixes.GetHead());
1932 SetEntryType(kOnLinkPrefix);
1933 break;
1934 }
1935 }
1936
1937 exit:
1938 return error;
1939 }
1940
1941 //---------------------------------------------------------------------------------------------------------------------
1942 // RxRaTracker::Router
1943
Matches(EmptyChecker aChecker) const1944 bool RoutingManager::RxRaTracker::Router::Matches(EmptyChecker aChecker) const
1945 {
1946 // Checks whether or not a `Router` instance has any useful info. An
1947 // entry can be removed if it does not advertise M or O flags and
1948 // also does not have any advertised prefix entries (RIO/PIO). If
1949 // the router already failed to respond to max NS probe attempts,
1950 // we consider it as offline and therefore do not consider its
1951 // flags anymore.
1952
1953 OT_UNUSED_VARIABLE(aChecker);
1954
1955 bool hasFlags = false;
1956
1957 if (mNsProbeCount <= kMaxNsProbes)
1958 {
1959 hasFlags = (mManagedAddressConfigFlag || mOtherConfigFlag);
1960 }
1961
1962 return !hasFlags && mOnLinkPrefixes.IsEmpty() && mRoutePrefixes.IsEmpty();
1963 }
1964
CopyInfoTo(RouterEntry & aEntry,TimeMilli aNow) const1965 void RoutingManager::RxRaTracker::Router::CopyInfoTo(RouterEntry &aEntry, TimeMilli aNow) const
1966 {
1967 aEntry.mAddress = mAddress;
1968 aEntry.mMsecSinceLastUpdate = aNow - mLastUpdateTime;
1969 aEntry.mManagedAddressConfigFlag = mManagedAddressConfigFlag;
1970 aEntry.mOtherConfigFlag = mOtherConfigFlag;
1971 aEntry.mStubRouterFlag = mStubRouterFlag;
1972 aEntry.mIsLocalDevice = mIsLocalDevice;
1973 }
1974
1975 //---------------------------------------------------------------------------------------------------------------------
1976 // FavoredOmrPrefix
1977
IsInfrastructureDerived(void) const1978 bool RoutingManager::FavoredOmrPrefix::IsInfrastructureDerived(void) const
1979 {
1980 // Indicate whether the OMR prefix is infrastructure-derived which
1981 // can be identified as a valid OMR prefix with preference of
1982 // medium or higher.
1983
1984 return !IsEmpty() && (mPreference >= NetworkData::kRoutePreferenceMedium);
1985 }
1986
SetFrom(const NetworkData::OnMeshPrefixConfig & aOnMeshPrefixConfig)1987 void RoutingManager::FavoredOmrPrefix::SetFrom(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig)
1988 {
1989 mPrefix = aOnMeshPrefixConfig.GetPrefix();
1990 mPreference = aOnMeshPrefixConfig.GetPreference();
1991 mIsDomainPrefix = aOnMeshPrefixConfig.mDp;
1992 }
1993
SetFrom(const OmrPrefix & aOmrPrefix)1994 void RoutingManager::FavoredOmrPrefix::SetFrom(const OmrPrefix &aOmrPrefix)
1995 {
1996 mPrefix = aOmrPrefix.GetPrefix();
1997 mPreference = aOmrPrefix.GetPreference();
1998 mIsDomainPrefix = aOmrPrefix.IsDomainPrefix();
1999 }
2000
IsFavoredOver(const NetworkData::OnMeshPrefixConfig & aOmrPrefixConfig) const2001 bool RoutingManager::FavoredOmrPrefix::IsFavoredOver(const NetworkData::OnMeshPrefixConfig &aOmrPrefixConfig) const
2002 {
2003 // This method determines whether this OMR prefix is favored
2004 // over another prefix. A prefix with higher preference is
2005 // favored. If the preference is the same, then the smaller
2006 // prefix (in the sense defined by `Ip6::Prefix`) is favored.
2007
2008 bool isFavored = (mPreference > aOmrPrefixConfig.GetPreference());
2009
2010 OT_ASSERT(IsValidOmrPrefix(aOmrPrefixConfig));
2011
2012 if (mPreference == aOmrPrefixConfig.GetPreference())
2013 {
2014 isFavored = (mPrefix < aOmrPrefixConfig.GetPrefix());
2015 }
2016
2017 return isFavored;
2018 }
2019
2020 //---------------------------------------------------------------------------------------------------------------------
2021 // OmrPrefixManager
2022
OmrPrefixManager(Instance & aInstance)2023 RoutingManager::OmrPrefixManager::OmrPrefixManager(Instance &aInstance)
2024 : InstanceLocator(aInstance)
2025 , mIsLocalAddedInNetData(false)
2026 , mDefaultRoute(false)
2027 {
2028 }
2029
Init(const Ip6::Prefix & aBrUlaPrefix)2030 void RoutingManager::OmrPrefixManager::Init(const Ip6::Prefix &aBrUlaPrefix)
2031 {
2032 mGeneratedPrefix = aBrUlaPrefix;
2033 mGeneratedPrefix.SetSubnetId(kOmrPrefixSubnetId);
2034 mGeneratedPrefix.SetLength(kOmrPrefixLength);
2035
2036 LogInfo("Generated local OMR prefix: %s", mGeneratedPrefix.ToString().AsCString());
2037 }
2038
Start(void)2039 void RoutingManager::OmrPrefixManager::Start(void) { DetermineFavoredPrefix(); }
2040
Stop(void)2041 void RoutingManager::OmrPrefixManager::Stop(void)
2042 {
2043 RemoveLocalFromNetData();
2044 mFavoredPrefix.Clear();
2045 }
2046
DetermineFavoredPrefix(void)2047 void RoutingManager::OmrPrefixManager::DetermineFavoredPrefix(void)
2048 {
2049 // Determine the favored OMR prefix present in Network Data.
2050
2051 NetworkData::Iterator iterator = NetworkData::kIteratorInit;
2052 NetworkData::OnMeshPrefixConfig prefixConfig;
2053
2054 mFavoredPrefix.Clear();
2055
2056 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
2057 {
2058 if (!IsValidOmrPrefix(prefixConfig) || !prefixConfig.mPreferred)
2059 {
2060 continue;
2061 }
2062
2063 if (mFavoredPrefix.IsEmpty() || !mFavoredPrefix.IsFavoredOver(prefixConfig))
2064 {
2065 mFavoredPrefix.SetFrom(prefixConfig);
2066 }
2067 }
2068 }
2069
Evaluate(void)2070 void RoutingManager::OmrPrefixManager::Evaluate(void)
2071 {
2072 OT_ASSERT(Get<RoutingManager>().IsRunning());
2073
2074 DetermineFavoredPrefix();
2075
2076 // Determine the local prefix and remove outdated prefix published by us.
2077 #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
2078 if (Get<RoutingManager>().mPdPrefixManager.HasPrefix())
2079 {
2080 if (mLocalPrefix.GetPrefix() != Get<RoutingManager>().mPdPrefixManager.GetPrefix())
2081 {
2082 RemoveLocalFromNetData();
2083 mLocalPrefix.mPrefix = Get<RoutingManager>().mPdPrefixManager.GetPrefix();
2084 mLocalPrefix.mPreference = RoutePreference::kRoutePreferenceMedium;
2085 mLocalPrefix.mIsDomainPrefix = false;
2086 LogInfo("Setting local OMR prefix to PD prefix: %s", mLocalPrefix.GetPrefix().ToString().AsCString());
2087 }
2088 }
2089 else
2090 #endif
2091 if (mLocalPrefix.GetPrefix() != mGeneratedPrefix)
2092 {
2093 RemoveLocalFromNetData();
2094 mLocalPrefix.mPrefix = mGeneratedPrefix;
2095 mLocalPrefix.mPreference = RoutePreference::kRoutePreferenceLow;
2096 mLocalPrefix.mIsDomainPrefix = false;
2097 LogInfo("Setting local OMR prefix to generated prefix: %s", mLocalPrefix.GetPrefix().ToString().AsCString());
2098 }
2099
2100 // Decide if we need to add or remove our local OMR prefix.
2101 if (mFavoredPrefix.IsEmpty() || mFavoredPrefix.GetPreference() < mLocalPrefix.GetPreference())
2102 {
2103 if (mFavoredPrefix.IsEmpty())
2104 {
2105 LogInfo("No favored OMR prefix found in Thread network.");
2106 }
2107 else
2108 {
2109 LogInfo("Replacing favored OMR prefix %s with higher preference local prefix %s.",
2110 mFavoredPrefix.GetPrefix().ToString().AsCString(), mLocalPrefix.GetPrefix().ToString().AsCString());
2111 }
2112
2113 // The `mFavoredPrefix` remains empty if we fail to publish
2114 // the local OMR prefix.
2115 SuccessOrExit(AddLocalToNetData());
2116
2117 mFavoredPrefix.SetFrom(mLocalPrefix);
2118 }
2119 else if (mFavoredPrefix.GetPrefix() == mLocalPrefix.GetPrefix())
2120 {
2121 IgnoreError(AddLocalToNetData());
2122 }
2123 else if (mIsLocalAddedInNetData)
2124 {
2125 LogInfo("There is already a favored OMR prefix %s in the Thread network",
2126 mFavoredPrefix.GetPrefix().ToString().AsCString());
2127
2128 RemoveLocalFromNetData();
2129 }
2130
2131 exit:
2132 return;
2133 }
2134
ShouldAdvertiseLocalAsRio(void) const2135 bool RoutingManager::OmrPrefixManager::ShouldAdvertiseLocalAsRio(void) const
2136 {
2137 // Determines whether the local OMR prefix should be advertised as
2138 // RIO in emitted RAs. To advertise, we must have decided to
2139 // publish it, and it must already be added and present in the
2140 // Network Data. This ensures that we only advertise the local
2141 // OMR prefix in emitted RAs when, as a Border Router, we can
2142 // accept and route messages using an OMR-based address
2143 // destination, which requires the prefix to be present in
2144 // Network Data. Similarly, we stop advertising (and start
2145 // deprecating) the OMR prefix in RAs as soon as we decide to
2146 // remove it. After requesting its removal from Network Data, it
2147 // may still be present in Network Data for a short interval due
2148 // to delays in registering changes with the leader.
2149
2150 return mIsLocalAddedInNetData && Get<NetworkData::Leader>().ContainsOmrPrefix(mLocalPrefix.GetPrefix());
2151 }
2152
AddLocalToNetData(void)2153 Error RoutingManager::OmrPrefixManager::AddLocalToNetData(void)
2154 {
2155 Error error = kErrorNone;
2156
2157 VerifyOrExit(!mIsLocalAddedInNetData);
2158 SuccessOrExit(error = AddOrUpdateLocalInNetData());
2159 mIsLocalAddedInNetData = true;
2160
2161 exit:
2162 return error;
2163 }
2164
AddOrUpdateLocalInNetData(void)2165 Error RoutingManager::OmrPrefixManager::AddOrUpdateLocalInNetData(void)
2166 {
2167 // Add the local OMR prefix in Thread Network Data or update it
2168 // (e.g., change default route flag) if it is already added.
2169
2170 Error error;
2171 NetworkData::OnMeshPrefixConfig config;
2172
2173 config.Clear();
2174 config.mPrefix = mLocalPrefix.GetPrefix();
2175 config.mStable = true;
2176 config.mSlaac = true;
2177 config.mPreferred = true;
2178 config.mOnMesh = true;
2179 config.mDefaultRoute = mDefaultRoute;
2180 config.mPreference = mLocalPrefix.GetPreference();
2181
2182 error = Get<NetworkData::Local>().AddOnMeshPrefix(config);
2183
2184 if (error != kErrorNone)
2185 {
2186 LogWarn("Failed to %s %s in Thread Network Data: %s", !mIsLocalAddedInNetData ? "add" : "update",
2187 LocalToString().AsCString(), ErrorToString(error));
2188 ExitNow();
2189 }
2190
2191 Get<NetworkData::Notifier>().HandleServerDataUpdated();
2192
2193 LogInfo("%s %s in Thread Network Data", !mIsLocalAddedInNetData ? "Added" : "Updated", LocalToString().AsCString());
2194
2195 exit:
2196 return error;
2197 }
2198
RemoveLocalFromNetData(void)2199 void RoutingManager::OmrPrefixManager::RemoveLocalFromNetData(void)
2200 {
2201 Error error = kErrorNone;
2202
2203 VerifyOrExit(mIsLocalAddedInNetData);
2204
2205 error = Get<NetworkData::Local>().RemoveOnMeshPrefix(mLocalPrefix.GetPrefix());
2206
2207 if (error != kErrorNone)
2208 {
2209 LogWarn("Failed to remove %s from Thread Network Data: %s", LocalToString().AsCString(), ErrorToString(error));
2210 ExitNow();
2211 }
2212
2213 mIsLocalAddedInNetData = false;
2214 Get<NetworkData::Notifier>().HandleServerDataUpdated();
2215 LogInfo("Removed %s from Thread Network Data", LocalToString().AsCString());
2216
2217 exit:
2218 return;
2219 }
2220
UpdateDefaultRouteFlag(bool aDefaultRoute)2221 void RoutingManager::OmrPrefixManager::UpdateDefaultRouteFlag(bool aDefaultRoute)
2222 {
2223 VerifyOrExit(aDefaultRoute != mDefaultRoute);
2224
2225 mDefaultRoute = aDefaultRoute;
2226
2227 VerifyOrExit(mIsLocalAddedInNetData);
2228 IgnoreError(AddOrUpdateLocalInNetData());
2229
2230 exit:
2231 return;
2232 }
2233
LocalToString(void) const2234 RoutingManager::OmrPrefixManager::InfoString RoutingManager::OmrPrefixManager::LocalToString(void) const
2235 {
2236 InfoString string;
2237
2238 string.Append("local OMR prefix %s (def-route:%s)", mLocalPrefix.GetPrefix().ToString().AsCString(),
2239 ToYesNo(mDefaultRoute));
2240 return string;
2241 }
2242
2243 //---------------------------------------------------------------------------------------------------------------------
2244 // OnLinkPrefixManager
2245
OnLinkPrefixManager(Instance & aInstance)2246 RoutingManager::OnLinkPrefixManager::OnLinkPrefixManager(Instance &aInstance)
2247 : InstanceLocator(aInstance)
2248 , mState(kIdle)
2249 , mTimer(aInstance)
2250 {
2251 mLocalPrefix.Clear();
2252 mFavoredDiscoveredPrefix.Clear();
2253 mOldLocalPrefixes.Clear();
2254 }
2255
SetState(State aState)2256 void RoutingManager::OnLinkPrefixManager::SetState(State aState)
2257 {
2258 VerifyOrExit(mState != aState);
2259
2260 LogInfo("Local on-link prefix state: %s -> %s (%s)", StateToString(mState), StateToString(aState),
2261 mLocalPrefix.ToString().AsCString());
2262 mState = aState;
2263
2264 // Mark the Advertising PIO (AP) flag in the published route, when
2265 // the local on-link prefix is being published, advertised, or
2266 // deprecated.
2267
2268 Get<RoutingManager>().mRoutePublisher.UpdateAdvPioFlags(aState != kIdle);
2269
2270 exit:
2271 return;
2272 }
2273
Init(void)2274 void RoutingManager::OnLinkPrefixManager::Init(void)
2275 {
2276 TimeMilli now = TimerMilli::GetNow();
2277 Settings::BrOnLinkPrefix savedPrefix;
2278 bool refreshStoredPrefixes = false;
2279
2280 // Restore old prefixes from `Settings`
2281
2282 for (int index = 0; Get<Settings>().ReadBrOnLinkPrefix(index, savedPrefix) == kErrorNone; index++)
2283 {
2284 uint32_t lifetime;
2285 OldPrefix *entry;
2286
2287 if (mOldLocalPrefixes.ContainsMatching(savedPrefix.GetPrefix()))
2288 {
2289 // We should not see duplicate entries in `Settings`
2290 // but if we do we refresh the stored prefixes to make
2291 // it consistent.
2292 refreshStoredPrefixes = true;
2293 continue;
2294 }
2295
2296 entry = mOldLocalPrefixes.PushBack();
2297
2298 if (entry == nullptr)
2299 {
2300 // If there are more stored prefixes, we refresh the
2301 // prefixes in `Settings` to remove the ones we cannot
2302 // handle.
2303
2304 refreshStoredPrefixes = true;
2305 break;
2306 }
2307
2308 lifetime = Min(savedPrefix.GetLifetime(), Time::MsecToSec(TimerMilli::kMaxDelay));
2309
2310 entry->mPrefix = savedPrefix.GetPrefix();
2311 entry->mExpireTime = now + Time::SecToMsec(lifetime);
2312
2313 LogInfo("Restored old prefix %s, lifetime:%lu", entry->mPrefix.ToString().AsCString(), ToUlong(lifetime));
2314
2315 mTimer.FireAtIfEarlier(entry->mExpireTime);
2316 }
2317
2318 if (refreshStoredPrefixes)
2319 {
2320 // We clear the entries in `Settings` and re-write the entries
2321 // from `mOldLocalPrefixes` array.
2322
2323 IgnoreError(Get<Settings>().DeleteAllBrOnLinkPrefixes());
2324
2325 for (OldPrefix &oldPrefix : mOldLocalPrefixes)
2326 {
2327 SavePrefix(oldPrefix.mPrefix, oldPrefix.mExpireTime);
2328 }
2329 }
2330
2331 GenerateLocalPrefix();
2332 }
2333
GenerateLocalPrefix(void)2334 void RoutingManager::OnLinkPrefixManager::GenerateLocalPrefix(void)
2335 {
2336 const MeshCoP::ExtendedPanId &extPanId = Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId();
2337 OldPrefix *entry;
2338 Ip6::Prefix oldLocalPrefix = mLocalPrefix;
2339
2340 // Global ID: 40 most significant bits of Extended PAN ID
2341 // Subnet ID: 16 least significant bits of Extended PAN ID
2342
2343 mLocalPrefix.mPrefix.mFields.m8[0] = 0xfd;
2344 memcpy(mLocalPrefix.mPrefix.mFields.m8 + 1, extPanId.m8, 5);
2345 memcpy(mLocalPrefix.mPrefix.mFields.m8 + 6, extPanId.m8 + 6, 2);
2346
2347 mLocalPrefix.SetLength(kOnLinkPrefixLength);
2348
2349 // We ensure that the local prefix did change, since not all the
2350 // bytes in Extended PAN ID are used in derivation of the local prefix.
2351
2352 VerifyOrExit(mLocalPrefix != oldLocalPrefix);
2353
2354 LogNote("Local on-link prefix: %s", mLocalPrefix.ToString().AsCString());
2355
2356 // Check if the new local prefix happens to be in `mOldLocalPrefixes` array.
2357 // If so, we remove it from the array and update the state accordingly.
2358
2359 entry = mOldLocalPrefixes.FindMatching(mLocalPrefix);
2360
2361 if (entry != nullptr)
2362 {
2363 SetState(kDeprecating);
2364 mExpireTime = entry->mExpireTime;
2365 mOldLocalPrefixes.Remove(*entry);
2366 }
2367 else
2368 {
2369 SetState(kIdle);
2370 }
2371
2372 exit:
2373 return;
2374 }
2375
Start(void)2376 void RoutingManager::OnLinkPrefixManager::Start(void) {}
2377
Stop(void)2378 void RoutingManager::OnLinkPrefixManager::Stop(void)
2379 {
2380 mFavoredDiscoveredPrefix.Clear();
2381
2382 switch (GetState())
2383 {
2384 case kIdle:
2385 break;
2386
2387 case kPublishing:
2388 case kAdvertising:
2389 case kDeprecating:
2390 SetState(kDeprecating);
2391 break;
2392 }
2393 }
2394
Evaluate(void)2395 void RoutingManager::OnLinkPrefixManager::Evaluate(void)
2396 {
2397 VerifyOrExit(!Get<RoutingManager>().mRsSender.IsInProgress());
2398
2399 Get<RoutingManager>().mRxRaTracker.FindFavoredOnLinkPrefix(mFavoredDiscoveredPrefix);
2400
2401 if ((mFavoredDiscoveredPrefix.GetLength() == 0) || (mFavoredDiscoveredPrefix == mLocalPrefix))
2402 {
2403 // We advertise the local on-link prefix if no other prefix is
2404 // discovered, or if the favored discovered prefix is the
2405 // same as the local prefix (for redundancy). Note that the
2406 // local on-link prefix, derived from the extended PAN ID, is
2407 // identical for all BRs on the same Thread mesh.
2408
2409 PublishAndAdvertise();
2410
2411 mFavoredDiscoveredPrefix.Clear();
2412 }
2413 else if (IsPublishingOrAdvertising())
2414 {
2415 // When an application-specific on-link prefix is received and
2416 // it is larger than the local prefix, we will not remove the
2417 // advertised local prefix. In this case, there will be two
2418 // on-link prefixes on the infra link. But all BRs will still
2419 // converge to the same smallest/favored on-link prefix and the
2420 // application-specific prefix is not used.
2421
2422 if (!(mLocalPrefix < mFavoredDiscoveredPrefix))
2423 {
2424 LogInfo("Found a favored on-link prefix %s", mFavoredDiscoveredPrefix.ToString().AsCString());
2425 Deprecate();
2426 }
2427 }
2428
2429 exit:
2430 return;
2431 }
2432
IsInitalEvaluationDone(void) const2433 bool RoutingManager::OnLinkPrefixManager::IsInitalEvaluationDone(void) const
2434 {
2435 // This method indicates whether or not we are done with the
2436 // initial policy evaluation of the on-link prefixes, i.e., either
2437 // we have discovered a favored on-link prefix (being advertised by
2438 // another router on infra link) or we are advertising our local
2439 // on-link prefix.
2440
2441 return (mFavoredDiscoveredPrefix.GetLength() != 0 || IsPublishingOrAdvertising());
2442 }
2443
HandleRaPrefixTableChanged(void)2444 void RoutingManager::OnLinkPrefixManager::HandleRaPrefixTableChanged(void)
2445 {
2446 // This is a callback from `mRxRaTracker` indicating that
2447 // there has been a change in the table. If the favored on-link
2448 // prefix has changed, we trigger a re-evaluation of the routing
2449 // policy.
2450
2451 Ip6::Prefix newFavoredPrefix;
2452
2453 Get<RoutingManager>().mRxRaTracker.FindFavoredOnLinkPrefix(newFavoredPrefix);
2454
2455 if (newFavoredPrefix != mFavoredDiscoveredPrefix)
2456 {
2457 Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
2458 }
2459 }
2460
PublishAndAdvertise(void)2461 void RoutingManager::OnLinkPrefixManager::PublishAndAdvertise(void)
2462 {
2463 // Start publishing and advertising the local on-link prefix if
2464 // not already.
2465
2466 switch (GetState())
2467 {
2468 case kIdle:
2469 case kDeprecating:
2470 break;
2471
2472 case kPublishing:
2473 case kAdvertising:
2474 ExitNow();
2475 }
2476
2477 SetState(kPublishing);
2478 ResetExpireTime(TimerMilli::GetNow());
2479
2480 // We wait for the ULA `fc00::/7` route or a sub-prefix of it (e.g.,
2481 // default route) to be added in Network Data before
2482 // starting to advertise the local on-link prefix in RAs.
2483 // However, if it is already present in Network Data (e.g.,
2484 // added by another BR on the same Thread mesh), we can
2485 // immediately start advertising it.
2486
2487 if (Get<RoutingManager>().NetworkDataContainsUlaRoute())
2488 {
2489 SetState(kAdvertising);
2490 }
2491
2492 exit:
2493 return;
2494 }
2495
Deprecate(void)2496 void RoutingManager::OnLinkPrefixManager::Deprecate(void)
2497 {
2498 // Deprecate the local on-link prefix if it was being advertised
2499 // before. While depreciating the prefix, we wait for the lifetime
2500 // timer to expire before unpublishing the prefix from the Network
2501 // Data. We also continue to include it as a PIO in the RA message
2502 // with zero preferred lifetime and the remaining valid lifetime
2503 // until the timer expires.
2504
2505 switch (GetState())
2506 {
2507 case kPublishing:
2508 case kAdvertising:
2509 SetState(kDeprecating);
2510 break;
2511
2512 case kIdle:
2513 case kDeprecating:
2514 break;
2515 }
2516 }
2517
ShouldPublishUlaRoute(void) const2518 bool RoutingManager::OnLinkPrefixManager::ShouldPublishUlaRoute(void) const
2519 {
2520 // Determine whether or not we should publish ULA prefix. We need
2521 // to publish if we are in any of `kPublishing`, `kAdvertising`,
2522 // or `kDeprecating` states, or if there is at least one old local
2523 // prefix being deprecated.
2524
2525 return (GetState() != kIdle) || !mOldLocalPrefixes.IsEmpty();
2526 }
2527
ResetExpireTime(TimeMilli aNow)2528 void RoutingManager::OnLinkPrefixManager::ResetExpireTime(TimeMilli aNow)
2529 {
2530 mExpireTime = aNow + TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime);
2531 mTimer.FireAtIfEarlier(mExpireTime);
2532 SavePrefix(mLocalPrefix, mExpireTime);
2533 }
2534
IsPublishingOrAdvertising(void) const2535 bool RoutingManager::OnLinkPrefixManager::IsPublishingOrAdvertising(void) const
2536 {
2537 return (GetState() == kPublishing) || (GetState() == kAdvertising);
2538 }
2539
AppendAsPiosTo(RouterAdvert::TxMessage & aRaMessage)2540 Error RoutingManager::OnLinkPrefixManager::AppendAsPiosTo(RouterAdvert::TxMessage &aRaMessage)
2541 {
2542 Error error;
2543
2544 SuccessOrExit(error = AppendCurPrefix(aRaMessage));
2545 error = AppendOldPrefixes(aRaMessage);
2546
2547 exit:
2548 return error;
2549 }
2550
AppendCurPrefix(RouterAdvert::TxMessage & aRaMessage)2551 Error RoutingManager::OnLinkPrefixManager::AppendCurPrefix(RouterAdvert::TxMessage &aRaMessage)
2552 {
2553 // Append the local on-link prefix to the `aRaMessage` as a PIO
2554 // only if it is being advertised or deprecated.
2555 //
2556 // If in `kAdvertising` state, we reset the expire time.
2557 // If in `kDeprecating` state, we include it as PIO with zero
2558 // preferred lifetime and the remaining valid lifetime.
2559
2560 Error error = kErrorNone;
2561 uint32_t validLifetime = kDefaultOnLinkPrefixLifetime;
2562 uint32_t preferredLifetime = kDefaultOnLinkPrefixLifetime;
2563 TimeMilli now = TimerMilli::GetNow();
2564
2565 switch (GetState())
2566 {
2567 case kAdvertising:
2568 ResetExpireTime(now);
2569 break;
2570
2571 case kDeprecating:
2572 VerifyOrExit(mExpireTime > now);
2573 validLifetime = TimeMilli::MsecToSec(mExpireTime - now);
2574 preferredLifetime = 0;
2575 break;
2576
2577 case kIdle:
2578 case kPublishing:
2579 ExitNow();
2580 }
2581
2582 SuccessOrExit(error = aRaMessage.AppendPrefixInfoOption(mLocalPrefix, validLifetime, preferredLifetime));
2583
2584 LogPrefixInfoOption(mLocalPrefix, validLifetime, preferredLifetime);
2585
2586 exit:
2587 return error;
2588 }
2589
AppendOldPrefixes(RouterAdvert::TxMessage & aRaMessage)2590 Error RoutingManager::OnLinkPrefixManager::AppendOldPrefixes(RouterAdvert::TxMessage &aRaMessage)
2591 {
2592 Error error = kErrorNone;
2593 TimeMilli now = TimerMilli::GetNow();
2594 uint32_t validLifetime;
2595
2596 for (const OldPrefix &oldPrefix : mOldLocalPrefixes)
2597 {
2598 if (oldPrefix.mExpireTime < now)
2599 {
2600 continue;
2601 }
2602
2603 validLifetime = TimeMilli::MsecToSec(oldPrefix.mExpireTime - now);
2604 SuccessOrExit(error = aRaMessage.AppendPrefixInfoOption(oldPrefix.mPrefix, validLifetime, 0));
2605
2606 LogPrefixInfoOption(oldPrefix.mPrefix, validLifetime, 0);
2607 }
2608
2609 exit:
2610 return error;
2611 }
2612
HandleNetDataChange(void)2613 void RoutingManager::OnLinkPrefixManager::HandleNetDataChange(void)
2614 {
2615 VerifyOrExit(GetState() == kPublishing);
2616
2617 if (Get<RoutingManager>().NetworkDataContainsUlaRoute())
2618 {
2619 SetState(kAdvertising);
2620 Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
2621 }
2622
2623 exit:
2624 return;
2625 }
2626
HandleExtPanIdChange(void)2627 void RoutingManager::OnLinkPrefixManager::HandleExtPanIdChange(void)
2628 {
2629 // If the current local prefix is being advertised or deprecated,
2630 // we save it in `mOldLocalPrefixes` and keep deprecating it. It will
2631 // be included in emitted RAs as PIO with zero preferred lifetime.
2632 // It will still be present in Network Data until its expire time
2633 // so to allow Thread nodes to continue to communicate with `InfraIf`
2634 // device using addresses based on this prefix.
2635
2636 uint16_t oldState = GetState();
2637 Ip6::Prefix oldPrefix = mLocalPrefix;
2638
2639 GenerateLocalPrefix();
2640
2641 VerifyOrExit(oldPrefix != mLocalPrefix);
2642
2643 switch (oldState)
2644 {
2645 case kIdle:
2646 case kPublishing:
2647 break;
2648
2649 case kAdvertising:
2650 case kDeprecating:
2651 DeprecateOldPrefix(oldPrefix, mExpireTime);
2652 break;
2653 }
2654
2655 Get<RoutingManager>().HandleLocalOnLinkPrefixChanged();
2656
2657 exit:
2658 return;
2659 }
2660
DeprecateOldPrefix(const Ip6::Prefix & aPrefix,TimeMilli aExpireTime)2661 void RoutingManager::OnLinkPrefixManager::DeprecateOldPrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime)
2662 {
2663 OldPrefix *entry = nullptr;
2664 Ip6::Prefix removedPrefix;
2665
2666 removedPrefix.Clear();
2667
2668 VerifyOrExit(!mOldLocalPrefixes.ContainsMatching(aPrefix));
2669
2670 LogInfo("Deprecating old on-link prefix %s", aPrefix.ToString().AsCString());
2671
2672 if (!mOldLocalPrefixes.IsFull())
2673 {
2674 entry = mOldLocalPrefixes.PushBack();
2675 }
2676 else
2677 {
2678 // If there is no more room in `mOldLocalPrefixes` array
2679 // we evict the entry with the earliest expiration time.
2680
2681 entry = &mOldLocalPrefixes[0];
2682
2683 for (OldPrefix &oldPrefix : mOldLocalPrefixes)
2684 {
2685 if ((oldPrefix.mExpireTime < entry->mExpireTime))
2686 {
2687 entry = &oldPrefix;
2688 }
2689 }
2690
2691 removedPrefix = entry->mPrefix;
2692
2693 IgnoreError(Get<Settings>().RemoveBrOnLinkPrefix(removedPrefix));
2694 }
2695
2696 entry->mPrefix = aPrefix;
2697 entry->mExpireTime = aExpireTime;
2698 mTimer.FireAtIfEarlier(aExpireTime);
2699
2700 SavePrefix(aPrefix, aExpireTime);
2701
2702 exit:
2703 return;
2704 }
2705
SavePrefix(const Ip6::Prefix & aPrefix,TimeMilli aExpireTime)2706 void RoutingManager::OnLinkPrefixManager::SavePrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime)
2707 {
2708 Settings::BrOnLinkPrefix savedPrefix;
2709
2710 savedPrefix.SetPrefix(aPrefix);
2711 savedPrefix.SetLifetime(TimeMilli::MsecToSec(aExpireTime - TimerMilli::GetNow()));
2712 IgnoreError(Get<Settings>().AddOrUpdateBrOnLinkPrefix(savedPrefix));
2713 }
2714
HandleTimer(void)2715 void RoutingManager::OnLinkPrefixManager::HandleTimer(void)
2716 {
2717 NextFireTime nextExpireTime;
2718
2719 Array<Ip6::Prefix, kMaxOldPrefixes> expiredPrefixes;
2720
2721 switch (GetState())
2722 {
2723 case kIdle:
2724 break;
2725 case kPublishing:
2726 case kAdvertising:
2727 case kDeprecating:
2728 if (nextExpireTime.GetNow() >= mExpireTime)
2729 {
2730 IgnoreError(Get<Settings>().RemoveBrOnLinkPrefix(mLocalPrefix));
2731 SetState(kIdle);
2732 }
2733 else
2734 {
2735 nextExpireTime.UpdateIfEarlier(mExpireTime);
2736 }
2737 break;
2738 }
2739
2740 for (OldPrefix &entry : mOldLocalPrefixes)
2741 {
2742 if (nextExpireTime.GetNow() >= entry.mExpireTime)
2743 {
2744 SuccessOrAssert(expiredPrefixes.PushBack(entry.mPrefix));
2745 }
2746 else
2747 {
2748 nextExpireTime.UpdateIfEarlier(entry.mExpireTime);
2749 }
2750 }
2751
2752 for (const Ip6::Prefix &prefix : expiredPrefixes)
2753 {
2754 LogInfo("Old local on-link prefix %s expired", prefix.ToString().AsCString());
2755 IgnoreError(Get<Settings>().RemoveBrOnLinkPrefix(prefix));
2756 mOldLocalPrefixes.RemoveMatching(prefix);
2757 }
2758
2759 mTimer.FireAtIfEarlier(nextExpireTime);
2760
2761 Get<RoutingManager>().mRoutePublisher.Evaluate();
2762 }
2763
StateToString(State aState)2764 const char *RoutingManager::OnLinkPrefixManager::StateToString(State aState)
2765 {
2766 static const char *const kStateStrings[] = {
2767 "Removed", // (0) kIdle
2768 "Publishing", // (1) kPublishing
2769 "Advertising", // (2) kAdvertising
2770 "Deprecating", // (3) kDeprecating
2771 };
2772
2773 static_assert(0 == kIdle, "kIdle value is incorrect");
2774 static_assert(1 == kPublishing, "kPublishing value is incorrect");
2775 static_assert(2 == kAdvertising, "kAdvertising value is incorrect");
2776 static_assert(3 == kDeprecating, "kDeprecating value is incorrect");
2777
2778 return kStateStrings[aState];
2779 }
2780
2781 //---------------------------------------------------------------------------------------------------------------------
2782 // RioAdvertiser
2783
RioAdvertiser(Instance & aInstance)2784 RoutingManager::RioAdvertiser::RioAdvertiser(Instance &aInstance)
2785 : InstanceLocator(aInstance)
2786 , mTimer(aInstance)
2787 , mPreference(NetworkData::kRoutePreferenceLow)
2788 , mUserSetPreference(false)
2789 {
2790 }
2791
SetPreference(RoutePreference aPreference)2792 void RoutingManager::RioAdvertiser::SetPreference(RoutePreference aPreference)
2793 {
2794 LogInfo("User explicitly set RIO Preference to %s", RoutePreferenceToString(aPreference));
2795 mUserSetPreference = true;
2796 UpdatePreference(aPreference);
2797 }
2798
ClearPreference(void)2799 void RoutingManager::RioAdvertiser::ClearPreference(void)
2800 {
2801 VerifyOrExit(mUserSetPreference);
2802
2803 LogInfo("User cleared explicitly set RIO Preference");
2804 mUserSetPreference = false;
2805 SetPreferenceBasedOnRole();
2806
2807 exit:
2808 return;
2809 }
2810
HandleRoleChanged(void)2811 void RoutingManager::RioAdvertiser::HandleRoleChanged(void)
2812 {
2813 if (!mUserSetPreference)
2814 {
2815 SetPreferenceBasedOnRole();
2816 }
2817 }
2818
SetPreferenceBasedOnRole(void)2819 void RoutingManager::RioAdvertiser::SetPreferenceBasedOnRole(void)
2820 {
2821 UpdatePreference(Get<Mle::Mle>().IsRouterOrLeader() ? NetworkData::kRoutePreferenceMedium
2822 : NetworkData::kRoutePreferenceLow);
2823 }
2824
UpdatePreference(RoutePreference aPreference)2825 void RoutingManager::RioAdvertiser::UpdatePreference(RoutePreference aPreference)
2826 {
2827 VerifyOrExit(mPreference != aPreference);
2828
2829 LogInfo("RIO Preference changed: %s -> %s", RoutePreferenceToString(mPreference),
2830 RoutePreferenceToString(aPreference));
2831 mPreference = aPreference;
2832
2833 Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
2834
2835 exit:
2836 return;
2837 }
2838
InvalidatPrevRios(RouterAdvert::TxMessage & aRaMessage)2839 Error RoutingManager::RioAdvertiser::InvalidatPrevRios(RouterAdvert::TxMessage &aRaMessage)
2840 {
2841 Error error = kErrorNone;
2842
2843 for (const RioPrefix &prefix : mPrefixes)
2844 {
2845 SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage));
2846 }
2847
2848 #if OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE
2849 mPrefixes.Free();
2850 #endif
2851
2852 mPrefixes.Clear();
2853 mTimer.Stop();
2854
2855 exit:
2856 return error;
2857 }
2858
AppendRios(RouterAdvert::TxMessage & aRaMessage)2859 Error RoutingManager::RioAdvertiser::AppendRios(RouterAdvert::TxMessage &aRaMessage)
2860 {
2861 Error error = kErrorNone;
2862 NextFireTime nextTime;
2863 RioPrefixArray oldPrefixes;
2864 NetworkData::Iterator iterator = NetworkData::kIteratorInit;
2865 NetworkData::OnMeshPrefixConfig prefixConfig;
2866 const OmrPrefixManager &omrPrefixManager = Get<RoutingManager>().mOmrPrefixManager;
2867
2868 #if OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE
2869 oldPrefixes.TakeFrom(static_cast<RioPrefixArray &&>(mPrefixes));
2870 #else
2871 oldPrefixes = mPrefixes;
2872 #endif
2873
2874 mPrefixes.Clear();
2875
2876 // `mPrefixes` array can have a limited size. We add more
2877 // important prefixes first in the array to ensure they are
2878 // advertised in the RA message. Note that `Add()` method
2879 // will ensure to add a prefix only once (will check if
2880 // prefix is already present in the array).
2881
2882 // (1) Local OMR prefix.
2883
2884 if (omrPrefixManager.ShouldAdvertiseLocalAsRio())
2885 {
2886 mPrefixes.Add(omrPrefixManager.GetLocalPrefix().GetPrefix());
2887 }
2888
2889 // (2) Favored OMR prefix.
2890
2891 if (!omrPrefixManager.GetFavoredPrefix().IsEmpty() && !omrPrefixManager.GetFavoredPrefix().IsDomainPrefix())
2892 {
2893 mPrefixes.Add(omrPrefixManager.GetFavoredPrefix().GetPrefix());
2894 }
2895
2896 // (3) All other OMR prefixes.
2897
2898 iterator = NetworkData::kIteratorInit;
2899
2900 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
2901 {
2902 // The decision to include the local OMR prefix as a RIO is
2903 // delegated to `OmrPrefixManager.ShouldAdvertiseLocalAsRio()`
2904 // at step (1). Here, as we iterate over Network Data prefixes,
2905 // we exclude entries matching the local OMR prefix. This is
2906 // because `OmrPrefixManager` may have decided to stop advertising
2907 // it, while it might still be present in the Network Data due to
2908 // delays in registering changes with the leader.
2909
2910 if (prefixConfig.mDp)
2911 {
2912 continue;
2913 }
2914
2915 if (IsValidOmrPrefix(prefixConfig) &&
2916 (prefixConfig.GetPrefix() != omrPrefixManager.GetLocalPrefix().GetPrefix()))
2917 {
2918 mPrefixes.Add(prefixConfig.GetPrefix());
2919 }
2920 }
2921
2922 // (4) All other on-mesh prefixes (excluding Domain Prefix).
2923
2924 iterator = NetworkData::kIteratorInit;
2925
2926 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
2927 {
2928 if (prefixConfig.mOnMesh && !prefixConfig.mDp && !IsValidOmrPrefix(prefixConfig))
2929 {
2930 mPrefixes.Add(prefixConfig.GetPrefix());
2931 }
2932 }
2933
2934 // Determine deprecating prefixes
2935
2936 for (RioPrefix &prefix : oldPrefixes)
2937 {
2938 if (mPrefixes.ContainsMatching(prefix.mPrefix))
2939 {
2940 continue;
2941 }
2942
2943 if (prefix.mIsDeprecating)
2944 {
2945 if (nextTime.GetNow() >= prefix.mExpirationTime)
2946 {
2947 SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage));
2948 continue;
2949 }
2950 }
2951 else
2952 {
2953 prefix.mIsDeprecating = true;
2954 prefix.mExpirationTime = nextTime.GetNow() + kDeprecationTime;
2955 }
2956
2957 if (mPrefixes.PushBack(prefix) != kErrorNone)
2958 {
2959 LogWarn("Too many deprecating on-mesh prefixes, removing %s", prefix.mPrefix.ToString().AsCString());
2960 SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage));
2961 }
2962
2963 nextTime.UpdateIfEarlier(prefix.mExpirationTime);
2964 }
2965
2966 // Advertise all prefixes in `mPrefixes`
2967
2968 for (const RioPrefix &prefix : mPrefixes)
2969 {
2970 uint32_t lifetime = kDefaultOmrPrefixLifetime;
2971
2972 if (prefix.mIsDeprecating)
2973 {
2974 lifetime = TimeMilli::MsecToSec(prefix.mExpirationTime - nextTime.GetNow());
2975 }
2976
2977 SuccessOrExit(error = AppendRio(prefix.mPrefix, lifetime, aRaMessage));
2978 }
2979
2980 mTimer.FireAtIfEarlier(nextTime);
2981
2982 exit:
2983 return error;
2984 }
2985
AppendRio(const Ip6::Prefix & aPrefix,uint32_t aRouteLifetime,RouterAdvert::TxMessage & aRaMessage)2986 Error RoutingManager::RioAdvertiser::AppendRio(const Ip6::Prefix &aPrefix,
2987 uint32_t aRouteLifetime,
2988 RouterAdvert::TxMessage &aRaMessage)
2989 {
2990 Error error;
2991
2992 SuccessOrExit(error = aRaMessage.AppendRouteInfoOption(aPrefix, aRouteLifetime, mPreference));
2993 LogRouteInfoOption(aPrefix, aRouteLifetime, mPreference);
2994
2995 exit:
2996 return error;
2997 }
2998
HandleTimer(void)2999 void RoutingManager::RioAdvertiser::HandleTimer(void)
3000 {
3001 Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kImmediately);
3002 }
3003
Add(const Ip6::Prefix & aPrefix)3004 void RoutingManager::RioAdvertiser::RioPrefixArray::Add(const Ip6::Prefix &aPrefix)
3005 {
3006 // Checks if `aPrefix` is already present in the array and if not
3007 // adds it as a new entry.
3008
3009 Error error;
3010 RioPrefix newEntry;
3011
3012 VerifyOrExit(!ContainsMatching(aPrefix));
3013
3014 newEntry.Clear();
3015 newEntry.mPrefix = aPrefix;
3016
3017 error = PushBack(newEntry);
3018
3019 if (error != kErrorNone)
3020 {
3021 LogWarn("Too many on-mesh prefixes in net data, ignoring prefix %s", aPrefix.ToString().AsCString());
3022 }
3023
3024 exit:
3025 return;
3026 }
3027
3028 //---------------------------------------------------------------------------------------------------------------------
3029 // RoutePublisher
3030
3031 const otIp6Prefix RoutingManager::RoutePublisher::kUlaPrefix = {
3032 {{{0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
3033 7,
3034 };
3035
RoutePublisher(Instance & aInstance)3036 RoutingManager::RoutePublisher::RoutePublisher(Instance &aInstance)
3037 : InstanceLocator(aInstance)
3038 , mState(kDoNotPublish)
3039 , mPreference(NetworkData::kRoutePreferenceMedium)
3040 , mUserSetPreference(false)
3041 , mAdvPioFlag(false)
3042 , mTimer(aInstance)
3043 {
3044 }
3045
Evaluate(void)3046 void RoutingManager::RoutePublisher::Evaluate(void)
3047 {
3048 State newState = kDoNotPublish;
3049
3050 VerifyOrExit(Get<RoutingManager>().IsRunning());
3051
3052 if (Get<RoutingManager>().mOmrPrefixManager.GetFavoredPrefix().IsInfrastructureDerived() &&
3053 Get<RoutingManager>().mRxRaTracker.ContainsDefaultOrNonUlaRoutePrefix())
3054 {
3055 newState = kPublishDefault;
3056 }
3057 else if (Get<RoutingManager>().mRxRaTracker.ContainsNonUlaOnLinkPrefix())
3058 {
3059 newState = kPublishDefault;
3060 }
3061 else if (Get<RoutingManager>().mRxRaTracker.ContainsUlaOnLinkPrefix() ||
3062 Get<RoutingManager>().mOnLinkPrefixManager.ShouldPublishUlaRoute())
3063 {
3064 newState = kPublishUla;
3065 }
3066
3067 exit:
3068 if (newState != mState)
3069 {
3070 LogInfo("RoutePublisher state: %s -> %s", StateToString(mState), StateToString(newState));
3071 UpdatePublishedRoute(newState);
3072 Get<RoutingManager>().mOmrPrefixManager.UpdateDefaultRouteFlag(newState == kPublishDefault);
3073 }
3074 }
3075
DeterminePrefixFor(State aState,Ip6::Prefix & aPrefix) const3076 void RoutingManager::RoutePublisher::DeterminePrefixFor(State aState, Ip6::Prefix &aPrefix) const
3077 {
3078 aPrefix.Clear();
3079
3080 switch (aState)
3081 {
3082 case kDoNotPublish:
3083 case kPublishDefault:
3084 // `Clear()` will set the prefix to `::/0`.
3085 break;
3086 case kPublishUla:
3087 aPrefix = GetUlaPrefix();
3088 break;
3089 }
3090 }
3091
UpdatePublishedRoute(State aNewState)3092 void RoutingManager::RoutePublisher::UpdatePublishedRoute(State aNewState)
3093 {
3094 // Updates the published route entry in Network Data, transitioning
3095 // from current `mState` to new `aNewState`. This method can be used
3096 // when there is no change to `mState` but a change to `mPreference`
3097 // or `mAdvPioFlag`.
3098
3099 Ip6::Prefix oldPrefix;
3100 NetworkData::ExternalRouteConfig routeConfig;
3101
3102 DeterminePrefixFor(mState, oldPrefix);
3103
3104 if (aNewState == kDoNotPublish)
3105 {
3106 VerifyOrExit(mState != kDoNotPublish);
3107 IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(oldPrefix));
3108 ExitNow();
3109 }
3110
3111 routeConfig.Clear();
3112 routeConfig.mPreference = mPreference;
3113 routeConfig.mAdvPio = mAdvPioFlag;
3114 routeConfig.mStable = true;
3115 DeterminePrefixFor(aNewState, routeConfig.GetPrefix());
3116
3117 // If we were not publishing a route prefix before, publish the new
3118 // `routeConfig`. Otherwise, use `ReplacePublishedExternalRoute()` to
3119 // replace the previously published prefix entry. This ensures that we do
3120 // not have a situation where the previous route is removed while the new
3121 // one is not yet added in the Network Data.
3122
3123 if (mState == kDoNotPublish)
3124 {
3125 SuccessOrAssert(Get<NetworkData::Publisher>().PublishExternalRoute(
3126 routeConfig, NetworkData::Publisher::kFromRoutingManager));
3127 }
3128 else
3129 {
3130 SuccessOrAssert(Get<NetworkData::Publisher>().ReplacePublishedExternalRoute(
3131 oldPrefix, routeConfig, NetworkData::Publisher::kFromRoutingManager));
3132 }
3133
3134 exit:
3135 mState = aNewState;
3136 }
3137
Unpublish(void)3138 void RoutingManager::RoutePublisher::Unpublish(void)
3139 {
3140 // Unpublish the previously published route based on `mState`
3141 // and update `mState`.
3142
3143 Ip6::Prefix prefix;
3144
3145 VerifyOrExit(mState != kDoNotPublish);
3146 DeterminePrefixFor(mState, prefix);
3147 IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(prefix));
3148 mState = kDoNotPublish;
3149
3150 exit:
3151 return;
3152 }
3153
UpdateAdvPioFlags(bool aAdvPioFlag)3154 void RoutingManager::RoutePublisher::UpdateAdvPioFlags(bool aAdvPioFlag)
3155 {
3156 VerifyOrExit(mAdvPioFlag != aAdvPioFlag);
3157 mAdvPioFlag = aAdvPioFlag;
3158 UpdatePublishedRoute(mState);
3159
3160 exit:
3161 return;
3162 }
3163
SetPreference(RoutePreference aPreference)3164 void RoutingManager::RoutePublisher::SetPreference(RoutePreference aPreference)
3165 {
3166 LogInfo("User explicitly set published route preference to %s", RoutePreferenceToString(aPreference));
3167 mUserSetPreference = true;
3168 mTimer.Stop();
3169 UpdatePreference(aPreference);
3170 }
3171
ClearPreference(void)3172 void RoutingManager::RoutePublisher::ClearPreference(void)
3173 {
3174 VerifyOrExit(mUserSetPreference);
3175
3176 LogInfo("User cleared explicitly set published route preference - set based on role");
3177 mUserSetPreference = false;
3178 SetPreferenceBasedOnRole();
3179
3180 exit:
3181 return;
3182 }
3183
SetPreferenceBasedOnRole(void)3184 void RoutingManager::RoutePublisher::SetPreferenceBasedOnRole(void)
3185 {
3186 RoutePreference preference = NetworkData::kRoutePreferenceMedium;
3187
3188 if (Get<Mle::Mle>().IsChild() && (Get<Mle::Mle>().GetParent().GetTwoWayLinkQuality() != kLinkQuality3))
3189 {
3190 preference = NetworkData::kRoutePreferenceLow;
3191 }
3192
3193 UpdatePreference(preference);
3194 mTimer.Stop();
3195 }
3196
HandleNotifierEvents(Events aEvents)3197 void RoutingManager::RoutePublisher::HandleNotifierEvents(Events aEvents)
3198 {
3199 VerifyOrExit(!mUserSetPreference);
3200
3201 if (aEvents.Contains(kEventThreadRoleChanged))
3202 {
3203 SetPreferenceBasedOnRole();
3204 }
3205
3206 if (aEvents.Contains(kEventParentLinkQualityChanged))
3207 {
3208 VerifyOrExit(Get<Mle::Mle>().IsChild());
3209
3210 if (Get<Mle::Mle>().GetParent().GetTwoWayLinkQuality() == kLinkQuality3)
3211 {
3212 VerifyOrExit(!mTimer.IsRunning());
3213 mTimer.Start(kDelayBeforePrfUpdateOnLinkQuality3);
3214 }
3215 else
3216 {
3217 UpdatePreference(NetworkData::kRoutePreferenceLow);
3218 mTimer.Stop();
3219 }
3220 }
3221
3222 exit:
3223 return;
3224 }
3225
HandleTimer(void)3226 void RoutingManager::RoutePublisher::HandleTimer(void) { SetPreferenceBasedOnRole(); }
3227
UpdatePreference(RoutePreference aPreference)3228 void RoutingManager::RoutePublisher::UpdatePreference(RoutePreference aPreference)
3229 {
3230 VerifyOrExit(mPreference != aPreference);
3231
3232 LogInfo("Published route preference changed: %s -> %s", RoutePreferenceToString(mPreference),
3233 RoutePreferenceToString(aPreference));
3234 mPreference = aPreference;
3235 UpdatePublishedRoute(mState);
3236
3237 exit:
3238 return;
3239 }
3240
StateToString(State aState)3241 const char *RoutingManager::RoutePublisher::StateToString(State aState)
3242 {
3243 static const char *const kStateStrings[] = {
3244 "none", // (0) kDoNotPublish
3245 "def-route", // (1) kPublishDefault
3246 "ula", // (2) kPublishUla
3247 };
3248
3249 static_assert(0 == kDoNotPublish, "kDoNotPublish value is incorrect");
3250 static_assert(1 == kPublishDefault, "kPublishDefault value is incorrect");
3251 static_assert(2 == kPublishUla, "kPublishUla value is incorrect");
3252
3253 return kStateStrings[aState];
3254 }
3255
3256 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
3257
3258 //---------------------------------------------------------------------------------------------------------------------
3259 // Nat64PrefixManager
3260
Nat64PrefixManager(Instance & aInstance)3261 RoutingManager::Nat64PrefixManager::Nat64PrefixManager(Instance &aInstance)
3262 : InstanceLocator(aInstance)
3263 , mEnabled(false)
3264 , mTimer(aInstance)
3265 {
3266 mInfraIfPrefix.Clear();
3267 mLocalPrefix.Clear();
3268 mPublishedPrefix.Clear();
3269 }
3270
SetEnabled(bool aEnabled)3271 void RoutingManager::Nat64PrefixManager::SetEnabled(bool aEnabled)
3272 {
3273 VerifyOrExit(mEnabled != aEnabled);
3274 mEnabled = aEnabled;
3275
3276 if (aEnabled)
3277 {
3278 if (Get<RoutingManager>().IsRunning())
3279 {
3280 Start();
3281 }
3282 }
3283 else
3284 {
3285 Stop();
3286 }
3287
3288 exit:
3289 return;
3290 }
3291
Start(void)3292 void RoutingManager::Nat64PrefixManager::Start(void)
3293 {
3294 VerifyOrExit(mEnabled);
3295 LogInfo("Starting Nat64PrefixManager");
3296 mTimer.Start(0);
3297
3298 exit:
3299 return;
3300 }
3301
Stop(void)3302 void RoutingManager::Nat64PrefixManager::Stop(void)
3303 {
3304 LogInfo("Stopping Nat64PrefixManager");
3305
3306 if (mPublishedPrefix.IsValidNat64())
3307 {
3308 IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(mPublishedPrefix));
3309 }
3310
3311 mPublishedPrefix.Clear();
3312 mInfraIfPrefix.Clear();
3313 mTimer.Stop();
3314
3315 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
3316 Get<Nat64::Translator>().ClearNat64Prefix();
3317 #endif
3318 }
3319
GenerateLocalPrefix(const Ip6::Prefix & aBrUlaPrefix)3320 void RoutingManager::Nat64PrefixManager::GenerateLocalPrefix(const Ip6::Prefix &aBrUlaPrefix)
3321 {
3322 mLocalPrefix = aBrUlaPrefix;
3323 mLocalPrefix.SetSubnetId(kNat64PrefixSubnetId);
3324 mLocalPrefix.mPrefix.mFields.m32[2] = 0;
3325 mLocalPrefix.SetLength(kNat64PrefixLength);
3326
3327 LogInfo("Generated local NAT64 prefix: %s", mLocalPrefix.ToString().AsCString());
3328 }
3329
GetFavoredPrefix(RoutePreference & aPreference) const3330 const Ip6::Prefix &RoutingManager::Nat64PrefixManager::GetFavoredPrefix(RoutePreference &aPreference) const
3331 {
3332 const Ip6::Prefix *favoredPrefix = &mLocalPrefix;
3333
3334 aPreference = NetworkData::kRoutePreferenceLow;
3335
3336 if (mInfraIfPrefix.IsValidNat64() &&
3337 Get<RoutingManager>().mOmrPrefixManager.GetFavoredPrefix().IsInfrastructureDerived())
3338 {
3339 favoredPrefix = &mInfraIfPrefix;
3340 aPreference = NetworkData::kRoutePreferenceMedium;
3341 }
3342
3343 return *favoredPrefix;
3344 }
3345
Evaluate(void)3346 void RoutingManager::Nat64PrefixManager::Evaluate(void)
3347 {
3348 Error error;
3349 Ip6::Prefix prefix;
3350 RoutePreference preference;
3351 NetworkData::ExternalRouteConfig netdataPrefixConfig;
3352 bool shouldPublish;
3353
3354 VerifyOrExit(mEnabled);
3355
3356 LogInfo("Evaluating NAT64 prefix");
3357
3358 prefix = GetFavoredPrefix(preference);
3359
3360 error = Get<NetworkData::Leader>().GetPreferredNat64Prefix(netdataPrefixConfig);
3361
3362 // NAT64 prefix is expected to be published from this BR
3363 // when one of the following is true:
3364 //
3365 // - No NAT64 prefix in Network Data.
3366 // - The preferred NAT64 prefix in Network Data has lower
3367 // preference than this BR's prefix.
3368 // - The preferred NAT64 prefix in Network Data was published
3369 // by this BR.
3370 // - The preferred NAT64 prefix in Network Data is same as the
3371 // discovered infrastructure prefix.
3372 //
3373 // TODO: change to check RLOC16 to determine if the NAT64 prefix
3374 // was published by this BR.
3375
3376 shouldPublish =
3377 ((error == kErrorNotFound) || (netdataPrefixConfig.mPreference < preference) ||
3378 (netdataPrefixConfig.GetPrefix() == mPublishedPrefix) || (netdataPrefixConfig.GetPrefix() == mInfraIfPrefix));
3379
3380 if (mPublishedPrefix.IsValidNat64() && (!shouldPublish || (prefix != mPublishedPrefix)))
3381 {
3382 IgnoreError(Get<NetworkData::Publisher>().UnpublishPrefix(mPublishedPrefix));
3383 mPublishedPrefix.Clear();
3384 }
3385
3386 if (shouldPublish && ((prefix != mPublishedPrefix) || (preference != mPublishedPreference)))
3387 {
3388 mPublishedPrefix = prefix;
3389 mPublishedPreference = preference;
3390 Publish();
3391 }
3392
3393 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
3394
3395 // If a prefix other than `mLocalPrefix` is present, an external
3396 // translator is available. To bypass the NAT64 translator, we
3397 // clear its NAT64 prefix.
3398
3399 if (mPublishedPrefix == mLocalPrefix)
3400 {
3401 Get<Nat64::Translator>().SetNat64Prefix(mLocalPrefix);
3402 }
3403 else
3404 {
3405 Get<Nat64::Translator>().ClearNat64Prefix();
3406 }
3407 #endif
3408
3409 exit:
3410 return;
3411 }
3412
Publish(void)3413 void RoutingManager::Nat64PrefixManager::Publish(void)
3414 {
3415 NetworkData::ExternalRouteConfig routeConfig;
3416
3417 routeConfig.Clear();
3418 routeConfig.SetPrefix(mPublishedPrefix);
3419 routeConfig.mPreference = mPublishedPreference;
3420 routeConfig.mStable = true;
3421 routeConfig.mNat64 = true;
3422
3423 SuccessOrAssert(
3424 Get<NetworkData::Publisher>().PublishExternalRoute(routeConfig, NetworkData::Publisher::kFromRoutingManager));
3425 }
3426
HandleTimer(void)3427 void RoutingManager::Nat64PrefixManager::HandleTimer(void)
3428 {
3429 OT_ASSERT(mEnabled);
3430
3431 Discover();
3432
3433 mTimer.Start(TimeMilli::SecToMsec(kDefaultNat64PrefixLifetime));
3434 LogInfo("NAT64 prefix timer scheduled in %lu seconds", ToUlong(kDefaultNat64PrefixLifetime));
3435 }
3436
Discover(void)3437 void RoutingManager::Nat64PrefixManager::Discover(void)
3438 {
3439 Error error = Get<RoutingManager>().mInfraIf.DiscoverNat64Prefix();
3440
3441 if (error == kErrorNone)
3442 {
3443 LogInfo("Discovering infraif NAT64 prefix");
3444 }
3445 else
3446 {
3447 LogWarn("Failed to discover infraif NAT64 prefix: %s", ErrorToString(error));
3448 }
3449 }
3450
HandleDiscoverDone(const Ip6::Prefix & aPrefix)3451 void RoutingManager::Nat64PrefixManager::HandleDiscoverDone(const Ip6::Prefix &aPrefix)
3452 {
3453 mInfraIfPrefix = aPrefix;
3454
3455 LogInfo("Infraif NAT64 prefix: %s", mInfraIfPrefix.IsValidNat64() ? mInfraIfPrefix.ToString().AsCString() : "none");
3456 Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
3457 }
3458
GetState(void) const3459 Nat64::State RoutingManager::Nat64PrefixManager::GetState(void) const
3460 {
3461 Nat64::State state = Nat64::kStateDisabled;
3462
3463 VerifyOrExit(mEnabled);
3464 VerifyOrExit(Get<RoutingManager>().IsRunning(), state = Nat64::kStateNotRunning);
3465 VerifyOrExit(mPublishedPrefix.IsValidNat64(), state = Nat64::kStateIdle);
3466 state = Nat64::kStateActive;
3467
3468 exit:
3469 return state;
3470 }
3471
3472 #endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
3473
3474 //---------------------------------------------------------------------------------------------------------------------
3475 // RaInfo
3476
IncrementTxCountAndSaveHash(const InfraIf::Icmp6Packet & aRaMessage)3477 void RoutingManager::TxRaInfo::IncrementTxCountAndSaveHash(const InfraIf::Icmp6Packet &aRaMessage)
3478 {
3479 mTxCount++;
3480 mLastHashIndex++;
3481
3482 if (mLastHashIndex == kNumHashEntries)
3483 {
3484 mLastHashIndex = 0;
3485 }
3486
3487 CalculateHash(RouterAdvert::RxMessage(aRaMessage), mHashes[mLastHashIndex]);
3488 }
3489
IsRaFromManager(const RouterAdvert::RxMessage & aRaMessage) const3490 bool RoutingManager::TxRaInfo::IsRaFromManager(const RouterAdvert::RxMessage &aRaMessage) const
3491 {
3492 // Determines whether or not a received RA message was prepared by
3493 // by `RoutingManager` itself (is present in the saved `mHashes`).
3494
3495 bool isFromManager = false;
3496 uint16_t hashIndex = mLastHashIndex;
3497 uint32_t count = Min<uint32_t>(mTxCount, kNumHashEntries);
3498 Hash hash;
3499
3500 CalculateHash(aRaMessage, hash);
3501
3502 for (; count > 0; count--)
3503 {
3504 if (mHashes[hashIndex] == hash)
3505 {
3506 isFromManager = true;
3507 break;
3508 }
3509
3510 // Go to the previous index (ring buffer)
3511
3512 if (hashIndex == 0)
3513 {
3514 hashIndex = kNumHashEntries - 1;
3515 }
3516 else
3517 {
3518 hashIndex--;
3519 }
3520 }
3521
3522 return isFromManager;
3523 }
3524
CalculateHash(const RouterAdvert::RxMessage & aRaMessage,Hash & aHash)3525 void RoutingManager::TxRaInfo::CalculateHash(const RouterAdvert::RxMessage &aRaMessage, Hash &aHash)
3526 {
3527 RouterAdvert::Header header;
3528 Crypto::Sha256 sha256;
3529
3530 header = aRaMessage.GetHeader();
3531 header.SetChecksum(0);
3532
3533 sha256.Start();
3534 sha256.Update(header);
3535 sha256.Update(aRaMessage.GetOptionStart(), aRaMessage.GetOptionLength());
3536 sha256.Finish(aHash);
3537 }
3538
3539 //---------------------------------------------------------------------------------------------------------------------
3540 // RsSender
3541
RsSender(Instance & aInstance)3542 RoutingManager::RsSender::RsSender(Instance &aInstance)
3543 : InstanceLocator(aInstance)
3544 , mTxCount(0)
3545 , mTimer(aInstance)
3546 {
3547 }
3548
Start(void)3549 void RoutingManager::RsSender::Start(void)
3550 {
3551 uint32_t delay;
3552
3553 VerifyOrExit(!IsInProgress());
3554
3555 delay = Random::NonCrypto::GetUint32InRange(0, kMaxStartDelay);
3556
3557 LogInfo("RsSender: Starting - will send first RS in %lu msec", ToUlong(delay));
3558
3559 mTxCount = 0;
3560 mStartTime = TimerMilli::GetNow();
3561 mTimer.Start(delay);
3562
3563 exit:
3564 return;
3565 }
3566
Stop(void)3567 void RoutingManager::RsSender::Stop(void) { mTimer.Stop(); }
3568
SendRs(void)3569 Error RoutingManager::RsSender::SendRs(void)
3570 {
3571 Ip6::Address destAddress;
3572 RouterSolicitMessage routerSolicit;
3573 InfraIf::Icmp6Packet packet;
3574 Error error;
3575
3576 packet.InitFrom(routerSolicit);
3577 destAddress.SetToLinkLocalAllRoutersMulticast();
3578
3579 error = Get<RoutingManager>().mInfraIf.Send(packet, destAddress);
3580
3581 if (error == kErrorNone)
3582 {
3583 Get<Ip6::Ip6>().GetBorderRoutingCounters().mRsTxSuccess++;
3584 }
3585 else
3586 {
3587 Get<Ip6::Ip6>().GetBorderRoutingCounters().mRsTxFailure++;
3588 }
3589 return error;
3590 }
3591
HandleTimer(void)3592 void RoutingManager::RsSender::HandleTimer(void)
3593 {
3594 Error error;
3595 uint32_t delay;
3596
3597 if (mTxCount >= kMaxTxCount)
3598 {
3599 LogInfo("RsSender: Finished sending RS msgs and waiting for RAs");
3600 Get<RoutingManager>().HandleRsSenderFinished(mStartTime);
3601 ExitNow();
3602 }
3603
3604 error = SendRs();
3605
3606 if (error == kErrorNone)
3607 {
3608 mTxCount++;
3609 delay = (mTxCount == kMaxTxCount) ? kWaitOnLastAttempt : kTxInterval;
3610 LogInfo("RsSender: Sent RS %u/%u", mTxCount, kMaxTxCount);
3611 }
3612 else
3613 {
3614 LogCrit("RsSender: Failed to send RS %u/%u: %s", mTxCount + 1, kMaxTxCount, ErrorToString(error));
3615
3616 // Note that `mTxCount` is intentionally not incremented
3617 // if the tx fails.
3618 delay = kRetryDelay;
3619 }
3620
3621 mTimer.Start(delay);
3622
3623 exit:
3624 return;
3625 }
3626
3627 //---------------------------------------------------------------------------------------------------------------------
3628 // PdPrefixManager
3629
3630 #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
3631
PdPrefixManager(Instance & aInstance)3632 RoutingManager::PdPrefixManager::PdPrefixManager(Instance &aInstance)
3633 : InstanceLocator(aInstance)
3634 , mEnabled(false)
3635 , mIsRunning(false)
3636 , mNumPlatformPioProcessed(0)
3637 , mNumPlatformRaReceived(0)
3638 , mLastPlatformRaTime(0)
3639 , mTimer(aInstance)
3640 {
3641 }
3642
SetEnabled(bool aEnabled)3643 void RoutingManager::PdPrefixManager::SetEnabled(bool aEnabled)
3644 {
3645 State oldState = GetState();
3646
3647 VerifyOrExit(mEnabled != aEnabled);
3648 mEnabled = aEnabled;
3649 EvaluateStateChange(oldState);
3650
3651 exit:
3652 return;
3653 }
3654
StartStop(bool aStart)3655 void RoutingManager::PdPrefixManager::StartStop(bool aStart)
3656 {
3657 State oldState = GetState();
3658
3659 VerifyOrExit(aStart != mIsRunning);
3660 mIsRunning = aStart;
3661 EvaluateStateChange(oldState);
3662
3663 exit:
3664 return;
3665 }
3666
GetState(void) const3667 RoutingManager::PdPrefixManager::State RoutingManager::PdPrefixManager::GetState(void) const
3668 {
3669 State state = kDhcp6PdStateDisabled;
3670
3671 if (mEnabled)
3672 {
3673 state = mIsRunning ? kDhcp6PdStateRunning : kDhcp6PdStateStopped;
3674 }
3675
3676 return state;
3677 }
3678
EvaluateStateChange(Dhcp6PdState aOldState)3679 void RoutingManager::PdPrefixManager::EvaluateStateChange(Dhcp6PdState aOldState)
3680 {
3681 State newState = GetState();
3682
3683 VerifyOrExit(aOldState != newState);
3684 LogInfo("PdPrefixManager: %s -> %s", StateToString(aOldState), StateToString(newState));
3685
3686 switch (newState)
3687 {
3688 case kDhcp6PdStateDisabled:
3689 case kDhcp6PdStateStopped:
3690 WithdrawPrefix();
3691 break;
3692 case kDhcp6PdStateRunning:
3693 break;
3694 }
3695
3696 mStateCallback.InvokeIfSet(static_cast<otBorderRoutingDhcp6PdState>(newState));
3697
3698 exit:
3699 return;
3700 }
3701
GetPrefixInfo(PrefixTableEntry & aInfo) const3702 Error RoutingManager::PdPrefixManager::GetPrefixInfo(PrefixTableEntry &aInfo) const
3703 {
3704 Error error = kErrorNone;
3705
3706 VerifyOrExit(IsRunning() && HasPrefix(), error = kErrorNotFound);
3707
3708 aInfo.mPrefix = mPrefix.GetPrefix();
3709 aInfo.mValidLifetime = mPrefix.GetValidLifetime();
3710 aInfo.mPreferredLifetime = mPrefix.GetPreferredLifetime();
3711 aInfo.mMsecSinceLastUpdate = TimerMilli::GetNow() - mPrefix.GetLastUpdateTime();
3712
3713 exit:
3714 return error;
3715 }
3716
GetProcessedRaInfo(PdProcessedRaInfo & aPdProcessedRaInfo) const3717 Error RoutingManager::PdPrefixManager::GetProcessedRaInfo(PdProcessedRaInfo &aPdProcessedRaInfo) const
3718 {
3719 Error error = kErrorNone;
3720
3721 VerifyOrExit(IsRunning() && HasPrefix(), error = kErrorNotFound);
3722
3723 aPdProcessedRaInfo.mNumPlatformRaReceived = mNumPlatformRaReceived;
3724 aPdProcessedRaInfo.mNumPlatformPioProcessed = mNumPlatformPioProcessed;
3725 aPdProcessedRaInfo.mLastPlatformRaMsec = TimerMilli::GetNow() - mLastPlatformRaTime;
3726
3727 exit:
3728 return error;
3729 }
3730
WithdrawPrefix(void)3731 void RoutingManager::PdPrefixManager::WithdrawPrefix(void)
3732 {
3733 VerifyOrExit(HasPrefix());
3734
3735 LogInfo("Withdrew DHCPv6 PD prefix %s", mPrefix.GetPrefix().ToString().AsCString());
3736
3737 mPrefix.Clear();
3738 mTimer.Stop();
3739
3740 Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kImmediately);
3741
3742 exit:
3743 return;
3744 }
3745
ProcessRa(const uint8_t * aRouterAdvert,const uint16_t aLength)3746 void RoutingManager::PdPrefixManager::ProcessRa(const uint8_t *aRouterAdvert, const uint16_t aLength)
3747 {
3748 // Processes a Router Advertisement (RA) message received on the
3749 // platform's Thread interface. This RA message, generated by
3750 // software entities like dnsmasq, radvd, or systemd-networkd, is
3751 // part of the DHCPv6 prefix delegation process for distributing
3752 // prefixes to interfaces.
3753
3754 RouterAdvert::Icmp6Packet packet;
3755
3756 packet.Init(aRouterAdvert, aLength);
3757 Process(&packet, nullptr);
3758 }
3759
ProcessPrefix(const PrefixTableEntry & aPrefixTableEntry)3760 void RoutingManager::PdPrefixManager::ProcessPrefix(const PrefixTableEntry &aPrefixTableEntry)
3761 {
3762 // Processes a prefix delegated by a DHCPv6 Prefix Delegation
3763 // (PD) server. Similar to `ProcessRa()`, but sets the prefix
3764 // directly instead of parsing an RA message. Calling this method
3765 // again with new values can update the prefix's lifetime.
3766
3767 Process(nullptr, &aPrefixTableEntry);
3768 }
3769
Process(const RouterAdvert::Icmp6Packet * aRaPacket,const PrefixTableEntry * aPrefixTableEntry)3770 void RoutingManager::PdPrefixManager::Process(const RouterAdvert::Icmp6Packet *aRaPacket,
3771 const PrefixTableEntry *aPrefixTableEntry)
3772 {
3773 // Processes DHCPv6 Prefix Delegation (PD) prefixes, either from
3774 // an RA message or directly set. Requires either `aRaPacket` or
3775 // `aPrefixTableEntry` to be non-null.
3776
3777 bool currentPrefixUpdated = false;
3778 Error error = kErrorNone;
3779 PrefixEntry favoredEntry;
3780 PrefixEntry entry;
3781
3782 VerifyOrExit(mEnabled, error = kErrorInvalidState);
3783
3784 if (aRaPacket != nullptr)
3785 {
3786 RouterAdvert::RxMessage raMsg = RouterAdvert::RxMessage(*aRaPacket);
3787
3788 VerifyOrExit(raMsg.IsValid(), error = kErrorParse);
3789
3790 for (const Option &option : raMsg)
3791 {
3792 if (option.GetType() != Option::kTypePrefixInfo || !static_cast<const PrefixInfoOption &>(option).IsValid())
3793 {
3794 continue;
3795 }
3796
3797 mNumPlatformPioProcessed++;
3798 entry.SetFrom(static_cast<const PrefixInfoOption &>(option));
3799 currentPrefixUpdated |= ProcessPrefixEntry(entry, favoredEntry);
3800 }
3801
3802 mNumPlatformRaReceived++;
3803 mLastPlatformRaTime = TimerMilli::GetNow();
3804 }
3805 else // aPrefixTableEntry != nullptr
3806 {
3807 entry.SetFrom(*aPrefixTableEntry);
3808 currentPrefixUpdated = ProcessPrefixEntry(entry, favoredEntry);
3809 }
3810
3811 if (currentPrefixUpdated && mPrefix.IsDeprecated())
3812 {
3813 LogInfo("DHCPv6 PD prefix %s is deprecated", mPrefix.GetPrefix().ToString().AsCString());
3814 mPrefix.Clear();
3815 Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kImmediately);
3816 }
3817
3818 if (favoredEntry.IsFavoredOver(mPrefix))
3819 {
3820 mPrefix = favoredEntry;
3821 currentPrefixUpdated = true;
3822 LogInfo("DHCPv6 PD prefix set to %s", mPrefix.GetPrefix().ToString().AsCString());
3823 Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kImmediately);
3824 }
3825
3826 if (HasPrefix() && currentPrefixUpdated)
3827 {
3828 mTimer.FireAt(mPrefix.GetDeprecationTime());
3829 }
3830 else
3831 {
3832 mTimer.Stop();
3833 }
3834
3835 exit:
3836 LogWarnOnError(error, "process DHCPv6 delegated prefix");
3837 OT_UNUSED_VARIABLE(error);
3838 }
3839
ProcessPrefixEntry(PrefixEntry & aEntry,PrefixEntry & aFavoredEntry)3840 bool RoutingManager::PdPrefixManager::ProcessPrefixEntry(PrefixEntry &aEntry, PrefixEntry &aFavoredEntry)
3841 {
3842 bool currentPrefixUpdated = false;
3843
3844 if (!aEntry.IsValidPdPrefix())
3845 {
3846 LogWarn("Ignore invalid DHCPv6 PD prefix %s", aEntry.GetPrefix().ToString().AsCString());
3847 ExitNow();
3848 }
3849
3850 aEntry.GetPrefix().Tidy();
3851 aEntry.GetPrefix().SetLength(kOmrPrefixLength);
3852
3853 // Check if there is an update to the current prefix. The valid or
3854 // preferred lifetime may have changed.
3855
3856 if (HasPrefix() && (mPrefix.GetPrefix() == aEntry.GetPrefix()))
3857 {
3858 currentPrefixUpdated = true;
3859 mPrefix = aEntry;
3860 }
3861
3862 VerifyOrExit(!aEntry.IsDeprecated());
3863
3864 // Some platforms may delegate multiple prefixes. We'll select the
3865 // smallest one, as GUA prefixes (`2000::/3`) are inherently
3866 // smaller than ULA prefixes (`fc00::/7`). This rule prefers GUA
3867 // prefixes over ULA.
3868
3869 if (aEntry.IsFavoredOver(aFavoredEntry))
3870 {
3871 aFavoredEntry = aEntry;
3872 }
3873
3874 exit:
3875 return currentPrefixUpdated;
3876 }
3877
IsValidPdPrefix(void) const3878 bool RoutingManager::PdPrefixManager::PrefixEntry::IsValidPdPrefix(void) const
3879 {
3880 // We should accept ULA prefix since it could be used by the internet infrastructure like NAT64.
3881
3882 return !IsEmpty() && (GetPrefix().GetLength() <= kOmrPrefixLength) && !GetPrefix().IsLinkLocal() &&
3883 !GetPrefix().IsMulticast();
3884 }
3885
IsFavoredOver(const PrefixEntry & aOther) const3886 bool RoutingManager::PdPrefixManager::PrefixEntry::IsFavoredOver(const PrefixEntry &aOther) const
3887 {
3888 bool isFavored;
3889
3890 if (IsEmpty())
3891 {
3892 // Empty prefix is not favored over any (including another
3893 // empty prefix).
3894 isFavored = false;
3895 ExitNow();
3896 }
3897
3898 if (aOther.IsEmpty())
3899 {
3900 // A non-empty prefix is favored over an empty one.
3901 isFavored = true;
3902 ExitNow();
3903 }
3904
3905 // Numerically smaller prefix is favored.
3906
3907 isFavored = GetPrefix() < aOther.GetPrefix();
3908
3909 exit:
3910 return isFavored;
3911 }
3912
StateToString(State aState)3913 const char *RoutingManager::PdPrefixManager::StateToString(State aState)
3914 {
3915 static const char *const kStateStrings[] = {
3916 "Disabled", // (0) kDisabled
3917 "Stopped", // (1) kStopped
3918 "Running", // (2) kRunning
3919 };
3920
3921 static_assert(0 == kDhcp6PdStateDisabled, "kDhcp6PdStateDisabled value is incorrect");
3922 static_assert(1 == kDhcp6PdStateStopped, "kDhcp6PdStateStopped value is incorrect");
3923 static_assert(2 == kDhcp6PdStateRunning, "kDhcp6PdStateRunning value is incorrect");
3924
3925 return kStateStrings[aState];
3926 }
3927
otPlatBorderRoutingProcessIcmp6Ra(otInstance * aInstance,const uint8_t * aMessage,uint16_t aLength)3928 extern "C" void otPlatBorderRoutingProcessIcmp6Ra(otInstance *aInstance, const uint8_t *aMessage, uint16_t aLength)
3929 {
3930 AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().mPdPrefixManager.ProcessRa(aMessage, aLength);
3931 }
3932
otPlatBorderRoutingProcessDhcp6PdPrefix(otInstance * aInstance,const otBorderRoutingPrefixTableEntry * aPrefixInfo)3933 extern "C" void otPlatBorderRoutingProcessDhcp6PdPrefix(otInstance *aInstance,
3934 const otBorderRoutingPrefixTableEntry *aPrefixInfo)
3935 {
3936 AssertPointerIsNotNull(aPrefixInfo);
3937
3938 AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().mPdPrefixManager.ProcessPrefix(*aPrefixInfo);
3939 }
3940 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
3941
3942 } // namespace BorderRouter
3943
3944 } // namespace ot
3945
3946 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
3947