[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;
uint16_t sessionId;
uint16_t baRloc16;
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));
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);
if (Get<NetworkData::Leader>().FindInCommissioningData<BorderAgentLocatorTlv>() != nullptr)
if (Get<NetworkData::Leader>().HasBorderAgentRloc())
{
SetState(kRejected);
ExitNow();
+5 -14
View File
@@ -112,10 +112,9 @@ exit:
template <> void Leader::HandleTmf<kUriLeaderKeepAlive>(Coap::Msg &aMsg)
{
uint8_t state;
uint16_t sessionId;
BorderAgentLocatorTlv *borderAgentLocator;
StateTlv::State responseState;
uint8_t state;
uint16_t sessionId;
StateTlv::State responseState;
LogInfo("Received %s", UriToString<kUriLeaderKeepAlive>());
@@ -125,9 +124,7 @@ template <> void Leader::HandleTmf<kUriLeaderKeepAlive>(Coap::Msg &aMsg)
SuccessOrExit(Tlv::Find<CommissionerSessionIdTlv>(aMsg.mMessage, sessionId));
borderAgentLocator = Get<NetworkData::Leader>().FindInCommissioningData<BorderAgentLocatorTlv>();
if ((borderAgentLocator == nullptr) || (sessionId != mSessionId))
if (!Get<NetworkData::Leader>().HasBorderAgentRloc() || (sessionId != mSessionId))
{
responseState = StateTlv::kReject;
}
@@ -138,13 +135,7 @@ template <> void Leader::HandleTmf<kUriLeaderKeepAlive>(Coap::Msg &aMsg)
}
else
{
uint16_t rloc = aMsg.mMessageInfo.GetPeerAddr().GetIid().GetLocator();
if (borderAgentLocator->GetBorderAgentLocator() != rloc)
{
borderAgentLocator->SetBorderAgentLocator(rloc);
Get<NetworkData::Leader>().IncrementVersion();
}
Get<NetworkData::Leader>().UpdateBorderAgentRloc(aMsg.mMessageInfo.GetPeerAddr().GetIid().GetLocator());
responseState = StateTlv::kAccept;
mTimer.Start(kLeaderPetitionTimeout);
+1 -3
View File
@@ -174,10 +174,8 @@ exit:
void EnergyScanServer::HandleNotifierEvents(Events aEvents)
{
uint16_t borderAgentRloc;
if (aEvents.Contains(kEventThreadNetdataChanged) && (mReportMessage != nullptr) &&
Get<NetworkData::Leader>().FindBorderAgentRloc(borderAgentRloc) != kErrorNone)
!Get<NetworkData::Leader>().HasBorderAgentRloc())
{
Stop();
}
+36 -18
View File
@@ -505,26 +505,36 @@ const CommissioningDataTlv *Leader::FindCommissioningData(void) const
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 MeshCoP::Tlv *subTlv;
VerifyOrExit(dataTlv != nullptr);
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:
return subTlv;
return error;
}
Error Leader::ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::Type aType, uint16_t &aValue) const
{
Error error = kErrorNone;
const MeshCoP::Tlv *subTlv = FindCommissioningDataSubTlv(aType);
Error error;
CommissioningDataSubTlvInfo subTlvInfo;
VerifyOrExit(subTlv != nullptr, error = kErrorNotFound);
VerifyOrExit(subTlv->GetLength() >= sizeof(uint16_t), error = kErrorParse);
aValue = BigEndian::ReadUint16(subTlv->GetValue());
SuccessOrExit(error = FindCommissioningDataSubTlv(aType, subTlvInfo));
VerifyOrExit(subTlvInfo.mLength >= sizeof(uint16_t), error = kErrorParse);
aValue = BigEndian::ReadUint16(subTlvInfo.mValue);
exit:
return error;
@@ -583,16 +593,14 @@ Coap::Message *Leader::ProcessCommissionerGetRequest(const Coap::Message &aMessa
while (!offsetRange.IsEmpty())
{
uint8_t type;
const MeshCoP::Tlv *subTlv;
uint8_t type;
CommissioningDataSubTlvInfo subTlvInfo;
IgnoreError(aMessage.ReadAndAdvance(offsetRange, type));
subTlv = FindCommissioningDataSubTlv(type);
if (subTlv != nullptr)
if (FindCommissioningDataSubTlv(type, subTlvInfo) == kErrorNone)
{
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);
}
bool Leader::HasBorderAgentRloc(void) const
{
uint16_t rloc16;
return (FindBorderAgentRloc(rloc16) == kErrorNone);
}
Error Leader::FindCommissioningSessionId(uint16_t &aSessionId) const
{
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 error = kErrorNone;
const MeshCoP::SteeringDataTlv *steeringDataTlv = FindInCommissioningData<MeshCoP::SteeringDataTlv>();
Error error = kErrorNotFound;
CommissioningDataSubTlvInfo subTlvInfo;
uint16_t steeringDataLength;
VerifyOrExit(steeringDataTlv != nullptr, error = kErrorNotFound);
error = steeringDataTlv->CopyTo(aSteeringData);
SuccessOrExit(FindCommissioningDataSubTlv(MeshCoP::Tlv::kSteeringData, subTlvInfo));
steeringDataLength = Min<uint16_t>(subTlvInfo.mLength, MeshCoP::SteeringData::kMaxLength);
error = aSteeringData.Init(static_cast<uint8_t>(steeringDataLength), subTlvInfo.mValue);
exit:
return error;
+28 -29
View File
@@ -186,30 +186,6 @@ public:
*/
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.
*
@@ -232,6 +208,14 @@ public:
*/
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.
*
@@ -356,6 +340,18 @@ public:
*/
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.
*
@@ -432,6 +428,13 @@ private:
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 *FindPrefixTlvForContextId(uint8_t aContextId, const ContextTlv *&aContextTlv) const;
@@ -453,11 +456,7 @@ private:
void SignalNetDataChanged(void);
const CommissioningDataTlv *FindCommissioningData(void) const;
CommissioningDataTlv *FindCommissioningData(void) { return AsNonConst(AsConst(this)->FindCommissioningData()); }
const MeshCoP::Tlv *FindCommissioningDataSubTlv(uint8_t aType) const;
MeshCoP::Tlv *FindCommissioningDataSubTlv(uint8_t aType)
{
return AsNonConst(AsConst(this)->FindCommissioningDataSubTlv(aType));
}
Error FindCommissioningDataSubTlv(uint8_t aType, CommissioningDataSubTlvInfo &aSubTlvInfo) const;
#if OPENTHREAD_FTD
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;
}
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 error = kErrorNone;
@@ -1285,7 +1301,6 @@ void Leader::HandleNetworkDataRestoredAfterReset(void)
const PrefixTlv *prefix;
TlvIterator tlvIterator(GetTlvsStart(), GetTlvsEnd());
ChangedFlags flags;
uint16_t rloc16;
uint16_t sessionId;
Rlocs rlocs;
@@ -1339,7 +1354,7 @@ void Leader::HandleNetworkDataRestoredAfterReset(void)
Get<MeshCoP::Leader>().SetSessionId(sessionId);
}
if (FindBorderAgentRloc(rloc16) == kErrorNone)
if (HasBorderAgentRloc())
{
Get<MeshCoP::Leader>().SetEmptyCommissionerData();
}
+2 -2
View File
@@ -721,7 +721,7 @@ void TestBorderAdmitterEnrollerInteraction(void)
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
VerifyOrQuit(!admitter.Get<Admitter>().IsActiveCommissioner());
VerifyOrQuit(admitter.Get<NetworkData::Leader>().FindBorderAgentRloc(rloc16) == kErrorNotFound);
VerifyOrQuit(!admitter.Get<NetworkData::Leader>().HasBorderAgentRloc());
nexus.AdvanceTime(10 * Time::kOneSecondInMsec);
@@ -933,7 +933,7 @@ void TestBorderAdmitterEnrollerInteraction(void)
VerifyOrQuit(admitter.Get<Admitter>().IsPrimeAdmitter());
VerifyOrQuit(!admitter.Get<Admitter>().IsActiveCommissioner());
VerifyOrQuit(admitter.Get<NetworkData::Leader>().FindBorderAgentRloc(rloc16) == kErrorNotFound);
VerifyOrQuit(!admitter.Get<NetworkData::Leader>().HasBorderAgentRloc());
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Re-establish DTLS session");