/* * Copyright (c) 2016, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * @file * This file implements MeshCoP Datasets manager to process commands. * */ #include "dataset_manager.hpp" #include #include "common/as_core_type.hpp" #include "common/instance.hpp" #include "common/locator_getters.hpp" #include "common/log.hpp" #include "common/notifier.hpp" #include "meshcop/meshcop.hpp" #include "meshcop/meshcop_tlvs.hpp" #include "radio/radio.hpp" #include "thread/thread_netif.hpp" #include "thread/thread_tlvs.hpp" #include "thread/uri_paths.hpp" namespace ot { namespace MeshCoP { RegisterLogModule("DatasetManager"); DatasetManager::DatasetManager(Instance &aInstance, Dataset::Type aType, Timer::Handler aTimerHandler) : InstanceLocator(aInstance) , mLocal(aInstance, aType) , mTimestampValid(false) , mMgmtPending(false) , mTimer(aInstance, aTimerHandler) { mTimestamp.Clear(); } const Timestamp *DatasetManager::GetTimestamp(void) const { return mTimestampValid ? &mTimestamp : nullptr; } Error DatasetManager::Restore(void) { Error error; Dataset dataset; mTimer.Stop(); mTimestampValid = false; SuccessOrExit(error = mLocal.Restore(dataset)); mTimestampValid = (dataset.GetTimestamp(GetType(), mTimestamp) == kErrorNone); if (IsActiveDataset()) { IgnoreError(dataset.ApplyConfiguration(GetInstance())); } SignalDatasetChange(); exit: return error; } Error DatasetManager::ApplyConfiguration(void) const { Error error; Dataset dataset; SuccessOrExit(error = Read(dataset)); SuccessOrExit(error = dataset.ApplyConfiguration(GetInstance())); exit: return error; } void DatasetManager::Clear(void) { mTimestamp.Clear(); mTimestampValid = false; mLocal.Clear(); mTimer.Stop(); SignalDatasetChange(); } void DatasetManager::HandleDetach(void) { IgnoreError(Restore()); } Error DatasetManager::Save(const Dataset &aDataset) { Error error = kErrorNone; int compare; bool isNetworkKeyUpdated = false; if (aDataset.GetTimestamp(GetType(), mTimestamp) == kErrorNone) { mTimestampValid = true; if (IsActiveDataset()) { SuccessOrExit(error = aDataset.ApplyConfiguration(GetInstance(), &isNetworkKeyUpdated)); } } compare = Timestamp::Compare(mTimestampValid ? &mTimestamp : nullptr, mLocal.GetTimestamp()); if (isNetworkKeyUpdated || compare > 0) { SuccessOrExit(error = mLocal.Save(aDataset)); #if OPENTHREAD_FTD Get().IncrementVersionAndStableVersion(); #endif } else if (compare < 0) { mTimer.Start(kSendSetDelay); } SignalDatasetChange(); exit: return error; } Error DatasetManager::Save(const Dataset::Info &aDatasetInfo) { Error error; SuccessOrExit(error = mLocal.Save(aDatasetInfo)); HandleDatasetUpdated(); exit: return error; } Error DatasetManager::Save(const otOperationalDatasetTlvs &aDataset) { Error error; SuccessOrExit(error = mLocal.Save(aDataset)); HandleDatasetUpdated(); exit: return error; } Error DatasetManager::SaveLocal(const Dataset &aDataset) { Error error; SuccessOrExit(error = mLocal.Save(aDataset)); HandleDatasetUpdated(); exit: return error; } void DatasetManager::HandleDatasetUpdated(void) { switch (Get().GetRole()) { case Mle::kRoleDisabled: IgnoreError(Restore()); break; case Mle::kRoleChild: SendSet(); break; #if OPENTHREAD_FTD case Mle::kRoleRouter: SendSet(); break; case Mle::kRoleLeader: IgnoreError(Restore()); Get().IncrementVersionAndStableVersion(); break; #endif default: break; } SignalDatasetChange(); } void DatasetManager::SignalDatasetChange(void) const { Get().Signal(mLocal.GetType() == Dataset::kActive ? kEventActiveDatasetChanged : kEventPendingDatasetChanged); } Error DatasetManager::GetChannelMask(Mac::ChannelMask &aChannelMask) const { Error error; const ChannelMaskTlv *channelMaskTlv; uint32_t mask; Dataset dataset; SuccessOrExit(error = Read(dataset)); channelMaskTlv = dataset.GetTlv(); VerifyOrExit(channelMaskTlv != nullptr, error = kErrorNotFound); VerifyOrExit((mask = channelMaskTlv->GetChannelMask()) != 0); aChannelMask.SetMask(mask & Get().GetSupportedChannelMask().GetMask()); VerifyOrExit(!aChannelMask.IsEmpty(), error = kErrorNotFound); exit: return error; } void DatasetManager::HandleTimer(void) { SendSet(); } void DatasetManager::SendSet(void) { Error error; Coap::Message *message = nullptr; Tmf::MessageInfo messageInfo(GetInstance()); Dataset dataset; VerifyOrExit(!mMgmtPending, error = kErrorBusy); VerifyOrExit(Get().IsChild() || Get().IsRouter(), error = kErrorInvalidState); VerifyOrExit(Timestamp::Compare(GetTimestamp(), mLocal.GetTimestamp()) < 0, error = kErrorAlready); if (IsActiveDataset()) { Dataset pendingDataset; Timestamp timestamp; IgnoreError(Get().Read(pendingDataset)); if ((pendingDataset.GetTimestamp(Dataset::kActive, timestamp) == kErrorNone) && (Timestamp::Compare(×tamp, mLocal.GetTimestamp()) == 0)) { // stop registration attempts during dataset transition ExitNow(error = kErrorInvalidState); } } message = Get().NewPriorityConfirmablePostMessage(IsActiveDataset() ? kUriActiveSet : kUriPendingSet); VerifyOrExit(message != nullptr, error = kErrorNoBufs); IgnoreError(Read(dataset)); SuccessOrExit(error = message->AppendBytes(dataset.GetBytes(), dataset.GetSize())); IgnoreError(messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc()); SuccessOrExit( error = Get().SendMessage(*message, messageInfo, &DatasetManager::HandleMgmtSetResponse, this)); LogInfo("Sent %s set to leader", Dataset::TypeToString(GetType())); exit: switch (error) { case kErrorNone: mMgmtPending = true; break; case kErrorNoBufs: mTimer.Start(kSendSetDelay); OT_FALL_THROUGH; default: LogError("send Dataset set to leader", error); FreeMessage(message); break; } } void DatasetManager::HandleMgmtSetResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aError) { static_cast(aContext)->HandleMgmtSetResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo), aError); } void DatasetManager::HandleMgmtSetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aError) { OT_UNUSED_VARIABLE(aMessageInfo); Error error; uint8_t state; SuccessOrExit(error = aError); VerifyOrExit(Tlv::Find(*aMessage, state) == kErrorNone, error = kErrorParse); switch (state) { case StateTlv::kReject: error = kErrorRejected; break; case StateTlv::kAccept: error = kErrorNone; break; default: error = kErrorParse; break; } exit: LogInfo("MGMT_SET finished: %s", ErrorToString(error)); mMgmtPending = false; if (mMgmtSetCallback.IsSet()) { Callback callbackCopy = mMgmtSetCallback; mMgmtSetCallback.Clear(); callbackCopy.Invoke(error); } mTimer.Start(kSendSetDelay); } void DatasetManager::HandleGet(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const { Tlv tlv; uint16_t offset = aMessage.GetOffset(); uint8_t tlvs[Dataset::kMaxGetTypes]; uint8_t length = 0; while (offset < aMessage.GetLength()) { SuccessOrExit(aMessage.Read(offset, tlv)); if (tlv.GetType() == Tlv::kGet) { length = tlv.GetLength(); if (length > (sizeof(tlvs) - 1)) { // leave space for potential DelayTimer type below length = sizeof(tlvs) - 1; } aMessage.ReadBytes(offset + sizeof(Tlv), tlvs, length); break; } offset += sizeof(tlv) + tlv.GetLength(); } // MGMT_PENDING_GET.rsp must include Delay Timer TLV (Thread 1.1.1 Section 8.7.5.4) VerifyOrExit(length > 0 && IsPendingDataset()); for (uint8_t i = 0; i < length; i++) { if (tlvs[i] == Tlv::kDelayTimer) { ExitNow(); } } tlvs[length++] = Tlv::kDelayTimer; exit: SendGetResponse(aMessage, aMessageInfo, tlvs, length); } void DatasetManager::SendGetResponse(const Coap::Message &aRequest, const Ip6::MessageInfo &aMessageInfo, uint8_t *aTlvs, uint8_t aLength) const { Error error = kErrorNone; Coap::Message *message; Dataset dataset; IgnoreError(Read(dataset)); message = Get().NewPriorityResponseMessage(aRequest); VerifyOrExit(message != nullptr, error = kErrorNoBufs); if (aLength == 0) { for (const Tlv *cur = dataset.GetTlvsStart(); cur < dataset.GetTlvsEnd(); cur = cur->GetNext()) { if (cur->GetType() != Tlv::kNetworkKey || Get().GetSecurityPolicy().mObtainNetworkKeyEnabled) { SuccessOrExit(error = cur->AppendTo(*message)); } } } else { for (uint8_t index = 0; index < aLength; index++) { const Tlv *tlv; if (aTlvs[index] == Tlv::kNetworkKey && !Get().GetSecurityPolicy().mObtainNetworkKeyEnabled) { continue; } if ((tlv = dataset.GetTlv(static_cast(aTlvs[index]))) != nullptr) { SuccessOrExit(error = tlv->AppendTo(*message)); } } } SuccessOrExit(error = Get().SendMessage(*message, aMessageInfo)); LogInfo("sent %s dataset get response to %s", (GetType() == Dataset::kActive ? "active" : "pending"), aMessageInfo.GetPeerAddr().ToString().AsCString()); exit: FreeMessageOnError(message, error); } Error DatasetManager::AppendDatasetToMessage(const Dataset::Info &aDatasetInfo, Message &aMessage) const { Error error; Dataset dataset; SuccessOrExit(error = dataset.SetFrom(aDatasetInfo)); error = aMessage.AppendBytes(dataset.GetBytes(), dataset.GetSize()); exit: return error; } Error DatasetManager::SendSetRequest(const Dataset::Info &aDatasetInfo, const uint8_t *aTlvs, uint8_t aLength, otDatasetMgmtSetCallback aCallback, void *aContext) { Error error = kErrorNone; Coap::Message *message = nullptr; Tmf::MessageInfo messageInfo(GetInstance()); VerifyOrExit(!mMgmtPending, error = kErrorBusy); message = Get().NewPriorityConfirmablePostMessage(IsActiveDataset() ? kUriActiveSet : kUriPendingSet); VerifyOrExit(message != nullptr, error = kErrorNoBufs); #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD if (Get().IsActive()) { const Tlv *end = reinterpret_cast(aTlvs + aLength); bool hasSessionId = false; for (const Tlv *cur = reinterpret_cast(aTlvs); cur < end; cur = cur->GetNext()) { VerifyOrExit((cur + 1) <= end, error = kErrorInvalidArgs); if (cur->GetType() == Tlv::kCommissionerSessionId) { hasSessionId = true; break; } } if (!hasSessionId) { SuccessOrExit(error = Tlv::Append(*message, Get().GetSessionId())); } } #endif // OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD SuccessOrExit(error = AppendDatasetToMessage(aDatasetInfo, *message)); if (aLength > 0) { SuccessOrExit(error = message->AppendBytes(aTlvs, aLength)); } IgnoreError(messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc()); SuccessOrExit(error = Get().SendMessage(*message, messageInfo, HandleMgmtSetResponse, this)); mMgmtSetCallback.Set(aCallback, aContext); mMgmtPending = true; LogInfo("sent dataset set request to leader"); exit: FreeMessageOnError(message, error); return error; } Error DatasetManager::SendGetRequest(const Dataset::Components &aDatasetComponents, const uint8_t *aTlvTypes, uint8_t aLength, const otIp6Address *aAddress) const { Error error = kErrorNone; Coap::Message *message; Tmf::MessageInfo messageInfo(GetInstance()); Tlv tlv; uint8_t datasetTlvs[kMaxDatasetTlvs]; uint8_t length; length = 0; if (aDatasetComponents.IsActiveTimestampPresent()) { datasetTlvs[length++] = Tlv::kActiveTimestamp; } if (aDatasetComponents.IsPendingTimestampPresent()) { datasetTlvs[length++] = Tlv::kPendingTimestamp; } if (aDatasetComponents.IsNetworkKeyPresent()) { datasetTlvs[length++] = Tlv::kNetworkKey; } if (aDatasetComponents.IsNetworkNamePresent()) { datasetTlvs[length++] = Tlv::kNetworkName; } if (aDatasetComponents.IsExtendedPanIdPresent()) { datasetTlvs[length++] = Tlv::kExtendedPanId; } if (aDatasetComponents.IsMeshLocalPrefixPresent()) { datasetTlvs[length++] = Tlv::kMeshLocalPrefix; } if (aDatasetComponents.IsDelayPresent()) { datasetTlvs[length++] = Tlv::kDelayTimer; } if (aDatasetComponents.IsPanIdPresent()) { datasetTlvs[length++] = Tlv::kPanId; } if (aDatasetComponents.IsChannelPresent()) { datasetTlvs[length++] = Tlv::kChannel; } if (aDatasetComponents.IsPskcPresent()) { datasetTlvs[length++] = Tlv::kPskc; } if (aDatasetComponents.IsSecurityPolicyPresent()) { datasetTlvs[length++] = Tlv::kSecurityPolicy; } if (aDatasetComponents.IsChannelMaskPresent()) { datasetTlvs[length++] = Tlv::kChannelMask; } message = Get().NewPriorityConfirmablePostMessage(IsActiveDataset() ? kUriActiveGet : kUriPendingGet); VerifyOrExit(message != nullptr, error = kErrorNoBufs); if (aLength + length > 0) { tlv.SetType(Tlv::kGet); tlv.SetLength(aLength + length); SuccessOrExit(error = message->Append(tlv)); if (length > 0) { SuccessOrExit(error = message->AppendBytes(datasetTlvs, length)); } if (aLength > 0) { SuccessOrExit(error = message->AppendBytes(aTlvTypes, aLength)); } } IgnoreError(messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc()); if (aAddress != nullptr) { // Use leader ALOC if `aAddress` is `nullptr`. messageInfo.SetPeerAddr(AsCoreType(aAddress)); } SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); LogInfo("sent dataset get request"); exit: FreeMessageOnError(message, error); return error; } ActiveDatasetManager::ActiveDatasetManager(Instance &aInstance) : DatasetManager(aInstance, Dataset::kActive, ActiveDatasetManager::HandleTimer) { } bool ActiveDatasetManager::IsPartiallyComplete(void) const { return mLocal.IsSaved() && !mTimestampValid; } bool ActiveDatasetManager::IsCommissioned(void) const { Dataset::Info datasetInfo; bool isValid = false; SuccessOrExit(Read(datasetInfo)); isValid = (datasetInfo.IsNetworkKeyPresent() && datasetInfo.IsNetworkNamePresent() && datasetInfo.IsExtendedPanIdPresent() && datasetInfo.IsPanIdPresent() && datasetInfo.IsChannelPresent()); exit: return isValid; } Error ActiveDatasetManager::Save(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint16_t aLength) { Error error = kErrorNone; Dataset dataset; SuccessOrExit(error = dataset.ReadFromMessage(aMessage, aOffset, aLength)); dataset.SetTimestamp(Dataset::kActive, aTimestamp); error = DatasetManager::Save(dataset); exit: return error; } template <> void ActiveDatasetManager::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { DatasetManager::HandleGet(aMessage, aMessageInfo); } void ActiveDatasetManager::HandleTimer(Timer &aTimer) { aTimer.Get().HandleTimer(); } PendingDatasetManager::PendingDatasetManager(Instance &aInstance) : DatasetManager(aInstance, Dataset::kPending, PendingDatasetManager::HandleTimer) , mDelayTimer(aInstance) { } void PendingDatasetManager::Clear(void) { DatasetManager::Clear(); mDelayTimer.Stop(); } void PendingDatasetManager::ClearNetwork(void) { Dataset dataset; mTimestamp.Clear(); mTimestampValid = false; IgnoreError(DatasetManager::Save(dataset)); } Error PendingDatasetManager::Save(const Dataset::Info &aDatasetInfo) { Error error; SuccessOrExit(error = DatasetManager::Save(aDatasetInfo)); StartDelayTimer(); exit: return error; } Error PendingDatasetManager::Save(const otOperationalDatasetTlvs &aDataset) { Error error; SuccessOrExit(error = DatasetManager::Save(aDataset)); StartDelayTimer(); exit: return error; } Error PendingDatasetManager::Save(const Dataset &aDataset) { Error error; SuccessOrExit(error = DatasetManager::SaveLocal(aDataset)); StartDelayTimer(); exit: return error; } Error PendingDatasetManager::Save(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint16_t aLength) { Error error = kErrorNone; Dataset dataset; SuccessOrExit(error = dataset.ReadFromMessage(aMessage, aOffset, aLength)); dataset.SetTimestamp(Dataset::kPending, aTimestamp); SuccessOrExit(error = DatasetManager::Save(dataset)); StartDelayTimer(); exit: return error; } void PendingDatasetManager::StartDelayTimer(void) { DelayTimerTlv *delayTimer; Dataset dataset; IgnoreError(Read(dataset)); mDelayTimer.Stop(); if ((delayTimer = dataset.GetTlv()) != nullptr) { uint32_t delay = delayTimer->GetDelayTimer(); // the Timer implementation does not support the full 32 bit range if (delay > Timer::kMaxDelay) { delay = Timer::kMaxDelay; } mDelayTimer.StartAt(dataset.GetUpdateTime(), delay); LogInfo("delay timer started %lu", ToUlong(delay)); } } void PendingDatasetManager::HandleDelayTimer(void) { DelayTimerTlv *delayTimer; Dataset dataset; IgnoreError(Read(dataset)); // if the Delay Timer value is larger than what our Timer implementation can handle, we have to compute // the remainder and wait some more. if ((delayTimer = dataset.GetTlv()) != nullptr) { uint32_t elapsed = mDelayTimer.GetFireTime() - dataset.GetUpdateTime(); uint32_t delay = delayTimer->GetDelayTimer(); if (elapsed < delay) { mDelayTimer.StartAt(mDelayTimer.GetFireTime(), delay - elapsed); ExitNow(); } } LogInfo("pending delay timer expired"); dataset.ConvertToActive(); Get().Save(dataset); Clear(); exit: return; } template <> void PendingDatasetManager::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { DatasetManager::HandleGet(aMessage, aMessageInfo); } void PendingDatasetManager::HandleTimer(Timer &aTimer) { aTimer.Get().HandleTimer(); } } // namespace MeshCoP } // namespace ot