diff --git a/src/core/meshcop/dataset.hpp b/src/core/meshcop/dataset.hpp index 897cc04cc..32f91e165 100644 --- a/src/core/meshcop/dataset.hpp +++ b/src/core/meshcop/dataset.hpp @@ -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). diff --git a/src/core/meshcop/dataset_manager.hpp b/src/core/meshcop/dataset_manager.hpp index 66ce5df37..bef0340d7 100644 --- a/src/core/meshcop/dataset_manager.hpp +++ b/src/core/meshcop/dataset_manager.hpp @@ -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 // 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); diff --git a/src/core/meshcop/dataset_manager_ftd.cpp b/src/core/meshcop/dataset_manager_ftd.cpp index 606839be2..077cd79e1 100644 --- a/src/core/meshcop/dataset_manager_ftd.cpp +++ b/src/core/meshcop/dataset_manager_ftd.cpp @@ -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(aMessage, activeTimestamp)); + // Verify that the request includes timestamps that are + // ahead of the locally stored values. + + SuccessOrExit(dataset.Read(activeTimestamp)); if (IsPendingDataset()) { Timestamp pendingTimestamp; - SuccessOrExit(Tlv::Find(aMessage, pendingTimestamp)); + SuccessOrExit(dataset.Read(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(aMessage, channelValue) == kErrorNone) - { - VerifyOrExit(channelValue.IsValid()); + // Determine whether the new Dataset affects connectivity + // or network key. - if (channelValue.GetChannel() != Get().GetPanChannel()) - { - doesAffectConnectivity = true; - } + if ((dataset.Read(channelValue) == kErrorNone) && + (channelValue.GetChannel() != Get().GetPanChannel())) + { + aInfo.mAffectsConnectivity = true; } - // check PAN ID - if (Tlv::Find(aMessage, panId) == kErrorNone && panId != Get().GetPanId()) + if ((dataset.Read(panId) == kErrorNone) && (panId != Get().GetPanId())) { - doesAffectConnectivity = true; + aInfo.mAffectsConnectivity = true; } - // check mesh local prefix - if (Tlv::Find(aMessage, meshLocalPrefix) == kErrorNone && - meshLocalPrefix != Get().GetMeshLocalPrefix()) + if ((dataset.Read(meshLocalPrefix) == kErrorNone) && + (meshLocalPrefix != Get().GetMeshLocalPrefix())) { - doesAffectConnectivity = true; + aInfo.mAffectsConnectivity = true; } - // check network key - if (Tlv::Find(aMessage, networkKey) == kErrorNone) + if (dataset.Read(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().GetTimestamp(); VerifyOrExit(Timestamp::Compare(&activeTimestamp, localActiveTimestamp) > 0); } - // check commissioner session id - if (Tlv::Find(aMessage, sessionId) == kErrorNone) + // Determine whether the request is from commissioner. + + if (dataset.Read(sessionId) == kErrorNone) { uint16_t localSessionId; - isUpdateFromCommissioner = true; + aInfo.mIsFromCommissioner = true; + + dataset.RemoveTlv(Tlv::kCommissionerSessionId); SuccessOrExit(Get().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().Read(dataset)); + IgnoreError(Get().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(delayTimer) == kErrorNone) + { + delayTimer = Min(delayTimer, DelayTimerTlv::kMaxDelay); + + if (aInfo.mAffectsNetworkKey && (delayTimer < DelayTimerTlv::kDefaultDelay)) + { + delayTimer = DelayTimerTlv::kDefaultDelay; + } + else + { + delayTimer = Max(delayTimer, Get().GetDelayTimerMinimal()); + } + + IgnoreError(aInfo.mDataset.Write(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().ApplyActiveDataset(activeTimestamp, aMessage); + Get().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::kMaxDelay); - - if (doesAffectNetworkKey && delayTimer < DelayTimerTlv::kDefaultDelay) - { - delayTimer = DelayTimerTlv::kDefaultDelay; - } - else - { - delayTimer = Max(delayTimer, Get().GetDelayTimerMinimal()); - } - - datasetTlv.WriteValueAs(delayTimer); - } - - OT_FALL_THROUGH; - - default: - SuccessOrExit(dataset.WriteTlv(datasetTlv)); - break; - } - - offset += static_cast(datasetTlv.GetSize()); - } - - SuccessOrExit(Save(dataset)); + SuccessOrExit(Save(info.mDataset)); Get().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().IsAttached()); + Timestamp activeTimestamp; - while (offset < aMessage.GetLength()) - { - DatasetTlv datasetTlv; - - SuccessOrExit(datasetTlv.ReadFromMessage(aMessage, offset)); - offset += static_cast(datasetTlv.GetSize()); - IgnoreError(dataset.WriteTlv(datasetTlv)); - } - - IgnoreError(dataset.Write(Get().GetDelayTimerMinimal())); - - IgnoreError(dataset.Write(aTimestamp)); - IgnoreError(DatasetManager::Save(dataset)); + SuccessOrExit(aDataset.Read(activeTimestamp)); + SuccessOrExit(aDataset.Write(activeTimestamp)); + SuccessOrExit(aDataset.Write(Get().GetDelayTimerMinimal())); + IgnoreError(DatasetManager::Save(aDataset)); StartDelayTimer(); exit: