blob: 16c1eb41d7fa8174bfa168c56f73ae3e2b5b50c9 [file] [log] [blame]
/*
* 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 <stdio.h>
#include "common/instance.hpp"
#include "common/locator-getters.hpp"
#include "common/logging.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/thread_uri_paths.hpp"
namespace ot {
namespace MeshCoP {
DatasetManager::DatasetManager(Instance & aInstance,
Dataset::Type aType,
const char * aUriGet,
const char * aUriSet,
Timer::Handler aTimerHandler)
: InstanceLocator(aInstance)
, mLocal(aInstance, aType)
, mTimestampValid(false)
, mTimer(aInstance, aTimerHandler, this)
, mUriGet(aUriGet)
, mUriSet(aUriSet)
, mCoapPending(false)
{
mTimestamp.Init();
}
const Timestamp *DatasetManager::GetTimestamp(void) const
{
return mTimestampValid ? &mTimestamp : nullptr;
}
int DatasetManager::Compare(const Timestamp &aTimestamp) const
{
const Timestamp *timestamp = GetTimestamp();
int rval = 1;
if (timestamp)
{
rval = timestamp->Compare(aTimestamp);
}
return rval;
}
otError DatasetManager::Restore(void)
{
otError error;
Dataset dataset(mLocal.GetType());
const Timestamp *timestamp;
mTimer.Stop();
mTimestampValid = false;
SuccessOrExit(error = mLocal.Restore(dataset));
timestamp = dataset.GetTimestamp();
if (timestamp != nullptr)
{
mTimestamp = *timestamp;
mTimestampValid = true;
}
if (mLocal.GetType() == Dataset::kActive)
{
IgnoreError(dataset.ApplyConfiguration(GetInstance()));
}
exit:
return error;
}
otError DatasetManager::ApplyConfiguration(void) const
{
otError error;
Dataset dataset(mLocal.GetType());
SuccessOrExit(error = mLocal.Read(dataset));
SuccessOrExit(error = dataset.ApplyConfiguration(GetInstance()));
exit:
return error;
}
void DatasetManager::Clear(void)
{
mTimestamp.Init();
mTimestampValid = false;
mLocal.Clear();
mTimer.Stop();
}
void DatasetManager::HandleDetach(void)
{
IgnoreError(Restore());
}
otError DatasetManager::Save(const Dataset &aDataset)
{
otError error = OT_ERROR_NONE;
const Timestamp *timestamp;
int compare;
bool isMasterkeyUpdated = false;
timestamp = aDataset.GetTimestamp();
if (timestamp != nullptr)
{
mTimestamp = *timestamp;
mTimestampValid = true;
if (mLocal.GetType() == Dataset::kActive)
{
SuccessOrExit(error = aDataset.ApplyConfiguration(GetInstance(), &isMasterkeyUpdated));
}
}
compare = mLocal.Compare(timestamp);
if (isMasterkeyUpdated || compare > 0)
{
IgnoreError(mLocal.Save(aDataset));
#if OPENTHREAD_FTD
Get<NetworkData::Leader>().IncrementVersionAndStableVersion();
#endif
}
else if (compare < 0)
{
VerifyOrExit(!Get<Mle::MleRouter>().IsLeader(), error = OT_ERROR_INVALID_STATE);
SendSet();
}
exit:
return error;
}
otError DatasetManager::Save(const otOperationalDataset &aDataset)
{
otError error;
SuccessOrExit(error = mLocal.Save(aDataset));
HandleDatasetUpdated();
exit:
return error;
}
otError DatasetManager::Save(const otOperationalDatasetTlvs &aDataset)
{
otError error;
SuccessOrExit(error = mLocal.Save(aDataset));
HandleDatasetUpdated();
exit:
return error;
}
void DatasetManager::HandleDatasetUpdated(void)
{
switch (Get<Mle::MleRouter>().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<NetworkData::Leader>().IncrementVersionAndStableVersion();
break;
#endif
default:
break;
}
}
otError DatasetManager::GetChannelMask(Mac::ChannelMask &aChannelMask) const
{
otError error;
const MeshCoP::ChannelMaskTlv *channelMaskTlv;
uint32_t mask;
Dataset dataset(mLocal.GetType());
SuccessOrExit(error = mLocal.Read(dataset));
channelMaskTlv = dataset.GetTlv<ChannelMaskTlv>();
VerifyOrExit(channelMaskTlv != nullptr, error = OT_ERROR_NOT_FOUND);
VerifyOrExit((mask = channelMaskTlv->GetChannelMask()) != 0, OT_NOOP);
aChannelMask.SetMask(mask & Get<Mac::Mac>().GetSupportedChannelMask().GetMask());
VerifyOrExit(!aChannelMask.IsEmpty(), error = OT_ERROR_NOT_FOUND);
exit:
return error;
}
void DatasetManager::HandleTimer(void)
{
SendSet();
}
void DatasetManager::SendSet(void)
{
otError error;
Coap::Message * message = nullptr;
Ip6::MessageInfo messageInfo;
Dataset dataset(mLocal.GetType());
VerifyOrExit(!mCoapPending, error = OT_ERROR_BUSY);
VerifyOrExit(Get<Mle::MleRouter>().IsAttached(), error = OT_ERROR_INVALID_STATE);
VerifyOrExit(mLocal.Compare(GetTimestamp()) < 0, error = OT_ERROR_INVALID_STATE);
if (mLocal.GetType() == Dataset::kActive)
{
Dataset pendingDataset(Dataset::kPending);
IgnoreError(Get<PendingDataset>().Read(pendingDataset));
const ActiveTimestampTlv *tlv = pendingDataset.GetTlv<ActiveTimestampTlv>();
const Timestamp * pendingActiveTimestamp = static_cast<const Timestamp *>(tlv);
if (pendingActiveTimestamp != nullptr && mLocal.Compare(pendingActiveTimestamp) == 0)
{
// stop registration attempts during dataset transition
ExitNow(error = OT_ERROR_INVALID_STATE);
}
}
VerifyOrExit((message = NewMeshCoPMessage(Get<Coap::Coap>())) != nullptr, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = message->Init(OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST, mUriSet));
SuccessOrExit(error = message->SetPayloadMarker());
IgnoreError(mLocal.Read(dataset));
SuccessOrExit(error = message->Append(dataset.GetBytes(), dataset.GetSize()));
messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
IgnoreError(Get<Mle::MleRouter>().GetLeaderAloc(messageInfo.GetPeerAddr()));
messageInfo.SetPeerPort(kCoapUdpPort);
SuccessOrExit(error =
Get<Coap::Coap>().SendMessage(*message, messageInfo, &DatasetManager::HandleCoapResponse, this));
otLogInfoMeshCoP("Sent %s to leader", mUriSet);
exit:
switch (error)
{
case OT_ERROR_NONE:
mCoapPending = true;
break;
case OT_ERROR_NO_BUFS:
mTimer.Start(kDelayNoBufs);
// fall through
default:
otLogWarnMeshCoP("Failed to send %s to leader: %s", mUriSet, otThreadErrorToString(error));
if (message != nullptr)
{
message->Free();
}
break;
}
}
void DatasetManager::HandleCoapResponse(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo,
otError aError)
{
OT_UNUSED_VARIABLE(aMessage);
OT_UNUSED_VARIABLE(aMessageInfo);
OT_UNUSED_VARIABLE(aError);
static_cast<DatasetManager *>(aContext)->HandleCoapResponse();
}
void DatasetManager::HandleCoapResponse(void)
{
mCoapPending = false;
SendSet();
}
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())
{
aMessage.Read(offset, sizeof(tlv), &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.Read(offset + sizeof(Tlv), length, tlvs);
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 && strcmp(mUriGet, OT_URI_PATH_PENDING_GET) == 0, OT_NOOP);
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
{
otError error = OT_ERROR_NONE;
Coap::Message *message;
Dataset dataset(mLocal.GetType());
IgnoreError(mLocal.Read(dataset));
VerifyOrExit((message = NewMeshCoPMessage(Get<Coap::Coap>())) != nullptr, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = message->SetDefaultResponseHeader(aRequest));
SuccessOrExit(error = message->SetPayloadMarker());
if (aLength == 0)
{
for (const Tlv *cur = dataset.GetTlvsStart(); cur < dataset.GetTlvsEnd(); cur = cur->GetNext())
{
if (cur->GetType() != Tlv::kNetworkMasterKey || Get<KeyManager>().IsObtainMasterKeyEnabled())
{
SuccessOrExit(error = cur->AppendTo(*message));
}
}
}
else
{
for (uint8_t index = 0; index < aLength; index++)
{
const Tlv *tlv;
if (aTlvs[index] == Tlv::kNetworkMasterKey && !Get<KeyManager>().IsObtainMasterKeyEnabled())
{
continue;
}
if ((tlv = dataset.GetTlv(static_cast<Tlv::Type>(aTlvs[index]))) != nullptr)
{
SuccessOrExit(error = tlv->AppendTo(*message));
}
}
}
if (message->GetLength() == message->GetOffset())
{
// no payload, remove coap payload marker
IgnoreError(message->SetLength(message->GetLength() - 1));
}
SuccessOrExit(error = Get<Coap::Coap>().SendMessage(*message, aMessageInfo));
otLogInfoMeshCoP("sent dataset get response");
exit:
if (error != OT_ERROR_NONE && message != nullptr)
{
message->Free();
}
}
otError DatasetManager::SendSetRequest(const otOperationalDataset &aDataset, const uint8_t *aTlvs, uint8_t aLength)
{
otError error = OT_ERROR_NONE;
Coap::Message * message;
Ip6::MessageInfo messageInfo;
VerifyOrExit((message = NewMeshCoPMessage(Get<Coap::Coap>())) != nullptr, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = message->Init(OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST, mUriSet));
SuccessOrExit(error = message->SetPayloadMarker());
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
if (Get<Commissioner>().IsActive())
{
const Tlv *end = reinterpret_cast<const Tlv *>(aTlvs + aLength);
bool hasSessionId = false;
for (const Tlv *cur = reinterpret_cast<const Tlv *>(aTlvs); cur < end; cur = cur->GetNext())
{
VerifyOrExit((cur + 1) <= end, error = OT_ERROR_INVALID_ARGS);
if (cur->GetType() == Tlv::kCommissionerSessionId)
{
hasSessionId = true;
break;
}
}
if (!hasSessionId)
{
SuccessOrExit(error = Tlv::AppendUint16Tlv(*message, Tlv::kCommissionerSessionId,
Get<Commissioner>().GetSessionId()));
}
}
#endif // OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
if (aDataset.mComponents.mIsActiveTimestampPresent)
{
ActiveTimestampTlv timestamp;
timestamp.Init();
timestamp.SetSeconds(aDataset.mActiveTimestamp);
timestamp.SetTicks(0);
SuccessOrExit(error = timestamp.AppendTo(*message));
}
if (aDataset.mComponents.mIsPendingTimestampPresent)
{
PendingTimestampTlv timestamp;
timestamp.Init();
timestamp.SetSeconds(aDataset.mPendingTimestamp);
timestamp.SetTicks(0);
SuccessOrExit(error = timestamp.AppendTo(*message));
}
if (aDataset.mComponents.mIsMasterKeyPresent)
{
SuccessOrExit(error =
Tlv::AppendTlv(*message, Tlv::kNetworkMasterKey, &aDataset.mMasterKey, sizeof(MasterKey)));
}
if (aDataset.mComponents.mIsNetworkNamePresent)
{
NetworkNameTlv networkname;
networkname.Init();
networkname.SetNetworkName(static_cast<const Mac::NetworkName &>(aDataset.mNetworkName).GetAsData());
SuccessOrExit(error = networkname.AppendTo(*message));
}
if (aDataset.mComponents.mIsExtendedPanIdPresent)
{
SuccessOrExit(error = Tlv::AppendTlv(*message, Tlv::kExtendedPanId, &aDataset.mExtendedPanId,
sizeof(Mac::ExtendedPanId)));
}
if (aDataset.mComponents.mIsMeshLocalPrefixPresent)
{
SuccessOrExit(error = Tlv::AppendTlv(*message, Tlv::kMeshLocalPrefix, &aDataset.mMeshLocalPrefix,
sizeof(otMeshLocalPrefix)));
}
if (aDataset.mComponents.mIsDelayPresent)
{
SuccessOrExit(error = Tlv::AppendUint32Tlv(*message, Tlv::kDelayTimer, aDataset.mDelay));
}
if (aDataset.mComponents.mIsPanIdPresent)
{
SuccessOrExit(error = Tlv::AppendUint16Tlv(*message, Tlv::kPanId, aDataset.mPanId));
}
if (aDataset.mComponents.mIsChannelPresent)
{
ChannelTlv channel;
channel.Init();
channel.SetChannel(aDataset.mChannel);
SuccessOrExit(error = channel.AppendTo(*message));
}
if (aDataset.mComponents.mIsChannelMaskPresent)
{
ChannelMaskTlv channelMask;
channelMask.Init();
channelMask.SetChannelMask(aDataset.mChannelMask);
SuccessOrExit(error = channelMask.AppendTo(*message));
}
if (aDataset.mComponents.mIsPskcPresent)
{
SuccessOrExit(error = Tlv::AppendTlv(*message, Tlv::kPskc, aDataset.mPskc.m8, sizeof(Pskc)));
}
if (aDataset.mComponents.mIsSecurityPolicyPresent)
{
SecurityPolicyTlv securityPolicy;
securityPolicy.Init();
securityPolicy.SetRotationTime(aDataset.mSecurityPolicy.mRotationTime);
securityPolicy.SetFlags(aDataset.mSecurityPolicy.mFlags);
SuccessOrExit(error = securityPolicy.AppendTo(*message));
}
if (aLength > 0)
{
SuccessOrExit(error = message->Append(aTlvs, aLength));
}
if (message->GetLength() == message->GetOffset())
{
// no payload, remove coap payload marker
IgnoreError(message->SetLength(message->GetLength() - 1));
}
messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
IgnoreError(Get<Mle::MleRouter>().GetLeaderAloc(messageInfo.GetPeerAddr()));
messageInfo.SetPeerPort(kCoapUdpPort);
SuccessOrExit(error = Get<Coap::Coap>().SendMessage(*message, messageInfo));
otLogInfoMeshCoP("sent dataset set request to leader");
exit:
if (error != OT_ERROR_NONE && message != nullptr)
{
message->Free();
}
return error;
}
otError DatasetManager::SendGetRequest(const otOperationalDatasetComponents &aDatasetComponents,
const uint8_t * aTlvTypes,
uint8_t aLength,
const otIp6Address * aAddress) const
{
otError error = OT_ERROR_NONE;
Coap::Message * message;
Ip6::MessageInfo messageInfo;
Tlv tlv;
uint8_t datasetTlvs[kMaxDatasetTlvs];
uint8_t length;
length = 0;
if (aDatasetComponents.mIsActiveTimestampPresent)
{
datasetTlvs[length++] = Tlv::kActiveTimestamp;
}
if (aDatasetComponents.mIsPendingTimestampPresent)
{
datasetTlvs[length++] = Tlv::kPendingTimestamp;
}
if (aDatasetComponents.mIsMasterKeyPresent)
{
datasetTlvs[length++] = Tlv::kNetworkMasterKey;
}
if (aDatasetComponents.mIsNetworkNamePresent)
{
datasetTlvs[length++] = Tlv::kNetworkName;
}
if (aDatasetComponents.mIsExtendedPanIdPresent)
{
datasetTlvs[length++] = Tlv::kExtendedPanId;
}
if (aDatasetComponents.mIsMeshLocalPrefixPresent)
{
datasetTlvs[length++] = Tlv::kMeshLocalPrefix;
}
if (aDatasetComponents.mIsDelayPresent)
{
datasetTlvs[length++] = Tlv::kDelayTimer;
}
if (aDatasetComponents.mIsPanIdPresent)
{
datasetTlvs[length++] = Tlv::kPanId;
}
if (aDatasetComponents.mIsChannelPresent)
{
datasetTlvs[length++] = Tlv::kChannel;
}
if (aDatasetComponents.mIsPskcPresent)
{
datasetTlvs[length++] = Tlv::kPskc;
}
if (aDatasetComponents.mIsSecurityPolicyPresent)
{
datasetTlvs[length++] = Tlv::kSecurityPolicy;
}
if (aDatasetComponents.mIsChannelMaskPresent)
{
datasetTlvs[length++] = Tlv::kChannelMask;
}
VerifyOrExit((message = NewMeshCoPMessage(Get<Coap::Coap>())) != nullptr, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = message->Init(OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST, mUriGet));
if (aLength + length > 0)
{
SuccessOrExit(error = message->SetPayloadMarker());
}
if (aLength + length > 0)
{
tlv.SetType(Tlv::kGet);
tlv.SetLength(aLength + length);
SuccessOrExit(error = message->Append(&tlv, sizeof(tlv)));
if (length > 0)
{
SuccessOrExit(error = message->Append(datasetTlvs, length));
}
if (aLength > 0)
{
SuccessOrExit(error = message->Append(aTlvTypes, aLength));
}
}
if (aAddress != nullptr)
{
messageInfo.SetPeerAddr(*static_cast<const Ip6::Address *>(aAddress));
}
else
{
IgnoreError(Get<Mle::MleRouter>().GetLeaderAloc(messageInfo.GetPeerAddr()));
}
messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
messageInfo.SetPeerPort(kCoapUdpPort);
SuccessOrExit(error = Get<Coap::Coap>().SendMessage(*message, messageInfo));
otLogInfoMeshCoP("sent dataset get request");
exit:
if (error != OT_ERROR_NONE && message != nullptr)
{
message->Free();
}
return error;
}
ActiveDataset::ActiveDataset(Instance &aInstance)
: DatasetManager(aInstance,
Dataset::kActive,
OT_URI_PATH_ACTIVE_GET,
OT_URI_PATH_ACTIVE_SET,
ActiveDataset::HandleTimer)
, mResourceGet(OT_URI_PATH_ACTIVE_GET, &ActiveDataset::HandleGet, this)
#if OPENTHREAD_FTD
, mResourceSet(OT_URI_PATH_ACTIVE_SET, &ActiveDataset::HandleSet, this)
#endif
{
Get<Coap::Coap>().AddResource(mResourceGet);
}
bool ActiveDataset::IsPartiallyComplete(void) const
{
return mLocal.IsSaved() && !mTimestampValid;
}
otError ActiveDataset::Save(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint8_t aLength)
{
otError error = OT_ERROR_NONE;
Dataset dataset(mLocal.GetType());
SuccessOrExit(error = dataset.Set(aMessage, aOffset, aLength));
dataset.SetTimestamp(aTimestamp);
IgnoreError(DatasetManager::Save(dataset));
exit:
return error;
}
void ActiveDataset::HandleGet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
static_cast<ActiveDataset *>(aContext)->HandleGet(*static_cast<Coap::Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
void ActiveDataset::HandleGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const
{
DatasetManager::HandleGet(aMessage, aMessageInfo);
}
void ActiveDataset::HandleTimer(Timer &aTimer)
{
aTimer.GetOwner<ActiveDataset>().HandleTimer();
}
PendingDataset::PendingDataset(Instance &aInstance)
: DatasetManager(aInstance,
Dataset::kPending,
OT_URI_PATH_PENDING_GET,
OT_URI_PATH_PENDING_SET,
PendingDataset::HandleTimer)
, mDelayTimer(aInstance, PendingDataset::HandleDelayTimer, this)
, mResourceGet(OT_URI_PATH_PENDING_GET, &PendingDataset::HandleGet, this)
#if OPENTHREAD_FTD
, mResourceSet(OT_URI_PATH_PENDING_SET, &PendingDataset::HandleSet, this)
#endif
{
Get<Coap::Coap>().AddResource(mResourceGet);
}
void PendingDataset::Clear(void)
{
DatasetManager::Clear();
mDelayTimer.Stop();
}
void PendingDataset::ClearNetwork(void)
{
Dataset dataset(mLocal.GetType());
mTimestamp.Init();
mTimestampValid = false;
IgnoreError(DatasetManager::Save(dataset));
}
otError PendingDataset::Save(const otOperationalDataset &aDataset)
{
otError error;
SuccessOrExit(error = DatasetManager::Save(aDataset));
StartDelayTimer();
exit:
return error;
}
otError PendingDataset::Save(const otOperationalDatasetTlvs &aDataset)
{
otError error;
SuccessOrExit(error = DatasetManager::Save(aDataset));
StartDelayTimer();
exit:
return error;
}
otError PendingDataset::Save(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint8_t aLength)
{
otError error = OT_ERROR_NONE;
Dataset dataset(mLocal.GetType());
SuccessOrExit(error = dataset.Set(aMessage, aOffset, aLength));
dataset.SetTimestamp(aTimestamp);
IgnoreError(DatasetManager::Save(dataset));
StartDelayTimer();
exit:
return error;
}
void PendingDataset::StartDelayTimer(void)
{
DelayTimerTlv *delayTimer;
Dataset dataset(mLocal.GetType());
IgnoreError(mLocal.Read(dataset));
mDelayTimer.Stop();
if ((delayTimer = dataset.GetTlv<DelayTimerTlv>()) != 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);
otLogInfoMeshCoP("delay timer started %d", delay);
}
}
void PendingDataset::HandleDelayTimer(Timer &aTimer)
{
aTimer.GetOwner<PendingDataset>().HandleDelayTimer();
}
void PendingDataset::HandleDelayTimer(void)
{
DelayTimerTlv *delayTimer;
Dataset dataset(mLocal.GetType());
IgnoreError(mLocal.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<DelayTimerTlv>()) != nullptr)
{
uint32_t elapsed = mDelayTimer.GetFireTime() - dataset.GetUpdateTime();
uint32_t delay = delayTimer->GetDelayTimer();
if (elapsed < delay)
{
mDelayTimer.StartAt(mDelayTimer.GetFireTime(), delay - elapsed);
ExitNow();
}
}
otLogInfoMeshCoP("pending delay timer expired");
dataset.ConvertToActive();
Get<ActiveDataset>().Save(dataset);
Clear();
exit:
return;
}
void PendingDataset::HandleGet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
static_cast<PendingDataset *>(aContext)->HandleGet(*static_cast<Coap::Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
void PendingDataset::HandleGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const
{
DatasetManager::HandleGet(aMessage, aMessageInfo);
}
void PendingDataset::HandleTimer(Timer &aTimer)
{
aTimer.GetOwner<PendingDataset>().HandleTimer();
}
} // namespace MeshCoP
} // namespace ot