[dataset] simplify dataset manager MGMT_SET request handling (#10139)

This commit adds `ProcessSetRequest()` to process a received
`MGMT_SET` request message, validate the included `Dataset`, and
determine if it affects connectivity or changes the network key. The
now unused `DatasetTlv` is removed as TLVs are read directly from the
`Message`. This change will also enable future support for
`MGMT_REPLACE` request.
This commit is contained in:
Abtin Keshavarzian
2024-05-02 16:10:36 -07:00
committed by GitHub
parent d8e3e8380c
commit 922059ce20
3 changed files with 101 additions and 162 deletions
+1 -2
View File
@@ -61,8 +61,7 @@ class Dataset
friend class DatasetLocal;
public:
static constexpr uint8_t kMaxLength = OT_OPERATIONAL_DATASET_MAX_LENGTH; ///< Max length of Dataset (bytes)
static constexpr uint8_t kMaxValueSize = 16; ///< Max size of a TLV value (bytes)
static constexpr uint8_t kMaxLength = OT_OPERATIONAL_DATASET_MAX_LENGTH; ///< Max length of Dataset (bytes)
/**
* Represents the Dataset type (active or pending).
+18 -35
View File
@@ -188,30 +188,6 @@ public:
#endif
protected:
/**
* Defines a generic Dataset TLV to read from a message.
*
*/
OT_TOOL_PACKED_BEGIN
class DatasetTlv : public Tlv
{
public:
/**
* Reads the Dataset TLV from a given message at a given offset.
*
* @param[in] aMessage A message to read the TLV from.
* @param[in] aOffset An offset into the message to read from.
*
* @retval kErrorNone The TLV was read successfully.
* @retval kErrorParse The TLV was not well-formed and could not be parsed.
*
*/
Error ReadFromMessage(const Message &aMessage, uint16_t aOffset);
private:
uint8_t mValue[Dataset::kMaxValueSize];
} OT_TOOL_PACKED_END;
/**
* Initializes the object.
*
@@ -331,7 +307,7 @@ protected:
* @retval kErrorDrop The MGMT_SET request message was dropped.
*
*/
Error HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
Error HandleSet(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
#endif
DatasetLocal mLocal;
@@ -348,6 +324,16 @@ private:
void Add(uint8_t aTlvType);
};
#if OPENTHREAD_FTD
struct SetRequestInfo : Clearable<SetRequestInfo> // Information from a MGMT_SET request message.
{
Dataset mDataset;
bool mIsFromCommissioner;
bool mAffectsConnectivity;
bool mAffectsNetworkKey;
};
#endif
static void HandleMgmtSetResponse(void *aContext,
otMessage *aMessage,
const otMessageInfo *aMessageInfo,
@@ -365,7 +351,8 @@ private:
const TlvList &aTlvList) const;
#if OPENTHREAD_FTD
void SendSetResponse(const Coap::Message &aRequest, const Ip6::MessageInfo &aMessageInfo, StateTlv::State aState);
Error ProcessSetRequest(const Coap::Message &aMessage, SetRequestInfo &aInfo) const;
void SendSetResponse(const Coap::Message &aRequest, const Ip6::MessageInfo &aMessageInfo, StateTlv::State aState);
#endif
static constexpr uint8_t kMaxDatasetTlvs = 16; // Maximum number of TLVs in a Dataset.
@@ -522,6 +509,7 @@ DeclareTmfHandler(ActiveDatasetManager, kUriActiveSet);
class PendingDatasetManager : public DatasetManager, private NonCopyable
{
friend class Tmf::Agent;
friend class DatasetManager;
public:
/**
@@ -606,18 +594,13 @@ public:
*
*/
void StartLeader(void);
/**
* Generates a Pending Dataset from an Active Dataset.
*
* @param[in] aTimestamp The Active Dataset Timestamp.
* @param[in] aMessage The MGMT_SET message that contains an Active Dataset.
*
*/
void ApplyActiveDataset(const Timestamp &aTimestamp, Coap::Message &aMessage);
#endif
private:
#if OPENTHREAD_FTD
void ApplyActiveDataset(Dataset &aDataset);
#endif
void StartDelayTimer(void);
static void HandleTimer(Timer &aTimer);
+82 -125
View File
@@ -78,14 +78,9 @@ Error DatasetManager::AppendMleDatasetTlv(Message &aMessage) const
return Tlv::AppendTlv(aMessage, mleTlvType, dataset.GetBytes(), dataset.GetLength());
}
Error DatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
Error DatasetManager::ProcessSetRequest(const Coap::Message &aMessage, SetRequestInfo &aInfo) const
{
Tlv tlv;
uint16_t offset = aMessage.GetOffset();
bool isUpdateFromCommissioner = false;
bool doesAffectConnectivity = false;
bool doesAffectNetworkKey = false;
StateTlv::State state = StateTlv::kReject;
Error error = kErrorParse;
Dataset dataset;
Timestamp activeTimestamp;
ChannelTlvValue channelValue;
@@ -93,27 +88,23 @@ Error DatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo
Ip6::NetworkPrefix meshLocalPrefix;
NetworkKey networkKey;
uint16_t panId;
uint32_t delayTimer;
// verify that TLV data size is less than maximum TLV value size
while (offset < aMessage.GetLength())
{
SuccessOrExit(aMessage.Read(offset, tlv));
VerifyOrExit(tlv.GetLength() <= Dataset::kMaxValueSize);
offset += sizeof(tlv) + tlv.GetLength();
VerifyOrExit(offset <= aMessage.GetLength());
}
aInfo.Clear();
// verify that does not overflow dataset buffer
VerifyOrExit((offset - aMessage.GetOffset()) <= Dataset::kMaxLength);
SuccessOrExit(dataset.SetFrom(aMessage, aMessage.GetOffset(), aMessage.GetLength() - aMessage.GetOffset()));
SuccessOrExit(dataset.ValidateTlvs());
// verify the request includes a timestamp that is ahead of the locally stored value
SuccessOrExit(Tlv::Find<ActiveTimestampTlv>(aMessage, activeTimestamp));
// Verify that the request includes timestamps that are
// ahead of the locally stored values.
SuccessOrExit(dataset.Read<ActiveTimestampTlv>(activeTimestamp));
if (IsPendingDataset())
{
Timestamp pendingTimestamp;
SuccessOrExit(Tlv::Find<PendingTimestampTlv>(aMessage, pendingTimestamp));
SuccessOrExit(dataset.Read<PendingTimestampTlv>(pendingTimestamp));
VerifyOrExit(Timestamp::Compare(&pendingTimestamp, mLocal.GetTimestamp()) > 0);
}
else
@@ -121,31 +112,27 @@ Error DatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo
VerifyOrExit(Timestamp::Compare(&activeTimestamp, mLocal.GetTimestamp()) > 0);
}
if (Tlv::Find<ChannelTlv>(aMessage, channelValue) == kErrorNone)
{
VerifyOrExit(channelValue.IsValid());
// Determine whether the new Dataset affects connectivity
// or network key.
if (channelValue.GetChannel() != Get<Mac::Mac>().GetPanChannel())
{
doesAffectConnectivity = true;
}
if ((dataset.Read<ChannelTlv>(channelValue) == kErrorNone) &&
(channelValue.GetChannel() != Get<Mac::Mac>().GetPanChannel()))
{
aInfo.mAffectsConnectivity = true;
}
// check PAN ID
if (Tlv::Find<PanIdTlv>(aMessage, panId) == kErrorNone && panId != Get<Mac::Mac>().GetPanId())
if ((dataset.Read<PanIdTlv>(panId) == kErrorNone) && (panId != Get<Mac::Mac>().GetPanId()))
{
doesAffectConnectivity = true;
aInfo.mAffectsConnectivity = true;
}
// check mesh local prefix
if (Tlv::Find<MeshLocalPrefixTlv>(aMessage, meshLocalPrefix) == kErrorNone &&
meshLocalPrefix != Get<Mle::MleRouter>().GetMeshLocalPrefix())
if ((dataset.Read<MeshLocalPrefixTlv>(meshLocalPrefix) == kErrorNone) &&
(meshLocalPrefix != Get<Mle::MleRouter>().GetMeshLocalPrefix()))
{
doesAffectConnectivity = true;
aInfo.mAffectsConnectivity = true;
}
// check network key
if (Tlv::Find<NetworkKeyTlv>(aMessage, networkKey) == kErrorNone)
if (dataset.Read<NetworkKeyTlv>(networkKey) == kErrorNone)
{
NetworkKey localNetworkKey;
@@ -153,105 +140,98 @@ Error DatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo
if (networkKey != localNetworkKey)
{
doesAffectConnectivity = true;
doesAffectNetworkKey = true;
aInfo.mAffectsConnectivity = true;
aInfo.mAffectsNetworkKey = true;
}
}
// check active timestamp rollback
if (IsPendingDataset() && !doesAffectNetworkKey)
// Check active timestamp rollback. If there is no change to
// network key, active timestamp must be ahead of local value.
if (IsPendingDataset() && !aInfo.mAffectsNetworkKey)
{
// no change to network key, active timestamp must be ahead
const Timestamp *localActiveTimestamp = Get<ActiveDatasetManager>().GetTimestamp();
VerifyOrExit(Timestamp::Compare(&activeTimestamp, localActiveTimestamp) > 0);
}
// check commissioner session id
if (Tlv::Find<CommissionerSessionIdTlv>(aMessage, sessionId) == kErrorNone)
// Determine whether the request is from commissioner.
if (dataset.Read<CommissionerSessionIdTlv>(sessionId) == kErrorNone)
{
uint16_t localSessionId;
isUpdateFromCommissioner = true;
aInfo.mIsFromCommissioner = true;
dataset.RemoveTlv(Tlv::kCommissionerSessionId);
SuccessOrExit(Get<NetworkData::Leader>().FindCommissioningSessionId(localSessionId));
VerifyOrExit(localSessionId == sessionId);
}
if (isUpdateFromCommissioner)
{
// Verify an MGMT_ACTIVE_SET.req from a Commissioner does not
// affect connectivity
// affect connectivity.
if (IsActiveDataset())
{
VerifyOrExit(!doesAffectConnectivity);
VerifyOrExit(!aInfo.mAffectsConnectivity);
}
// Thread specification allows partial dataset changes for
// MGMT_ACTIVE_SET.req/MGMT_PENDING_SET.req from Commissioner
// based on existing active dataset.
IgnoreError(Get<ActiveDatasetManager>().Read(dataset));
IgnoreError(Get<ActiveDatasetManager>().Read(aInfo.mDataset));
}
if (IsActiveDataset() && doesAffectConnectivity)
SuccessOrExit(error = aInfo.mDataset.WriteTlvsFrom(dataset));
// Check and update the Delay Timer TLV value if present.
if (aInfo.mDataset.Read<DelayTimerTlv>(delayTimer) == kErrorNone)
{
delayTimer = Min(delayTimer, DelayTimerTlv::kMaxDelay);
if (aInfo.mAffectsNetworkKey && (delayTimer < DelayTimerTlv::kDefaultDelay))
{
delayTimer = DelayTimerTlv::kDefaultDelay;
}
else
{
delayTimer = Max(delayTimer, Get<Leader>().GetDelayTimerMinimal());
}
IgnoreError(aInfo.mDataset.Write<DelayTimerTlv>(delayTimer));
}
exit:
return error;
}
Error DatasetManager::HandleSet(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
StateTlv::State state = StateTlv::kReject;
SetRequestInfo info;
SuccessOrExit(ProcessSetRequest(aMessage, info));
if (IsActiveDataset() && info.mAffectsConnectivity)
{
// MGMT_ACTIVE_SET.req which affects connectivity
// MUST be delayed using pending dataset.
Get<PendingDatasetManager>().ApplyActiveDataset(activeTimestamp, aMessage);
Get<PendingDatasetManager>().ApplyActiveDataset(info.mDataset);
}
else
{
offset = aMessage.GetOffset();
while (offset < aMessage.GetLength())
{
DatasetTlv datasetTlv;
SuccessOrExit(datasetTlv.ReadFromMessage(aMessage, offset));
switch (datasetTlv.GetType())
{
case Tlv::kCommissionerSessionId:
// do not store Commissioner Session ID TLV
break;
case Tlv::kDelayTimer:
{
uint32_t delayTimer = Min(datasetTlv.ReadValueAs<DelayTimerTlv>(), DelayTimerTlv::kMaxDelay);
if (doesAffectNetworkKey && delayTimer < DelayTimerTlv::kDefaultDelay)
{
delayTimer = DelayTimerTlv::kDefaultDelay;
}
else
{
delayTimer = Max(delayTimer, Get<Leader>().GetDelayTimerMinimal());
}
datasetTlv.WriteValueAs<DelayTimerTlv>(delayTimer);
}
OT_FALL_THROUGH;
default:
SuccessOrExit(dataset.WriteTlv(datasetTlv));
break;
}
offset += static_cast<uint16_t>(datasetTlv.GetSize());
}
SuccessOrExit(Save(dataset));
SuccessOrExit(Save(info.mDataset));
Get<NetworkData::Leader>().IncrementVersionAndStableVersion();
}
state = StateTlv::kAccept;
// notify commissioner if update is from thread device
if (!isUpdateFromCommissioner)
// Notify commissioner if update is from a Thread device.
if (!info.mIsFromCommissioner)
{
uint16_t localSessionId;
Ip6::Address destination;
@@ -287,19 +267,6 @@ exit:
FreeMessageOnError(message, error);
}
Error DatasetManager::DatasetTlv::ReadFromMessage(const Message &aMessage, uint16_t aOffset)
{
Error error = kErrorNone;
SuccessOrExit(error = aMessage.Read(aOffset, this, sizeof(Tlv)));
VerifyOrExit(GetLength() <= Dataset::kMaxValueSize, error = kErrorParse);
SuccessOrExit(error = aMessage.Read(aOffset + sizeof(Tlv), mValue, GetLength()));
VerifyOrExit(Dataset::IsTlvValid(*this), error = kErrorParse);
exit:
return error;
}
#if OPENTHREAD_CONFIG_OPERATIONAL_DATASET_AUTO_INIT
Error ActiveDatasetManager::GenerateLocal(void)
{
@@ -428,27 +395,17 @@ exit:
return;
}
void PendingDatasetManager::ApplyActiveDataset(const Timestamp &aTimestamp, Coap::Message &aMessage)
void PendingDatasetManager::ApplyActiveDataset(Dataset &aDataset)
{
uint16_t offset = aMessage.GetOffset();
Dataset dataset;
// Generates and applies Pending Dataset from an Active Dataset.
VerifyOrExit(Get<Mle::MleRouter>().IsAttached());
Timestamp activeTimestamp;
while (offset < aMessage.GetLength())
{
DatasetTlv datasetTlv;
SuccessOrExit(datasetTlv.ReadFromMessage(aMessage, offset));
offset += static_cast<uint16_t>(datasetTlv.GetSize());
IgnoreError(dataset.WriteTlv(datasetTlv));
}
IgnoreError(dataset.Write<DelayTimerTlv>(Get<Leader>().GetDelayTimerMinimal()));
IgnoreError(dataset.Write<PendingTimestampTlv>(aTimestamp));
IgnoreError(DatasetManager::Save(dataset));
SuccessOrExit(aDataset.Read<ActiveTimestampTlv>(activeTimestamp));
SuccessOrExit(aDataset.Write<PendingTimestampTlv>(activeTimestamp));
SuccessOrExit(aDataset.Write<DelayTimerTlv>(Get<Leader>().GetDelayTimerMinimal()));
IgnoreError(DatasetManager::Save(aDataset));
StartDelayTimer();
exit: