/* * Copyright (c) 2018, 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 includes definitions for non-volatile storage of settings. */ #include "settings.hpp" #include "common/array.hpp" #include "common/code_utils.hpp" #include "common/instance.hpp" #include "common/locator_getters.hpp" #include "meshcop/dataset.hpp" #include "thread/mle.hpp" namespace ot { RegisterLogModule("Settings"); //--------------------------------------------------------------------------------------------------------------------- // SettingsBase // LCOV_EXCL_START #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) void SettingsBase::NetworkInfo::Log(Action aAction) const { LogInfo("%s NetworkInfo {rloc:0x%04x, extaddr:%s, role:%s, mode:0x%02x, version:%hu, keyseq:0x%x, ...", ActionToString(aAction), GetRloc16(), GetExtAddress().ToString().AsCString(), Mle::Mle::RoleToString(static_cast(GetRole())), GetDeviceMode(), GetVersion(), GetKeySequence()); LogInfo("... pid:0x%x, mlecntr:0x%x, maccntr:0x%x, mliid:%s}", GetPreviousPartitionId(), GetMleFrameCounter(), GetMacFrameCounter(), GetMeshLocalIid().ToString().AsCString()); } void SettingsBase::ParentInfo::Log(Action aAction) const { LogInfo("%s ParentInfo {extaddr:%s, version:%hu}", ActionToString(aAction), GetExtAddress().ToString().AsCString(), GetVersion()); } #if OPENTHREAD_FTD void SettingsBase::ChildInfo::Log(Action aAction) const { LogInfo("%s ChildInfo {rloc:0x%04x, extaddr:%s, timeout:%u, mode:0x%02x, version:%hu}", ActionToString(aAction), GetRloc16(), GetExtAddress().ToString().AsCString(), GetTimeout(), GetMode(), GetVersion()); } #endif #if OPENTHREAD_CONFIG_DUA_ENABLE void SettingsBase::DadInfo::Log(Action aAction) const { LogInfo("%s DadInfo {DadCounter:%2d}", ActionToString(aAction), GetDadCounter()); } #endif #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE void SettingsBase::LogPrefix(Action aAction, Key aKey, const Ip6::Prefix &aPrefix) { LogInfo("%s %s %s", ActionToString(aAction), KeyToString(aKey), aPrefix.ToString().AsCString()); } #endif #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE void SettingsBase::SrpClientInfo::Log(Action aAction) const { LogInfo("%s SrpClientInfo {Server:[%s]:%u}", ActionToString(aAction), GetServerAddress().ToString().AsCString(), GetServerPort()); } #endif #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE && OPENTHREAD_CONFIG_SRP_SERVER_PORT_SWITCH_ENABLE void SettingsBase::SrpServerInfo::Log(Action aAction) const { LogInfo("%s SrpServerInfo {port:%u}", ActionToString(aAction), GetPort()); } #endif #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) const char *SettingsBase::ActionToString(Action aAction) { static const char *const kActionStrings[] = { "Read", // (0) kActionRead "Saved", // (1) kActionSave "Re-saved", // (2) kActionResave "Deleted", // (3) kActionDelete #if OPENTHREAD_FTD "Added", // (4) kActionAdd, "Removed", // (5) kActionRemove, "Deleted all" // (6) kActionDeleteAll #endif }; static_assert(0 == kActionRead, "kActionRead value is incorrect"); static_assert(1 == kActionSave, "kActionSave value is incorrect"); static_assert(2 == kActionResave, "kActionResave value is incorrect"); static_assert(3 == kActionDelete, "kActionDelete value is incorrect"); #if OPENTHREAD_FTD static_assert(4 == kActionAdd, "kActionAdd value is incorrect"); static_assert(5 == kActionRemove, "kActionRemove value is incorrect"); static_assert(6 == kActionDeleteAll, "kActionDeleteAll value is incorrect"); #endif return kActionStrings[aAction]; } #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) const char *SettingsBase::KeyToString(Key aKey) { static const char *const kKeyStrings[] = { "", // (0) (Unused) "ActiveDataset", // (1) kKeyActiveDataset "PendingDataset", // (2) kKeyPendingDataset "NetworkInfo", // (3) kKeyNetworkInfo "ParentInfo", // (4) kKeyParentInfo "ChildInfo", // (5) kKeyChildInfo "", // (6) Removed (previously auto-start). "SlaacIidSecretKey", // (7) kKeySlaacIidSecretKey "DadInfo", // (8) kKeyDadInfo "", // (9) Removed (previously OMR prefix). "", // (10) Removed (previously on-link prefix). "SrpEcdsaKey", // (11) kKeySrpEcdsaKey "SrpClientInfo", // (12) kKeySrpClientInfo "SrpServerInfo", // (13) kKeySrpServerInfo "", // (14) Removed (previously NAT64 prefix) "BrUlaPrefix", // (15) kKeyBrUlaPrefix }; static_assert(1 == kKeyActiveDataset, "kKeyActiveDataset value is incorrect"); static_assert(2 == kKeyPendingDataset, "kKeyPendingDataset value is incorrect"); static_assert(3 == kKeyNetworkInfo, "kKeyNetworkInfo value is incorrect"); static_assert(4 == kKeyParentInfo, "kKeyParentInfo value is incorrect"); static_assert(5 == kKeyChildInfo, "kKeyChildInfo value is incorrect"); static_assert(7 == kKeySlaacIidSecretKey, "kKeySlaacIidSecretKey value is incorrect"); static_assert(8 == kKeyDadInfo, "kKeyDadInfo value is incorrect"); static_assert(11 == kKeySrpEcdsaKey, "kKeySrpEcdsaKey value is incorrect"); static_assert(12 == kKeySrpClientInfo, "kKeySrpClientInfo value is incorrect"); static_assert(13 == kKeySrpServerInfo, "kKeySrpServerInfo value is incorrect"); static_assert(15 == kKeyBrUlaPrefix, "kKeyBrUlaPrefix value is incorrect"); static_assert(kLastKey == kKeyBrUlaPrefix, "kLastKey is not valid"); OT_ASSERT(aKey <= kLastKey); return kKeyStrings[aKey]; } #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) // LCOV_EXCL_STOP //--------------------------------------------------------------------------------------------------------------------- // Settings // This array contains sensitive keys that should be stored in the secure area. const uint16_t Settings::kSensitiveKeys[] = { SettingsBase::kKeyActiveDataset, SettingsBase::kKeyPendingDataset, SettingsBase::kKeySrpEcdsaKey, }; void Settings::Init(void) { Get().Init(kSensitiveKeys, GetArrayLength(kSensitiveKeys)); } void Settings::Deinit(void) { Get().Deinit(); } void Settings::Wipe(void) { Get().Wipe(); LogInfo("Wiped all info"); } Settings::Key Settings::KeyForDatasetType(MeshCoP::Dataset::Type aType) { return (aType == MeshCoP::Dataset::kActive) ? kKeyActiveDataset : kKeyPendingDataset; } Error Settings::SaveOperationalDataset(MeshCoP::Dataset::Type aType, const MeshCoP::Dataset &aDataset) { Key key = KeyForDatasetType(aType); Error error = Get().Set(key, aDataset.GetBytes(), aDataset.GetSize()); Log(kActionSave, error, key); return error; } Error Settings::ReadOperationalDataset(MeshCoP::Dataset::Type aType, MeshCoP::Dataset &aDataset) const { Error error = kErrorNone; uint16_t length = MeshCoP::Dataset::kMaxSize; SuccessOrExit(error = Get().Get(KeyForDatasetType(aType), aDataset.GetBytes(), &length)); VerifyOrExit(length <= MeshCoP::Dataset::kMaxSize, error = kErrorNotFound); aDataset.SetSize(length); exit: return error; } Error Settings::DeleteOperationalDataset(MeshCoP::Dataset::Type aType) { Key key = KeyForDatasetType(aType); Error error = Get().Delete(key); Log(kActionDelete, error, key); return error; } #if OPENTHREAD_FTD Error Settings::AddChildInfo(const ChildInfo &aChildInfo) { Error error = Get().Add(kKeyChildInfo, &aChildInfo, sizeof(aChildInfo)); Log(kActionAdd, error, kKeyChildInfo, &aChildInfo); return error; } Error Settings::DeleteAllChildInfo(void) { Error error = Get().Delete(kKeyChildInfo); Log(kActionDeleteAll, error, kKeyChildInfo); return error; } Settings::ChildInfoIterator::ChildInfoIterator(Instance &aInstance) : SettingsBase(aInstance) , mIndex(0) , mIsDone(false) { Read(); } void Settings::ChildInfoIterator::Advance(void) { if (!mIsDone) { mIndex++; Read(); } } Error Settings::ChildInfoIterator::Delete(void) { Error error = kErrorNone; VerifyOrExit(!mIsDone, error = kErrorInvalidState); SuccessOrExit(error = Get().Delete(kKeyChildInfo, mIndex)); exit: Log(kActionRemove, error, kKeyChildInfo, &mChildInfo); return error; } void Settings::ChildInfoIterator::Read(void) { uint16_t length = sizeof(ChildInfo); Error error; mChildInfo.Init(); SuccessOrExit( error = Get().Get(kKeyChildInfo, mIndex, reinterpret_cast(&mChildInfo), &length)); exit: Log(kActionRead, error, kKeyChildInfo, &mChildInfo); mIsDone = (error != kErrorNone); } #endif // OPENTHREAD_FTD Error Settings::ReadEntry(Key aKey, void *aValue, uint16_t aMaxLength) const { Error error; uint16_t length = aMaxLength; error = Get().Get(aKey, aValue, &length); Log(kActionRead, error, aKey, aValue); return error; } Error Settings::SaveEntry(Key aKey, const void *aValue, void *aPrev, uint16_t aLength) { Error error = kErrorNone; uint16_t readLength = aLength; Action action = kActionSave; if ((Get().Get(aKey, aPrev, &readLength) == kErrorNone) && (readLength == aLength) && (memcmp(aValue, aPrev, aLength) == 0)) { action = kActionResave; } else { error = Get().Set(aKey, aValue, aLength); } Log(action, error, aKey, aValue); return error; } Error Settings::DeleteEntry(Key aKey) { Error error = Get().Delete(aKey); Log(kActionDelete, error, aKey); return error; } void Settings::Log(Action aAction, Error aError, Key aKey, const void *aValue) { OT_UNUSED_VARIABLE(aAction); OT_UNUSED_VARIABLE(aKey); OT_UNUSED_VARIABLE(aError); OT_UNUSED_VARIABLE(aValue); if (aError != kErrorNone) { // Log error if log level is at "warn" or higher. #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) const char *actionText = ""; switch (aAction) { case kActionSave: case kActionResave: actionText = "saving"; break; case kActionDelete: VerifyOrExit(aError != kErrorNotFound); actionText = "deleting"; break; #if OPENTHREAD_FTD case kActionAdd: actionText = "adding"; break; case kActionRemove: VerifyOrExit(aError != kErrorNotFound); actionText = "removing"; break; case kActionDeleteAll: VerifyOrExit(aError != kErrorNotFound); actionText = "deleting all"; break; #endif case kActionRead: ExitNow(); } LogWarn("Error %s %s %s", ErrorToString(aError), actionText, KeyToString(aKey)); #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) ExitNow(); } // We reach here when `aError` is `kErrorNone`. // Log success if log level is at "info" or higher. #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) if (aValue != nullptr) { switch (aKey) { case kKeyNetworkInfo: reinterpret_cast(aValue)->Log(aAction); break; case kKeyParentInfo: reinterpret_cast(aValue)->Log(aAction); break; #if OPENTHREAD_FTD case kKeyChildInfo: reinterpret_cast(aValue)->Log(aAction); break; #endif #if OPENTHREAD_CONFIG_DUA_ENABLE case kKeyDadInfo: reinterpret_cast(aValue)->Log(aAction); break; #endif #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE case kKeyBrUlaPrefix: LogPrefix(aAction, aKey, *reinterpret_cast(aValue)); break; #endif #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE case kKeySrpClientInfo: reinterpret_cast(aValue)->Log(aAction); break; #endif #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE && OPENTHREAD_CONFIG_SRP_SERVER_PORT_SWITCH_ENABLE case kKeySrpServerInfo: reinterpret_cast(aValue)->Log(aAction); break; #endif default: // For any other keys, we do not want to include the value // in the log, so even if it is given we set `aValue` to // `nullptr`. This ensures that we just log the action and // the key. aValue = nullptr; break; } } if (aValue == nullptr) { LogInfo("%s %s", ActionToString(aAction), KeyToString(aKey)); } #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) exit: return; } } // namespace ot //--------------------------------------------------------------------------------------------------------------------- // Default/weak implementation of settings platform APIs OT_TOOL_WEAK void otPlatSettingsSetCriticalKeys(otInstance *aInstance, const uint16_t *aKeys, uint16_t aKeysLength) { OT_UNUSED_VARIABLE(aInstance); OT_UNUSED_VARIABLE(aKeys); OT_UNUSED_VARIABLE(aKeysLength); }