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