[dataset] allow setting partially complete active dataset (#3773)

When a partial dataset is stored, the device will attempt to attach to a
Thread network using the limited parameters available. If the device
successfully attaches, the device will then obtain the complete Active
Dataset from its Parent. If the device is router-capable, it will not
become a Router/Leader until it has successfully retrieved a complete Active
Dataset.

Saving a partial dataset supports out-of-band commissioning scenarios.
This commit is contained in:
Jonathan Hui
2019-04-23 22:10:40 -07:00
committed by GitHub
parent c5537e1b01
commit 4e45b45796
8 changed files with 92 additions and 43 deletions
+13
View File
@@ -306,6 +306,19 @@ OTAPI otError OTCALL otDatasetGetActive(otInstance *aInstance, otOperationalData
/**
* This function sets the Active Operational Dataset.
*
* If the dataset does not include an Active Timestamp, the dataset is only partially complete.
*
* If Thread is enabled on a device that has a partially complete Active Dataset, the device will attempt to attach to
* an existing Thread network using any existing information in the dataset. The minimum set of information needed to
* attach is the PAN ID and Thread Master Key.
*
* If channel is not included in the dataset, the device will send MLE Announce messages across different channels to
* find neighbors on other channels.
*
* If the device successfully attaches to a Thread network, the device will then retrieve the full Active Dataset from
* its Parent. Note that a router-capable device will not transition to the Router or Leader roles until it has a
* complete Active Dataset.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aDataset A pointer to the Active Operational Dataset.
*
+22 -25
View File
@@ -257,34 +257,32 @@ void Dataset::Set(const Dataset &aDataset)
otError Dataset::Set(const otOperationalDataset &aDataset)
{
otError error = OT_ERROR_NONE;
MeshCoP::ActiveTimestampTlv activeTimestampTlv;
otError error = OT_ERROR_NONE;
VerifyOrExit(aDataset.mComponents.mIsActiveTimestampPresent, error = OT_ERROR_INVALID_ARGS);
activeTimestampTlv.Init();
activeTimestampTlv.SetSeconds(aDataset.mActiveTimestamp);
activeTimestampTlv.SetTicks(0);
Set(activeTimestampTlv);
if (mType == Tlv::kPendingTimestamp)
if (aDataset.mComponents.mIsActiveTimestampPresent)
{
MeshCoP::PendingTimestampTlv pendingTimestampTlv;
MeshCoP::ActiveTimestampTlv tlv;
tlv.Init();
tlv.SetSeconds(aDataset.mActiveTimestamp);
tlv.SetTicks(0);
Set(tlv);
}
VerifyOrExit(aDataset.mComponents.mIsPendingTimestampPresent, error = OT_ERROR_INVALID_ARGS);
if (aDataset.mComponents.mIsPendingTimestampPresent)
{
MeshCoP::PendingTimestampTlv tlv;
tlv.Init();
tlv.SetSeconds(aDataset.mPendingTimestamp);
tlv.SetTicks(0);
Set(tlv);
}
pendingTimestampTlv.Init();
pendingTimestampTlv.SetSeconds(aDataset.mPendingTimestamp);
pendingTimestampTlv.SetTicks(0);
Set(pendingTimestampTlv);
if (aDataset.mComponents.mIsDelayPresent)
{
MeshCoP::DelayTimerTlv tlv;
tlv.Init();
tlv.SetDelayTimer(aDataset.mDelay);
Set(tlv);
}
if (aDataset.mComponents.mIsDelayPresent)
{
MeshCoP::DelayTimerTlv tlv;
tlv.Init();
tlv.SetDelayTimer(aDataset.mDelay);
Set(tlv);
}
if (aDataset.mComponents.mIsChannelPresent)
@@ -362,7 +360,6 @@ otError Dataset::Set(const otOperationalDataset &aDataset)
mUpdateTime = TimerMilli::GetNow();
exit:
return error;
}
+11 -10
View File
@@ -61,9 +61,10 @@ DatasetLocal::DatasetLocal(Instance &aInstance, const Tlv::Type aType)
void DatasetLocal::Clear(void)
{
Get<Settings>().DeleteOperationalDataset(IsActive());
mTimestamp.Init();
mTimestampPresent = false;
Get<Settings>().DeleteOperationalDataset(IsActive());
mSaved = false;
}
otError DatasetLocal::Restore(Dataset &aDataset)
@@ -71,9 +72,12 @@ otError DatasetLocal::Restore(Dataset &aDataset)
const Timestamp *timestamp;
otError error;
mTimestampPresent = false;
error = Read(aDataset);
SuccessOrExit(error);
mSaved = true;
timestamp = aDataset.GetTimestamp();
if (timestamp != NULL)
@@ -81,10 +85,6 @@ otError DatasetLocal::Restore(Dataset &aDataset)
mTimestamp = *timestamp;
mTimestampPresent = true;
}
else
{
mTimestampPresent = false;
}
exit:
return error;
@@ -161,21 +161,22 @@ exit:
otError DatasetLocal::Save(const Dataset &aDataset)
{
const Timestamp *timestamp;
otError error;
otError error = OT_ERROR_NONE;
if (aDataset.GetSize() == 0)
{
error = Get<Settings>().DeleteOperationalDataset(IsActive());
// do not propagate error back
Get<Settings>().DeleteOperationalDataset(IsActive());
mSaved = false;
otLogInfoMeshCoP("%s dataset deleted", mType == Tlv::kActiveTimestamp ? "Active" : "Pending");
}
else
{
error = Get<Settings>().SaveOperationalDataset(IsActive(), aDataset);
SuccessOrExit(error = Get<Settings>().SaveOperationalDataset(IsActive(), aDataset));
mSaved = true;
otLogInfoMeshCoP("%s dataset set", mType == Tlv::kActiveTimestamp ? "Active" : "Pending");
}
SuccessOrExit(error);
timestamp = aDataset.GetTimestamp();
if (timestamp != NULL)
+11 -1
View File
@@ -71,6 +71,15 @@ public:
*/
void Clear(void);
/**
* This method indicates whether an Active or Pending Dataset is saved in non-volatile memory.
*
* @retval TRUE if an Active or Pending Dataset is saved in non-volatile memory.
* @retval FALSE if an Active or Pending Dataset is not saved in non-volatile memory.
*
*/
bool IsSaved(void) const { return mSaved; }
/**
* This method restores and retrieves the dataset from non-volatile memory.
*
@@ -149,7 +158,8 @@ private:
Timestamp mTimestamp; ///< Active or Pending Timestamp
uint32_t mUpdateTime; ///< Local time last updated
Tlv::Type mType; ///< Active or Pending
bool mTimestampPresent : 1; ///< Whether or not timestamp is present
bool mTimestampPresent : 1; ///< Whether a timestamp is present
bool mSaved : 1; ///< Whether a dataset is saved in non-volatile
};
} // namespace MeshCoP
+11 -4
View File
@@ -92,6 +92,8 @@ otError DatasetManager::Restore(void)
mTimer.Stop();
mTimestampValid = false;
SuccessOrExit(error = mLocal.Restore(dataset));
timestamp = dataset.GetTimestamp();
@@ -100,11 +102,11 @@ otError DatasetManager::Restore(void)
{
mTimestamp = *timestamp;
mTimestampValid = true;
}
if (mLocal.GetType() == Tlv::kActiveTimestamp)
{
dataset.ApplyConfiguration(GetInstance());
}
if (mLocal.GetType() == Tlv::kActiveTimestamp)
{
dataset.ApplyConfiguration(GetInstance());
}
exit:
@@ -703,6 +705,11 @@ ActiveDataset::ActiveDataset(Instance &aInstance)
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;
+12
View File
@@ -313,6 +313,18 @@ public:
*/
explicit ActiveDataset(Instance &aInstance);
/**
* This method indicates whether the Active Dataset is partially complete.
*
* This method is primarily used to determine whether a user has supplied a partial Active Dataset for use
* with joining a network.
*
* @retval TRUE if an Active Dataset is saved but does not include an Active Timestamp.
* @retval FALSE if an Active Dataset is not saved or does include an Active Timestamp.
*
*/
bool IsPartiallyComplete(void) const;
/**
* This method clears the Active Operational Dataset.
*
+11 -3
View File
@@ -1713,9 +1713,13 @@ bool Mle::PrepareAnnounceState(void)
bool shouldAnnounce = false;
Mac::ChannelMask channelMask;
VerifyOrExit((mRole != OT_DEVICE_ROLE_CHILD) && !IsFullThreadDevice() && (mReattachState == kReattachStop));
VerifyOrExit((mRole != OT_DEVICE_ROLE_CHILD) && (mReattachState == kReattachStop) &&
(Get<MeshCoP::ActiveDataset>().IsPartiallyComplete() || !IsFullThreadDevice()));
SuccessOrExit(Get<MeshCoP::ActiveDataset>().GetChannelMask(channelMask));
if (Get<MeshCoP::ActiveDataset>().GetChannelMask(channelMask) != OT_ERROR_NONE)
{
channelMask = Get<Mac::Mac>().GetSupportedChannelMask();
}
mAnnounceDelay = kAnnounceTimeout / (channelMask.GetNumberOfChannels() + 1);
@@ -2374,7 +2378,11 @@ otError Mle::SendOrphanAnnounce(void)
otError error;
Mac::ChannelMask channelMask;
SuccessOrExit(error = Get<MeshCoP::ActiveDataset>().GetChannelMask(channelMask));
if (Get<MeshCoP::ActiveDataset>().GetChannelMask(channelMask) != OT_ERROR_NONE)
{
channelMask = Get<Mac::Mac>().GetSupportedChannelMask();
}
SuccessOrExit(error = channelMask.GetNextChannel(mAnnounceChannel));
SendAnnounce(mAnnounceChannel, true);
+1
View File
@@ -169,6 +169,7 @@ otError MleRouter::BecomeLeader(void)
uint32_t partitionId;
uint8_t leaderId;
VerifyOrExit(!Get<MeshCoP::ActiveDataset>().IsPartiallyComplete(), error = OT_ERROR_INVALID_STATE);
VerifyOrExit(mRole != OT_DEVICE_ROLE_DISABLED, error = OT_ERROR_INVALID_STATE);
VerifyOrExit(mRole != OT_DEVICE_ROLE_LEADER, error = OT_ERROR_NONE);
VerifyOrExit(IsRouterRoleEnabled(), error = OT_ERROR_NOT_CAPABLE);