| /* |
| * 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 <assert.h> |
| #include <limits.h> |
| #include <string.h> |
| #include <time.h> |
| |
| #include <openthread/border_agent.h> |
| #include <openthread/border_router.h> |
| #include <openthread/channel_manager.h> |
| #include <openthread/dataset_ftd.h> |
| #if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY |
| #include <openthread/dnssd_server.h> |
| #endif |
| #include <openthread/jam_detection.h> |
| #include <openthread/joiner.h> |
| #if OTBR_ENABLE_NAT64 |
| #include <openthread/crypto.h> |
| #include <openthread/nat64.h> |
| #include "utils/sha256.hpp" |
| #endif |
| #if OTBR_ENABLE_DHCP6_PD |
| #include "utils/sha256.hpp" |
| #endif |
| #if OTBR_ENABLE_LINK_METRICS_TELEMETRY |
| #include <openthread/link_metrics.h> |
| #endif |
| #if OTBR_ENABLE_SRP_ADVERTISING_PROXY |
| #include <openthread/srp_server.h> |
| #endif |
| #include <openthread/thread_ftd.h> |
| #if OTBR_ENABLE_TREL |
| #include <openthread/trel.h> |
| #endif |
| #include <net/if.h> |
| #include <openthread/platform/radio.h> |
| |
| #include "common/byteswap.hpp" |
| #include "common/code_utils.hpp" |
| #include "common/logging.hpp" |
| #include "common/tlv.hpp" |
| #include "host/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<const Tlv *>(aTlvs); |
| reinterpret_cast<const uint8_t *>(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::Host::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<std::string, std::vector<uint8_t>> 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<uint16_t>(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<uint8_t *>(aBuf); |
| |
| for (size_t i = 0; i < size; i++) |
| { |
| buf[i] = static_cast<uint8_t>(dist(mRandomDevice)); |
| } |
| } |
| |
| void ThreadHelper::ActiveScanHandler(otActiveScanResult *aResult, void *aThreadHelper) |
| { |
| ThreadHelper *helper = static_cast<ThreadHelper *>(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 && OTBR_ENABLE_BORDER_ROUTING |
| 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<ThreadHelper *>(aThreadHelper); |
| |
| helper->BorderRoutingDhcp6PdCallback(aState); |
| } |
| |
| void ThreadHelper::BorderRoutingDhcp6PdCallback(otBorderRoutingDhcp6PdState aState) |
| { |
| if (mDhcp6PdCallback != nullptr) |
| { |
| mDhcp6PdCallback(aState); |
| } |
| } |
| #endif // OTBR_ENABLE_DHCP6_PD && OTBR_ENABLE_BORDER_ROUTING |
| |
| void ThreadHelper::EnergyScanCallback(otEnergyScanResult *aResult, void *aThreadHelper) |
| { |
| ThreadHelper *helper = static_cast<ThreadHelper *>(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<unsigned int>(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<uint8_t>((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<uint8_t> &aNetworkKey, |
| const std::vector<uint8_t> &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<ThreadHelper *>(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<uint8_t> &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<ThreadHelper *>(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<ThreadHelper *>(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<uint8_t> hashedPdHeader = {0x20, 0x01, 0x0d, 0xb8}; |
| std::vector<uint8_t> hashedPdTailer = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
| std::vector<uint8_t> 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<const char *>(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<otNeighborInfo> 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<float>(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<otChildInfo> 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<uint8_t> networkData; |
| |
| if (otNetDataGet(mInstance, /*stable=*/false, data, &len) == OT_ERROR_NONE) |
| { |
| networkData = std::vector<uint8_t>(&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<uint8_t> networkData; |
| |
| if (otNetDataGet(mInstance, /*stable=*/true, data, &len) == OT_ERROR_NONE) |
| { |
| networkData = std::vector<uint8_t>(&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<uint16_t, const otChildInfo *> 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<int>(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<float>(neighborInfo.mFrameErrorRate) / 0xffff); |
| topoEntry->set_ip_message_error_rate(static_cast<float>(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<int>(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<const char *>(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<int>(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<Tlv *>(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<uint64_t>(currentTime.tv_sec) << 16); // Set the 48 bits of Timestamp seconds. |
| pendingTimestamp |= (((static_cast<uint64_t>(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<uint8_t *>(tlv->GetNext()) - aDatasetTlvs.mTlvs; |
| |
| exit: |
| return error; |
| } |
| |
| } // namespace agent |
| } // namespace otbr |