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