/* * Copyright (c) 2020, 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. */ #define OTBR_LOG_TAG "UTILS" #include "utils/thread_helper.hpp" #include #include #include #include #include #include #include #include #if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY #include #endif #include #include #if OTBR_ENABLE_NAT64 #include #include #include "utils/sha256.hpp" #endif #if OTBR_ENABLE_DHCP6_PD #include "utils/sha256.hpp" #endif #if OTBR_ENABLE_LINK_METRICS_TELEMETRY #include #endif #if OTBR_ENABLE_SRP_ADVERTISING_PROXY #include #endif #include #if OTBR_ENABLE_TREL #include #endif #include #include #include "common/byteswap.hpp" #include "common/code_utils.hpp" #include "common/logging.hpp" #include "common/tlv.hpp" #include "ncp/rcp_host.hpp" namespace otbr { namespace agent { namespace { const Tlv *FindTlv(uint8_t aTlvType, const uint8_t *aTlvs, int aTlvsSize) { const Tlv *result = nullptr; for (const Tlv *tlv = reinterpret_cast(aTlvs); reinterpret_cast(tlv) + sizeof(Tlv) < aTlvs + aTlvsSize; tlv = tlv->GetNext()) { if (tlv->GetType() == aTlvType) { ExitNow(result = tlv); } } exit: return result; } #if OTBR_ENABLE_TELEMETRY_DATA_API static uint32_t TelemetryNodeTypeFromRoleAndLinkMode(const otDeviceRole &aRole, const otLinkModeConfig &aLinkModeCfg) { uint32_t nodeType; switch (aRole) { case OT_DEVICE_ROLE_DISABLED: nodeType = threadnetwork::TelemetryData::NODE_TYPE_DISABLED; break; case OT_DEVICE_ROLE_DETACHED: nodeType = threadnetwork::TelemetryData::NODE_TYPE_DETACHED; break; case OT_DEVICE_ROLE_ROUTER: nodeType = threadnetwork::TelemetryData::NODE_TYPE_ROUTER; break; case OT_DEVICE_ROLE_LEADER: nodeType = threadnetwork::TelemetryData::NODE_TYPE_LEADER; break; case OT_DEVICE_ROLE_CHILD: if (!aLinkModeCfg.mRxOnWhenIdle) { nodeType = threadnetwork::TelemetryData::NODE_TYPE_SLEEPY_END; } else if (!aLinkModeCfg.mDeviceType) { // If it's not an FTD, return as minimal end device. nodeType = threadnetwork::TelemetryData::NODE_TYPE_MINIMAL_END; } else { nodeType = threadnetwork::TelemetryData::NODE_TYPE_END; } break; default: nodeType = threadnetwork::TelemetryData::NODE_TYPE_UNSPECIFIED; } return nodeType; } #if OTBR_ENABLE_SRP_ADVERTISING_PROXY threadnetwork::TelemetryData_SrpServerState SrpServerStateFromOtSrpServerState(otSrpServerState srpServerState) { switch (srpServerState) { case OT_SRP_SERVER_STATE_DISABLED: return threadnetwork::TelemetryData::SRP_SERVER_STATE_DISABLED; case OT_SRP_SERVER_STATE_RUNNING: return threadnetwork::TelemetryData::SRP_SERVER_STATE_RUNNING; case OT_SRP_SERVER_STATE_STOPPED: return threadnetwork::TelemetryData::SRP_SERVER_STATE_STOPPED; default: return threadnetwork::TelemetryData::SRP_SERVER_STATE_UNSPECIFIED; } } threadnetwork::TelemetryData_SrpServerAddressMode SrpServerAddressModeFromOtSrpServerAddressMode( otSrpServerAddressMode srpServerAddressMode) { switch (srpServerAddressMode) { case OT_SRP_SERVER_ADDRESS_MODE_ANYCAST: return threadnetwork::TelemetryData::SRP_SERVER_ADDRESS_MODE_STATE_ANYCAST; case OT_SRP_SERVER_ADDRESS_MODE_UNICAST: return threadnetwork::TelemetryData::SRP_SERVER_ADDRESS_MODE_UNICAST; default: return threadnetwork::TelemetryData::SRP_SERVER_ADDRESS_MODE_UNSPECIFIED; } } #endif // OTBR_ENABLE_SRP_ADVERTISING_PROXY #if OTBR_ENABLE_NAT64 threadnetwork::TelemetryData_Nat64State Nat64StateFromOtNat64State(otNat64State nat64State) { switch (nat64State) { case OT_NAT64_STATE_DISABLED: return threadnetwork::TelemetryData::NAT64_STATE_DISABLED; case OT_NAT64_STATE_NOT_RUNNING: return threadnetwork::TelemetryData::NAT64_STATE_NOT_RUNNING; case OT_NAT64_STATE_IDLE: return threadnetwork::TelemetryData::NAT64_STATE_IDLE; case OT_NAT64_STATE_ACTIVE: return threadnetwork::TelemetryData::NAT64_STATE_ACTIVE; default: return threadnetwork::TelemetryData::NAT64_STATE_UNSPECIFIED; } } void CopyNat64TrafficCounters(const otNat64Counters &from, threadnetwork::TelemetryData_Nat64TrafficCounters *to) { to->set_ipv4_to_ipv6_packets(from.m4To6Packets); to->set_ipv4_to_ipv6_bytes(from.m4To6Bytes); to->set_ipv6_to_ipv4_packets(from.m6To4Packets); to->set_ipv6_to_ipv4_bytes(from.m6To4Bytes); } #endif // OTBR_ENABLE_NAT64 #if OTBR_ENABLE_DHCP6_PD threadnetwork::TelemetryData_Dhcp6PdState Dhcp6PdStateFromOtDhcp6PdState(otBorderRoutingDhcp6PdState dhcp6PdState) { threadnetwork::TelemetryData_Dhcp6PdState pdState = threadnetwork::TelemetryData::DHCP6_PD_STATE_UNSPECIFIED; switch (dhcp6PdState) { case OT_BORDER_ROUTING_DHCP6_PD_STATE_DISABLED: pdState = threadnetwork::TelemetryData::DHCP6_PD_STATE_DISABLED; break; case OT_BORDER_ROUTING_DHCP6_PD_STATE_STOPPED: pdState = threadnetwork::TelemetryData::DHCP6_PD_STATE_STOPPED; break; case OT_BORDER_ROUTING_DHCP6_PD_STATE_RUNNING: pdState = threadnetwork::TelemetryData::DHCP6_PD_STATE_RUNNING; break; default: break; } return pdState; } #endif // OTBR_ENABLE_DHCP6_PD void CopyMdnsResponseCounters(const MdnsResponseCounters &from, threadnetwork::TelemetryData_MdnsResponseCounters *to) { to->set_success_count(from.mSuccess); to->set_not_found_count(from.mNotFound); to->set_invalid_args_count(from.mInvalidArgs); to->set_duplicated_count(from.mDuplicated); to->set_not_implemented_count(from.mNotImplemented); to->set_unknown_error_count(from.mUnknownError); to->set_aborted_count(from.mAborted); to->set_invalid_state_count(from.mInvalidState); } #endif // OTBR_ENABLE_TELEMETRY_DATA_API } // namespace ThreadHelper::ThreadHelper(otInstance *aInstance, otbr::Ncp::RcpHost *aHost) : mInstance(aInstance) , mHost(aHost) { #if OTBR_ENABLE_TELEMETRY_DATA_API && (OTBR_ENABLE_NAT64 || OTBR_ENABLE_DHCP6_PD) otError error; SuccessOrExit(error = otPlatCryptoRandomGet(mNat64PdCommonSalt, sizeof(mNat64PdCommonSalt))); exit: if (error != OT_ERROR_NONE) { otbrLogWarning("Error otPlatCryptoRandomGet: %s", otThreadErrorToString(error)); } #endif } void ThreadHelper::StateChangedCallback(otChangedFlags aFlags) { if (aFlags & OT_CHANGED_THREAD_ROLE) { otDeviceRole role = mHost->GetDeviceRole(); for (const auto &handler : mDeviceRoleHandlers) { handler(role); } if (role != OT_DEVICE_ROLE_DISABLED && role != OT_DEVICE_ROLE_DETACHED) { if (mAttachHandler != nullptr) { if (mWaitingMgmtSetResponse) { otbrLogInfo("StateChangedCallback is called during waiting for Mgmt Set Response"); ExitNow(); } if (mAttachPendingDatasetTlvs.mLength == 0) { AttachHandler handler = mAttachHandler; mAttachHandler = nullptr; handler(OT_ERROR_NONE, mAttachDelayMs); } else { otOperationalDataset emptyDataset = {}; otError error = otDatasetSendMgmtPendingSet(mInstance, &emptyDataset, mAttachPendingDatasetTlvs.mTlvs, mAttachPendingDatasetTlvs.mLength, MgmtSetResponseHandler, this); if (error != OT_ERROR_NONE) { AttachHandler handler = mAttachHandler; mAttachHandler = nullptr; mAttachPendingDatasetTlvs = {}; mWaitingMgmtSetResponse = false; handler(error, 0); } else { mWaitingMgmtSetResponse = true; } } } else if (mJoinerHandler != nullptr) { mJoinerHandler(OT_ERROR_NONE); mJoinerHandler = nullptr; } } } if (aFlags & OT_CHANGED_ACTIVE_DATASET) { ActiveDatasetChangedCallback(); } exit: return; } void ThreadHelper::ActiveDatasetChangedCallback() { otError error; otOperationalDatasetTlvs datasetTlvs; SuccessOrExit(error = otDatasetGetActiveTlvs(mInstance, &datasetTlvs)); for (const auto &handler : mActiveDatasetChangeHandlers) { handler(datasetTlvs); } exit: if (error != OT_ERROR_NONE) { otbrLogWarning("Error handling active dataset change: %s", otThreadErrorToString(error)); } } #if OTBR_ENABLE_DBUS_SERVER void ThreadHelper::OnUpdateMeshCopTxt(std::map> aUpdate) { if (mUpdateMeshCopTxtHandler) { mUpdateMeshCopTxtHandler(std::move(aUpdate)); } else { otbrLogErr("No UpdateMeshCopTxtHandler"); } } #endif void ThreadHelper::AddDeviceRoleHandler(DeviceRoleHandler aHandler) { mDeviceRoleHandlers.emplace_back(aHandler); } void ThreadHelper::Scan(ScanHandler aHandler) { otError error = OT_ERROR_NONE; VerifyOrExit(aHandler != nullptr); mScanHandler = aHandler; mScanResults.clear(); error = otLinkActiveScan(mInstance, /*scanChannels =*/0, /*scanDuration=*/0, &ThreadHelper::ActiveScanHandler, this); exit: if (error != OT_ERROR_NONE) { if (aHandler) { mScanHandler(error, {}); } mScanHandler = nullptr; } } void ThreadHelper::EnergyScan(uint32_t aScanDuration, EnergyScanHandler aHandler) { otError error = OT_ERROR_NONE; uint32_t preferredChannels = otPlatRadioGetPreferredChannelMask(mInstance); VerifyOrExit(aHandler != nullptr, error = OT_ERROR_BUSY); VerifyOrExit(aScanDuration < UINT16_MAX, error = OT_ERROR_INVALID_ARGS); mEnergyScanHandler = aHandler; mEnergyScanResults.clear(); error = otLinkEnergyScan(mInstance, preferredChannels, static_cast(aScanDuration), &ThreadHelper::EnergyScanCallback, this); exit: if (error != OT_ERROR_NONE) { if (aHandler) { mEnergyScanHandler(error, {}); } mEnergyScanHandler = nullptr; } } void ThreadHelper::RandomFill(void *aBuf, size_t size) { std::uniform_int_distribution<> dist(0, UINT8_MAX); uint8_t *buf = static_cast(aBuf); for (size_t i = 0; i < size; i++) { buf[i] = static_cast(dist(mRandomDevice)); } } void ThreadHelper::ActiveScanHandler(otActiveScanResult *aResult, void *aThreadHelper) { ThreadHelper *helper = static_cast(aThreadHelper); helper->ActiveScanHandler(aResult); } void ThreadHelper::ActiveScanHandler(otActiveScanResult *aResult) { if (aResult == nullptr) { if (mScanHandler != nullptr) { mScanHandler(OT_ERROR_NONE, mScanResults); } } else { mScanResults.push_back(*aResult); } } #if OTBR_ENABLE_DHCP6_PD void ThreadHelper::SetDhcp6PdStateCallback(Dhcp6PdStateCallback aCallback) { mDhcp6PdCallback = std::move(aCallback); otBorderRoutingDhcp6PdSetRequestCallback(mInstance, &ThreadHelper::BorderRoutingDhcp6PdCallback, this); } void ThreadHelper::BorderRoutingDhcp6PdCallback(otBorderRoutingDhcp6PdState aState, void *aThreadHelper) { ThreadHelper *helper = static_cast(aThreadHelper); helper->BorderRoutingDhcp6PdCallback(aState); } void ThreadHelper::BorderRoutingDhcp6PdCallback(otBorderRoutingDhcp6PdState aState) { if (mDhcp6PdCallback != nullptr) { mDhcp6PdCallback(aState); } } #endif // OTBR_ENABLE_DHCP6_PD void ThreadHelper::EnergyScanCallback(otEnergyScanResult *aResult, void *aThreadHelper) { ThreadHelper *helper = static_cast(aThreadHelper); helper->EnergyScanCallback(aResult); } void ThreadHelper::EnergyScanCallback(otEnergyScanResult *aResult) { if (aResult == nullptr) { if (mEnergyScanHandler != nullptr) { mEnergyScanHandler(OT_ERROR_NONE, mEnergyScanResults); } } else { mEnergyScanResults.push_back(*aResult); } } uint8_t ThreadHelper::RandomChannelFromChannelMask(uint32_t aChannelMask) { // 8 bit per byte constexpr uint8_t kNumChannels = sizeof(aChannelMask) * 8; uint8_t channels[kNumChannels]; uint8_t numValidChannels = 0; for (uint8_t i = 0; i < kNumChannels; i++) { if (aChannelMask & (1 << i)) { channels[numValidChannels++] = i; } } return channels[std::uniform_int_distribution(0, numValidChannels - 1)(mRandomDevice)]; } static otExtendedPanId ToOtExtendedPanId(uint64_t aExtPanId) { otExtendedPanId extPanId; uint64_t mask = UINT8_MAX; for (size_t i = 0; i < sizeof(uint64_t); i++) { extPanId.m8[i] = static_cast((aExtPanId >> ((sizeof(uint64_t) - i - 1) * 8)) & mask); } return extPanId; } void ThreadHelper::Attach(const std::string &aNetworkName, uint16_t aPanId, uint64_t aExtPanId, const std::vector &aNetworkKey, const std::vector &aPSKc, uint32_t aChannelMask, AttachHandler aHandler) { otError error = OT_ERROR_NONE; otOperationalDataset dataset = {}; VerifyOrExit(aHandler != nullptr, error = OT_ERROR_INVALID_ARGS); VerifyOrExit(mAttachHandler == nullptr && mJoinerHandler == nullptr, error = OT_ERROR_INVALID_STATE); VerifyOrExit(aNetworkKey.empty() || aNetworkKey.size() == sizeof(dataset.mNetworkKey.m8), error = OT_ERROR_INVALID_ARGS); VerifyOrExit(aPSKc.empty() || aPSKc.size() == sizeof(dataset.mPskc.m8), error = OT_ERROR_INVALID_ARGS); VerifyOrExit(aChannelMask != 0, error = OT_ERROR_INVALID_ARGS); SuccessOrExit(error = otDatasetCreateNewNetwork(mInstance, &dataset)); if (aExtPanId != UINT64_MAX) { dataset.mExtendedPanId = ToOtExtendedPanId(aExtPanId); } if (!aNetworkKey.empty()) { memcpy(dataset.mNetworkKey.m8, &aNetworkKey[0], sizeof(dataset.mNetworkKey.m8)); } if (aPanId != UINT16_MAX) { dataset.mPanId = aPanId; } if (!aPSKc.empty()) { memcpy(dataset.mPskc.m8, &aPSKc[0], sizeof(dataset.mPskc.m8)); } SuccessOrExit(error = otNetworkNameFromString(&dataset.mNetworkName, aNetworkName.c_str())); dataset.mChannelMask &= aChannelMask; VerifyOrExit(dataset.mChannelMask != 0, otbrLogWarning("Invalid channel mask"), error = OT_ERROR_INVALID_ARGS); dataset.mChannel = RandomChannelFromChannelMask(dataset.mChannelMask); SuccessOrExit(error = otDatasetSetActive(mInstance, &dataset)); if (!otIp6IsEnabled(mInstance)) { SuccessOrExit(error = otIp6SetEnabled(mInstance, true)); } SuccessOrExit(error = otThreadSetEnabled(mInstance, true)); mAttachDelayMs = 0; mAttachHandler = aHandler; exit: if (error != OT_ERROR_NONE) { if (aHandler) { aHandler(error, 0); } } } void ThreadHelper::Attach(AttachHandler aHandler) { otError error = OT_ERROR_NONE; VerifyOrExit(mAttachHandler == nullptr && mJoinerHandler == nullptr, error = OT_ERROR_INVALID_STATE); if (!otIp6IsEnabled(mInstance)) { SuccessOrExit(error = otIp6SetEnabled(mInstance, true)); } SuccessOrExit(error = otThreadSetEnabled(mInstance, true)); mAttachHandler = aHandler; exit: if (error != OT_ERROR_NONE) { if (aHandler) { aHandler(error, 0); } } } otError ThreadHelper::Detach(void) { otError error = OT_ERROR_NONE; SuccessOrExit(error = otThreadSetEnabled(mInstance, false)); SuccessOrExit(error = otIp6SetEnabled(mInstance, false)); exit: return error; } otError ThreadHelper::Reset(void) { mDeviceRoleHandlers.clear(); otInstanceReset(mInstance); return OT_ERROR_NONE; } void ThreadHelper::JoinerStart(const std::string &aPskd, const std::string &aProvisioningUrl, const std::string &aVendorName, const std::string &aVendorModel, const std::string &aVendorSwVersion, const std::string &aVendorData, ResultHandler aHandler) { otError error = OT_ERROR_NONE; VerifyOrExit(aHandler != nullptr, error = OT_ERROR_INVALID_ARGS); VerifyOrExit(mAttachHandler == nullptr && mJoinerHandler == nullptr, error = OT_ERROR_INVALID_STATE); if (!otIp6IsEnabled(mInstance)) { SuccessOrExit(error = otIp6SetEnabled(mInstance, true)); } SuccessOrExit(error = otJoinerStart(mInstance, aPskd.c_str(), aProvisioningUrl.c_str(), aVendorName.c_str(), aVendorModel.c_str(), aVendorSwVersion.c_str(), aVendorData.c_str(), JoinerCallback, this)); mJoinerHandler = aHandler; exit: if (error != OT_ERROR_NONE) { if (aHandler) { aHandler(error); } } } void ThreadHelper::JoinerCallback(otError aError, void *aThreadHelper) { ThreadHelper *helper = static_cast(aThreadHelper); helper->JoinerCallback(aError); } void ThreadHelper::JoinerCallback(otError aError) { if (aError != OT_ERROR_NONE) { otbrLogWarning("Failed to join Thread network: %s", otThreadErrorToString(aError)); mJoinerHandler(aError); mJoinerHandler = nullptr; } else { LogOpenThreadResult("Start Thread network", otThreadSetEnabled(mInstance, true)); } } otError ThreadHelper::TryResumeNetwork(void) { otError error = OT_ERROR_NONE; if (otLinkGetPanId(mInstance) != UINT16_MAX && mHost->GetDeviceRole() == OT_DEVICE_ROLE_DISABLED) { if (!otIp6IsEnabled(mInstance)) { SuccessOrExit(error = otIp6SetEnabled(mInstance, true)); SuccessOrExit(error = otThreadSetEnabled(mInstance, true)); } } exit: if (error != OT_ERROR_NONE) { (void)otIp6SetEnabled(mInstance, false); } return error; } void ThreadHelper::LogOpenThreadResult(const char *aAction, otError aError) { if (aError == OT_ERROR_NONE) { otbrLogInfo("%s: %s", aAction, otThreadErrorToString(aError)); } else { otbrLogWarning("%s: %s", aAction, otThreadErrorToString(aError)); } } void ThreadHelper::AttachAllNodesTo(const std::vector &aDatasetTlvs, AttachHandler aHandler) { constexpr uint32_t kDelayTimerMilliseconds = 300 * 1000; otError error = OT_ERROR_NONE; otOperationalDatasetTlvs datasetTlvs; otOperationalDataset dataset; otOperationalDataset emptyDataset{}; otDeviceRole role = mHost->GetDeviceRole(); if (aHandler == nullptr) { otbrLogWarning("Attach Handler is nullptr"); ExitNow(error = OT_ERROR_INVALID_ARGS); } VerifyOrExit(mAttachHandler == nullptr && mJoinerHandler == nullptr, error = OT_ERROR_BUSY); VerifyOrExit(aDatasetTlvs.size() <= sizeof(datasetTlvs.mTlvs), error = OT_ERROR_INVALID_ARGS); std::copy(aDatasetTlvs.begin(), aDatasetTlvs.end(), datasetTlvs.mTlvs); datasetTlvs.mLength = aDatasetTlvs.size(); SuccessOrExit(error = otDatasetParseTlvs(&datasetTlvs, &dataset)); VerifyOrExit(dataset.mComponents.mIsActiveTimestampPresent, error = OT_ERROR_INVALID_ARGS); VerifyOrExit(dataset.mComponents.mIsNetworkKeyPresent, error = OT_ERROR_INVALID_ARGS); VerifyOrExit(dataset.mComponents.mIsNetworkNamePresent, error = OT_ERROR_INVALID_ARGS); VerifyOrExit(dataset.mComponents.mIsExtendedPanIdPresent, error = OT_ERROR_INVALID_ARGS); VerifyOrExit(dataset.mComponents.mIsMeshLocalPrefixPresent, error = OT_ERROR_INVALID_ARGS); VerifyOrExit(dataset.mComponents.mIsPanIdPresent, error = OT_ERROR_INVALID_ARGS); VerifyOrExit(dataset.mComponents.mIsChannelPresent, error = OT_ERROR_INVALID_ARGS); VerifyOrExit(dataset.mComponents.mIsPskcPresent, error = OT_ERROR_INVALID_ARGS); VerifyOrExit(dataset.mComponents.mIsSecurityPolicyPresent, error = OT_ERROR_INVALID_ARGS); VerifyOrExit(dataset.mComponents.mIsChannelMaskPresent, error = OT_ERROR_INVALID_ARGS); SuccessOrExit(error = ProcessDatasetForMigration(datasetTlvs, kDelayTimerMilliseconds)); assert(datasetTlvs.mLength > 0); if (role == OT_DEVICE_ROLE_DISABLED || role == OT_DEVICE_ROLE_DETACHED) { otOperationalDataset existingDataset; bool hasActiveDataset; error = otDatasetGetActive(mInstance, &existingDataset); VerifyOrExit(error == OT_ERROR_NONE || error == OT_ERROR_NOT_FOUND); hasActiveDataset = (error == OT_ERROR_NONE); if (!hasActiveDataset) { SuccessOrExit(error = otDatasetSetActiveTlvs(mInstance, &datasetTlvs)); } if (!otIp6IsEnabled(mInstance)) { SuccessOrExit(error = otIp6SetEnabled(mInstance, true)); } SuccessOrExit(error = otThreadSetEnabled(mInstance, true)); if (hasActiveDataset) { mAttachDelayMs = kDelayTimerMilliseconds; mAttachPendingDatasetTlvs = datasetTlvs; } else { mAttachDelayMs = 0; mAttachPendingDatasetTlvs = {}; } mWaitingMgmtSetResponse = false; mAttachHandler = aHandler; ExitNow(); } SuccessOrExit(error = otDatasetSendMgmtPendingSet(mInstance, &emptyDataset, datasetTlvs.mTlvs, datasetTlvs.mLength, MgmtSetResponseHandler, this)); mAttachDelayMs = kDelayTimerMilliseconds; mAttachHandler = aHandler; mWaitingMgmtSetResponse = true; exit: if (error != OT_ERROR_NONE) { aHandler(error, 0); } } void ThreadHelper::MgmtSetResponseHandler(otError aResult, void *aContext) { static_cast(aContext)->MgmtSetResponseHandler(aResult); } void ThreadHelper::MgmtSetResponseHandler(otError aResult) { AttachHandler handler; int64_t attachDelayMs; LogOpenThreadResult("MgmtSetResponseHandler()", aResult); mWaitingMgmtSetResponse = false; if (mAttachHandler == nullptr) { otbrLogWarning("mAttachHandler is nullptr"); mAttachDelayMs = 0; mAttachPendingDatasetTlvs = {}; ExitNow(); } switch (aResult) { case OT_ERROR_NONE: case OT_ERROR_REJECTED: break; default: aResult = OT_ERROR_FAILED; break; } attachDelayMs = mAttachDelayMs; handler = mAttachHandler; mAttachDelayMs = 0; mAttachHandler = nullptr; mAttachPendingDatasetTlvs = {}; if (aResult == OT_ERROR_NONE) { handler(aResult, attachDelayMs); } else { handler(aResult, 0); } exit: return; } #if OTBR_ENABLE_UNSECURE_JOIN otError ThreadHelper::PermitUnsecureJoin(uint16_t aPort, uint32_t aSeconds) { otError error = OT_ERROR_NONE; otExtAddress steeringData; // 0xff to allow all devices to join memset(&steeringData.m8, 0xff, sizeof(steeringData.m8)); SuccessOrExit(error = otIp6AddUnsecurePort(mInstance, aPort)); otThreadSetSteeringData(mInstance, &steeringData); if (aSeconds > 0) { auto delay = Milliseconds(aSeconds * 1000); ++mUnsecurePortRefCounter[aPort]; mHost->PostTimerTask(delay, [this, aPort]() { assert(mUnsecurePortRefCounter.find(aPort) != mUnsecurePortRefCounter.end()); assert(mUnsecurePortRefCounter[aPort] > 0); if (--mUnsecurePortRefCounter[aPort] == 0) { otExtAddress noneAddress; // 0 to clean steering data memset(&noneAddress.m8, 0, sizeof(noneAddress.m8)); (void)otIp6RemoveUnsecurePort(mInstance, aPort); otThreadSetSteeringData(mInstance, &noneAddress); mUnsecurePortRefCounter.erase(aPort); } }); } else { otExtAddress noneAddress; memset(&noneAddress.m8, 0, sizeof(noneAddress.m8)); (void)otIp6RemoveUnsecurePort(mInstance, aPort); otThreadSetSteeringData(mInstance, &noneAddress); } exit: return error; } #endif void ThreadHelper::AddActiveDatasetChangeHandler(DatasetChangeHandler aHandler) { mActiveDatasetChangeHandlers.push_back(std::move(aHandler)); } void ThreadHelper::DetachGracefully(ResultHandler aHandler) { otError error = OT_ERROR_NONE; VerifyOrExit(mDetachGracefullyHandler == nullptr, error = OT_ERROR_BUSY); SuccessOrExit(error = otThreadDetachGracefully(mInstance, &ThreadHelper::DetachGracefullyCallback, this)); mDetachGracefullyHandler = aHandler; exit: if (error != OT_ERROR_NONE) { aHandler(error); } } void ThreadHelper::DetachGracefullyCallback(void *aContext) { static_cast(aContext)->DetachGracefullyCallback(); } void ThreadHelper::DetachGracefullyCallback(void) { if (mDetachGracefullyHandler != nullptr) { mDetachGracefullyHandler(OT_ERROR_NONE); } } #if OTBR_ENABLE_TELEMETRY_DATA_API #if OTBR_ENABLE_BORDER_ROUTING void ThreadHelper::RetrieveInfraLinkInfo(threadnetwork::TelemetryData::InfraLinkInfo &aInfraLinkInfo) { { otSysInfraNetIfAddressCounters addressCounters; uint32_t ifrFlags = otSysGetInfraNetifFlags(); otSysCountInfraNetifAddresses(&addressCounters); aInfraLinkInfo.set_name(otSysGetInfraNetifName()); aInfraLinkInfo.set_is_up((ifrFlags & IFF_UP) != 0); aInfraLinkInfo.set_is_running((ifrFlags & IFF_RUNNING) != 0); aInfraLinkInfo.set_is_multicast((ifrFlags & IFF_MULTICAST) != 0); aInfraLinkInfo.set_link_local_address_count(addressCounters.mLinkLocalAddresses); aInfraLinkInfo.set_unique_local_address_count(addressCounters.mUniqueLocalAddresses); aInfraLinkInfo.set_global_unicast_address_count(addressCounters.mGlobalUnicastAddresses); } //---- peer_br_count { uint32_t count = 0; otBorderRoutingPrefixTableIterator iterator; otBorderRoutingRouterEntry entry; otBorderRoutingPrefixTableInitIterator(mInstance, &iterator); while (otBorderRoutingGetNextRouterEntry(mInstance, &iterator, &entry) == OT_ERROR_NONE) { if (entry.mIsPeerBr) { count++; } } aInfraLinkInfo.set_peer_br_count(count); } } void ThreadHelper::RetrieveExternalRouteInfo(threadnetwork::TelemetryData::ExternalRoutes &aExternalRouteInfo) { bool isDefaultRouteAdded = false; bool isUlaRouteAdded = false; bool isOthersRouteAdded = false; Ip6Prefix prefix; uint16_t rloc16 = otThreadGetRloc16(mInstance); otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; otExternalRouteConfig config; while (otNetDataGetNextRoute(mInstance, &iterator, &config) == OT_ERROR_NONE) { if (!config.mStable || config.mRloc16 != rloc16) { continue; } prefix.Set(config.mPrefix); if (prefix.IsDefaultRoutePrefix()) { isDefaultRouteAdded = true; } else if (prefix.IsUlaPrefix()) { isUlaRouteAdded = true; } else { isOthersRouteAdded = true; } } aExternalRouteInfo.set_has_default_route_added(isDefaultRouteAdded); aExternalRouteInfo.set_has_ula_route_added(isUlaRouteAdded); aExternalRouteInfo.set_has_others_route_added(isOthersRouteAdded); } #endif // OTBR_ENABLE_BORDER_ROUTING #if OTBR_ENABLE_DHCP6_PD void ThreadHelper::RetrievePdInfo(threadnetwork::TelemetryData::WpanBorderRouter *aWpanBorderRouter) { aWpanBorderRouter->set_dhcp6_pd_state(Dhcp6PdStateFromOtDhcp6PdState(otBorderRoutingDhcp6PdGetState(mInstance))); RetrieveHashedPdPrefix(aWpanBorderRouter->mutable_hashed_pd_prefix()); RetrievePdProcessedRaInfo(aWpanBorderRouter->mutable_pd_processed_ra_info()); } void ThreadHelper::RetrieveHashedPdPrefix(std::string *aHashedPdPrefix) { otBorderRoutingPrefixTableEntry aPrefixInfo; const uint8_t *prefixAddr = nullptr; const uint8_t *truncatedHash = nullptr; constexpr size_t kHashPrefixLength = 6; constexpr size_t kHashedPrefixLength = 2; std::vector hashedPdHeader = {0x20, 0x01, 0x0d, 0xb8}; std::vector hashedPdTailer = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; std::vector hashedPdPrefix; hashedPdPrefix.reserve(16); Sha256 sha256; Sha256::Hash hash; SuccessOrExit(otBorderRoutingGetPdOmrPrefix(mInstance, &aPrefixInfo)); prefixAddr = aPrefixInfo.mPrefix.mPrefix.mFields.m8; // TODO: Put below steps into a reusable function. sha256.Start(); sha256.Update(prefixAddr, kHashPrefixLength); sha256.Update(mNat64PdCommonSalt, kNat64PdCommonHashSaltLength); sha256.Finish(hash); // Append hashedPdHeader hashedPdPrefix.insert(hashedPdPrefix.end(), hashedPdHeader.begin(), hashedPdHeader.end()); // Append the first 2 bytes of the hashed prefix truncatedHash = hash.GetBytes(); hashedPdPrefix.insert(hashedPdPrefix.end(), truncatedHash, truncatedHash + kHashedPrefixLength); // Append ip[6] and ip[7] hashedPdPrefix.push_back(prefixAddr[6]); hashedPdPrefix.push_back(prefixAddr[7]); // Append hashedPdTailer hashedPdPrefix.insert(hashedPdPrefix.end(), hashedPdTailer.begin(), hashedPdTailer.end()); aHashedPdPrefix->append(reinterpret_cast(hashedPdPrefix.data()), hashedPdPrefix.size()); exit: return; } void ThreadHelper::RetrievePdProcessedRaInfo(threadnetwork::TelemetryData::PdProcessedRaInfo *aPdProcessedRaInfo) { otPdProcessedRaInfo raInfo; SuccessOrExit(otBorderRoutingGetPdProcessedRaInfo(mInstance, &raInfo)); aPdProcessedRaInfo->set_num_platform_ra_received(raInfo.mNumPlatformRaReceived); aPdProcessedRaInfo->set_num_platform_pio_processed(raInfo.mNumPlatformPioProcessed); aPdProcessedRaInfo->set_last_platform_ra_msec(raInfo.mLastPlatformRaMsec); exit: return; } #endif // OTBR_ENABLE_DHCP6_PD #if OTBR_ENABLE_BORDER_AGENT void ThreadHelper::RetrieveBorderAgentInfo(threadnetwork::TelemetryData::BorderAgentInfo *aBorderAgentInfo) { auto baCounters = aBorderAgentInfo->mutable_border_agent_counters(); auto otBorderAgentCounters = *otBorderAgentGetCounters(mInstance); baCounters->set_epskc_activations(otBorderAgentCounters.mEpskcActivations); baCounters->set_epskc_deactivation_clears(otBorderAgentCounters.mEpskcDeactivationClears); baCounters->set_epskc_deactivation_timeouts(otBorderAgentCounters.mEpskcDeactivationTimeouts); baCounters->set_epskc_deactivation_max_attempts(otBorderAgentCounters.mEpskcDeactivationMaxAttempts); baCounters->set_epskc_deactivation_disconnects(otBorderAgentCounters.mEpskcDeactivationDisconnects); baCounters->set_epskc_invalid_ba_state_errors(otBorderAgentCounters.mEpskcInvalidBaStateErrors); baCounters->set_epskc_invalid_args_errors(otBorderAgentCounters.mEpskcInvalidArgsErrors); baCounters->set_epskc_start_secure_session_errors(otBorderAgentCounters.mEpskcStartSecureSessionErrors); baCounters->set_epskc_secure_session_successes(otBorderAgentCounters.mEpskcSecureSessionSuccesses); baCounters->set_epskc_secure_session_failures(otBorderAgentCounters.mEpskcSecureSessionFailures); baCounters->set_epskc_commissioner_petitions(otBorderAgentCounters.mEpskcCommissionerPetitions); baCounters->set_pskc_secure_session_successes(otBorderAgentCounters.mPskcSecureSessionSuccesses); baCounters->set_pskc_secure_session_failures(otBorderAgentCounters.mPskcSecureSessionFailures); baCounters->set_pskc_commissioner_petitions(otBorderAgentCounters.mPskcCommissionerPetitions); baCounters->set_mgmt_active_get_reqs(otBorderAgentCounters.mMgmtActiveGets); baCounters->set_mgmt_pending_get_reqs(otBorderAgentCounters.mMgmtPendingGets); } #endif otError ThreadHelper::RetrieveTelemetryData(Mdns::Publisher *aPublisher, threadnetwork::TelemetryData &telemetryData) { otError error = OT_ERROR_NONE; std::vector neighborTable; // Begin of WpanStats section. auto wpanStats = telemetryData.mutable_wpan_stats(); { otDeviceRole role = mHost->GetDeviceRole(); otLinkModeConfig otCfg = otThreadGetLinkMode(mInstance); wpanStats->set_node_type(TelemetryNodeTypeFromRoleAndLinkMode(role, otCfg)); } wpanStats->set_channel(otLinkGetChannel(mInstance)); { uint16_t ccaFailureRate = otLinkGetCcaFailureRate(mInstance); wpanStats->set_mac_cca_fail_rate(static_cast(ccaFailureRate) / 0xffff); } { int8_t radioTxPower; if (otPlatRadioGetTransmitPower(mInstance, &radioTxPower) == OT_ERROR_NONE) { wpanStats->set_radio_tx_power(radioTxPower); } else { error = OT_ERROR_FAILED; } } { const otMacCounters *linkCounters = otLinkGetCounters(mInstance); wpanStats->set_phy_rx(linkCounters->mRxTotal); wpanStats->set_phy_tx(linkCounters->mTxTotal); wpanStats->set_mac_unicast_rx(linkCounters->mRxUnicast); wpanStats->set_mac_unicast_tx(linkCounters->mTxUnicast); wpanStats->set_mac_broadcast_rx(linkCounters->mRxBroadcast); wpanStats->set_mac_broadcast_tx(linkCounters->mTxBroadcast); wpanStats->set_mac_tx_ack_req(linkCounters->mTxAckRequested); wpanStats->set_mac_tx_no_ack_req(linkCounters->mTxNoAckRequested); wpanStats->set_mac_tx_acked(linkCounters->mTxAcked); wpanStats->set_mac_tx_data(linkCounters->mTxData); wpanStats->set_mac_tx_data_poll(linkCounters->mTxDataPoll); wpanStats->set_mac_tx_beacon(linkCounters->mTxBeacon); wpanStats->set_mac_tx_beacon_req(linkCounters->mTxBeaconRequest); wpanStats->set_mac_tx_other_pkt(linkCounters->mTxOther); wpanStats->set_mac_tx_retry(linkCounters->mTxRetry); wpanStats->set_mac_rx_data(linkCounters->mRxData); wpanStats->set_mac_rx_data_poll(linkCounters->mRxDataPoll); wpanStats->set_mac_rx_beacon(linkCounters->mRxBeacon); wpanStats->set_mac_rx_beacon_req(linkCounters->mRxBeaconRequest); wpanStats->set_mac_rx_other_pkt(linkCounters->mRxOther); wpanStats->set_mac_rx_filter_whitelist(linkCounters->mRxAddressFiltered); wpanStats->set_mac_rx_filter_dest_addr(linkCounters->mRxDestAddrFiltered); wpanStats->set_mac_tx_fail_cca(linkCounters->mTxErrCca); wpanStats->set_mac_rx_fail_decrypt(linkCounters->mRxErrSec); wpanStats->set_mac_rx_fail_no_frame(linkCounters->mRxErrNoFrame); wpanStats->set_mac_rx_fail_unknown_neighbor(linkCounters->mRxErrUnknownNeighbor); wpanStats->set_mac_rx_fail_invalid_src_addr(linkCounters->mRxErrInvalidSrcAddr); wpanStats->set_mac_rx_fail_fcs(linkCounters->mRxErrFcs); wpanStats->set_mac_rx_fail_other(linkCounters->mRxErrOther); } { const otIpCounters *ipCounters = otThreadGetIp6Counters(mInstance); wpanStats->set_ip_tx_success(ipCounters->mTxSuccess); wpanStats->set_ip_rx_success(ipCounters->mRxSuccess); wpanStats->set_ip_tx_failure(ipCounters->mTxFailure); wpanStats->set_ip_rx_failure(ipCounters->mRxFailure); } // End of WpanStats section. { // Begin of WpanTopoFull section. auto wpanTopoFull = telemetryData.mutable_wpan_topo_full(); uint16_t rloc16 = otThreadGetRloc16(mInstance); wpanTopoFull->set_rloc16(rloc16); { otRouterInfo info; if (otThreadGetRouterInfo(mInstance, rloc16, &info) == OT_ERROR_NONE) { wpanTopoFull->set_router_id(info.mRouterId); } else { error = OT_ERROR_FAILED; } } otNeighborInfoIterator iter = OT_NEIGHBOR_INFO_ITERATOR_INIT; otNeighborInfo neighborInfo; while (otThreadGetNextNeighborInfo(mInstance, &iter, &neighborInfo) == OT_ERROR_NONE) { neighborTable.push_back(neighborInfo); } wpanTopoFull->set_neighbor_table_size(neighborTable.size()); uint16_t childIndex = 0; otChildInfo childInfo; std::vector childTable; while (otThreadGetChildInfoByIndex(mInstance, childIndex, &childInfo) == OT_ERROR_NONE) { childTable.push_back(childInfo); childIndex++; } wpanTopoFull->set_child_table_size(childTable.size()); { struct otLeaderData leaderData; if (otThreadGetLeaderData(mInstance, &leaderData) == OT_ERROR_NONE) { wpanTopoFull->set_leader_router_id(leaderData.mLeaderRouterId); wpanTopoFull->set_leader_weight(leaderData.mWeighting); wpanTopoFull->set_network_data_version(leaderData.mDataVersion); wpanTopoFull->set_stable_network_data_version(leaderData.mStableDataVersion); } else { error = OT_ERROR_FAILED; } } uint8_t weight = otThreadGetLocalLeaderWeight(mInstance); wpanTopoFull->set_leader_local_weight(weight); uint32_t partitionId = otThreadGetPartitionId(mInstance); wpanTopoFull->set_partition_id(partitionId); static constexpr size_t kNetworkDataMaxSize = 255; { uint8_t data[kNetworkDataMaxSize]; uint8_t len = sizeof(data); std::vector networkData; if (otNetDataGet(mInstance, /*stable=*/false, data, &len) == OT_ERROR_NONE) { networkData = std::vector(&data[0], &data[len]); wpanTopoFull->set_network_data(std::string(networkData.begin(), networkData.end())); } else { error = OT_ERROR_FAILED; } } { uint8_t data[kNetworkDataMaxSize]; uint8_t len = sizeof(data); std::vector networkData; if (otNetDataGet(mInstance, /*stable=*/true, data, &len) == OT_ERROR_NONE) { networkData = std::vector(&data[0], &data[len]); wpanTopoFull->set_stable_network_data(std::string(networkData.begin(), networkData.end())); } else { error = OT_ERROR_FAILED; } } int8_t rssi = otPlatRadioGetRssi(mInstance); wpanTopoFull->set_instant_rssi(rssi); const otExtendedPanId *extPanId = otThreadGetExtendedPanId(mInstance); uint64_t extPanIdVal; extPanIdVal = ConvertOpenThreadUint64(extPanId->m8); wpanTopoFull->set_extended_pan_id(extPanIdVal); #if OTBR_ENABLE_BORDER_ROUTING wpanTopoFull->set_peer_br_count(otBorderRoutingCountPeerBrs(mInstance, /*minAge=*/nullptr)); #endif // End of WpanTopoFull section. // Begin of TopoEntry section. std::map childMap; for (const otChildInfo &childInfo : childTable) { auto pair = childMap.insert({childInfo.mRloc16, &childInfo}); if (!pair.second) { // This shouldn't happen, so log an error. It doesn't matter which // duplicate is kept. otbrLogErr("Children with duplicate RLOC16 found: 0x%04x", static_cast(childInfo.mRloc16)); } } for (const otNeighborInfo &neighborInfo : neighborTable) { auto topoEntry = telemetryData.add_topo_entries(); topoEntry->set_rloc16(neighborInfo.mRloc16); topoEntry->mutable_age()->set_seconds(neighborInfo.mAge); topoEntry->set_link_quality_in(neighborInfo.mLinkQualityIn); topoEntry->set_average_rssi(neighborInfo.mAverageRssi); topoEntry->set_last_rssi(neighborInfo.mLastRssi); topoEntry->set_link_frame_counter(neighborInfo.mLinkFrameCounter); topoEntry->set_mle_frame_counter(neighborInfo.mMleFrameCounter); topoEntry->set_rx_on_when_idle(neighborInfo.mRxOnWhenIdle); topoEntry->set_secure_data_request(true); topoEntry->set_full_function(neighborInfo.mFullThreadDevice); topoEntry->set_full_network_data(neighborInfo.mFullNetworkData); topoEntry->set_mac_frame_error_rate(static_cast(neighborInfo.mFrameErrorRate) / 0xffff); topoEntry->set_ip_message_error_rate(static_cast(neighborInfo.mMessageErrorRate) / 0xffff); topoEntry->set_version(neighborInfo.mVersion); if (!neighborInfo.mIsChild) { continue; } auto it = childMap.find(neighborInfo.mRloc16); if (it == childMap.end()) { otbrLogErr("Neighbor 0x%04x not found in child table", static_cast(neighborInfo.mRloc16)); continue; } const otChildInfo *childInfo = it->second; topoEntry->set_is_child(true); topoEntry->mutable_timeout()->set_seconds(childInfo->mTimeout); topoEntry->set_network_data_version(childInfo->mNetworkDataVersion); } // End of TopoEntry section. } { // Begin of WpanBorderRouter section. auto wpanBorderRouter = telemetryData.mutable_wpan_border_router(); // Begin of BorderRoutingCounters section. auto borderRoutingCouters = wpanBorderRouter->mutable_border_routing_counters(); const otBorderRoutingCounters *otBorderRoutingCounters = otIp6GetBorderRoutingCounters(mInstance); borderRoutingCouters->mutable_inbound_unicast()->set_packet_count( otBorderRoutingCounters->mInboundUnicast.mPackets); borderRoutingCouters->mutable_inbound_unicast()->set_byte_count( otBorderRoutingCounters->mInboundUnicast.mBytes); borderRoutingCouters->mutable_inbound_multicast()->set_packet_count( otBorderRoutingCounters->mInboundMulticast.mPackets); borderRoutingCouters->mutable_inbound_multicast()->set_byte_count( otBorderRoutingCounters->mInboundMulticast.mBytes); borderRoutingCouters->mutable_outbound_unicast()->set_packet_count( otBorderRoutingCounters->mOutboundUnicast.mPackets); borderRoutingCouters->mutable_outbound_unicast()->set_byte_count( otBorderRoutingCounters->mOutboundUnicast.mBytes); borderRoutingCouters->mutable_outbound_multicast()->set_packet_count( otBorderRoutingCounters->mOutboundMulticast.mPackets); borderRoutingCouters->mutable_outbound_multicast()->set_byte_count( otBorderRoutingCounters->mOutboundMulticast.mBytes); borderRoutingCouters->set_ra_rx(otBorderRoutingCounters->mRaRx); borderRoutingCouters->set_ra_tx_success(otBorderRoutingCounters->mRaTxSuccess); borderRoutingCouters->set_ra_tx_failure(otBorderRoutingCounters->mRaTxFailure); borderRoutingCouters->set_rs_rx(otBorderRoutingCounters->mRsRx); borderRoutingCouters->set_rs_tx_success(otBorderRoutingCounters->mRsTxSuccess); borderRoutingCouters->set_rs_tx_failure(otBorderRoutingCounters->mRsTxFailure); borderRoutingCouters->mutable_inbound_internet()->set_packet_count( otBorderRoutingCounters->mInboundInternet.mPackets); borderRoutingCouters->mutable_inbound_internet()->set_byte_count( otBorderRoutingCounters->mInboundInternet.mBytes); borderRoutingCouters->mutable_outbound_internet()->set_packet_count( otBorderRoutingCounters->mOutboundInternet.mPackets); borderRoutingCouters->mutable_outbound_internet()->set_byte_count( otBorderRoutingCounters->mOutboundInternet.mBytes); #if OTBR_ENABLE_NAT64 { auto nat64IcmpCounters = borderRoutingCouters->mutable_nat64_protocol_counters()->mutable_icmp(); auto nat64UdpCounters = borderRoutingCouters->mutable_nat64_protocol_counters()->mutable_udp(); auto nat64TcpCounters = borderRoutingCouters->mutable_nat64_protocol_counters()->mutable_tcp(); otNat64ProtocolCounters otCounters; otNat64GetCounters(mInstance, &otCounters); nat64IcmpCounters->set_ipv4_to_ipv6_packets(otCounters.mIcmp.m4To6Packets); nat64IcmpCounters->set_ipv4_to_ipv6_bytes(otCounters.mIcmp.m4To6Bytes); nat64IcmpCounters->set_ipv6_to_ipv4_packets(otCounters.mIcmp.m6To4Packets); nat64IcmpCounters->set_ipv6_to_ipv4_bytes(otCounters.mIcmp.m6To4Bytes); nat64UdpCounters->set_ipv4_to_ipv6_packets(otCounters.mUdp.m4To6Packets); nat64UdpCounters->set_ipv4_to_ipv6_bytes(otCounters.mUdp.m4To6Bytes); nat64UdpCounters->set_ipv6_to_ipv4_packets(otCounters.mUdp.m6To4Packets); nat64UdpCounters->set_ipv6_to_ipv4_bytes(otCounters.mUdp.m6To4Bytes); nat64TcpCounters->set_ipv4_to_ipv6_packets(otCounters.mTcp.m4To6Packets); nat64TcpCounters->set_ipv4_to_ipv6_bytes(otCounters.mTcp.m4To6Bytes); nat64TcpCounters->set_ipv6_to_ipv4_packets(otCounters.mTcp.m6To4Packets); nat64TcpCounters->set_ipv6_to_ipv4_bytes(otCounters.mTcp.m6To4Bytes); } { auto errorCounters = borderRoutingCouters->mutable_nat64_error_counters(); otNat64ErrorCounters otCounters; otNat64GetErrorCounters(mInstance, &otCounters); errorCounters->mutable_unknown()->set_ipv4_to_ipv6_packets( otCounters.mCount4To6[OT_NAT64_DROP_REASON_UNKNOWN]); errorCounters->mutable_unknown()->set_ipv6_to_ipv4_packets( otCounters.mCount6To4[OT_NAT64_DROP_REASON_UNKNOWN]); errorCounters->mutable_illegal_packet()->set_ipv4_to_ipv6_packets( otCounters.mCount4To6[OT_NAT64_DROP_REASON_ILLEGAL_PACKET]); errorCounters->mutable_illegal_packet()->set_ipv6_to_ipv4_packets( otCounters.mCount6To4[OT_NAT64_DROP_REASON_ILLEGAL_PACKET]); errorCounters->mutable_unsupported_protocol()->set_ipv4_to_ipv6_packets( otCounters.mCount4To6[OT_NAT64_DROP_REASON_UNSUPPORTED_PROTO]); errorCounters->mutable_unsupported_protocol()->set_ipv6_to_ipv4_packets( otCounters.mCount6To4[OT_NAT64_DROP_REASON_UNSUPPORTED_PROTO]); errorCounters->mutable_no_mapping()->set_ipv4_to_ipv6_packets( otCounters.mCount4To6[OT_NAT64_DROP_REASON_NO_MAPPING]); errorCounters->mutable_no_mapping()->set_ipv6_to_ipv4_packets( otCounters.mCount6To4[OT_NAT64_DROP_REASON_NO_MAPPING]); } #endif // OTBR_ENABLE_NAT64 // End of BorderRoutingCounters section. #if OTBR_ENABLE_TREL // Begin of TrelInfo section. { auto trelInfo = wpanBorderRouter->mutable_trel_info(); auto otTrelCounters = otTrelGetCounters(mInstance); auto trelCounters = trelInfo->mutable_counters(); trelInfo->set_is_trel_enabled(otTrelIsEnabled(mInstance)); trelInfo->set_num_trel_peers(otTrelGetNumberOfPeers(mInstance)); trelCounters->set_trel_tx_packets(otTrelCounters->mTxPackets); trelCounters->set_trel_tx_bytes(otTrelCounters->mTxBytes); trelCounters->set_trel_tx_packets_failed(otTrelCounters->mTxFailure); trelCounters->set_tre_rx_packets(otTrelCounters->mRxPackets); trelCounters->set_trel_rx_bytes(otTrelCounters->mRxBytes); } // End of TrelInfo section. #endif // OTBR_ENABLE_TREL #if OTBR_ENABLE_BORDER_ROUTING RetrieveInfraLinkInfo(*wpanBorderRouter->mutable_infra_link_info()); RetrieveExternalRouteInfo(*wpanBorderRouter->mutable_external_route_info()); #endif #if OTBR_ENABLE_SRP_ADVERTISING_PROXY // Begin of SrpServerInfo section. { auto srpServer = wpanBorderRouter->mutable_srp_server(); otSrpServerLeaseInfo leaseInfo; const otSrpServerHost *host = nullptr; const otSrpServerResponseCounters *responseCounters = otSrpServerGetResponseCounters(mInstance); srpServer->set_state(SrpServerStateFromOtSrpServerState(otSrpServerGetState(mInstance))); srpServer->set_port(otSrpServerGetPort(mInstance)); srpServer->set_address_mode( SrpServerAddressModeFromOtSrpServerAddressMode(otSrpServerGetAddressMode(mInstance))); auto srpServerHosts = srpServer->mutable_hosts(); auto srpServerServices = srpServer->mutable_services(); auto srpServerResponseCounters = srpServer->mutable_response_counters(); while ((host = otSrpServerGetNextHost(mInstance, host))) { const otSrpServerService *service = nullptr; if (otSrpServerHostIsDeleted(host)) { srpServerHosts->set_deleted_count(srpServerHosts->deleted_count() + 1); } else { srpServerHosts->set_fresh_count(srpServerHosts->fresh_count() + 1); otSrpServerHostGetLeaseInfo(host, &leaseInfo); srpServerHosts->set_lease_time_total_ms(srpServerHosts->lease_time_total_ms() + leaseInfo.mLease); srpServerHosts->set_key_lease_time_total_ms(srpServerHosts->key_lease_time_total_ms() + leaseInfo.mKeyLease); srpServerHosts->set_remaining_lease_time_total_ms(srpServerHosts->remaining_lease_time_total_ms() + leaseInfo.mRemainingLease); srpServerHosts->set_remaining_key_lease_time_total_ms( srpServerHosts->remaining_key_lease_time_total_ms() + leaseInfo.mRemainingKeyLease); } while ((service = otSrpServerHostGetNextService(host, service))) { if (otSrpServerServiceIsDeleted(service)) { srpServerServices->set_deleted_count(srpServerServices->deleted_count() + 1); } else { srpServerServices->set_fresh_count(srpServerServices->fresh_count() + 1); otSrpServerServiceGetLeaseInfo(service, &leaseInfo); srpServerServices->set_lease_time_total_ms(srpServerServices->lease_time_total_ms() + leaseInfo.mLease); srpServerServices->set_key_lease_time_total_ms(srpServerServices->key_lease_time_total_ms() + leaseInfo.mKeyLease); srpServerServices->set_remaining_lease_time_total_ms( srpServerServices->remaining_lease_time_total_ms() + leaseInfo.mRemainingLease); srpServerServices->set_remaining_key_lease_time_total_ms( srpServerServices->remaining_key_lease_time_total_ms() + leaseInfo.mRemainingKeyLease); } } } srpServerResponseCounters->set_success_count(responseCounters->mSuccess); srpServerResponseCounters->set_server_failure_count(responseCounters->mServerFailure); srpServerResponseCounters->set_format_error_count(responseCounters->mFormatError); srpServerResponseCounters->set_name_exists_count(responseCounters->mNameExists); srpServerResponseCounters->set_refused_count(responseCounters->mRefused); srpServerResponseCounters->set_other_count(responseCounters->mOther); } // End of SrpServerInfo section. #endif // OTBR_ENABLE_SRP_ADVERTISING_PROXY #if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY // Begin of DnsServerInfo section. { auto dnsServer = wpanBorderRouter->mutable_dns_server(); auto dnsServerResponseCounters = dnsServer->mutable_response_counters(); otDnssdCounters otDnssdCounters = *otDnssdGetCounters(mInstance); dnsServerResponseCounters->set_success_count(otDnssdCounters.mSuccessResponse); dnsServerResponseCounters->set_server_failure_count(otDnssdCounters.mServerFailureResponse); dnsServerResponseCounters->set_format_error_count(otDnssdCounters.mFormatErrorResponse); dnsServerResponseCounters->set_name_error_count(otDnssdCounters.mNameErrorResponse); dnsServerResponseCounters->set_not_implemented_count(otDnssdCounters.mNotImplementedResponse); dnsServerResponseCounters->set_other_count(otDnssdCounters.mOtherResponse); // The counters of queries, responses, failures handled by upstream DNS server. dnsServerResponseCounters->set_upstream_dns_queries(otDnssdCounters.mUpstreamDnsCounters.mQueries); dnsServerResponseCounters->set_upstream_dns_responses(otDnssdCounters.mUpstreamDnsCounters.mResponses); dnsServerResponseCounters->set_upstream_dns_failures(otDnssdCounters.mUpstreamDnsCounters.mFailures); dnsServer->set_resolved_by_local_srp_count(otDnssdCounters.mResolvedBySrp); #if OTBR_ENABLE_DNS_UPSTREAM_QUERY dnsServer->set_upstream_dns_query_state( otDnssdUpstreamQueryIsEnabled(mInstance) ? threadnetwork::TelemetryData::UPSTREAMDNS_QUERY_STATE_ENABLED : threadnetwork::TelemetryData::UPSTREAMDNS_QUERY_STATE_DISABLED); #endif // OTBR_ENABLE_DNS_UPSTREAM_QUERY } // End of DnsServerInfo section. #endif // OTBR_ENABLE_DNSSD_DISCOVERY_PROXY // Start of MdnsInfo section. if (aPublisher != nullptr) { auto mdns = wpanBorderRouter->mutable_mdns(); const MdnsTelemetryInfo &mdnsInfo = aPublisher->GetMdnsTelemetryInfo(); CopyMdnsResponseCounters(mdnsInfo.mHostRegistrations, mdns->mutable_host_registration_responses()); CopyMdnsResponseCounters(mdnsInfo.mServiceRegistrations, mdns->mutable_service_registration_responses()); CopyMdnsResponseCounters(mdnsInfo.mHostResolutions, mdns->mutable_host_resolution_responses()); CopyMdnsResponseCounters(mdnsInfo.mServiceResolutions, mdns->mutable_service_resolution_responses()); mdns->set_host_registration_ema_latency_ms(mdnsInfo.mHostRegistrationEmaLatency); mdns->set_service_registration_ema_latency_ms(mdnsInfo.mServiceRegistrationEmaLatency); mdns->set_host_resolution_ema_latency_ms(mdnsInfo.mHostResolutionEmaLatency); mdns->set_service_resolution_ema_latency_ms(mdnsInfo.mServiceResolutionEmaLatency); } // End of MdnsInfo section. #if OTBR_ENABLE_NAT64 // Start of BorderRoutingNat64State section. { auto nat64State = wpanBorderRouter->mutable_nat64_state(); nat64State->set_prefix_manager_state(Nat64StateFromOtNat64State(otNat64GetPrefixManagerState(mInstance))); nat64State->set_translator_state(Nat64StateFromOtNat64State(otNat64GetTranslatorState(mInstance))); } // End of BorderRoutingNat64State section. // Start of Nat64Mapping section. { otNat64AddressMappingIterator iterator; otNat64AddressMapping otMapping; Sha256::Hash hash; Sha256 sha256; otNat64InitAddressMappingIterator(mInstance, &iterator); while (otNat64GetNextAddressMapping(mInstance, &iterator, &otMapping) == OT_ERROR_NONE) { auto nat64Mapping = wpanBorderRouter->add_nat64_mappings(); auto nat64MappingCounters = nat64Mapping->mutable_counters(); nat64Mapping->set_mapping_id(otMapping.mId); CopyNat64TrafficCounters(otMapping.mCounters.mTcp, nat64MappingCounters->mutable_tcp()); CopyNat64TrafficCounters(otMapping.mCounters.mUdp, nat64MappingCounters->mutable_udp()); CopyNat64TrafficCounters(otMapping.mCounters.mIcmp, nat64MappingCounters->mutable_icmp()); sha256.Start(); sha256.Update(otMapping.mIp6.mFields.m8, sizeof(otMapping.mIp6.mFields.m8)); sha256.Update(mNat64PdCommonSalt, sizeof(mNat64PdCommonSalt)); sha256.Finish(hash); nat64Mapping->mutable_hashed_ipv6_address()->append(reinterpret_cast(hash.GetBytes()), Sha256::Hash::kSize); // Remaining time is not included in the telemetry } } // End of Nat64Mapping section. #endif // OTBR_ENABLE_NAT64 #if OTBR_ENABLE_DHCP6_PD RetrievePdInfo(wpanBorderRouter); #endif // OTBR_ENABLE_DHCP6_PD #if OTBR_ENABLE_BORDER_AGENT RetrieveBorderAgentInfo(wpanBorderRouter->mutable_border_agent_info()); #endif // OTBR_ENABLE_BORDER_AGENT // End of WpanBorderRouter section. // Start of WpanRcp section. { auto wpanRcp = telemetryData.mutable_wpan_rcp(); const otRadioSpinelMetrics *otRadioSpinelMetrics = otSysGetRadioSpinelMetrics(); auto rcpStabilityStatistics = wpanRcp->mutable_rcp_stability_statistics(); if (otRadioSpinelMetrics != nullptr) { rcpStabilityStatistics->set_rcp_timeout_count(otRadioSpinelMetrics->mRcpTimeoutCount); rcpStabilityStatistics->set_rcp_reset_count(otRadioSpinelMetrics->mRcpUnexpectedResetCount); rcpStabilityStatistics->set_rcp_restoration_count(otRadioSpinelMetrics->mRcpRestorationCount); rcpStabilityStatistics->set_spinel_parse_error_count(otRadioSpinelMetrics->mSpinelParseErrorCount); } // TODO: provide rcp_firmware_update_count info. rcpStabilityStatistics->set_thread_stack_uptime(otInstanceGetUptime(mInstance)); const otRcpInterfaceMetrics *otRcpInterfaceMetrics = otSysGetRcpInterfaceMetrics(); if (otRcpInterfaceMetrics != nullptr) { auto rcpInterfaceStatistics = wpanRcp->mutable_rcp_interface_statistics(); rcpInterfaceStatistics->set_rcp_interface_type(otRcpInterfaceMetrics->mRcpInterfaceType); rcpInterfaceStatistics->set_transferred_frames_count(otRcpInterfaceMetrics->mTransferredFrameCount); rcpInterfaceStatistics->set_transferred_valid_frames_count( otRcpInterfaceMetrics->mTransferredValidFrameCount); rcpInterfaceStatistics->set_transferred_garbage_frames_count( otRcpInterfaceMetrics->mTransferredGarbageFrameCount); rcpInterfaceStatistics->set_rx_frames_count(otRcpInterfaceMetrics->mRxFrameCount); rcpInterfaceStatistics->set_rx_bytes_count(otRcpInterfaceMetrics->mRxFrameByteCount); rcpInterfaceStatistics->set_tx_frames_count(otRcpInterfaceMetrics->mTxFrameCount); rcpInterfaceStatistics->set_tx_bytes_count(otRcpInterfaceMetrics->mTxFrameByteCount); } } // End of WpanRcp section. // Start of CoexMetrics section. { auto coexMetrics = telemetryData.mutable_coex_metrics(); otRadioCoexMetrics otRadioCoexMetrics; if (otPlatRadioGetCoexMetrics(mInstance, &otRadioCoexMetrics) == OT_ERROR_NONE) { coexMetrics->set_count_tx_request(otRadioCoexMetrics.mNumTxRequest); coexMetrics->set_count_tx_grant_immediate(otRadioCoexMetrics.mNumTxGrantImmediate); coexMetrics->set_count_tx_grant_wait(otRadioCoexMetrics.mNumTxGrantWait); coexMetrics->set_count_tx_grant_wait_activated(otRadioCoexMetrics.mNumTxGrantWaitActivated); coexMetrics->set_count_tx_grant_wait_timeout(otRadioCoexMetrics.mNumTxGrantWaitTimeout); coexMetrics->set_count_tx_grant_deactivated_during_request( otRadioCoexMetrics.mNumTxGrantDeactivatedDuringRequest); coexMetrics->set_tx_average_request_to_grant_time_us(otRadioCoexMetrics.mAvgTxRequestToGrantTime); coexMetrics->set_count_rx_request(otRadioCoexMetrics.mNumRxRequest); coexMetrics->set_count_rx_grant_immediate(otRadioCoexMetrics.mNumRxGrantImmediate); coexMetrics->set_count_rx_grant_wait(otRadioCoexMetrics.mNumRxGrantWait); coexMetrics->set_count_rx_grant_wait_activated(otRadioCoexMetrics.mNumRxGrantWaitActivated); coexMetrics->set_count_rx_grant_wait_timeout(otRadioCoexMetrics.mNumRxGrantWaitTimeout); coexMetrics->set_count_rx_grant_deactivated_during_request( otRadioCoexMetrics.mNumRxGrantDeactivatedDuringRequest); coexMetrics->set_count_rx_grant_none(otRadioCoexMetrics.mNumRxGrantNone); coexMetrics->set_rx_average_request_to_grant_time_us(otRadioCoexMetrics.mAvgRxRequestToGrantTime); } else { error = OT_ERROR_FAILED; } } // End of CoexMetrics section. } #if OTBR_ENABLE_LINK_METRICS_TELEMETRY { auto lowPowerMetrics = telemetryData.mutable_low_power_metrics(); // Begin of Link Metrics section. for (const otNeighborInfo &neighborInfo : neighborTable) { otError query_error; otLinkMetricsValues values; query_error = otLinkMetricsManagerGetMetricsValueByExtAddr(mInstance, &neighborInfo.mExtAddress, &values); // Some neighbors don't support Link Metrics Subject feature. So it's expected that some other errors // are returned. if (query_error == OT_ERROR_NONE) { auto linkMetricsStats = lowPowerMetrics->add_link_metrics_entries(); linkMetricsStats->set_link_margin(values.mLinkMarginValue); linkMetricsStats->set_rssi(values.mRssiValue); } } } #endif // OTBR_ENABLE_LINK_METRICS_TELEMETRY return error; } #endif // OTBR_ENABLE_TELEMETRY_DATA_API otError ThreadHelper::ProcessDatasetForMigration(otOperationalDatasetTlvs &aDatasetTlvs, uint32_t aDelayMilli) { otError error = OT_ERROR_NONE; Tlv *tlv; timespec currentTime; uint64_t pendingTimestamp = 0; VerifyOrExit(FindTlv(OT_MESHCOP_TLV_PENDINGTIMESTAMP, aDatasetTlvs.mTlvs, aDatasetTlvs.mLength) == nullptr, error = OT_ERROR_INVALID_ARGS); VerifyOrExit(FindTlv(OT_MESHCOP_TLV_DELAYTIMER, aDatasetTlvs.mTlvs, aDatasetTlvs.mLength) == nullptr, error = OT_ERROR_INVALID_ARGS); // There must be sufficient space for a Pending Timestamp TLV and a Delay Timer TLV. VerifyOrExit( static_cast(aDatasetTlvs.mLength + (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint64_t)) // Pending Timestamp TLV (10 bytes) + (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t))) // Delay Timer TLV (6 bytes) <= int{sizeof(aDatasetTlvs.mTlvs)}, error = OT_ERROR_INVALID_ARGS); tlv = reinterpret_cast(aDatasetTlvs.mTlvs + aDatasetTlvs.mLength); /* * Pending Timestamp TLV * * | Type | Value | Timestamp Seconds | Timestamp Ticks | U bit | * | 8 | 8 | 48 | 15 | 1 | */ tlv->SetType(OT_MESHCOP_TLV_PENDINGTIMESTAMP); clock_gettime(CLOCK_REALTIME, ¤tTime); pendingTimestamp |= (static_cast(currentTime.tv_sec) << 16); // Set the 48 bits of Timestamp seconds. pendingTimestamp |= (((static_cast(currentTime.tv_nsec) * 32768 / 1000000000) & 0x7fff) << 1); // Set the 15 bits of Timestamp ticks, the fractional Unix Time value in 32.768 kHz // resolution. Leave the U-bit unset. tlv->SetValue(pendingTimestamp); tlv = tlv->GetNext(); tlv->SetType(OT_MESHCOP_TLV_DELAYTIMER); tlv->SetValue(aDelayMilli); aDatasetTlvs.mLength = reinterpret_cast(tlv->GetNext()) - aDatasetTlvs.mTlvs; exit: return error; } } // namespace agent } // namespace otbr