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