From f12785def66514710d12a4f554ececc23fbcb472 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 13 May 2024 09:57:19 -0700 Subject: [PATCH] [dataset] add support for handling `MGMT_ACTIVE_REPLACE` on leader (#10201) --- src/core/meshcop/dataset.cpp | 30 ++++++ src/core/meshcop/dataset.hpp | 11 +++ src/core/meshcop/dataset_manager.hpp | 25 +++-- src/core/meshcop/dataset_manager_ftd.cpp | 55 ++++++++--- src/core/thread/tmf.cpp | 1 + src/core/thread/uri_paths.cpp | 111 ++++++++++++----------- src/core/thread/uri_paths.hpp | 2 + 7 files changed, 160 insertions(+), 75 deletions(-) diff --git a/src/core/meshcop/dataset.cpp b/src/core/meshcop/dataset.cpp index 5849781fe..6ff0eac33 100644 --- a/src/core/meshcop/dataset.cpp +++ b/src/core/meshcop/dataset.cpp @@ -238,6 +238,36 @@ exit: return isValid; } +bool Dataset::ContainsAllRequiredTlvsFor(Type aType) const +{ + static const Tlv::Type kActiveDatasetTlvs[] = { + Tlv::kActiveTimestamp, Tlv::kChannel, Tlv::kChannelMask, Tlv::kExtendedPanId, Tlv::kMeshLocalPrefix, + Tlv::kNetworkKey, Tlv::kNetworkName, Tlv::kPanId, Tlv::kPskc, Tlv::kSecurityPolicy, + }; + + static const Tlv::Type kPendingDatasetExtraTlvs[] = {Tlv::kPendingTimestamp, Tlv::kDelayTimer}; + + bool containsAll = false; + + for (Tlv::Type tlvType : kActiveDatasetTlvs) + { + VerifyOrExit(ContainsTlv(tlvType)); + } + + if (aType == kPending) + { + for (Tlv::Type tlvType : kPendingDatasetExtraTlvs) + { + VerifyOrExit(ContainsTlv(tlvType)); + } + } + + containsAll = true; + +exit: + return containsAll; +} + const Tlv *Dataset::FindTlv(Tlv::Type aType) const { return As(Tlv::FindTlv(mTlvs, mLength, aType)); } void Dataset::ConvertTo(Info &aDatasetInfo) const diff --git a/src/core/meshcop/dataset.hpp b/src/core/meshcop/dataset.hpp index 32f91e165..ac350a65c 100644 --- a/src/core/meshcop/dataset.hpp +++ b/src/core/meshcop/dataset.hpp @@ -296,6 +296,17 @@ public: return ContainsTlv(static_cast(TlvType::kType)); } + /** + * Indicates whether or not the Dataset contains all the required TLVs for an Active or Pending Dataset. + * + * @param[in] aType The Dataset type, Active or Pending. + * + * @retval TRUE The Dataset contains all the required TLVs for @p aType. + * @retval FALSE The Dataset does not contain all the required TLVs for @p aType. + * + */ + bool ContainsAllRequiredTlvsFor(Type aType) const; + /** * Searches for a given TLV type in the Dataset. * diff --git a/src/core/meshcop/dataset_manager.hpp b/src/core/meshcop/dataset_manager.hpp index bef0340d7..0cf12f92b 100644 --- a/src/core/meshcop/dataset_manager.hpp +++ b/src/core/meshcop/dataset_manager.hpp @@ -297,17 +297,25 @@ protected: void HandleTimer(void); #if OPENTHREAD_FTD + enum MgmtCommand : uint8_t ///< A MGMT command type. + { + kMgmtSet, ///< MGMT_SET command + kMgmtReplace, ///< MGMT_REPLACE command + }; + /** - * Handles the MGMT_SET request message. + * Handles the MGMT_SET or MGMT_REPLACE request message. * + * + * @param[in] aCommand The MGMT command type (MGMT_REPLACE or MGMT_SET). * @param[in] aMessage The CoAP message buffer. * @param[in] aMessageInfo The message info. * - * @retval kErrorNone The MGMT_SET request message was handled successfully. - * @retval kErrorDrop The MGMT_SET request message was dropped. + * @retval kErrorNone The request message was handled successfully. + * @retval kErrorDrop The request message was dropped. * */ - Error HandleSet(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + Error HandleSetOrReplace(MgmtCommand aCommand, const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); #endif DatasetLocal mLocal; @@ -325,7 +333,7 @@ private: }; #if OPENTHREAD_FTD - struct SetRequestInfo : Clearable // Information from a MGMT_SET request message. + struct RequestInfo : Clearable // Info from a MGMT_SET or MGMT_REPLACE request. { Dataset mDataset; bool mIsFromCommissioner; @@ -351,8 +359,10 @@ private: const TlvList &aTlvList) const; #if OPENTHREAD_FTD - Error ProcessSetRequest(const Coap::Message &aMessage, SetRequestInfo &aInfo) const; - void SendSetResponse(const Coap::Message &aRequest, const Ip6::MessageInfo &aMessageInfo, StateTlv::State aState); + Error ProcessSetOrReplaceRequest(MgmtCommand aCommand, const Coap::Message &aMessage, RequestInfo &aInfo) const; + void SendSetOrReplaceResponse(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. @@ -504,6 +514,7 @@ private: DeclareTmfHandler(ActiveDatasetManager, kUriActiveGet); #if OPENTHREAD_FTD DeclareTmfHandler(ActiveDatasetManager, kUriActiveSet); +DeclareTmfHandler(ActiveDatasetManager, kUriActiveReplace); #endif class PendingDatasetManager : public DatasetManager, private NonCopyable diff --git a/src/core/meshcop/dataset_manager_ftd.cpp b/src/core/meshcop/dataset_manager_ftd.cpp index 171919e23..9c2c20edc 100644 --- a/src/core/meshcop/dataset_manager_ftd.cpp +++ b/src/core/meshcop/dataset_manager_ftd.cpp @@ -78,7 +78,9 @@ Error DatasetManager::AppendMleDatasetTlv(Message &aMessage) const return Tlv::AppendTlv(aMessage, mleTlvType, dataset.GetBytes(), dataset.GetLength()); } -Error DatasetManager::ProcessSetRequest(const Coap::Message &aMessage, SetRequestInfo &aInfo) const +Error DatasetManager::ProcessSetOrReplaceRequest(MgmtCommand aCommand, + const Coap::Message &aMessage, + RequestInfo &aInfo) const { Error error = kErrorParse; Dataset dataset; @@ -180,7 +182,19 @@ Error DatasetManager::ProcessSetRequest(const Coap::Message &aMessage, SetReques // MGMT_ACTIVE_SET.req/MGMT_PENDING_SET.req from Commissioner // based on existing active dataset. - IgnoreError(Get().Read(aInfo.mDataset)); + if (aCommand == kMgmtSet) + { + IgnoreError(Get().Read(aInfo.mDataset)); + } + } + + if (aCommand == kMgmtReplace) + { + // MGMT_ACTIVE_REPLACE can only be used by commissioner. + + VerifyOrExit(aInfo.mIsFromCommissioner); + VerifyOrExit(IsActiveDataset()); + VerifyOrExit(dataset.ContainsAllRequiredTlvsFor(Dataset::kActive)); } SuccessOrExit(error = aInfo.mDataset.WriteTlvsFrom(dataset)); @@ -207,19 +221,22 @@ exit: return error; } -Error DatasetManager::HandleSet(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +Error DatasetManager::HandleSetOrReplace(MgmtCommand aCommand, + const Coap::Message &aMessage, + const Ip6::MessageInfo &aMessageInfo) { StateTlv::State state = StateTlv::kReject; - SetRequestInfo info; + RequestInfo info; VerifyOrExit(Get().IsLeader()); - SuccessOrExit(ProcessSetRequest(aMessage, info)); + SuccessOrExit(ProcessSetOrReplaceRequest(aCommand, aMessage, info)); if (IsActiveDataset() && info.mAffectsConnectivity) { - // MGMT_ACTIVE_SET.req which affects connectivity - // MUST be delayed using pending dataset. + // MGMT_ACTIVE_SET/REPLACE.req which affects + // connectivity MUST be delayed using pending + // dataset. Get().ApplyActiveDataset(info.mDataset); } @@ -244,14 +261,14 @@ Error DatasetManager::HandleSet(const Coap::Message &aMessage, const Ip6::Messag } exit: - SendSetResponse(aMessage, aMessageInfo, state); + SendSetOrReplaceResponse(aMessage, aMessageInfo, state); return (state == StateTlv::kAccept) ? kErrorNone : kErrorDrop; } -void DatasetManager::SendSetResponse(const Coap::Message &aRequest, - const Ip6::MessageInfo &aMessageInfo, - StateTlv::State aState) +void DatasetManager::SendSetOrReplaceResponse(const Coap::Message &aRequest, + const Ip6::MessageInfo &aMessageInfo, + StateTlv::State aState) { Error error = kErrorNone; Coap::Message *message; @@ -263,7 +280,7 @@ void DatasetManager::SendSetResponse(const Coap::Message &aRequest, SuccessOrExit(error = Get().SendMessage(*message, aMessageInfo)); - LogInfo("sent dataset set response"); + LogInfo("sent dataset set/replace response"); exit: FreeMessageOnError(message, error); @@ -376,7 +393,17 @@ void ActiveDatasetManager::StartLeader(void) {} template <> void ActiveDatasetManager::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - SuccessOrExit(DatasetManager::HandleSet(aMessage, aMessageInfo)); + SuccessOrExit(DatasetManager::HandleSetOrReplace(kMgmtSet, aMessage, aMessageInfo)); + IgnoreError(ApplyConfiguration()); + +exit: + return; +} + +template <> +void ActiveDatasetManager::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + SuccessOrExit(DatasetManager::HandleSetOrReplace(kMgmtReplace, aMessage, aMessageInfo)); IgnoreError(ApplyConfiguration()); exit: @@ -388,7 +415,7 @@ void PendingDatasetManager::StartLeader(void) { StartDelayTimer(); } template <> void PendingDatasetManager::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - SuccessOrExit(DatasetManager::HandleSet(aMessage, aMessageInfo)); + SuccessOrExit(DatasetManager::HandleSetOrReplace(kMgmtSet, aMessage, aMessageInfo)); StartDelayTimer(); exit: diff --git a/src/core/thread/tmf.cpp b/src/core/thread/tmf.cpp index 477e4d1ed..dec43943d 100644 --- a/src/core/thread/tmf.cpp +++ b/src/core/thread/tmf.cpp @@ -132,6 +132,7 @@ bool Agent::HandleResource(const char *aUriPath, Message &aMessage, const Ip6::M Case(kUriAddressSolicit, Mle::MleRouter); Case(kUriAddressRelease, Mle::MleRouter); Case(kUriActiveSet, MeshCoP::ActiveDatasetManager); + Case(kUriActiveReplace, MeshCoP::ActiveDatasetManager); Case(kUriPendingSet, MeshCoP::PendingDatasetManager); Case(kUriLeaderPetition, MeshCoP::Leader); Case(kUriLeaderKeepAlive, MeshCoP::Leader); diff --git a/src/core/thread/uri_paths.cpp b/src/core/thread/uri_paths.cpp index ff26d2e76..e597d64a7 100644 --- a/src/core/thread/uri_paths.cpp +++ b/src/core/thread/uri_paths.cpp @@ -69,33 +69,34 @@ static constexpr Entry kEntries[] = { {"b/bq"}, // (9) kUriBackboneQuery {"c/ab"}, // (10) kUriAnnounceBegin {"c/ag"}, // (11) kUriActiveGet - {"c/as"}, // (12) kUriActiveSet - {"c/ca"}, // (13) kUriCommissionerKeepAlive - {"c/cg"}, // (14) kUriCommissionerGet - {"c/cp"}, // (15) kUriCommissionerPetition - {"c/cs"}, // (16) kUriCommissionerSet - {"c/dc"}, // (17) kUriDatasetChanged - {"c/er"}, // (18) kUriEnergyReport - {"c/es"}, // (19) kUriEnergyScan - {"c/je"}, // (20) kUriJoinerEntrust - {"c/jf"}, // (21) kUriJoinerFinalize - {"c/la"}, // (22) kUriLeaderKeepAlive - {"c/lp"}, // (23) kUriLeaderPetition - {"c/pc"}, // (24) kUriPanIdConflict - {"c/pg"}, // (25) kUriPendingGet - {"c/pq"}, // (26) kUriPanIdQuery - {"c/ps"}, // (27) kUriPendingSet - {"c/rx"}, // (28) kUriRelayRx - {"c/tx"}, // (29) kUriRelayTx - {"c/ur"}, // (30) kUriProxyRx - {"c/ut"}, // (31) kUriProxyTx - {"d/da"}, // (32) kUriDiagnosticGetAnswer - {"d/dg"}, // (33) kUriDiagnosticGetRequest - {"d/dq"}, // (34) kUriDiagnosticGetQuery - {"d/dr"}, // (35) kUriDiagnosticReset - {"n/dn"}, // (36) kUriDuaRegistrationNotify - {"n/dr"}, // (37) kUriDuaRegistrationRequest - {"n/mr"}, // (38) kUriMlr + {"c/ar"}, // (12) kUriActiveReplace + {"c/as"}, // (13) kUriActiveSet + {"c/ca"}, // (14) kUriCommissionerKeepAlive + {"c/cg"}, // (15) kUriCommissionerGet + {"c/cp"}, // (16) kUriCommissionerPetition + {"c/cs"}, // (17) kUriCommissionerSet + {"c/dc"}, // (18) kUriDatasetChanged + {"c/er"}, // (19) kUriEnergyReport + {"c/es"}, // (20) kUriEnergyScan + {"c/je"}, // (21) kUriJoinerEntrust + {"c/jf"}, // (22) kUriJoinerFinalize + {"c/la"}, // (23) kUriLeaderKeepAlive + {"c/lp"}, // (24) kUriLeaderPetition + {"c/pc"}, // (25) kUriPanIdConflict + {"c/pg"}, // (26) kUriPendingGet + {"c/pq"}, // (27) kUriPanIdQuery + {"c/ps"}, // (28) kUriPendingSet + {"c/rx"}, // (29) kUriRelayRx + {"c/tx"}, // (30) kUriRelayTx + {"c/ur"}, // (31) kUriProxyRx + {"c/ut"}, // (32) kUriProxyTx + {"d/da"}, // (33) kUriDiagnosticGetAnswer + {"d/dg"}, // (34) kUriDiagnosticGetRequest + {"d/dq"}, // (35) kUriDiagnosticGetQuery + {"d/dr"}, // (36) kUriDiagnosticReset + {"n/dn"}, // (37) kUriDuaRegistrationNotify + {"n/dr"}, // (38) kUriDuaRegistrationRequest + {"n/mr"}, // (39) kUriMlr }; static_assert(BinarySearch::IsSorted(kEntries), "kEntries is not sorted"); @@ -112,33 +113,34 @@ static_assert(8 == kUriBackboneMlr, "kUriBackboneMlr (`b/bmr`) is invalid"); static_assert(9 == kUriBackboneQuery, "kUriBackboneQuery (`b/bq`) is invalid"); static_assert(10 == kUriAnnounceBegin, "kUriAnnounceBegin (`c/ab`) is invalid"); static_assert(11 == kUriActiveGet, "kUriActiveGet (`c/ag`) is invalid"); -static_assert(12 == kUriActiveSet, "kUriActiveSet (`c/as`) is invalid"); -static_assert(13 == kUriCommissionerKeepAlive, "kUriCommissionerKeepAlive (`c/ca`) is invalid"); -static_assert(14 == kUriCommissionerGet, "kUriCommissionerGet (`c/cg`) is invalid"); -static_assert(15 == kUriCommissionerPetition, "kUriCommissionerPetition (`c/cp`) is invalid"); -static_assert(16 == kUriCommissionerSet, "kUriCommissionerSet (`c/cs`) is invalid"); -static_assert(17 == kUriDatasetChanged, "kUriDatasetChanged (`c/dc`) is invalid"); -static_assert(18 == kUriEnergyReport, "kUriEnergyReport (`c/er`) is invalid"); -static_assert(19 == kUriEnergyScan, "kUriEnergyScan (`c/es`) is invalid"); -static_assert(20 == kUriJoinerEntrust, "kUriJoinerEntrust (`c/je`) is invalid"); -static_assert(21 == kUriJoinerFinalize, "kUriJoinerFinalize (`c/jf`) is invalid"); -static_assert(22 == kUriLeaderKeepAlive, "kUriLeaderKeepAlive (`c/la`) is invalid"); -static_assert(23 == kUriLeaderPetition, "kUriLeaderPetition (`c/lp`) is invalid"); -static_assert(24 == kUriPanIdConflict, "kUriPanIdConflict (`c/pc`) is invalid"); -static_assert(25 == kUriPendingGet, "kUriPendingGet (`c/pg`) is invalid"); -static_assert(26 == kUriPanIdQuery, "kUriPanIdQuery (`c/pq`) is invalid"); -static_assert(27 == kUriPendingSet, "kUriPendingSet (`c/ps`) is invalid"); -static_assert(28 == kUriRelayRx, "kUriRelayRx (`c/rx`) is invalid"); -static_assert(29 == kUriRelayTx, "kUriRelayTx (`c/tx`) is invalid"); -static_assert(30 == kUriProxyRx, "kUriProxyRx (`c/ur`) is invalid"); -static_assert(31 == kUriProxyTx, "kUriProxyTx (`c/ut`) is invalid"); -static_assert(32 == kUriDiagnosticGetAnswer, "kUriDiagnosticGetAnswer (`d/da`) is invalid"); -static_assert(33 == kUriDiagnosticGetRequest, "kUriDiagnosticGetRequest (`d/dg`) is invalid"); -static_assert(34 == kUriDiagnosticGetQuery, "kUriDiagnosticGetQuery (`d/dq`) is invalid"); -static_assert(35 == kUriDiagnosticReset, "kUriDiagnosticReset (`d/dr`) is invalid"); -static_assert(36 == kUriDuaRegistrationNotify, "kUriDuaRegistrationNotify (`n/dn`) is invalid"); -static_assert(37 == kUriDuaRegistrationRequest, "kUriDuaRegistrationRequest (`n/dr`) is invalid"); -static_assert(38 == kUriMlr, "kUriMlr (`n/mr`) is invalid"); +static_assert(12 == kUriActiveReplace, "kUriActiveReplace (`c/ar`) is invalid"); +static_assert(13 == kUriActiveSet, "kUriActiveSet (`c/as`) is invalid"); +static_assert(14 == kUriCommissionerKeepAlive, "kUriCommissionerKeepAlive (`c/ca`) is invalid"); +static_assert(15 == kUriCommissionerGet, "kUriCommissionerGet (`c/cg`) is invalid"); +static_assert(16 == kUriCommissionerPetition, "kUriCommissionerPetition (`c/cp`) is invalid"); +static_assert(17 == kUriCommissionerSet, "kUriCommissionerSet (`c/cs`) is invalid"); +static_assert(18 == kUriDatasetChanged, "kUriDatasetChanged (`c/dc`) is invalid"); +static_assert(19 == kUriEnergyReport, "kUriEnergyReport (`c/er`) is invalid"); +static_assert(20 == kUriEnergyScan, "kUriEnergyScan (`c/es`) is invalid"); +static_assert(21 == kUriJoinerEntrust, "kUriJoinerEntrust (`c/je`) is invalid"); +static_assert(22 == kUriJoinerFinalize, "kUriJoinerFinalize (`c/jf`) is invalid"); +static_assert(23 == kUriLeaderKeepAlive, "kUriLeaderKeepAlive (`c/la`) is invalid"); +static_assert(24 == kUriLeaderPetition, "kUriLeaderPetition (`c/lp`) is invalid"); +static_assert(25 == kUriPanIdConflict, "kUriPanIdConflict (`c/pc`) is invalid"); +static_assert(26 == kUriPendingGet, "kUriPendingGet (`c/pg`) is invalid"); +static_assert(27 == kUriPanIdQuery, "kUriPanIdQuery (`c/pq`) is invalid"); +static_assert(28 == kUriPendingSet, "kUriPendingSet (`c/ps`) is invalid"); +static_assert(29 == kUriRelayRx, "kUriRelayRx (`c/rx`) is invalid"); +static_assert(30 == kUriRelayTx, "kUriRelayTx (`c/tx`) is invalid"); +static_assert(31 == kUriProxyRx, "kUriProxyRx (`c/ur`) is invalid"); +static_assert(32 == kUriProxyTx, "kUriProxyTx (`c/ut`) is invalid"); +static_assert(33 == kUriDiagnosticGetAnswer, "kUriDiagnosticGetAnswer (`d/da`) is invalid"); +static_assert(34 == kUriDiagnosticGetRequest, "kUriDiagnosticGetRequest (`d/dg`) is invalid"); +static_assert(35 == kUriDiagnosticGetQuery, "kUriDiagnosticGetQuery (`d/dq`) is invalid"); +static_assert(36 == kUriDiagnosticReset, "kUriDiagnosticReset (`d/dr`) is invalid"); +static_assert(37 == kUriDuaRegistrationNotify, "kUriDuaRegistrationNotify (`n/dn`) is invalid"); +static_assert(38 == kUriDuaRegistrationRequest, "kUriDuaRegistrationRequest (`n/dr`) is invalid"); +static_assert(39 == kUriMlr, "kUriMlr (`n/mr`) is invalid"); } // namespace UriList @@ -173,6 +175,7 @@ template <> const char *UriToString(void) { return "BackboneMlr template <> const char *UriToString(void) { return "BackboneQuery"; } template <> const char *UriToString(void) { return "AnnounceBegin"; } template <> const char *UriToString(void) { return "ActiveGet"; } +template <> const char *UriToString(void) { return "ActiveReplace"; } template <> const char *UriToString(void) { return "ActiveSet"; } template <> const char *UriToString(void) { return "CommissionerKeepAlive"; } template <> const char *UriToString(void) { return "CommissionerGet"; } diff --git a/src/core/thread/uri_paths.hpp b/src/core/thread/uri_paths.hpp index e40666920..54b44965c 100644 --- a/src/core/thread/uri_paths.hpp +++ b/src/core/thread/uri_paths.hpp @@ -58,6 +58,7 @@ enum Uri : uint8_t kUriBackboneQuery, ///< Backbone Query ("b/bq") kUriAnnounceBegin, ///< Announce Begin ("c/ab") kUriActiveGet, ///< MGMT_ACTIVE_GET "c/ag" + kUriActiveReplace, ///< MGMT_ACTIVE_REPLACE ("c/ar") kUriActiveSet, ///< MGMT_ACTIVE_SET ("c/as") kUriCommissionerKeepAlive, ///< Commissioner Keep Alive ("c/ca") kUriCommissionerGet, ///< MGMT_COMMISSIONER_GET ("c/cg") @@ -131,6 +132,7 @@ template <> const char *UriToString(void); template <> const char *UriToString(void); template <> const char *UriToString(void); template <> const char *UriToString(void); +template <> const char *UriToString(void); template <> const char *UriToString(void); template <> const char *UriToString(void); template <> const char *UriToString(void);