[netdata] support Extended TLVs in Commissioning Data sub-TLVs (#13594)

This commit enhances `NetworkData::Leader` handling of Commissioning
Data sub-TLVs to support MeshCoP regular and Extended TLVs:

- Define `CommissioningDataSubTlvInfo` and `FindCommissioningDataSubTlv()`
  to retrieve sub-TLVs, their value pointers, and lengths, correctly
  handling both standard and extended TLVs.
- Updates `ReadCommissioningDataUint16SubTlv`, `FindSteeringData`, and
  `ProcessCommissionerGetRequest` to use `CommissioningDataSubTlvInfo`.
- Removes the `FindInCommissioningData<SubTlvType>` template to prevent
  potential unsafe use.
- Introduces `Leader::UpdateBorderAgentRloc()` to safely update the
  Border Agent Locator sub-TLV in-place and increment the Network Data
  version (if the RLOC changed).
- Introduces `Leader::HasBorderAgentRloc()` to check for the presence
  of a valid Border Agent Locator sub-TLV without requiring dummy
  out-parameters at call sites.
This commit is contained in:
Abtin Keshavarzian
2026-09-10 21:01:13 -07:00
committed by GitHub
parent f34c5e5476
commit a33e599cca
8 changed files with 92 additions and 71 deletions
+2 -2
View File
@@ -535,10 +535,10 @@ Error Manager::EvictActiveCommissioner(void)
{ {
Error error = kErrorNone; Error error = kErrorNone;
uint16_t sessionId; uint16_t sessionId;
uint16_t baRloc16;
OwnedPtr<Coap::Message> message; OwnedPtr<Coap::Message> message;
SuccessOrExit(error = Get<NetworkData::Leader>().FindBorderAgentRloc(baRloc16)); VerifyOrExit(Get<NetworkData::Leader>().HasBorderAgentRloc(), error = kErrorNotFound);
SuccessOrExit(error = Get<NetworkData::Leader>().FindCommissioningSessionId(sessionId)); SuccessOrExit(error = Get<NetworkData::Leader>().FindCommissioningSessionId(sessionId));
message.Reset(Get<Tmf::Agent>().AllocateAndInitPriorityConfirmablePostMessage(kUriLeaderKeepAlive)); message.Reset(Get<Tmf::Agent>().AllocateAndInitPriorityConfirmablePostMessage(kUriLeaderKeepAlive));
+1 -1
View File
@@ -571,7 +571,7 @@ void Admitter::CommissionerPetitioner::SendPetitionIfNoOtherCommissioner(void)
OT_ASSERT(mState == kToPetition || mState == kRejected); OT_ASSERT(mState == kToPetition || mState == kRejected);
if (Get<NetworkData::Leader>().FindInCommissioningData<BorderAgentLocatorTlv>() != nullptr) if (Get<NetworkData::Leader>().HasBorderAgentRloc())
{ {
SetState(kRejected); SetState(kRejected);
ExitNow(); ExitNow();
+5 -14
View File
@@ -112,10 +112,9 @@ exit:
template <> void Leader::HandleTmf<kUriLeaderKeepAlive>(Coap::Msg &aMsg) template <> void Leader::HandleTmf<kUriLeaderKeepAlive>(Coap::Msg &aMsg)
{ {
uint8_t state; uint8_t state;
uint16_t sessionId; uint16_t sessionId;
BorderAgentLocatorTlv *borderAgentLocator; StateTlv::State responseState;
StateTlv::State responseState;
LogInfo("Received %s", UriToString<kUriLeaderKeepAlive>()); LogInfo("Received %s", UriToString<kUriLeaderKeepAlive>());
@@ -125,9 +124,7 @@ template <> void Leader::HandleTmf<kUriLeaderKeepAlive>(Coap::Msg &aMsg)
SuccessOrExit(Tlv::Find<CommissionerSessionIdTlv>(aMsg.mMessage, sessionId)); SuccessOrExit(Tlv::Find<CommissionerSessionIdTlv>(aMsg.mMessage, sessionId));
borderAgentLocator = Get<NetworkData::Leader>().FindInCommissioningData<BorderAgentLocatorTlv>(); if (!Get<NetworkData::Leader>().HasBorderAgentRloc() || (sessionId != mSessionId))
if ((borderAgentLocator == nullptr) || (sessionId != mSessionId))
{ {
responseState = StateTlv::kReject; responseState = StateTlv::kReject;
} }
@@ -138,13 +135,7 @@ template <> void Leader::HandleTmf<kUriLeaderKeepAlive>(Coap::Msg &aMsg)
} }
else else
{ {
uint16_t rloc = aMsg.mMessageInfo.GetPeerAddr().GetIid().GetLocator(); Get<NetworkData::Leader>().UpdateBorderAgentRloc(aMsg.mMessageInfo.GetPeerAddr().GetIid().GetLocator());
if (borderAgentLocator->GetBorderAgentLocator() != rloc)
{
borderAgentLocator->SetBorderAgentLocator(rloc);
Get<NetworkData::Leader>().IncrementVersion();
}
responseState = StateTlv::kAccept; responseState = StateTlv::kAccept;
mTimer.Start(kLeaderPetitionTimeout); mTimer.Start(kLeaderPetitionTimeout);
+1 -3
View File
@@ -174,10 +174,8 @@ exit:
void EnergyScanServer::HandleNotifierEvents(Events aEvents) void EnergyScanServer::HandleNotifierEvents(Events aEvents)
{ {
uint16_t borderAgentRloc;
if (aEvents.Contains(kEventThreadNetdataChanged) && (mReportMessage != nullptr) && if (aEvents.Contains(kEventThreadNetdataChanged) && (mReportMessage != nullptr) &&
Get<NetworkData::Leader>().FindBorderAgentRloc(borderAgentRloc) != kErrorNone) !Get<NetworkData::Leader>().HasBorderAgentRloc())
{ {
Stop(); Stop();
} }
+36 -18
View File
@@ -505,26 +505,36 @@ const CommissioningDataTlv *Leader::FindCommissioningData(void) const
return NetworkDataTlv::Find<CommissioningDataTlv>(GetTlvsStart(), GetTlvsEnd()); return NetworkDataTlv::Find<CommissioningDataTlv>(GetTlvsStart(), GetTlvsEnd());
} }
const MeshCoP::Tlv *Leader::FindCommissioningDataSubTlv(uint8_t aType) const Error Leader::FindCommissioningDataSubTlv(uint8_t aType, CommissioningDataSubTlvInfo &aSubTlvInfo) const
{ {
const MeshCoP::Tlv *subTlv = nullptr; Error error = kErrorNotFound;
const NetworkDataTlv *dataTlv = FindCommissioningData(); const NetworkDataTlv *dataTlv = FindCommissioningData();
const MeshCoP::Tlv *subTlv;
VerifyOrExit(dataTlv != nullptr); VerifyOrExit(dataTlv != nullptr);
subTlv = As<MeshCoP::Tlv>(Tlv::FindTlv(dataTlv->GetValue(), dataTlv->GetLength(), aType)); subTlv = As<MeshCoP::Tlv>(Tlv::FindTlv(dataTlv->GetValue(), dataTlv->GetLength(), aType));
VerifyOrExit(subTlv != nullptr);
aSubTlvInfo.mTlv = subTlv;
aSubTlvInfo.mValue = subTlv->GetValue();
aSubTlvInfo.mLength = subTlv->IsExtended() ? As<ExtendedTlv>(subTlv)->GetLength() : subTlv->GetLength();
error = kErrorNone;
exit: exit:
return subTlv; return error;
} }
Error Leader::ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::Type aType, uint16_t &aValue) const Error Leader::ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::Type aType, uint16_t &aValue) const
{ {
Error error = kErrorNone; Error error;
const MeshCoP::Tlv *subTlv = FindCommissioningDataSubTlv(aType); CommissioningDataSubTlvInfo subTlvInfo;
VerifyOrExit(subTlv != nullptr, error = kErrorNotFound); SuccessOrExit(error = FindCommissioningDataSubTlv(aType, subTlvInfo));
VerifyOrExit(subTlv->GetLength() >= sizeof(uint16_t), error = kErrorParse);
aValue = BigEndian::ReadUint16(subTlv->GetValue()); VerifyOrExit(subTlvInfo.mLength >= sizeof(uint16_t), error = kErrorParse);
aValue = BigEndian::ReadUint16(subTlvInfo.mValue);
exit: exit:
return error; return error;
@@ -583,16 +593,14 @@ Coap::Message *Leader::ProcessCommissionerGetRequest(const Coap::Message &aMessa
while (!offsetRange.IsEmpty()) while (!offsetRange.IsEmpty())
{ {
uint8_t type; uint8_t type;
const MeshCoP::Tlv *subTlv; CommissioningDataSubTlvInfo subTlvInfo;
IgnoreError(aMessage.ReadAndAdvance(offsetRange, type)); IgnoreError(aMessage.ReadAndAdvance(offsetRange, type));
subTlv = FindCommissioningDataSubTlv(type); if (FindCommissioningDataSubTlv(type, subTlvInfo) == kErrorNone)
if (subTlv != nullptr)
{ {
SuccessOrExit(error = subTlv->AppendTo(*response)); SuccessOrExit(error = subTlvInfo.mTlv->AppendTo(*response));
} }
} }
} }
@@ -618,6 +626,13 @@ Error Leader::FindBorderAgentRloc(uint16_t &aRloc16) const
return ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::kBorderAgentLocator, aRloc16); return ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::kBorderAgentLocator, aRloc16);
} }
bool Leader::HasBorderAgentRloc(void) const
{
uint16_t rloc16;
return (FindBorderAgentRloc(rloc16) == kErrorNone);
}
Error Leader::FindCommissioningSessionId(uint16_t &aSessionId) const Error Leader::FindCommissioningSessionId(uint16_t &aSessionId) const
{ {
return ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::kCommissionerSessionId, aSessionId); return ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::kCommissionerSessionId, aSessionId);
@@ -630,11 +645,14 @@ Error Leader::FindJoinerUdpPort(uint16_t &aPort) const
Error Leader::FindSteeringData(MeshCoP::SteeringData &aSteeringData) const Error Leader::FindSteeringData(MeshCoP::SteeringData &aSteeringData) const
{ {
Error error = kErrorNone; Error error = kErrorNotFound;
const MeshCoP::SteeringDataTlv *steeringDataTlv = FindInCommissioningData<MeshCoP::SteeringDataTlv>(); CommissioningDataSubTlvInfo subTlvInfo;
uint16_t steeringDataLength;
VerifyOrExit(steeringDataTlv != nullptr, error = kErrorNotFound); SuccessOrExit(FindCommissioningDataSubTlv(MeshCoP::Tlv::kSteeringData, subTlvInfo));
error = steeringDataTlv->CopyTo(aSteeringData);
steeringDataLength = Min<uint16_t>(subTlvInfo.mLength, MeshCoP::SteeringData::kMaxLength);
error = aSteeringData.Init(static_cast<uint8_t>(steeringDataLength), subTlvInfo.mValue);
exit: exit:
return error; return error;
+28 -29
View File
@@ -186,30 +186,6 @@ public:
*/ */
Coap::Message *ProcessCommissionerGetRequest(const Coap::Message &aMessage) const; Coap::Message *ProcessCommissionerGetRequest(const Coap::Message &aMessage) const;
/**
* Searches for given sub-TLV in Commissioning Data TLV.
*
* @tparam SubTlvType The sub-TLV type to search for.
*
* @returns A pointer to the Commissioning Data Sub-TLV or `nullptr` if no such sub-TLV exists.
*/
template <typename SubTlvType> const SubTlvType *FindInCommissioningData(void) const
{
return As<SubTlvType>(FindCommissioningDataSubTlv(SubTlvType::kType));
}
/**
* Searches for given sub-TLV in Commissioning Data TLV.
*
* @tparam SubTlvType The sub-TLV type to search for.
*
* @returns A pointer to the Commissioning Data Sub-TLV or `nullptr` if no such sub-TLV exists.
*/
template <typename SubTlvType> SubTlvType *FindInCommissioningData(void)
{
return As<SubTlvType>(FindCommissioningDataSubTlv(SubTlvType::kType));
}
/** /**
* Finds and reads the Commissioning Session ID in Commissioning Data TLV. * Finds and reads the Commissioning Session ID in Commissioning Data TLV.
* *
@@ -232,6 +208,14 @@ public:
*/ */
Error FindBorderAgentRloc(uint16_t &aRloc16) const; Error FindBorderAgentRloc(uint16_t &aRloc16) const;
/**
* Indicates whether the Commissioning Data contains a valid Border Agent Locator sub-TLV.
*
* @retval TRUE The Commissioning Data contains a valid Border Agent Locator sub-TLV.
* @retval FALSE The Commissioning Data does not contain a valid Border Agent Locator sub-TLV.
*/
bool HasBorderAgentRloc(void) const;
/** /**
* Finds and reads the Joiner UDP Port in Commissioning Data TLV. * Finds and reads the Joiner UDP Port in Commissioning Data TLV.
* *
@@ -356,6 +340,18 @@ public:
*/ */
void IncrementVersionAndStableVersion(void); void IncrementVersionAndStableVersion(void);
/**
* Updates the Border Agent Locator sub-TLV in the Commissioning Data.
*
* If the Border Agent Locator sub-TLV is present in the Commissioning Data and its value differs from @p aRloc16,
* this method updates its value in-place and increments the Thread Network Data version. If the sub-TLV is not
* present, or if its value already matches @p aRloc16, no changes are made and the Network Data version is not
* incremented.
*
* @param[in] aRloc16 The new Border Agent RLOC16 value.
*/
void UpdateBorderAgentRloc(uint16_t aRloc16);
/** /**
* Performs anycast ALOC route lookup using the Network Data. * Performs anycast ALOC route lookup using the Network Data.
* *
@@ -432,6 +428,13 @@ private:
typedef bool (&EntryChecker)(const BorderRouterEntry &aEntry); typedef bool (&EntryChecker)(const BorderRouterEntry &aEntry);
struct CommissioningDataSubTlvInfo
{
const Tlv *mTlv;
const uint8_t *mValue;
uint16_t mLength;
};
const PrefixTlv *FindNextMatchingPrefixTlv(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const; const PrefixTlv *FindNextMatchingPrefixTlv(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const;
const PrefixTlv *FindPrefixTlvForContextId(uint8_t aContextId, const ContextTlv *&aContextTlv) const; const PrefixTlv *FindPrefixTlvForContextId(uint8_t aContextId, const ContextTlv *&aContextTlv) const;
@@ -453,11 +456,7 @@ private:
void SignalNetDataChanged(void); void SignalNetDataChanged(void);
const CommissioningDataTlv *FindCommissioningData(void) const; const CommissioningDataTlv *FindCommissioningData(void) const;
CommissioningDataTlv *FindCommissioningData(void) { return AsNonConst(AsConst(this)->FindCommissioningData()); } CommissioningDataTlv *FindCommissioningData(void) { return AsNonConst(AsConst(this)->FindCommissioningData()); }
const MeshCoP::Tlv *FindCommissioningDataSubTlv(uint8_t aType) const; Error FindCommissioningDataSubTlv(uint8_t aType, CommissioningDataSubTlvInfo &aSubTlvInfo) const;
MeshCoP::Tlv *FindCommissioningDataSubTlv(uint8_t aType)
{
return AsNonConst(AsConst(this)->FindCommissioningDataSubTlv(aType));
}
#if OPENTHREAD_FTD #if OPENTHREAD_FTD
static constexpr uint32_t kMaxNetDataSyncWait = 60 * 1000; // Maximum time to wait for netdata sync in msec. static constexpr uint32_t kMaxNetDataSyncWait = 60 * 1000; // Maximum time to wait for netdata sync in msec.
+17 -2
View File
@@ -99,6 +99,22 @@ exit:
return; return;
} }
void Leader::UpdateBorderAgentRloc(uint16_t aRloc16)
{
CommissioningDataSubTlvInfo subTlvInfo;
SuccessOrExit(FindCommissioningDataSubTlv(MeshCoP::Tlv::kBorderAgentLocator, subTlvInfo));
VerifyOrExit(subTlvInfo.mLength >= sizeof(uint16_t));
VerifyOrExit(BigEndian::ReadUint16(subTlvInfo.mValue) != aRloc16);
BigEndian::WriteUint16(aRloc16, AsNonConst(subTlvInfo.mValue));
IncrementVersion();
exit:
return;
}
Error Leader::AnycastLookup(uint16_t aAloc16, uint16_t &aRloc16) const Error Leader::AnycastLookup(uint16_t aAloc16, uint16_t &aRloc16) const
{ {
Error error = kErrorNone; Error error = kErrorNone;
@@ -1285,7 +1301,6 @@ void Leader::HandleNetworkDataRestoredAfterReset(void)
const PrefixTlv *prefix; const PrefixTlv *prefix;
TlvIterator tlvIterator(GetTlvsStart(), GetTlvsEnd()); TlvIterator tlvIterator(GetTlvsStart(), GetTlvsEnd());
ChangedFlags flags; ChangedFlags flags;
uint16_t rloc16;
uint16_t sessionId; uint16_t sessionId;
Rlocs rlocs; Rlocs rlocs;
@@ -1339,7 +1354,7 @@ void Leader::HandleNetworkDataRestoredAfterReset(void)
Get<MeshCoP::Leader>().SetSessionId(sessionId); Get<MeshCoP::Leader>().SetSessionId(sessionId);
} }
if (FindBorderAgentRloc(rloc16) == kErrorNone) if (HasBorderAgentRloc())
{ {
Get<MeshCoP::Leader>().SetEmptyCommissionerData(); Get<MeshCoP::Leader>().SetEmptyCommissionerData();
} }
+2 -2
View File
@@ -721,7 +721,7 @@ void TestBorderAdmitterEnrollerInteraction(void)
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter()); VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
VerifyOrQuit(!admitter.Get<Admitter>().IsActiveCommissioner()); VerifyOrQuit(!admitter.Get<Admitter>().IsActiveCommissioner());
VerifyOrQuit(admitter.Get<NetworkData::Leader>().FindBorderAgentRloc(rloc16) == kErrorNotFound); VerifyOrQuit(!admitter.Get<NetworkData::Leader>().HasBorderAgentRloc());
nexus.AdvanceTime(10 * Time::kOneSecondInMsec); nexus.AdvanceTime(10 * Time::kOneSecondInMsec);
@@ -933,7 +933,7 @@ void TestBorderAdmitterEnrollerInteraction(void)
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter()); VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
VerifyOrQuit(!admitter.Get<Admitter>().IsActiveCommissioner()); VerifyOrQuit(!admitter.Get<Admitter>().IsActiveCommissioner());
VerifyOrQuit(admitter.Get<NetworkData::Leader>().FindBorderAgentRloc(rloc16) == kErrorNotFound); VerifyOrQuit(!admitter.Get<NetworkData::Leader>().HasBorderAgentRloc());
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Re-establish DTLS session"); Log("Re-establish DTLS session");