diff --git a/include/openthread/dataset.h b/include/openthread/dataset.h index 15f2d2dae..c48e602dd 100644 --- a/include/openthread/dataset.h +++ b/include/openthread/dataset.h @@ -290,6 +290,21 @@ typedef enum otMeshcopTlvType OT_MESHCOP_TLV_JOINERADVERTISEMENT = 241, ///< meshcop Joiner Advertisement TLV } otMeshcopTlvType; +/** + * This function pointer is called when a response to a MGMT_SET request is received or times out. + * + * @param[in] aResult A result of the operation. + * @param[in] aContext A pointer to application-specific context. + * + * @retval OT_ERROR_NONE The request was accepted by the leader. + * @retval OT_ERROR_REJECTED The request was rejected by the leader. + * @retval OT_ERROR_PARSE An error occurred during parsing the response. + * @retval OT_ERROR_ABORT The request was reset by peer. + * @retval OT_ERROR_RESPONSE_TIMEOUT No response or acknowledgment received during timeout period. + * + */ +typedef void (*otDatasetMgmtSetCallback)(otError aResult, void *aContext); + /** * This function indicates whether a valid network is present in the Active Operational Dataset or not. * @@ -452,15 +467,20 @@ otError otDatasetSendMgmtActiveGet(otInstance * aInstan * @param[in] aDataset A pointer to operational dataset. * @param[in] aTlvs A pointer to TLVs. * @param[in] aLength The length of TLVs. + * @param[in] aCallback A pointer to a function that is called on response reception or timeout. + * @param[in] aContext A pointer to application-specific context for @p aCallback. * * @retval OT_ERROR_NONE Successfully send the meshcop dataset command. * @retval OT_ERROR_NO_BUFS Insufficient buffer space to send. + * @retval OT_ERROR_BUSY A previous request is ongoing. * */ otError otDatasetSendMgmtActiveSet(otInstance * aInstance, const otOperationalDataset *aDataset, const uint8_t * aTlvs, - uint8_t aLength); + uint8_t aLength, + otDatasetMgmtSetCallback aCallback, + void * aContext); /** * This function sends MGMT_PENDING_GET. @@ -488,15 +508,20 @@ otError otDatasetSendMgmtPendingGet(otInstance * aInsta * @param[in] aDataset A pointer to operational dataset. * @param[in] aTlvs A pointer to TLVs. * @param[in] aLength The length of TLVs. + * @param[in] aCallback A pointer to a function that is called on response reception or timeout. + * @param[in] aContext A pointer to application-specific context for @p aCallback. * * @retval OT_ERROR_NONE Successfully send the meshcop dataset command. * @retval OT_ERROR_NO_BUFS Insufficient buffer space to send. + * @retval OT_ERROR_BUSY A previous request is ongoing. * */ otError otDatasetSendMgmtPendingSet(otInstance * aInstance, const otOperationalDataset *aDataset, const uint8_t * aTlvs, - uint8_t aLength); + uint8_t aLength, + otDatasetMgmtSetCallback aCallback, + void * aContext); /** * This function generates PSKc from a given pass-phrase, network name, and extended PAN ID. diff --git a/include/openthread/error.h b/include/openthread/error.h index 3ddce32fd..66cb3c5b5 100644 --- a/include/openthread/error.h +++ b/include/openthread/error.h @@ -234,6 +234,11 @@ typedef enum OT_MUST_USE_RESULT otError */ OT_ERROR_PENDING = 36, + /** + * Request rejected. + */ + OT_ERROR_REJECTED = 37, + /** * The number of defined errors. */ diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 1a1477f28..1d83dfc3a 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (145) +#define OPENTHREAD_API_VERSION (146) /** * @addtogroup api-instance diff --git a/src/cli/cli_dataset.cpp b/src/cli/cli_dataset.cpp index eaec978c8..acc519fe9 100644 --- a/src/cli/cli_dataset.cpp +++ b/src/cli/cli_dataset.cpp @@ -565,11 +565,13 @@ otError Dataset::ProcessMgmtSetCommand(Arg aArgs[]) if (aArgs[0] == "active") { - error = otDatasetSendMgmtActiveSet(mInterpreter.mInstance, &dataset, tlvs, tlvsLength); + error = otDatasetSendMgmtActiveSet(mInterpreter.mInstance, &dataset, tlvs, tlvsLength, /* aCallback */ nullptr, + /* aContext */ nullptr); } else if (aArgs[0] == "pending") { - error = otDatasetSendMgmtPendingSet(mInterpreter.mInstance, &dataset, tlvs, tlvsLength); + error = otDatasetSendMgmtPendingSet(mInterpreter.mInstance, &dataset, tlvs, tlvsLength, /* aCallback */ nullptr, + /* aContext */ nullptr); } else { diff --git a/src/core/api/dataset_api.cpp b/src/core/api/dataset_api.cpp index 30247f890..209f61849 100644 --- a/src/core/api/dataset_api.cpp +++ b/src/core/api/dataset_api.cpp @@ -136,12 +136,14 @@ otError otDatasetSendMgmtActiveGet(otInstance * aInstan otError otDatasetSendMgmtActiveSet(otInstance * aInstance, const otOperationalDataset *aDataset, const uint8_t * aTlvs, - uint8_t aLength) + uint8_t aLength, + otDatasetMgmtSetCallback aCallback, + void * aContext) { Instance &instance = *static_cast(aInstance); return instance.Get().SendSetRequest(*static_cast(aDataset), - aTlvs, aLength); + aTlvs, aLength, aCallback, aContext); } otError otDatasetSendMgmtPendingGet(otInstance * aInstance, @@ -159,12 +161,14 @@ otError otDatasetSendMgmtPendingGet(otInstance * aInsta otError otDatasetSendMgmtPendingSet(otInstance * aInstance, const otOperationalDataset *aDataset, const uint8_t * aTlvs, - uint8_t aLength) + uint8_t aLength, + otDatasetMgmtSetCallback aCallback, + void * aContext) { Instance &instance = *static_cast(aInstance); return instance.Get().SendSetRequest( - *static_cast(aDataset), aTlvs, aLength); + *static_cast(aDataset), aTlvs, aLength, aCallback, aContext); } #if OPENTHREAD_FTD diff --git a/src/core/common/error.cpp b/src/core/common/error.cpp index 3d0e4a536..5d4a92c81 100644 --- a/src/core/common/error.cpp +++ b/src/core/common/error.cpp @@ -77,6 +77,7 @@ const char *ErrorToString(Error aError) "LinkMarginLow", // (34) kErrorLinkMarginLow "InvalidCommand", // (35) kErrorInvalidCommand "Pending", // (36) kErrorPending + "Rejected", // (37) kErrorRejected }; return aError < OT_ARRAY_LENGTH(kErrorStrings) ? kErrorStrings[aError] : "UnknownErrorType"; diff --git a/src/core/common/error.hpp b/src/core/common/error.hpp index 838bbb19c..d89be292a 100644 --- a/src/core/common/error.hpp +++ b/src/core/common/error.hpp @@ -88,6 +88,7 @@ constexpr Error kErrorNotLowpanDataFrame = OT_ERROR_NOT_LOWPAN_DATA_FRAM constexpr Error kErrorLinkMarginLow = OT_ERROR_LINK_MARGIN_LOW; constexpr Error kErrorInvalidCommand = OT_ERROR_INVALID_COMMAND; constexpr Error kErrorPending = OT_ERROR_PENDING; +constexpr Error kErrorRejected = OT_ERROR_REJECTED; constexpr Error kErrorGeneric = OT_ERROR_GENERIC; constexpr uint8_t kNumErrors = OT_NUM_ERRORS; diff --git a/src/core/meshcop/dataset_manager.cpp b/src/core/meshcop/dataset_manager.cpp index 9dafa81d9..96a796219 100644 --- a/src/core/meshcop/dataset_manager.cpp +++ b/src/core/meshcop/dataset_manager.cpp @@ -54,8 +54,10 @@ DatasetManager::DatasetManager(Instance &aInstance, Dataset::Type aType, Timer:: : InstanceLocator(aInstance) , mLocal(aInstance, aType) , mTimestampValid(false) - , mCoapPending(false) + , mMgmtPending(false) , mTimer(aInstance, aTimerHandler) + , mMgmtSetCallback(nullptr) + , mMgmtSetCallbackContext(nullptr) { mTimestamp.Init(); } @@ -277,7 +279,7 @@ void DatasetManager::SendSet(void) Ip6::MessageInfo messageInfo; Dataset dataset; - VerifyOrExit(!mCoapPending, error = kErrorBusy); + VerifyOrExit(!mMgmtPending, error = kErrorBusy); VerifyOrExit(Get().IsChild() || Get().IsRouter(), error = kErrorInvalidState); VerifyOrExit(mLocal.Compare(GetTimestamp()) < 0, error = kErrorInvalidState); @@ -308,8 +310,8 @@ void DatasetManager::SendSet(void) messageInfo.SetSockAddr(Get().GetMeshLocal16()); IgnoreError(Get().GetLeaderAloc(messageInfo.GetPeerAddr())); messageInfo.SetPeerPort(Tmf::kUdpPort); - SuccessOrExit(error = - Get().SendMessage(*message, messageInfo, &DatasetManager::HandleCoapResponse, this)); + SuccessOrExit( + error = Get().SendMessage(*message, messageInfo, &DatasetManager::HandleMgmtSetResponse, this)); otLogInfoMeshCoP("Sent %s set to leader", Dataset::TypeToString(GetType())); @@ -318,7 +320,7 @@ exit: switch (error) { case kErrorNone: - mCoapPending = true; + mMgmtPending = true; break; case kErrorNoBufs: @@ -332,21 +334,54 @@ exit: } } -void DatasetManager::HandleCoapResponse(void * aContext, - otMessage * aMessage, - const otMessageInfo *aMessageInfo, - Error aError) +void DatasetManager::HandleMgmtSetResponse(void * aContext, + otMessage * aMessage, + const otMessageInfo *aMessageInfo, + Error aError) { - OT_UNUSED_VARIABLE(aMessage); - OT_UNUSED_VARIABLE(aMessageInfo); - OT_UNUSED_VARIABLE(aError); - - static_cast(aContext)->HandleCoapResponse(); + static_cast(aContext)->HandleMgmtSetResponse( + *static_cast(aMessage), *static_cast(aMessageInfo), aError); } -void DatasetManager::HandleCoapResponse(void) +void DatasetManager::HandleMgmtSetResponse(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Error aError) { - mCoapPending = false; + OT_UNUSED_VARIABLE(aMessageInfo); + + Error error; + StateTlv stateTlv; + + SuccessOrExit(error = aError); + VerifyOrExit(Tlv::FindTlv(aMessage, stateTlv) == kErrorNone, error = kErrorParse); + + switch (stateTlv.GetState()) + { + case StateTlv::kReject: + error = kErrorRejected; + break; + case StateTlv::kAccept: + error = kErrorNone; + break; + default: + error = kErrorParse; + break; + } + +exit: + otLogInfoMeshCoP("MGMT_SET finished: %s", ErrorToString(error)); + + mMgmtPending = false; + + if (mMgmtSetCallback != nullptr) + { + otDatasetMgmtSetCallback callback = mMgmtSetCallback; + void * context = mMgmtSetCallbackContext; + + mMgmtSetCallback = nullptr; + mMgmtSetCallbackContext = nullptr; + + callback(error, context); + } + mTimer.Start(kSendSetDelay); } @@ -460,12 +495,18 @@ exit: return error; } -Error DatasetManager::SendSetRequest(const Dataset::Info &aDatasetInfo, const uint8_t *aTlvs, uint8_t aLength) +Error DatasetManager::SendSetRequest(const Dataset::Info & aDatasetInfo, + const uint8_t * aTlvs, + uint8_t aLength, + otDatasetMgmtSetCallback aCallback, + void * aContext) { - Error error = kErrorNone; - Coap::Message * message; + Error error = kErrorNone; + Coap::Message * message = nullptr; Ip6::MessageInfo messageInfo; + VerifyOrExit(!mMgmtPending, error = kErrorBusy); + VerifyOrExit((message = NewMeshCoPMessage(Get())) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = @@ -508,7 +549,11 @@ Error DatasetManager::SendSetRequest(const Dataset::Info &aDatasetInfo, const ui messageInfo.SetSockAddr(Get().GetMeshLocal16()); IgnoreError(Get().GetLeaderAloc(messageInfo.GetPeerAddr())); messageInfo.SetPeerPort(Tmf::kUdpPort); - SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); + + SuccessOrExit(error = Get().SendMessage(*message, messageInfo, HandleMgmtSetResponse, this)); + mMgmtSetCallback = aCallback; + mMgmtSetCallbackContext = aContext; + mMgmtPending = true; otLogInfoMeshCoP("sent dataset set request to leader"); diff --git a/src/core/meshcop/dataset_manager.hpp b/src/core/meshcop/dataset_manager.hpp index 9679b3f1b..774cc425d 100644 --- a/src/core/meshcop/dataset_manager.hpp +++ b/src/core/meshcop/dataset_manager.hpp @@ -149,12 +149,19 @@ public: * @param[in] aDatasetInfo The Operational Dataset. * @param[in] aTlvs Any additional raw TLVs to include. * @param[in] aLength Number of bytes in @p aTlvs. + * @param[in] aCallback A pointer to a function that is called on response reception or timeout. + * @param[in] aContext A pointer to application-specific context for @p aCallback. * * @retval kErrorNone Successfully send the meshcop dataset command. * @retval kErrorNoBufs Insufficient buffer space to send. + * @retval kErrorBusy A previous request is ongoing. * */ - Error SendSetRequest(const Dataset::Info &aDatasetInfo, const uint8_t *aTlvs, uint8_t aLength); + Error SendSetRequest(const Dataset::Info & aDatasetInfo, + const uint8_t * aTlvs, + uint8_t aLength, + otDatasetMgmtSetCallback aCallback, + void * aContext); /** * This method sends a MGMT_GET request. @@ -334,11 +341,11 @@ protected: bool mTimestampValid : 1; private: - static void HandleCoapResponse(void * aContext, - otMessage * aMessage, - const otMessageInfo *aMessageInfo, - Error aError); - void HandleCoapResponse(void); + static void HandleMgmtSetResponse(void * aContext, + otMessage * aMessage, + const otMessageInfo *aMessageInfo, + Error aError); + void HandleMgmtSetResponse(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Error aError); bool IsActiveDataset(void) const { return GetType() == Dataset::kActive; } bool IsPendingDataset(void) const { return GetType() == Dataset::kPending; } @@ -358,8 +365,11 @@ private: static constexpr uint8_t kMaxDatasetTlvs = 16; // Maximum number of TLVs in a Dataset. static constexpr uint32_t kSendSetDelay = 5000; // Milliseconds - bool mCoapPending : 1; + bool mMgmtPending : 1; TimerMilli mTimer; + + otDatasetMgmtSetCallback mMgmtSetCallback; + void * mMgmtSetCallbackContext; }; class ActiveDataset : public DatasetManager, private NonCopyable diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp index 6524c27d7..8589b6a58 100644 --- a/src/ncp/ncp_base_mtd.cpp +++ b/src/ncp/ncp_base_mtd.cpp @@ -1672,7 +1672,8 @@ template <> otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertySet