• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2018, 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 #include "router_table.hpp"
30 
31 #if OPENTHREAD_FTD
32 
33 #include "instance/instance.hpp"
34 
35 namespace ot {
36 
37 RegisterLogModule("RouterTable");
38 
RouterTable(Instance & aInstance)39 RouterTable::RouterTable(Instance &aInstance)
40     : InstanceLocator(aInstance)
41     , mRouters(aInstance)
42     , mChangedTask(aInstance)
43     , mRouterIdSequenceLastUpdated(0)
44     , mRouterIdSequence(Random::NonCrypto::GetUint8())
45 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
46     , mMinRouterId(0)
47     , mMaxRouterId(Mle::kMaxRouterId)
48 #endif
49 {
50     Clear();
51 }
52 
Clear(void)53 void RouterTable::Clear(void)
54 {
55     ClearNeighbors();
56     mRouterIdMap.Clear();
57     mRouters.Clear();
58     SignalTableChanged();
59 }
60 
IsRouteTlvIdSequenceMoreRecent(const Mle::RouteTlv & aRouteTlv) const61 bool RouterTable::IsRouteTlvIdSequenceMoreRecent(const Mle::RouteTlv &aRouteTlv) const
62 {
63     return (GetActiveRouterCount() == 0) ||
64            SerialNumber::IsGreater(aRouteTlv.GetRouterIdSequence(), GetRouterIdSequence());
65 }
66 
ClearNeighbors(void)67 void RouterTable::ClearNeighbors(void)
68 {
69     for (Router &router : mRouters)
70     {
71         if (router.IsStateValid())
72         {
73             Get<NeighborTable>().Signal(NeighborTable::kRouterRemoved, router);
74             SignalTableChanged();
75         }
76 
77         router.SetState(Neighbor::kStateInvalid);
78     }
79 }
80 
AddRouter(uint8_t aRouterId)81 Router *RouterTable::AddRouter(uint8_t aRouterId)
82 {
83     // Add a new `Router` entry to `mRouters` array with given
84     // `aRouterId` and update the `mRouterIdMap`.
85 
86     Router *router = mRouters.PushBack();
87 
88     VerifyOrExit(router != nullptr);
89 
90     router->Clear();
91     router->SetRloc16(Mle::Rloc16FromRouterId(aRouterId));
92     router->SetNextHopToInvalid();
93 
94     mRouterIdMap.SetIndex(aRouterId, mRouters.IndexOf(*router));
95     SignalTableChanged();
96 
97 exit:
98     return router;
99 }
100 
RemoveRouter(Router & aRouter)101 void RouterTable::RemoveRouter(Router &aRouter)
102 {
103     // Remove an existing `aRouter` entry from `mRouters` and update the
104     // `mRouterIdMap`.
105 
106     if (aRouter.IsStateValid())
107     {
108         Get<NeighborTable>().Signal(NeighborTable::kRouterRemoved, aRouter);
109     }
110 
111     mRouterIdMap.Release(aRouter.GetRouterId());
112     mRouters.Remove(aRouter);
113 
114     // Removing `aRouter` from `mRouters` array will replace it with
115     // the last entry in the array (if not already the last entry) so
116     // we update the index in `mRouteIdMap` for the moved entry.
117 
118     if (IsAllocated(aRouter.GetRouterId()))
119     {
120         mRouterIdMap.SetIndex(aRouter.GetRouterId(), mRouters.IndexOf((aRouter)));
121     }
122 
123     SignalTableChanged();
124 }
125 
Allocate(void)126 Router *RouterTable::Allocate(void)
127 {
128     Router *router           = nullptr;
129     uint8_t numAvailable     = 0;
130     uint8_t selectedRouterId = Mle::kInvalidRouterId;
131 
132     VerifyOrExit(!mRouters.IsFull());
133 
134 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
135     for (uint8_t routerId = mMinRouterId; routerId <= mMaxRouterId; routerId++)
136 #else
137     for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++)
138 #endif
139     {
140         if (mRouterIdMap.CanAllocate(routerId))
141         {
142             numAvailable++;
143 
144             // Randomly select a router ID as we iterate through the
145             // list using Reservoir algorithm: We replace the
146             // selected ID with current entry in the list with
147             // probably `1/numAvailable`.
148 
149             if (Random::NonCrypto::GetUint8InRange(0, numAvailable) == 0)
150             {
151                 selectedRouterId = routerId;
152             }
153         }
154     }
155 
156     VerifyOrExit(selectedRouterId != Mle::kInvalidRouterId);
157 
158     router = Allocate(selectedRouterId);
159     OT_ASSERT(router != nullptr);
160 
161 exit:
162     return router;
163 }
164 
Allocate(uint8_t aRouterId)165 Router *RouterTable::Allocate(uint8_t aRouterId)
166 {
167     Router *router = nullptr;
168 
169     VerifyOrExit(aRouterId <= Mle::kMaxRouterId && mRouterIdMap.CanAllocate(aRouterId));
170 
171     router = AddRouter(aRouterId);
172     VerifyOrExit(router != nullptr);
173 
174     router->SetLastHeard(TimerMilli::GetNow());
175 
176     mRouterIdSequence++;
177     mRouterIdSequenceLastUpdated = TimerMilli::GetNow();
178     Get<Mle::MleRouter>().ResetAdvertiseInterval();
179 
180     LogNote("Allocate router id %d", aRouterId);
181 
182 exit:
183     return router;
184 }
185 
Release(uint8_t aRouterId)186 Error RouterTable::Release(uint8_t aRouterId)
187 {
188     Error   error = kErrorNone;
189     Router *router;
190 
191     OT_ASSERT(aRouterId <= Mle::kMaxRouterId);
192 
193     VerifyOrExit(Get<Mle::MleRouter>().IsLeader(), error = kErrorInvalidState);
194 
195     router = FindRouterById(aRouterId);
196     VerifyOrExit(router != nullptr, error = kErrorNotFound);
197 
198     RemoveRouter(*router);
199 
200     for (Router &otherRouter : mRouters)
201     {
202         if (otherRouter.GetNextHop() == aRouterId)
203         {
204             otherRouter.SetNextHopToInvalid();
205         }
206     }
207 
208     mRouterIdSequence++;
209     mRouterIdSequenceLastUpdated = TimerMilli::GetNow();
210 
211     Get<AddressResolver>().RemoveEntriesForRouterId(aRouterId);
212     Get<NetworkData::Leader>().RemoveBorderRouter(Mle::Rloc16FromRouterId(aRouterId),
213                                                   NetworkData::Leader::kMatchModeRouterId);
214     Get<Mle::MleRouter>().ResetAdvertiseInterval();
215 
216     LogNote("Release router id %d", aRouterId);
217 
218 exit:
219     return error;
220 }
221 
RemoveRouterLink(Router & aRouter)222 void RouterTable::RemoveRouterLink(Router &aRouter)
223 {
224     if (aRouter.GetLinkQualityOut() != kLinkQuality0)
225     {
226         aRouter.SetLinkQualityOut(kLinkQuality0);
227         aRouter.SetLastHeard(TimerMilli::GetNow());
228         SignalTableChanged();
229     }
230 
231     for (Router &router : mRouters)
232     {
233         if (router.GetNextHop() == aRouter.GetRouterId())
234         {
235             router.SetNextHopToInvalid();
236             SignalTableChanged();
237 
238             if (GetLinkCost(router) >= Mle::kMaxRouteCost)
239             {
240                 Get<Mle::MleRouter>().ResetAdvertiseInterval();
241             }
242         }
243     }
244 
245     if (aRouter.GetNextHop() == Mle::kInvalidRouterId)
246     {
247         Get<Mle::MleRouter>().ResetAdvertiseInterval();
248 
249         // Clear all EID-to-RLOC entries associated with the router.
250         Get<AddressResolver>().RemoveEntriesForRouterId(aRouter.GetRouterId());
251     }
252 }
253 
FindRouter(const Router::AddressMatcher & aMatcher) const254 const Router *RouterTable::FindRouter(const Router::AddressMatcher &aMatcher) const
255 {
256     return mRouters.FindMatching(aMatcher);
257 }
258 
FindNeighbor(uint16_t aRloc16)259 Router *RouterTable::FindNeighbor(uint16_t aRloc16)
260 {
261     Router *router = nullptr;
262 
263     VerifyOrExit(!Get<Mle::Mle>().HasRloc16(aRloc16));
264     router = FindRouter(Router::AddressMatcher(aRloc16, Router::kInStateValid));
265 
266 exit:
267     return router;
268 }
269 
FindNeighbor(const Mac::ExtAddress & aExtAddress)270 Router *RouterTable::FindNeighbor(const Mac::ExtAddress &aExtAddress)
271 {
272     return FindRouter(Router::AddressMatcher(aExtAddress, Router::kInStateValid));
273 }
274 
FindNeighbor(const Mac::Address & aMacAddress)275 Router *RouterTable::FindNeighbor(const Mac::Address &aMacAddress)
276 {
277     return FindRouter(Router::AddressMatcher(aMacAddress, Router::kInStateValid));
278 }
279 
FindRouterById(uint8_t aRouterId) const280 const Router *RouterTable::FindRouterById(uint8_t aRouterId) const
281 {
282     const Router *router = nullptr;
283 
284     VerifyOrExit(aRouterId <= Mle::kMaxRouterId);
285 
286     VerifyOrExit(IsAllocated(aRouterId));
287     router = &mRouters[mRouterIdMap.GetIndex(aRouterId)];
288 
289 exit:
290     return router;
291 }
292 
FindRouterByRloc16(uint16_t aRloc16) const293 const Router *RouterTable::FindRouterByRloc16(uint16_t aRloc16) const
294 {
295     return FindRouterById(Mle::RouterIdFromRloc16(aRloc16));
296 }
297 
FindNextHopOf(const Router & aRouter) const298 const Router *RouterTable::FindNextHopOf(const Router &aRouter) const { return FindRouterById(aRouter.GetNextHop()); }
299 
FindRouter(const Mac::ExtAddress & aExtAddress)300 Router *RouterTable::FindRouter(const Mac::ExtAddress &aExtAddress)
301 {
302     return FindRouter(Router::AddressMatcher(aExtAddress, Router::kInStateAny));
303 }
304 
GetRouterInfo(uint16_t aRouterId,Router::Info & aRouterInfo)305 Error RouterTable::GetRouterInfo(uint16_t aRouterId, Router::Info &aRouterInfo)
306 {
307     Error   error = kErrorNone;
308     Router *router;
309     uint8_t routerId;
310 
311     if (aRouterId <= Mle::kMaxRouterId)
312     {
313         routerId = static_cast<uint8_t>(aRouterId);
314     }
315     else
316     {
317         VerifyOrExit(Mle::IsRouterRloc16(aRouterId), error = kErrorInvalidArgs);
318         routerId = Mle::RouterIdFromRloc16(aRouterId);
319         VerifyOrExit(routerId <= Mle::kMaxRouterId, error = kErrorInvalidArgs);
320     }
321 
322     router = FindRouterById(routerId);
323     VerifyOrExit(router != nullptr, error = kErrorNotFound);
324 
325     aRouterInfo.SetFrom(*router);
326 
327 exit:
328     return error;
329 }
330 
GetLeader(void) const331 const Router *RouterTable::GetLeader(void) const { return FindRouterById(Get<Mle::MleRouter>().GetLeaderId()); }
332 
GetLeaderAge(void) const333 uint32_t RouterTable::GetLeaderAge(void) const
334 {
335     return (!mRouters.IsEmpty()) ? Time::MsecToSec(TimerMilli::GetNow() - mRouterIdSequenceLastUpdated) : 0xffffffff;
336 }
337 
GetNeighborCount(LinkQuality aLinkQuality) const338 uint8_t RouterTable::GetNeighborCount(LinkQuality aLinkQuality) const
339 {
340     uint8_t count = 0;
341 
342     for (const Router &router : mRouters)
343     {
344         if (router.IsStateValid() && (router.GetLinkQualityIn() >= aLinkQuality))
345         {
346             count++;
347         }
348     }
349 
350     return count;
351 }
352 
GetLinkCost(const Router & aRouter) const353 uint8_t RouterTable::GetLinkCost(const Router &aRouter) const
354 {
355     uint8_t rval = Mle::kMaxRouteCost;
356 
357     VerifyOrExit(!Get<Mle::Mle>().HasRloc16(aRouter.GetRloc16()) && aRouter.IsStateValid());
358 
359     rval = CostForLinkQuality(aRouter.GetTwoWayLinkQuality());
360 
361 exit:
362     return rval;
363 }
364 
GetLinkCost(uint8_t aRouterId) const365 uint8_t RouterTable::GetLinkCost(uint8_t aRouterId) const
366 {
367     uint8_t       rval = Mle::kMaxRouteCost;
368     const Router *router;
369 
370     router = FindRouterById(aRouterId);
371 
372     // `nullptr` aRouterId indicates non-existing next hop, hence return kMaxRouteCost for it.
373     VerifyOrExit(router != nullptr);
374 
375     rval = GetLinkCost(*router);
376 
377 exit:
378     return rval;
379 }
380 
GetPathCost(uint16_t aDestRloc16) const381 uint8_t RouterTable::GetPathCost(uint16_t aDestRloc16) const
382 {
383     uint8_t  pathCost;
384     uint16_t nextHopRloc16;
385 
386     GetNextHopAndPathCost(aDestRloc16, nextHopRloc16, pathCost);
387 
388     return pathCost;
389 }
390 
GetPathCostToLeader(void) const391 uint8_t RouterTable::GetPathCostToLeader(void) const { return GetPathCost(Get<Mle::Mle>().GetLeaderRloc16()); }
392 
GetNextHopAndPathCost(uint16_t aDestRloc16,uint16_t & aNextHopRloc16,uint8_t & aPathCost) const393 void RouterTable::GetNextHopAndPathCost(uint16_t aDestRloc16, uint16_t &aNextHopRloc16, uint8_t &aPathCost) const
394 {
395     const Router *router;
396     const Router *nextHop;
397 
398     aPathCost      = Mle::kMaxRouteCost;
399     aNextHopRloc16 = Mle::kInvalidRloc16;
400 
401     VerifyOrExit(Get<Mle::Mle>().IsAttached());
402 
403     if (Get<Mle::Mle>().HasRloc16(aDestRloc16))
404     {
405         // Destination is this device, return cost as zero.
406         aPathCost      = 0;
407         aNextHopRloc16 = aDestRloc16;
408         ExitNow();
409     }
410 
411     router  = FindRouterById(Mle::RouterIdFromRloc16(aDestRloc16));
412     nextHop = (router != nullptr) ? FindNextHopOf(*router) : nullptr;
413 
414     if (Get<Mle::MleRouter>().IsChild())
415     {
416         const Router &parent = Get<Mle::Mle>().GetParent();
417         bool          destIsParentOrItsChild;
418 
419         if (parent.IsStateValid())
420         {
421             aNextHopRloc16 = parent.GetRloc16();
422         }
423 
424         // If destination is our parent or another child of our
425         // parent, we use the link cost to our parent. Otherwise we
426         // check if we have a next hop towards the destination and
427         // add its cost to the link cost to parent.
428 
429         destIsParentOrItsChild = Mle::RouterIdMatch(aDestRloc16, parent.GetRloc16());
430 
431         VerifyOrExit(destIsParentOrItsChild || (nextHop != nullptr));
432 
433         aPathCost = CostForLinkQuality(parent.GetLinkQualityIn());
434 
435         if (!destIsParentOrItsChild)
436         {
437             aPathCost += router->GetCost();
438         }
439 
440         // The case where destination itself is a child is handled at
441         // the end (after `else` block).
442     }
443     else // Role is router or leader
444     {
445         if (Get<Mle::Mle>().HasMatchingRouterIdWith(aDestRloc16))
446         {
447             // Destination is a one of our children.
448 
449             const Child *child = Get<ChildTable>().FindChild(aDestRloc16, Child::kInStateAnyExceptInvalid);
450 
451             VerifyOrExit(child != nullptr);
452             aNextHopRloc16 = aDestRloc16;
453             aPathCost      = CostForLinkQuality(child->GetLinkQualityIn());
454             ExitNow();
455         }
456 
457         VerifyOrExit(router != nullptr);
458 
459         aPathCost = GetLinkCost(*router);
460 
461         if (aPathCost < Mle::kMaxRouteCost)
462         {
463             aNextHopRloc16 = router->GetRloc16();
464         }
465 
466         if (nextHop != nullptr)
467         {
468             // Determine whether direct link or forwarding hop link
469             // through `nextHop` has a lower path cost.
470 
471             uint8_t nextHopPathCost = router->GetCost() + GetLinkCost(*nextHop);
472 
473             if (nextHopPathCost < aPathCost)
474             {
475                 aPathCost      = nextHopPathCost;
476                 aNextHopRloc16 = nextHop->GetRloc16();
477             }
478         }
479     }
480 
481     if (Mle::IsChildRloc16(aDestRloc16))
482     {
483         // Destination is a child. we assume best link quality
484         // between destination and its parent router.
485 
486         aPathCost += kCostForLinkQuality3;
487     }
488 
489 exit:
490     return;
491 }
492 
GetNextHop(uint16_t aDestRloc16) const493 uint16_t RouterTable::GetNextHop(uint16_t aDestRloc16) const
494 {
495     uint8_t  pathCost;
496     uint16_t nextHopRloc16;
497 
498     GetNextHopAndPathCost(aDestRloc16, nextHopRloc16, pathCost);
499 
500     return nextHopRloc16;
501 }
502 
UpdateRouterIdSet(uint8_t aRouterIdSequence,const Mle::RouterIdSet & aRouterIdSet)503 void RouterTable::UpdateRouterIdSet(uint8_t aRouterIdSequence, const Mle::RouterIdSet &aRouterIdSet)
504 {
505     bool shouldAdd = false;
506 
507     mRouterIdSequence            = aRouterIdSequence;
508     mRouterIdSequenceLastUpdated = TimerMilli::GetNow();
509 
510     // Remove all previously allocated routers that are now removed in
511     // new `aRouterIdSet`.
512 
513     for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++)
514     {
515         if (IsAllocated(routerId) == aRouterIdSet.Contains(routerId))
516         {
517             continue;
518         }
519 
520         if (IsAllocated(routerId))
521         {
522             Router *router = FindRouterById(routerId);
523 
524             OT_ASSERT(router != nullptr);
525             router->SetNextHopToInvalid();
526             RemoveRouterLink(*router);
527             RemoveRouter(*router);
528         }
529         else
530         {
531             shouldAdd = true;
532         }
533     }
534 
535     VerifyOrExit(shouldAdd);
536 
537     // Now add all new routers in `aRouterIdSet`.
538 
539     for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++)
540     {
541         if (!IsAllocated(routerId) && aRouterIdSet.Contains(routerId))
542         {
543             AddRouter(routerId);
544         }
545     }
546 
547     Get<Mle::MleRouter>().ResetAdvertiseInterval();
548 
549 exit:
550     return;
551 }
552 
UpdateRoutes(const Mle::RouteTlv & aRouteTlv,uint8_t aNeighborId)553 void RouterTable::UpdateRoutes(const Mle::RouteTlv &aRouteTlv, uint8_t aNeighborId)
554 {
555     Router          *neighbor;
556     Mle::RouterIdSet finitePathCostIdSet;
557     uint8_t          linkCostToNeighbor;
558 
559     neighbor = FindRouterById(aNeighborId);
560     VerifyOrExit(neighbor != nullptr);
561 
562     // Before updating the routes, we track which routers have finite
563     // path cost. After the update we check again to see if any path
564     // cost changed from finite to infinite or vice versa to decide
565     // whether to reset the  MLE Advertisement interval.
566 
567     finitePathCostIdSet.Clear();
568 
569     for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++)
570     {
571         if (GetPathCost(Mle::Rloc16FromRouterId(routerId)) < Mle::kMaxRouteCost)
572         {
573             finitePathCostIdSet.Add(routerId);
574         }
575     }
576 
577     // Find the entry corresponding to our Router ID in the received
578     // `aRouteTlv` to get the `LinkQualityIn` from the perspective of
579     // neighbor. We use this to update our `LinkQualityOut` to the
580     // neighbor.
581 
582     for (uint8_t routerId = 0, index = 0; routerId <= Mle::kMaxRouterId;
583          index += aRouteTlv.IsRouterIdSet(routerId) ? 1 : 0, routerId++)
584     {
585         if (!Get<Mle::Mle>().MatchesRouterId(routerId))
586         {
587             continue;
588         }
589 
590         if (aRouteTlv.IsRouterIdSet(routerId))
591         {
592             LinkQuality linkQuality = aRouteTlv.GetLinkQualityIn(index);
593 
594             if (neighbor->GetLinkQualityOut() != linkQuality)
595             {
596                 neighbor->SetLinkQualityOut(linkQuality);
597                 SignalTableChanged();
598             }
599 
600             // If the `aRouteTlv` indicates that the neighboring
601             // router claims to have no link to us (by setting its
602             // `GetLinkQualityOut()` towards us as `kLinkQuality0`),
603             // and we have previously established a link with it, and
604             // our two-way link quality to this router is at least
605             // `kLinkQuality2`, we schedule a unicast Advertisement to
606             // be sent to this neighbor. This helps expedite recovery
607             // from any temporary router link quality mismatch.
608             // Otherwise, the neighboring router will continue to
609             // advertise that it has no link to us until our next
610             // trickle timer-triggered Advertisement transmission
611             // (which can be up to 32 seconds later).
612 
613             if (neighbor->IsStateValid() && (aRouteTlv.GetLinkQualityOut(index) == kLinkQuality0) &&
614                 (neighbor->GetTwoWayLinkQuality() >= kLinkQuality2))
615             {
616                 Get<Mle::MleRouter>().ScheduleUnicastAdvertisementTo(*neighbor);
617             }
618         }
619 
620         break;
621     }
622 
623     linkCostToNeighbor = GetLinkCost(*neighbor);
624 
625     for (uint8_t routerId = 0, index = 0; routerId <= Mle::kMaxRouterId;
626          index += aRouteTlv.IsRouterIdSet(routerId) ? 1 : 0, routerId++)
627     {
628         Router *router;
629         Router *nextHop;
630         uint8_t cost;
631 
632         if (!aRouteTlv.IsRouterIdSet(routerId))
633         {
634             continue;
635         }
636 
637         router = FindRouterById(routerId);
638 
639         if (router == nullptr || Get<Mle::Mle>().HasRloc16(router->GetRloc16()) || router == neighbor)
640         {
641             continue;
642         }
643 
644         nextHop = FindNextHopOf(*router);
645 
646         cost = aRouteTlv.GetRouteCost(index);
647         cost = (cost == 0) ? Mle::kMaxRouteCost : cost;
648 
649         if ((nextHop == nullptr) || (nextHop == neighbor))
650         {
651             // `router` has no next hop or next hop is neighbor (sender)
652 
653             if (cost + linkCostToNeighbor < Mle::kMaxRouteCost)
654             {
655                 if (router->SetNextHopAndCost(aNeighborId, cost))
656                 {
657                     SignalTableChanged();
658                 }
659             }
660             else if (nextHop == neighbor)
661             {
662                 router->SetNextHopToInvalid();
663                 router->SetLastHeard(TimerMilli::GetNow());
664                 SignalTableChanged();
665             }
666         }
667         else
668         {
669             uint8_t curCost = router->GetCost() + GetLinkCost(*nextHop);
670             uint8_t newCost = cost + linkCostToNeighbor;
671 
672             if (newCost < curCost)
673             {
674                 router->SetNextHopAndCost(aNeighborId, cost);
675                 SignalTableChanged();
676             }
677         }
678     }
679 
680     for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++)
681     {
682         bool oldCostFinite = finitePathCostIdSet.Contains(routerId);
683         bool newCostFinite = (GetPathCost(Mle::Rloc16FromRouterId(routerId)) < Mle::kMaxRouteCost);
684 
685         if (newCostFinite != oldCostFinite)
686         {
687             Get<Mle::MleRouter>().ResetAdvertiseInterval();
688             break;
689         }
690     }
691 
692 exit:
693     return;
694 }
695 
UpdateRouterOnFtdChild(const Mle::RouteTlv & aRouteTlv,uint8_t aParentId)696 void RouterTable::UpdateRouterOnFtdChild(const Mle::RouteTlv &aRouteTlv, uint8_t aParentId)
697 {
698     for (uint8_t routerId = 0, index = 0; routerId <= Mle::kMaxRouterId;
699          index += aRouteTlv.IsRouterIdSet(routerId) ? 1 : 0, routerId++)
700     {
701         Router *router;
702         uint8_t cost;
703         uint8_t nextHopId;
704 
705         if (!aRouteTlv.IsRouterIdSet(routerId) || (routerId == aParentId))
706         {
707             continue;
708         }
709 
710         router = FindRouterById(routerId);
711 
712         if (router == nullptr)
713         {
714             continue;
715         }
716 
717         cost      = aRouteTlv.GetRouteCost(index);
718         nextHopId = (cost == 0) ? Mle::kInvalidRouterId : aParentId;
719 
720         if (router->SetNextHopAndCost(nextHopId, cost))
721         {
722             SignalTableChanged();
723         }
724     }
725 }
726 
FillRouteTlv(Mle::RouteTlv & aRouteTlv,const Neighbor * aNeighbor) const727 void RouterTable::FillRouteTlv(Mle::RouteTlv &aRouteTlv, const Neighbor *aNeighbor) const
728 {
729     uint8_t          routerIdSequence = mRouterIdSequence;
730     Mle::RouterIdSet routerIdSet;
731     uint8_t          routerIndex;
732 
733     mRouterIdMap.GetAsRouterIdSet(routerIdSet);
734 
735     if ((aNeighbor != nullptr) && Mle::IsRouterRloc16(aNeighbor->GetRloc16()))
736     {
737         // Sending a Link Accept message that may require truncation
738         // of Route64 TLV.
739 
740         uint8_t routerCount = mRouters.GetLength();
741 
742         if (routerCount > kMaxRoutersInRouteTlvForLinkAccept)
743         {
744             for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++)
745             {
746                 if (routerCount <= kMaxRoutersInRouteTlvForLinkAccept)
747                 {
748                     break;
749                 }
750 
751                 if (Get<Mle::Mle>().MatchesRouterId(routerId) || (routerId == aNeighbor->GetRouterId()) ||
752                     (routerId == Get<Mle::Mle>().GetLeaderId()))
753                 {
754                     // Route64 TLV must contain this device and the
755                     // neighboring router to ensure that at least this
756                     // link can be established.
757                     continue;
758                 }
759 
760                 if (routerIdSet.Contains(routerId))
761                 {
762                     routerIdSet.Remove(routerId);
763                     routerCount--;
764                 }
765             }
766 
767             // Ensure that the neighbor will process the current
768             // Route64 TLV in a subsequent message exchange
769             routerIdSequence -= kLinkAcceptSequenceRollback;
770         }
771     }
772 
773     aRouteTlv.SetRouterIdSequence(routerIdSequence);
774     aRouteTlv.SetRouterIdMask(routerIdSet);
775 
776     routerIndex = 0;
777 
778     for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++)
779     {
780         uint16_t routerRloc16;
781 
782         if (!routerIdSet.Contains(routerId))
783         {
784             continue;
785         }
786 
787         routerRloc16 = Mle::Rloc16FromRouterId(routerId);
788 
789         if (Get<Mle::Mle>().HasRloc16(routerRloc16))
790         {
791             aRouteTlv.SetRouteData(routerIndex, kLinkQuality0, kLinkQuality0, 1);
792         }
793         else
794         {
795             const Router *router = FindRouterById(routerId);
796             uint8_t       pathCost;
797 
798             OT_ASSERT(router != nullptr);
799 
800             pathCost = GetPathCost(routerRloc16);
801 
802             if (pathCost >= Mle::kMaxRouteCost)
803             {
804                 pathCost = 0;
805             }
806 
807             aRouteTlv.SetRouteData(routerIndex, router->GetLinkQualityIn(), router->GetLinkQualityOut(), pathCost);
808         }
809 
810         routerIndex++;
811     }
812 
813     aRouteTlv.SetRouteDataLength(routerIndex);
814 }
815 
HandleTimeTick(void)816 void RouterTable::HandleTimeTick(void)
817 {
818     mRouterIdMap.HandleTimeTick();
819 
820     VerifyOrExit(Get<Mle::MleRouter>().IsLeader());
821 
822     // Update router id sequence
823     if (GetLeaderAge() >= kRouterIdSequencePeriod)
824     {
825         mRouterIdSequence++;
826         mRouterIdSequenceLastUpdated = TimerMilli::GetNow();
827     }
828 
829 exit:
830     return;
831 }
832 
833 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
GetRouterIdRange(uint8_t & aMinRouterId,uint8_t & aMaxRouterId) const834 void RouterTable::GetRouterIdRange(uint8_t &aMinRouterId, uint8_t &aMaxRouterId) const
835 {
836     aMinRouterId = mMinRouterId;
837     aMaxRouterId = mMaxRouterId;
838 }
839 
SetRouterIdRange(uint8_t aMinRouterId,uint8_t aMaxRouterId)840 Error RouterTable::SetRouterIdRange(uint8_t aMinRouterId, uint8_t aMaxRouterId)
841 {
842     Error error = kErrorNone;
843 
844     VerifyOrExit(aMinRouterId <= aMaxRouterId, error = kErrorInvalidArgs);
845     VerifyOrExit(aMaxRouterId <= Mle::kMaxRouterId, error = kErrorInvalidArgs);
846     mMinRouterId = aMinRouterId;
847     mMaxRouterId = aMaxRouterId;
848 
849 exit:
850     return error;
851 }
852 #endif
853 
GetAsRouterIdSet(Mle::RouterIdSet & aRouterIdSet) const854 void RouterTable::RouterIdMap::GetAsRouterIdSet(Mle::RouterIdSet &aRouterIdSet) const
855 {
856     aRouterIdSet.Clear();
857 
858     for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++)
859     {
860         if (IsAllocated(routerId))
861         {
862             aRouterIdSet.Add(routerId);
863         }
864     }
865 }
866 
HandleTimeTick(void)867 void RouterTable::RouterIdMap::HandleTimeTick(void)
868 {
869     for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++)
870     {
871         // If Router ID is not allocated the `mIndexes` tracks the
872         // remaining reuse delay time in seconds.
873 
874         if (!IsAllocated(routerId) && (mIndexes[routerId] > 0))
875         {
876             mIndexes[routerId]--;
877         }
878     }
879 }
880 
SignalTableChanged(void)881 void RouterTable::SignalTableChanged(void) { mChangedTask.Post(); }
882 
HandleTableChanged(void)883 void RouterTable::HandleTableChanged(void)
884 {
885 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
886     LogRouteTable();
887 #endif
888 
889 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
890     Get<Utils::HistoryTracker>().RecordRouterTableChange();
891 #endif
892 
893     Get<Mle::MleRouter>().UpdateAdvertiseInterval();
894 }
895 
896 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
LogRouteTable(void) const897 void RouterTable::LogRouteTable(void) const
898 {
899     static constexpr uint16_t kStringSize = 128;
900 
901     LogInfo("Route table");
902 
903     for (const Router &router : mRouters)
904     {
905         String<kStringSize> string;
906 
907         string.Append("    %2d 0x%04x", router.GetRouterId(), router.GetRloc16());
908 
909         if (Get<Mle::Mle>().HasRloc16(router.GetRloc16()))
910         {
911             string.Append(" - me");
912         }
913         else if (Get<Mle::Mle>().IsChild() && (router.GetRloc16() == Get<Mle::Mle>().GetParent().GetRloc16()))
914         {
915             string.Append(" - parent");
916         }
917         else
918         {
919             if (router.IsStateValid())
920             {
921                 string.Append(" - nbr{lq[i/o]:%d/%d cost:%d}", router.GetLinkQualityIn(), router.GetLinkQualityOut(),
922                               GetLinkCost(router));
923             }
924 
925             if (router.GetNextHop() != Mle::kInvalidRouterId)
926             {
927                 string.Append(" - nexthop{%d cost:%d}", router.GetNextHop(), router.GetCost());
928             }
929         }
930 
931         if (router.GetRouterId() == Get<Mle::Mle>().GetLeaderId())
932         {
933             string.Append(" - leader");
934         }
935 
936         LogInfo("%s", string.AsCString());
937     }
938 }
939 #endif
940 
941 } // namespace ot
942 
943 #endif // OPENTHREAD_FTD
944