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