Skip to content

Commit

Permalink
[OpenThread] Revert configuration on boot with fail-safe armed (#21597)
Browse files Browse the repository at this point in the history
* [OpenThread] Revert configuration on boot with fail-safe armed

After recent changes related to the fail-safe commit marker
persistence RevertConfiguration() is no longer called by the
common code when the device reboots with the fail-safe armed.

Due to the nature of OpenThread API, all network credential
changes are persistent, so an explicit revert is needed in
such a case.

By the way, remove the workaround for KVS not being able to
store empty values as the scenario has already been verified
by the storage audit.

* Address code review comment
  • Loading branch information
Damian-Nordic authored and pull[bot] committed Jul 14, 2023
1 parent bf1a30b commit 1682070
Showing 1 changed file with 19 additions and 21 deletions.
40 changes: 19 additions & 21 deletions src/platform/OpenThread/GenericNetworkCommissioningThreadDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,15 @@ namespace NetworkCommissioning {
// KVS slot upon any changes and restored when the fail-safe timeout is triggered or the device
// reboots without completing all the changes.

// Not all KVS implementations support zero-length values, so use this special value, that is not a valid
// dataset, to represent an empty dataset. We need that to be able to revert the network configuration
// in the case of an unsuccessful commissioning.
constexpr uint8_t kEmptyDataset[1] = {};

CHIP_ERROR GenericThreadDriver::Init(Internal::BaseDriver::NetworkStatusChangeCallback * statusChangeCallback)
{
ThreadStackMgrImpl().SetNetworkStatusChangeCallback(statusChangeCallback);
ThreadStackMgrImpl().GetThreadProvision(mStagingNetwork);

VerifyOrReturnError(ThreadStackMgrImpl().IsThreadAttached(), CHIP_NO_ERROR);
VerifyOrReturnError(ThreadStackMgrImpl().GetThreadProvision(mStagingNetwork) == CHIP_NO_ERROR, CHIP_NO_ERROR);
// If the network configuration backup exists, it means that the device has been rebooted with
// the fail-safe armed. Since OpenThread persists all operational dataset changes, the backup
// must be restored on the boot. If there's no backup, the below function is a no-op.
RevertConfiguration();

return CHIP_NO_ERROR;
}
Expand Down Expand Up @@ -86,23 +84,23 @@ CHIP_ERROR GenericThreadDriver::RevertConfiguration()
// If no backup could be found, it means that the network configuration has not been modified
// since the fail-safe was armed, so return with no error.
ReturnErrorCodeIf(error == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND, CHIP_NO_ERROR);
ReturnErrorOnFailure(error);

ChipLogError(NetworkProvisioning, "Found Thread configuration backup: reverting configuration");
ChipLogProgress(NetworkProvisioning, "Reverting Thread operational dataset");

// Not all KVS implementations support zero-length values, so handle a special value representing an empty dataset.
ByteSpan dataset(datasetBytes, datasetLength);
if (error == CHIP_NO_ERROR)
{
error = mStagingNetwork.Init(ByteSpan(datasetBytes, datasetLength));
}

if (dataset.data_equal(ByteSpan(kEmptyDataset)))
if (error == CHIP_NO_ERROR)
{
dataset = {};
error = DeviceLayer::ThreadStackMgrImpl().AttachToThreadNetwork(mStagingNetwork, /* callback */ nullptr);
}

ReturnErrorOnFailure(mStagingNetwork.Init(dataset));
ReturnErrorOnFailure(DeviceLayer::ThreadStackMgrImpl().AttachToThreadNetwork(mStagingNetwork, /* callback */ nullptr));
// Always remove the backup, regardless if it can be successfully restored.
KeyValueStoreMgr().Delete(key.FailSafeNetworkConfig());

// TODO: What happens on errors above? Why do we not remove the failsafe?
return KeyValueStoreMgr().Delete(key.FailSafeNetworkConfig());
return error;
}

Status GenericThreadDriver::AddOrUpdateNetwork(ByteSpan operationalDataset, MutableCharSpan & outDebugText,
Expand Down Expand Up @@ -203,16 +201,16 @@ Status GenericThreadDriver::MatchesNetworkId(const Thread::OperationalDataset &
CHIP_ERROR GenericThreadDriver::BackupConfiguration()
{
DefaultStorageKeyAllocator key;
uint8_t dummy;

// If configuration is already backed up, return with no error
if (KeyValueStoreMgr().Get(key.FailSafeNetworkConfig(), &dummy, 0) == CHIP_ERROR_BUFFER_TOO_SMALL)
CHIP_ERROR err = KeyValueStoreMgr().Get(key.FailSafeNetworkConfig(), nullptr, 0);

if (err == CHIP_NO_ERROR || err == CHIP_ERROR_BUFFER_TOO_SMALL)
{
return CHIP_NO_ERROR;
}

// Not all KVS implementations support zero-length values, so use a special value in such a case.
ByteSpan dataset = mStagingNetwork.IsEmpty() ? ByteSpan(kEmptyDataset) : mStagingNetwork.AsByteSpan();
ByteSpan dataset = mStagingNetwork.AsByteSpan();

return KeyValueStoreMgr().Put(key.FailSafeNetworkConfig(), dataset.data(), dataset.size());
}
Expand Down

0 comments on commit 1682070

Please sign in to comment.