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