[mle] update and enhance processing of Route TLV (#8851)

This commit updates how we process Route TLV in a received
"MLE Link Accept" message when device is already acting as
router or leader:

- We ensure that the Router ID Sequence number in the received
  Route TLV is more recent compared to the current one being
  used before adopting the Router ID Set. We still update
  the router table (next hops and costs) based on the received
  Route TLV even if Sequence number is older.
- We also check that the ID of the router which sent the Link
  Accept is marked as allocated in the Route TLV that it includes
  in the message. This protects against an edge-case where the
  sending router may be misbehaving.

In addition to the above two changes, this commit contains some
enhancements and simplifications:

- `RxMessage::ReadRouteTlv()` is added which reads and validates
  a `RouteTlv` from an `Mle::RxMessage`.
- `MleRouter::ReadAndProcessRouteTlvOnFed()` is added which reads
  and processes `RouteTlv` on an FED (avoid repeating same code).
- `MleRouter::ProcessRouteTlv()` is simplified.
This commit is contained in:
Abtin Keshavarzian
2023-03-13 19:51:06 -07:00
committed by GitHub
parent 7ecc5d104e
commit 5ddd9d7346
6 changed files with 110 additions and 71 deletions
+15 -30
View File
@@ -2772,21 +2772,7 @@ void Mle::HandleAdvertisement(RxInfo &aRxInfo)
SetLeaderData(leaderData.GetPartitionId(), leaderData.GetWeighting(), leaderData.GetLeaderRouterId());
#if OPENTHREAD_FTD
if (IsFullThreadDevice())
{
RouteTlv routeTlv;
switch (Get<MleRouter>().ProcessRouteTlv(aRxInfo, routeTlv))
{
case kErrorNone:
Get<RouterTable>().UpdateRoutesOnFed(routeTlv, mParent.GetRouterId());
break;
case kErrorNotFound:
break;
default:
ExitNow(error = kErrorParse);
}
}
SuccessOrExit(error = Get<MleRouter>().ReadAndProcessRouteTlvOnFed(aRxInfo, mParent.GetRouterId()));
#endif
mRetrieveNewNetworkData = true;
@@ -3395,21 +3381,7 @@ void Mle::HandleChildIdResponse(RxInfo &aRxInfo)
SetLeaderData(leaderData.GetPartitionId(), leaderData.GetWeighting(), leaderData.GetLeaderRouterId());
#if OPENTHREAD_FTD
if (IsFullThreadDevice())
{
RouteTlv routeTlv;
switch (Get<MleRouter>().ProcessRouteTlv(aRxInfo, routeTlv))
{
case kErrorNone:
Get<RouterTable>().UpdateRoutesOnFed(routeTlv, RouterIdFromRloc16(sourceAddress));
break;
case kErrorNotFound:
break;
default:
ExitNow(error = kErrorParse);
}
}
SuccessOrExit(error = Get<MleRouter>().ReadAndProcessRouteTlvOnFed(aRxInfo, RouterIdFromRloc16(sourceAddress)));
#endif
mParentCandidate.CopyTo(mParent);
@@ -5047,6 +5019,19 @@ exit:
}
#endif
#if OPENTHREAD_FTD
Error Mle::RxMessage::ReadRouteTlv(RouteTlv &aRouteTlv) const
{
Error error;
SuccessOrExit(error = Tlv::FindTlv(*this, aRouteTlv));
VerifyOrExit(aRouteTlv.IsValid(), error = kErrorParse);
exit:
return error;
}
#endif
//---------------------------------------------------------------------------------------------------------------------
// ParentCandidate
+15 -1
View File
@@ -1360,7 +1360,7 @@ protected:
/**
* This method reads CSL Clock Accuracy TLV from a message.
*
* @param[out] A reference to output the CSL accuracy.
* @param[out] aCslAccuracy A reference to output the CSL accuracy.
*
* @retval kErrorNone Successfully read the TLV.
* @retval kErrorNotFound TLV was not found in the message.
@@ -1370,6 +1370,20 @@ protected:
Error ReadCslClockAccuracyTlv(Mac::CslAccuracy &aCslAccuracy) const;
#endif
#if OPENTHREAD_FTD
/**
* This method reads and validates Route TLV from a message.
*
* @param[out] aRouteTlv A reference to output the read Route TLV.
*
* @retval kErrorNone Successfully read and validated the Route TLV.
* @retval kErrorNotFound TLV was not found in the message.
* @retval kErrorParse TLV was found but could not be parsed or is not valid.
*
*/
Error ReadRouteTlv(RouteTlv &aRouteTlv) const;
#endif
private:
Error ReadChallengeOrResponse(uint8_t aTlvType, Challenge &aBuffer) const;
};
+58 -38
View File
@@ -893,7 +893,8 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest)
// Route
mRouterTable.Clear();
SuccessOrExit(error = ProcessRouteTlv(aRxInfo));
SuccessOrExit(error = aRxInfo.mMessage.ReadRouteTlv(routeTlv));
SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo));
router = mRouterTable.FindRouterById(routerId);
VerifyOrExit(router != nullptr);
@@ -935,24 +936,28 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest)
}
// Route (optional)
switch (error = ProcessRouteTlv(aRxInfo, routeTlv))
switch (aRxInfo.mMessage.ReadRouteTlv(routeTlv))
{
case kErrorNone:
VerifyOrExit(routeTlv.IsRouterIdSet(routerId), error = kErrorParse);
if (mRouterTable.IsRouteTlvIdSequenceMoreRecent(routeTlv))
{
SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo));
router = mRouterTable.FindRouterById(routerId);
OT_ASSERT(router != nullptr);
}
mRouterTable.UpdateRoutes(routeTlv, routerId);
// Need to update router after ProcessRouteTlv
router = mRouterTable.FindRouterById(routerId);
OT_ASSERT(router != nullptr);
break;
case kErrorNotFound:
error = kErrorNone;
break;
default:
ExitNow();
ExitNow(error = kErrorParse);
}
// update routing table
if (routerId != mRouterId && !IsRouterIdValid(router->GetNextHop()))
{
ResetAdvertiseInterval();
@@ -1018,20 +1023,9 @@ exit:
return error;
}
Error MleRouter::ProcessRouteTlv(RxInfo &aRxInfo)
Error MleRouter::ProcessRouteTlv(const RouteTlv &aRouteTlv, RxInfo &aRxInfo)
{
RouteTlv routeTlv;
return ProcessRouteTlv(aRxInfo, routeTlv);
}
Error MleRouter::ProcessRouteTlv(RxInfo &aRxInfo, RouteTlv &aRouteTlv)
{
// This method processes Route TLV in a received MLE message
// (from `RxInfo`). In case of success, `aRouteTlv` is updated
// to return the read/processed route TLV from the message.
// If the message contains no Route TLV, `kErrorNotFound` is
// returned.
// This method processes `aRouteTlv` read from an MLE message.
//
// During processing of Route TLV, the entries in the router table
// may shuffle. This method ensures that the `aRxInfo.mNeighbor`
@@ -1040,7 +1034,7 @@ Error MleRouter::ProcessRouteTlv(RxInfo &aRxInfo, RouteTlv &aRouteTlv)
// (in case `mNeighbor` was pointing to a router entry from the
// `RouterTable`).
Error error;
Error error = kErrorNone;
uint16_t neighborRloc16 = Mac::kShortAddrInvalid;
if ((aRxInfo.mNeighbor != nullptr) && Get<RouterTable>().Contains(*aRxInfo.mNeighbor))
@@ -1048,13 +1042,9 @@ Error MleRouter::ProcessRouteTlv(RxInfo &aRxInfo, RouteTlv &aRouteTlv)
neighborRloc16 = aRxInfo.mNeighbor->GetRloc16();
}
SuccessOrExit(error = Tlv::FindTlv(aRxInfo.mMessage, aRouteTlv));
mRouterTable.UpdateRouterIdSet(aRouteTlv.GetRouterIdSequence(), aRouteTlv.GetRouterIdMask());
VerifyOrExit(aRouteTlv.IsValid(), error = kErrorParse);
Get<RouterTable>().UpdateRouterIdSet(aRouteTlv.GetRouterIdSequence(), aRouteTlv.GetRouterIdMask());
if (IsRouter() && !Get<RouterTable>().IsAllocated(mRouterId))
if (IsRouter() && !mRouterTable.IsAllocated(mRouterId))
{
IgnoreError(BecomeDetached());
error = kErrorNoRoute;
@@ -1065,6 +1055,36 @@ Error MleRouter::ProcessRouteTlv(RxInfo &aRxInfo, RouteTlv &aRouteTlv)
aRxInfo.mNeighbor = Get<NeighborTable>().FindNeighbor(neighborRloc16);
}
return error;
}
Error MleRouter::ReadAndProcessRouteTlvOnFed(RxInfo &aRxInfo, uint8_t aParentId)
{
// This method reads and processes Route TLV from message on an
// FED if message contains one. It returns `kErrorNone` when
// successfully processed or if there is no Route TLV in the
// message.
//
// It MUST be used only when device is acting as a child and
// for a message received from device's current parent.
Error error = kErrorNone;
RouteTlv routeTlv;
VerifyOrExit(IsFullThreadDevice());
switch (aRxInfo.mMessage.ReadRouteTlv(routeTlv))
{
case kErrorNone:
SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo));
mRouterTable.UpdateRoutesOnFed(routeTlv, aParentId);
break;
case kErrorNotFound:
break;
default:
ExitNow(error = kErrorParse);
}
exit:
return error;
}
@@ -1116,13 +1136,15 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo, uint16_t aSourceAddress, c
Router *router;
uint8_t routerId;
if (Tlv::FindTlv(aRxInfo.mMessage, routeTlv) == kErrorNone)
switch (aRxInfo.mMessage.ReadRouteTlv(routeTlv))
{
VerifyOrExit(routeTlv.IsValid(), error = kErrorParse);
}
else
{
routeTlv.SetLength(0); // Mark that a Route TLV was not included
case kErrorNone:
break;
case kErrorNotFound:
routeTlv.SetLength(0); // Mark that a Route TLV was not included.
break;
default:
ExitNow(error = kErrorParse);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -1187,9 +1209,7 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo, uint16_t aSourceAddress, c
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Process `RouteTlv`
if (aRxInfo.IsNeighborStateValid() &&
((mRouterTable.GetActiveRouterCount() == 0) ||
SerialNumber::IsGreater(routeTlv.GetRouterIdSequence(), mRouterTable.GetRouterIdSequence())))
if (aRxInfo.IsNeighborStateValid() && mRouterTable.IsRouteTlvIdSequenceMoreRecent(routeTlv))
{
bool processRouteTlv = false;
@@ -1216,7 +1236,7 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo, uint16_t aSourceAddress, c
if (processRouteTlv)
{
SuccessOrExit(error = ProcessRouteTlv(aRxInfo));
SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo));
}
}
+3 -2
View File
@@ -579,8 +579,9 @@ private:
void HandleTimeSync(RxInfo &aRxInfo);
#endif
Error ProcessRouteTlv(RxInfo &aRxInfo);
Error ProcessRouteTlv(RxInfo &aRxInfo, RouteTlv &aRouteTlv);
Error ProcessRouteTlv(const RouteTlv &aRouteTlv, RxInfo &aRxInfo);
Error ReadAndProcessRouteTlvOnFed(RxInfo &aRxInfo, uint8_t aParentId);
void StopAdvertiseTrickleTimer(void);
Error SendAddressSolicit(ThreadStatusTlv::Status aStatus);
void SendAddressSolicitResponse(const Coap::Message &aRequest,
+6
View File
@@ -66,6 +66,12 @@ void RouterTable::Clear(void)
SignalTableChanged();
}
bool RouterTable::IsRouteTlvIdSequenceMoreRecent(const Mle::RouteTlv &aRouteTlv) const
{
return (GetActiveRouterCount() == 0) ||
SerialNumber::IsGreater(aRouteTlv.GetRouterIdSequence(), GetRouterIdSequence());
}
void RouterTable::ClearNeighbors(void)
{
for (Router &router : mRouters)
+13
View File
@@ -39,6 +39,7 @@
#include "common/iterator_utils.hpp"
#include "common/locator.hpp"
#include "common/non_copyable.hpp"
#include "common/serial_number.hpp"
#include "common/tasklet.hpp"
#include "mac/mac_types.hpp"
#include "thread/mle_tlvs.hpp"
@@ -315,6 +316,18 @@ public:
*/
TimeMilli GetRouterIdSequenceLastUpdated(void) const { return mRouterIdSequenceLastUpdated; }
/**
* This method determines whether the Router ID Sequence in a received Route TLV is more recent than the current
* Router ID Sequence being used by `RouterTable`.
*
* @param[in] aRouteTlv The Route TLV to compare.
*
* @retval TRUE The Router ID Sequence in @p aRouteTlv is more recent.
* @retval FALSE The Router ID Sequence in @p aRouteTlv is not more recent.
*
*/
bool IsRouteTlvIdSequenceMoreRecent(const Mle::RouteTlv &aRouteTlv) const;
/**
* This method returns the number of neighbor links.
*