| /* |
| * |
| * Copyright (c) 2020 Project CHIP Authors |
| * Copyright (c) 2019 Nest Labs, Inc. |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * Contains non-inline method definitions for the |
| * GenericThreadStackManagerImpl_OpenThread<> template. |
| */ |
| |
| #ifndef GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_IPP |
| #define GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_IPP |
| |
| #include <cassert> |
| |
| #include <openthread/cli.h> |
| #include <openthread/dataset.h> |
| #include <openthread/joiner.h> |
| #include <openthread/link.h> |
| #include <openthread/netdata.h> |
| #include <openthread/tasklet.h> |
| #include <openthread/thread.h> |
| |
| #if CHIP_DEVICE_CONFIG_THREAD_FTD |
| #include <openthread/dataset_ftd.h> |
| #include <openthread/thread_ftd.h> |
| #endif |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT |
| #include <openthread/srp_client.h> |
| #endif |
| |
| #include <app/AttributeAccessInterface.h> |
| #include <app/clusters/network-commissioning/network-commissioning.h> |
| #include <lib/core/CHIPEncoding.h> |
| #include <lib/support/CHIPMemString.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/FixedBufferAllocator.h> |
| #include <lib/support/ThreadOperationalDataset.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| #include <platform/OpenThread/GenericNetworkCommissioningThreadDriver.h> |
| #include <platform/OpenThread/GenericThreadStackManagerImpl_OpenThread.h> |
| #include <platform/OpenThread/OpenThreadUtils.h> |
| #include <platform/ThreadStackManager.h> |
| #include <platform/internal/CHIPDeviceLayerInternal.h> |
| |
| #include <app-common/zap-generated/ids/Attributes.h> |
| #include <app-common/zap-generated/ids/Clusters.h> |
| #include <app/MessageDef/AttributeDataIB.h> |
| #include <app/data-model/Encode.h> |
| |
| #include <limits> |
| #if CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT |
| #include <app/server/Server.h> |
| #endif // CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT |
| extern "C" void otSysProcessDrivers(otInstance * aInstance); |
| |
| #if CHIP_DEVICE_CONFIG_THREAD_ENABLE_CLI |
| extern "C" void otAppCliInit(otInstance * aInstance); |
| #endif |
| |
| namespace chip { |
| namespace DeviceLayer { |
| namespace Internal { |
| |
| // Network commissioning |
| namespace { |
| #ifndef _NO_NETWORK_COMMISSIONING_DRIVER_ |
| NetworkCommissioning::GenericThreadDriver sGenericThreadDriver; |
| app::Clusters::NetworkCommissioning::Instance sThreadNetworkCommissioningInstance(0 /* Endpoint Id */, &sGenericThreadDriver); |
| #endif |
| |
| void initNetworkCommissioningThreadDriver(void) |
| { |
| #ifndef _NO_NETWORK_COMMISSIONING_DRIVER_ |
| sThreadNetworkCommissioningInstance.Init(); |
| #endif |
| } |
| |
| NetworkCommissioning::ThreadScanResponse * sScanResult; |
| NetworkCommissioning::otScanResponseIterator<NetworkCommissioning::ThreadScanResponse> mScanResponseIter(sScanResult); |
| } // namespace |
| |
| /** |
| * Called by OpenThread to alert the ThreadStackManager of a change in the state of the Thread stack. |
| * |
| * By default, applications never need to call this method directly. However, applications that |
| * wish to receive OpenThread state change call-backs directly from OpenThread (e.g. by calling |
| * otSetStateChangedCallback() with their own callback function) can call this method to pass |
| * state change events to the ThreadStackManager. |
| */ |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::OnOpenThreadStateChange(uint32_t flags, void * context) |
| { |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kThreadStateChange; |
| event.ThreadStateChange.RoleChanged = (flags & OT_CHANGED_THREAD_ROLE) != 0; |
| event.ThreadStateChange.AddressChanged = (flags & (OT_CHANGED_IP6_ADDRESS_ADDED | OT_CHANGED_IP6_ADDRESS_REMOVED)) != 0; |
| event.ThreadStateChange.NetDataChanged = (flags & OT_CHANGED_THREAD_NETDATA) != 0; |
| event.ThreadStateChange.ChildNodesChanged = (flags & (OT_CHANGED_THREAD_CHILD_ADDED | OT_CHANGED_THREAD_CHILD_REMOVED)) != 0; |
| event.ThreadStateChange.OpenThread.Flags = flags; |
| |
| CHIP_ERROR status = PlatformMgr().PostEvent(&event); |
| if (status != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "Failed to post Thread state change: %" CHIP_ERROR_FORMAT, status.Format()); |
| } |
| |
| DeviceLayer::SystemLayer().ScheduleLambda([]() { ThreadStackMgrImpl()._UpdateNetworkStatus(); }); |
| } |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::_ProcessThreadActivity(void) |
| { |
| otTaskletsProcess(mOTInst); |
| otSysProcessDrivers(mOTInst); |
| } |
| |
| template <class ImplClass> |
| bool GenericThreadStackManagerImpl_OpenThread<ImplClass>::_HaveRouteToAddress(const Inet::IPAddress & destAddr) |
| { |
| bool res = false; |
| |
| // Lock OpenThread |
| Impl()->LockThreadStack(); |
| |
| // No routing of IPv4 over Thread. |
| VerifyOrExit(!destAddr.IsIPv4(), res = false); |
| |
| // If the device is attached to a Thread network... |
| if (IsThreadAttachedNoLock()) |
| { |
| // Link-local addresses are always presumed to be routable, provided the device is attached. |
| if (destAddr.IsIPv6LinkLocal()) |
| { |
| ExitNow(res = true); |
| } |
| |
| // Iterate over the routes known to the OpenThread stack looking for a route that covers the |
| // destination address. If found, consider the address routable. |
| // Ignore any routes advertised by this device. |
| // If the destination address is a ULA, ignore default routes. Border routers advertising |
| // default routes are not expected to be capable of routing CHIP fabric ULAs unless they |
| // advertise those routes specifically. |
| { |
| otError otErr; |
| otNetworkDataIterator routeIter = OT_NETWORK_DATA_ITERATOR_INIT; |
| otExternalRouteConfig routeConfig; |
| const bool destIsULA = destAddr.IsIPv6ULA(); |
| |
| while ((otErr = otNetDataGetNextRoute(Impl()->OTInstance(), &routeIter, &routeConfig)) == OT_ERROR_NONE) |
| { |
| const Inet::IPPrefix prefix = ToIPPrefix(routeConfig.mPrefix); |
| char addrStr[64]; |
| prefix.IPAddr.ToString(addrStr); |
| if (!routeConfig.mNextHopIsThisDevice && (!destIsULA || routeConfig.mPrefix.mLength > 0) && |
| ToIPPrefix(routeConfig.mPrefix).MatchAddress(destAddr)) |
| { |
| ExitNow(res = true); |
| } |
| } |
| } |
| } |
| |
| exit: |
| |
| // Unlock OpenThread |
| Impl()->UnlockThreadStack(); |
| |
| return res; |
| } |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::_OnPlatformEvent(const ChipDeviceEvent * event) |
| { |
| if (event->Type == DeviceEventType::kThreadStateChange) |
| { |
| #if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT |
| if (event->ThreadStateChange.AddressChanged) |
| { |
| const otSrpClientHostInfo * hostInfo = otSrpClientGetHostInfo(Impl()->OTInstance()); |
| if (hostInfo && hostInfo->mName) |
| { |
| Impl()->_SetupSrpHost(hostInfo->mName); |
| } |
| } |
| #endif |
| Impl()->LockThreadStack(); |
| |
| #if CHIP_DETAIL_LOGGING |
| |
| LogOpenThreadStateChange(mOTInst, event->ThreadStateChange.OpenThread.Flags); |
| |
| #endif // CHIP_DETAIL_LOGGING |
| |
| #if CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT |
| if (event->ThreadStateChange.RoleChanged || event->ThreadStateChange.AddressChanged) |
| { |
| bool isInterfaceUp; |
| isInterfaceUp = GenericThreadStackManagerImpl_OpenThread<ImplClass>::IsThreadInterfaceUpNoLock(); |
| // Post an event signaling the change in Thread interface connectivity state. |
| { |
| ChipDeviceEvent event; |
| event.Clear(); |
| event.Type = DeviceEventType::kThreadConnectivityChange; |
| event.ThreadConnectivityChange.Result = (isInterfaceUp) ? kConnectivity_Established : kConnectivity_Lost; |
| CHIP_ERROR status = PlatformMgr().PostEvent(&event); |
| if (status != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "Failed to post Thread connectivity change: %" CHIP_ERROR_FORMAT, status.Format()); |
| } |
| } |
| |
| // Refresh Multicast listening |
| if (GenericThreadStackManagerImpl_OpenThread<ImplClass>::IsThreadAttachedNoLock()) |
| { |
| ChipLogDetail(DeviceLayer, "Thread Attached updating Multicast address"); |
| Server::GetInstance().RejoinExistingMulticastGroups(); |
| } |
| } |
| #endif // CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT |
| Impl()->UnlockThreadStack(); |
| } |
| } |
| |
| template <class ImplClass> |
| bool GenericThreadStackManagerImpl_OpenThread<ImplClass>::_IsThreadEnabled(void) |
| { |
| otDeviceRole curRole; |
| |
| Impl()->LockThreadStack(); |
| curRole = otThreadGetDeviceRole(mOTInst); |
| Impl()->UnlockThreadStack(); |
| |
| return (curRole != OT_DEVICE_ROLE_DISABLED); |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_SetThreadEnabled(bool val) |
| { |
| otError otErr = OT_ERROR_NONE; |
| |
| Impl()->LockThreadStack(); |
| |
| bool isEnabled = (otThreadGetDeviceRole(mOTInst) != OT_DEVICE_ROLE_DISABLED); |
| bool isIp6Enabled = otIp6IsEnabled(mOTInst); |
| |
| if (val && !isIp6Enabled) |
| { |
| otErr = otIp6SetEnabled(mOTInst, val); |
| VerifyOrExit(otErr == OT_ERROR_NONE, ); |
| } |
| |
| if (val != isEnabled) |
| { |
| otErr = otThreadSetEnabled(mOTInst, val); |
| VerifyOrExit(otErr == OT_ERROR_NONE, ); |
| } |
| |
| if (!val && isIp6Enabled) |
| { |
| otErr = otIp6SetEnabled(mOTInst, val); |
| VerifyOrExit(otErr == OT_ERROR_NONE, ); |
| } |
| |
| exit: |
| Impl()->UnlockThreadStack(); |
| |
| return MapOpenThreadError(otErr); |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_SetThreadProvision(ByteSpan netInfo) |
| { |
| otError otErr = OT_ERROR_FAILED; |
| otOperationalDatasetTlvs tlvs; |
| |
| assert(netInfo.size() <= Thread::kSizeOperationalDataset); |
| tlvs.mLength = static_cast<uint8_t>(netInfo.size()); |
| memcpy(tlvs.mTlvs, netInfo.data(), netInfo.size()); |
| |
| // Set the dataset as the active dataset for the node. |
| Impl()->LockThreadStack(); |
| otErr = otDatasetSetActiveTlvs(mOTInst, &tlvs); |
| Impl()->UnlockThreadStack(); |
| if (otErr != OT_ERROR_NONE) |
| { |
| return MapOpenThreadError(otErr); |
| } |
| |
| // post an event alerting other subsystems about change in provisioning state |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kServiceProvisioningChange; |
| event.ServiceProvisioningChange.IsServiceProvisioned = true; |
| return PlatformMgr().PostEvent(&event); |
| } |
| |
| template <class ImplClass> |
| bool GenericThreadStackManagerImpl_OpenThread<ImplClass>::_IsThreadProvisioned(void) |
| { |
| bool provisioned; |
| |
| Impl()->LockThreadStack(); |
| provisioned = otDatasetIsCommissioned(mOTInst); |
| Impl()->UnlockThreadStack(); |
| |
| return provisioned; |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetThreadProvision(Thread::OperationalDataset & dataset) |
| { |
| VerifyOrReturnError(Impl()->IsThreadProvisioned(), CHIP_ERROR_INCORRECT_STATE); |
| otOperationalDatasetTlvs datasetTlv; |
| |
| Impl()->LockThreadStack(); |
| otError otErr = otDatasetGetActiveTlvs(mOTInst, &datasetTlv); |
| Impl()->UnlockThreadStack(); |
| if (otErr != OT_ERROR_NONE) |
| { |
| return MapOpenThreadError(otErr); |
| } |
| |
| ReturnErrorOnFailure(dataset.Init(ByteSpan(datasetTlv.mTlvs, datasetTlv.mLength))); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| template <class ImplClass> |
| bool GenericThreadStackManagerImpl_OpenThread<ImplClass>::_IsThreadAttached(void) |
| { |
| otDeviceRole curRole; |
| |
| Impl()->LockThreadStack(); |
| curRole = otThreadGetDeviceRole(mOTInst); |
| Impl()->UnlockThreadStack(); |
| |
| return (curRole != OT_DEVICE_ROLE_DISABLED && curRole != OT_DEVICE_ROLE_DETACHED); |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_AttachToThreadNetwork( |
| const Thread::OperationalDataset & dataset, NetworkCommissioning::Internal::WirelessDriver::ConnectCallback * callback) |
| { |
| // Reset the previously set callback since it will never be called in case incorrect dataset was supplied. |
| mpConnectCallback = nullptr; |
| ReturnErrorOnFailure(Impl()->SetThreadEnabled(false)); |
| ReturnErrorOnFailure(Impl()->SetThreadProvision(dataset.AsByteSpan())); |
| |
| if (dataset.IsCommissioned()) |
| { |
| ReturnErrorOnFailure(Impl()->SetThreadEnabled(true)); |
| mpConnectCallback = callback; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::_OnThreadAttachFinished() |
| { |
| if (mpConnectCallback != nullptr) |
| { |
| DeviceLayer::SystemLayer().ScheduleLambda([this]() { |
| VerifyOrReturn(mpConnectCallback != nullptr); |
| mpConnectCallback->OnResult(NetworkCommissioning::Status::kSuccess, CharSpan(), 0); |
| mpConnectCallback = nullptr; |
| }); |
| } |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR |
| GenericThreadStackManagerImpl_OpenThread<ImplClass>::_StartThreadScan(NetworkCommissioning::ThreadDriver::ScanCallback * callback) |
| { |
| // If there is another ongoing scan request, reject the new one. |
| VerifyOrReturnError(mpScanCallback == nullptr, CHIP_ERROR_INCORRECT_STATE); |
| mpScanCallback = callback; |
| CHIP_ERROR err = MapOpenThreadError(otLinkActiveScan(mOTInst, 0, /* all channels */ |
| 0, /* default value `kScanDurationDefault` = 300 ms. */ |
| _OnNetworkScanFinished, this)); |
| if (err != CHIP_NO_ERROR) |
| { |
| mpScanCallback = nullptr; |
| } |
| return err; |
| } |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::_OnNetworkScanFinished(otActiveScanResult * aResult, void * aContext) |
| { |
| reinterpret_cast<GenericThreadStackManagerImpl_OpenThread *>(aContext)->_OnNetworkScanFinished(aResult); |
| } |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::_OnNetworkScanFinished(otActiveScanResult * aResult) |
| { |
| if (aResult == nullptr) // scan completed |
| { |
| if (mpScanCallback != nullptr) |
| { |
| DeviceLayer::SystemLayer().ScheduleLambda([this]() { |
| mpScanCallback->OnFinished(NetworkCommissioning::Status::kSuccess, CharSpan(), &mScanResponseIter); |
| mpScanCallback = nullptr; |
| }); |
| } |
| } |
| else |
| { |
| ChipLogProgress(DeviceLayer, "Thread Network: %s Panid 0x%x Channel %u RSSI %d LQI %u Version %u", aResult->mNetworkName.m8, |
| aResult->mPanId, aResult->mChannel, aResult->mRssi, aResult->mLqi, aResult->mVersion); |
| |
| NetworkCommissioning::ThreadScanResponse scanResponse = { 0 }; |
| |
| scanResponse.panId = aResult->mPanId; // why is scanResponse.panID 64b |
| scanResponse.channel = aResult->mChannel; // why is scanResponse.channel 16b |
| scanResponse.version = aResult->mVersion; |
| scanResponse.rssi = aResult->mRssi; |
| scanResponse.lqi = aResult->mLqi; |
| scanResponse.extendedAddress = Encoding::BigEndian::Get64(aResult->mExtAddress.m8); |
| scanResponse.extendedPanId = Encoding::BigEndian::Get64(aResult->mExtendedPanId.m8); |
| scanResponse.networkNameLen = strnlen(aResult->mNetworkName.m8, OT_NETWORK_NAME_MAX_SIZE); |
| memcpy(scanResponse.networkName, aResult->mNetworkName.m8, scanResponse.networkNameLen); |
| |
| mScanResponseIter.Add(&scanResponse); |
| } |
| } |
| |
| template <class ImplClass> |
| ConnectivityManager::ThreadDeviceType GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetThreadDeviceType(void) |
| { |
| ConnectivityManager::ThreadDeviceType deviceType; |
| |
| Impl()->LockThreadStack(); |
| |
| const otLinkModeConfig linkMode = otThreadGetLinkMode(mOTInst); |
| |
| #if CHIP_DEVICE_CONFIG_THREAD_FTD |
| if (linkMode.mDeviceType && otThreadIsRouterEligible(mOTInst)) |
| ExitNow(deviceType = ConnectivityManager::kThreadDeviceType_Router); |
| if (linkMode.mDeviceType) |
| ExitNow(deviceType = ConnectivityManager::kThreadDeviceType_FullEndDevice); |
| #endif |
| if (linkMode.mRxOnWhenIdle) |
| ExitNow(deviceType = ConnectivityManager::kThreadDeviceType_MinimalEndDevice); |
| |
| #if CHIP_DEVICE_CONFIG_THREAD_SSED |
| if (otLinkCslGetPeriod(mOTInst) != 0) |
| ExitNow(deviceType = ConnectivityManager::kThreadDeviceType_SynchronizedSleepyEndDevice); |
| #endif |
| |
| ExitNow(deviceType = ConnectivityManager::kThreadDeviceType_SleepyEndDevice); |
| |
| exit: |
| Impl()->UnlockThreadStack(); |
| |
| return deviceType; |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR |
| GenericThreadStackManagerImpl_OpenThread<ImplClass>::_SetThreadDeviceType(ConnectivityManager::ThreadDeviceType deviceType) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| otLinkModeConfig linkMode; |
| |
| switch (deviceType) |
| { |
| #if CHIP_DEVICE_CONFIG_THREAD_FTD |
| case ConnectivityManager::kThreadDeviceType_Router: |
| case ConnectivityManager::kThreadDeviceType_FullEndDevice: |
| #endif |
| case ConnectivityManager::kThreadDeviceType_MinimalEndDevice: |
| case ConnectivityManager::kThreadDeviceType_SleepyEndDevice: |
| #if CHIP_DEVICE_CONFIG_THREAD_SSED |
| case ConnectivityManager::kThreadDeviceType_SynchronizedSleepyEndDevice: |
| #endif |
| break; |
| default: |
| ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT); |
| } |
| |
| #if CHIP_PROGRESS_LOGGING |
| { |
| const char * deviceTypeStr; |
| switch (deviceType) |
| { |
| case ConnectivityManager::kThreadDeviceType_Router: |
| deviceTypeStr = "ROUTER"; |
| break; |
| case ConnectivityManager::kThreadDeviceType_FullEndDevice: |
| deviceTypeStr = "FULL END DEVICE"; |
| break; |
| case ConnectivityManager::kThreadDeviceType_MinimalEndDevice: |
| deviceTypeStr = "MINIMAL END DEVICE"; |
| break; |
| case ConnectivityManager::kThreadDeviceType_SleepyEndDevice: |
| deviceTypeStr = "SLEEPY END DEVICE"; |
| break; |
| #if CHIP_DEVICE_CONFIG_THREAD_SSED |
| case ConnectivityManager::kThreadDeviceType_SynchronizedSleepyEndDevice: |
| deviceTypeStr = "SYNCHRONIZED SLEEPY END DEVICE"; |
| break; |
| #endif |
| default: |
| deviceTypeStr = "(unknown)"; |
| break; |
| } |
| ChipLogProgress(DeviceLayer, "Setting OpenThread device type to %s", deviceTypeStr); |
| } |
| |
| #endif // CHIP_PROGRESS_LOGGING |
| |
| Impl()->LockThreadStack(); |
| |
| linkMode = otThreadGetLinkMode(mOTInst); |
| |
| switch (deviceType) |
| { |
| #if CHIP_DEVICE_CONFIG_THREAD_FTD |
| case ConnectivityManager::kThreadDeviceType_Router: |
| case ConnectivityManager::kThreadDeviceType_FullEndDevice: |
| linkMode.mDeviceType = true; |
| linkMode.mRxOnWhenIdle = true; |
| otThreadSetRouterEligible(mOTInst, deviceType == ConnectivityManager::kThreadDeviceType_Router); |
| break; |
| #endif |
| case ConnectivityManager::kThreadDeviceType_MinimalEndDevice: |
| linkMode.mDeviceType = false; |
| linkMode.mRxOnWhenIdle = true; |
| break; |
| case ConnectivityManager::kThreadDeviceType_SleepyEndDevice: |
| case ConnectivityManager::kThreadDeviceType_SynchronizedSleepyEndDevice: |
| linkMode.mDeviceType = false; |
| linkMode.mRxOnWhenIdle = false; |
| break; |
| default: |
| break; |
| } |
| |
| otThreadSetLinkMode(mOTInst, linkMode); |
| |
| Impl()->UnlockThreadStack(); |
| |
| exit: |
| return err; |
| } |
| |
| template <class ImplClass> |
| bool GenericThreadStackManagerImpl_OpenThread<ImplClass>::_HaveMeshConnectivity(void) |
| { |
| bool res; |
| otDeviceRole curRole; |
| |
| Impl()->LockThreadStack(); |
| |
| // Get the current Thread role. |
| curRole = otThreadGetDeviceRole(mOTInst); |
| |
| // If Thread is disabled, or the node is detached, then the node has no mesh connectivity. |
| if (curRole == OT_DEVICE_ROLE_DISABLED || curRole == OT_DEVICE_ROLE_DETACHED) |
| { |
| res = false; |
| } |
| |
| // If the node is a child, that implies the existence of a parent node which provides connectivity |
| // to the mesh. |
| else if (curRole == OT_DEVICE_ROLE_CHILD) |
| { |
| res = true; |
| } |
| |
| // Otherwise, if the node is acting as a router, scan the Thread neighbor table looking for at least |
| // one other node that is also acting as router. |
| else |
| { |
| otNeighborInfoIterator neighborIter = OT_NEIGHBOR_INFO_ITERATOR_INIT; |
| otNeighborInfo neighborInfo; |
| |
| res = false; |
| |
| while (otThreadGetNextNeighborInfo(mOTInst, &neighborIter, &neighborInfo) == OT_ERROR_NONE) |
| { |
| if (!neighborInfo.mIsChild) |
| { |
| res = true; |
| break; |
| } |
| } |
| } |
| |
| Impl()->UnlockThreadStack(); |
| |
| return res; |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetAndLogThreadStatsCounters(void) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| otError otErr; |
| otOperationalDataset activeDataset; |
| |
| Impl()->LockThreadStack(); |
| #if CHIP_PROGRESS_LOGGING |
| { |
| otDeviceRole role; |
| |
| role = otThreadGetDeviceRole(mOTInst); |
| ChipLogProgress(DeviceLayer, "Thread Role: %d\n", role); |
| } |
| #endif // CHIP_PROGRESS_LOGGING |
| |
| if (otDatasetIsCommissioned(mOTInst)) |
| { |
| otErr = otDatasetGetActive(mOTInst, &activeDataset); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| if (activeDataset.mComponents.mIsChannelPresent) |
| { |
| ChipLogProgress(DeviceLayer, "Thread Channel: %d\n", activeDataset.mChannel); |
| } |
| } |
| |
| #if CHIP_PROGRESS_LOGGING |
| { |
| const otIpCounters * ipCounters; |
| const otMacCounters * macCounters; |
| |
| macCounters = otLinkGetCounters(mOTInst); |
| |
| ChipLogProgress(DeviceLayer, |
| "Rx Counters:\n" |
| "PHY Rx Total: %" PRIu32 "\n" |
| "MAC Rx Unicast: %" PRIu32 "\n" |
| "MAC Rx Broadcast: %" PRIu32 "\n" |
| "MAC Rx Data: %" PRIu32 "\n" |
| "MAC Rx Data Polls: %" PRIu32 "\n" |
| "MAC Rx Beacons: %" PRIu32 "\n" |
| "MAC Rx Beacon Reqs: %" PRIu32 "\n" |
| "MAC Rx Other: %" PRIu32 "\n" |
| "MAC Rx Filtered Whitelist: %" PRIu32 "\n" |
| "MAC Rx Filtered DestAddr: %" PRIu32 "\n", |
| macCounters->mRxTotal, macCounters->mRxUnicast, macCounters->mRxBroadcast, macCounters->mRxData, |
| macCounters->mRxDataPoll, macCounters->mRxBeacon, macCounters->mRxBeaconRequest, macCounters->mRxOther, |
| macCounters->mRxAddressFiltered, macCounters->mRxDestAddrFiltered); |
| |
| ChipLogProgress(DeviceLayer, |
| "Tx Counters:\n" |
| "PHY Tx Total: %" PRIu32 "\n" |
| "MAC Tx Unicast: %" PRIu32 "\n" |
| "MAC Tx Broadcast: %" PRIu32 "\n" |
| "MAC Tx Data: %" PRIu32 "\n" |
| "MAC Tx Data Polls: %" PRIu32 "\n" |
| "MAC Tx Beacons: %" PRIu32 "\n" |
| "MAC Tx Beacon Reqs: %" PRIu32 "\n" |
| "MAC Tx Other: %" PRIu32 "\n" |
| "MAC Tx Retry: %" PRIu32 "\n" |
| "MAC Tx CCA Fail: %" PRIu32 "\n", |
| macCounters->mTxTotal, macCounters->mTxUnicast, macCounters->mTxBroadcast, macCounters->mTxData, |
| macCounters->mTxDataPoll, macCounters->mTxBeacon, macCounters->mTxBeaconRequest, macCounters->mTxOther, |
| macCounters->mTxRetry, macCounters->mTxErrCca); |
| |
| ChipLogProgress(DeviceLayer, |
| "Failure Counters:\n" |
| "MAC Rx Decrypt Fail: %" PRIu32 "\n" |
| "MAC Rx No Frame Fail: %" PRIu32 "\n" |
| "MAC Rx Unknown Neighbor Fail: %" PRIu32 "\n" |
| "MAC Rx Invalid Src Addr Fail: %" PRIu32 "\n" |
| "MAC Rx FCS Fail: %" PRIu32 "\n" |
| "MAC Rx Other Fail: %" PRIu32 "\n", |
| macCounters->mRxErrSec, macCounters->mRxErrNoFrame, macCounters->mRxErrUnknownNeighbor, |
| macCounters->mRxErrInvalidSrcAddr, macCounters->mRxErrFcs, macCounters->mRxErrOther); |
| |
| ipCounters = otThreadGetIp6Counters(mOTInst); |
| |
| ChipLogProgress(DeviceLayer, |
| "IP Counters:\n" |
| "IP Tx Success: %" PRIu32 "\n" |
| "IP Rx Success: %" PRIu32 "\n" |
| "IP Tx Fail: %" PRIu32 "\n" |
| "IP Rx Fail: %" PRIu32 "\n", |
| ipCounters->mTxSuccess, ipCounters->mRxSuccess, ipCounters->mTxFailure, ipCounters->mRxFailure); |
| } |
| #endif // CHIP_PROGRESS_LOGGING |
| exit: |
| Impl()->UnlockThreadStack(); |
| return err; |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetAndLogThreadTopologyMinimal(void) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| #if CHIP_PROGRESS_LOGGING |
| otError otErr; |
| const otExtAddress * extAddress; |
| uint16_t rloc16; |
| uint16_t routerId; |
| uint16_t leaderRouterId; |
| uint32_t partitionId; |
| int8_t parentAverageRssi; |
| int8_t parentLastRssi; |
| int8_t instantRssi; |
| |
| Impl()->LockThreadStack(); |
| |
| rloc16 = otThreadGetRloc16(mOTInst); |
| |
| // Router ID is the top 6 bits of the RLOC |
| routerId = (rloc16 >> 10) & 0x3f; |
| |
| leaderRouterId = otThreadGetLeaderRouterId(mOTInst); |
| |
| otErr = otThreadGetParentAverageRssi(mOTInst, &parentAverageRssi); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| otErr = otThreadGetParentLastRssi(mOTInst, &parentLastRssi); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| partitionId = otThreadGetPartitionId(mOTInst); |
| |
| extAddress = otLinkGetExtendedAddress(mOTInst); |
| |
| instantRssi = otPlatRadioGetRssi(mOTInst); |
| |
| ChipLogProgress(DeviceLayer, |
| "Thread Topology:\n" |
| "RLOC16: %04X\n" |
| "Router ID: %u\n" |
| "Leader Router ID: %u\n" |
| "Parent Avg RSSI: %d\n" |
| "Parent Last RSSI: %d\n" |
| "Partition ID: %" PRIu32 "\n", |
| rloc16, routerId, leaderRouterId, parentAverageRssi, parentLastRssi, partitionId); |
| |
| ChipLogProgress(DeviceLayer, |
| "Extended Address: %02X%02X:%02X%02X:%02X%02X:%02X%02X\n" |
| "Instant RSSI: %d\n", |
| extAddress->m8[0], extAddress->m8[1], extAddress->m8[2], extAddress->m8[3], extAddress->m8[4], |
| extAddress->m8[5], extAddress->m8[6], extAddress->m8[7], instantRssi); |
| |
| exit: |
| Impl()->UnlockThreadStack(); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "GetAndLogThreadTopologyMinimul failed: %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| #endif // CHIP_PROGRESS_LOGGING |
| return err; |
| } |
| |
| #define TELEM_NEIGHBOR_TABLE_SIZE (64) |
| #define TELEM_PRINT_BUFFER_SIZE (64) |
| |
| #if CHIP_DEVICE_CONFIG_THREAD_FTD |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetAndLogThreadTopologyFull() |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| #if CHIP_PROGRESS_LOGGING |
| otError otErr; |
| otIp6Address * leaderAddr = NULL; |
| uint8_t * networkData = NULL; |
| uint8_t * stableNetworkData = NULL; |
| uint8_t networkDataLen = 0; |
| uint8_t stableNetworkDataLen = 0; |
| const otExtAddress * extAddress; |
| otNeighborInfo neighborInfo[TELEM_NEIGHBOR_TABLE_SIZE]; |
| otNeighborInfoIterator iter; |
| otNeighborInfoIterator iterCopy; |
| char printBuf[TELEM_PRINT_BUFFER_SIZE] = { 0 }; |
| uint16_t rloc16; |
| uint16_t routerId; |
| uint16_t leaderRouterId; |
| uint8_t leaderWeight; |
| uint8_t leaderLocalWeight; |
| uint32_t partitionId; |
| int8_t instantRssi; |
| uint8_t networkDataVersion; |
| uint8_t stableNetworkDataVersion; |
| uint16_t neighborTableSize = 0; |
| uint16_t childTableSize = 0; |
| |
| Impl()->LockThreadStack(); |
| |
| rloc16 = otThreadGetRloc16(mOTInst); |
| |
| // Router ID is the top 6 bits of the RLOC |
| routerId = (rloc16 >> 10) & 0x3f; |
| |
| leaderRouterId = otThreadGetLeaderRouterId(mOTInst); |
| |
| otErr = otThreadGetLeaderRloc(mOTInst, leaderAddr); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| leaderWeight = otThreadGetLeaderWeight(mOTInst); |
| |
| leaderLocalWeight = otThreadGetLocalLeaderWeight(mOTInst); |
| |
| otErr = otNetDataGet(mOTInst, false, networkData, &networkDataLen); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| networkDataVersion = otNetDataGetVersion(mOTInst); |
| |
| otErr = otNetDataGet(mOTInst, true, stableNetworkData, &stableNetworkDataLen); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| stableNetworkDataVersion = otNetDataGetStableVersion(mOTInst); |
| |
| extAddress = otLinkGetExtendedAddress(mOTInst); |
| |
| partitionId = otThreadGetPartitionId(mOTInst); |
| |
| instantRssi = otPlatRadioGetRssi(mOTInst); |
| |
| iter = OT_NEIGHBOR_INFO_ITERATOR_INIT; |
| iterCopy = OT_NEIGHBOR_INFO_ITERATOR_INIT; |
| neighborTableSize = 0; |
| childTableSize = 0; |
| |
| while (otThreadGetNextNeighborInfo(mOTInst, &iter, &neighborInfo[iter]) == OT_ERROR_NONE) |
| { |
| neighborTableSize++; |
| if (neighborInfo[iterCopy].mIsChild) |
| { |
| childTableSize++; |
| } |
| iterCopy = iter; |
| } |
| |
| snprintf(printBuf, TELEM_PRINT_BUFFER_SIZE, "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", |
| leaderAddr->mFields.m8[0], leaderAddr->mFields.m8[1], leaderAddr->mFields.m8[2], leaderAddr->mFields.m8[3], |
| leaderAddr->mFields.m8[4], leaderAddr->mFields.m8[5], leaderAddr->mFields.m8[6], leaderAddr->mFields.m8[7], |
| leaderAddr->mFields.m8[8], leaderAddr->mFields.m8[9], leaderAddr->mFields.m8[10], leaderAddr->mFields.m8[11], |
| leaderAddr->mFields.m8[12], leaderAddr->mFields.m8[13], leaderAddr->mFields.m8[14], leaderAddr->mFields.m8[15]); |
| |
| ChipLogProgress(DeviceLayer, |
| "Thread Topology:\n" |
| "RLOC16: %04X\n" |
| "Router ID: %u\n" |
| "Leader Router ID: %u\n" |
| "Leader Address: %s\n" |
| "Leader Weight: %d\n" |
| "Local Leader Weight: %d\n" |
| "Network Data Len: %d\n" |
| "Network Data Version: %d\n" |
| "Stable Network Data Version: %d\n", |
| rloc16, routerId, leaderRouterId, printBuf, leaderWeight, leaderLocalWeight, networkDataLen, networkDataVersion, |
| stableNetworkDataVersion); |
| |
| memset(printBuf, 0x00, TELEM_PRINT_BUFFER_SIZE); |
| |
| ChipLogProgress(DeviceLayer, |
| "Extended Address: %02X%02X:%02X%02X:%02X%02X:%02X%02X\n" |
| "Partition ID: %" PRIx32 "\n" |
| "Instant RSSI: %d\n" |
| "Neighbor Table Length: %d\n" |
| "Child Table Length: %d\n", |
| extAddress->m8[0], extAddress->m8[1], extAddress->m8[2], extAddress->m8[3], extAddress->m8[4], |
| extAddress->m8[5], extAddress->m8[6], extAddress->m8[7], partitionId, instantRssi, neighborTableSize, |
| childTableSize); |
| |
| // Handle each neighbor event seperatly. |
| for (uint32_t i = 0; i < neighborTableSize; i++) |
| { |
| otNeighborInfo * neighbor = &neighborInfo[i]; |
| |
| if (neighbor->mIsChild) |
| { |
| otChildInfo * child = NULL; |
| otErr = otThreadGetChildInfoById(mOTInst, neighbor->mRloc16, child); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| snprintf(printBuf, TELEM_PRINT_BUFFER_SIZE, ", Timeout: %10" PRIu32 " NetworkDataVersion: %3u", child->mTimeout, |
| child->mNetworkDataVersion); |
| } |
| else |
| { |
| printBuf[0] = 0; |
| } |
| |
| ChipLogProgress(DeviceLayer, |
| "TopoEntry[%" PRIu32 "]: %02X%02X:%02X%02X:%02X%02X:%02X%02X\n" |
| "RLOC: %04X\n" |
| "Age: %3" PRIu32 "\n" |
| "LQI: %1d\n" |
| "AvgRSSI: %3d\n" |
| "LastRSSI: %3d\n", |
| i, neighbor->mExtAddress.m8[0], neighbor->mExtAddress.m8[1], neighbor->mExtAddress.m8[2], |
| neighbor->mExtAddress.m8[3], neighbor->mExtAddress.m8[4], neighbor->mExtAddress.m8[5], |
| neighbor->mExtAddress.m8[6], neighbor->mExtAddress.m8[7], neighbor->mRloc16, neighbor->mAge, |
| neighbor->mLinkQualityIn, neighbor->mAverageRssi, neighbor->mLastRssi); |
| |
| ChipLogProgress(DeviceLayer, |
| "LinkFrameCounter: %10" PRIu32 "\n" |
| "MleFrameCounter: %10" PRIu32 "\n" |
| "RxOnWhenIdle: %c\n" |
| "FullFunction: %c\n" |
| "FullNetworkData: %c\n" |
| "IsChild: %c%s\n", |
| neighbor->mLinkFrameCounter, neighbor->mMleFrameCounter, neighbor->mRxOnWhenIdle ? 'Y' : 'n', |
| neighbor->mFullThreadDevice ? 'Y' : 'n', neighbor->mFullNetworkData ? 'Y' : 'n', |
| neighbor->mIsChild ? 'Y' : 'n', printBuf); |
| } |
| |
| exit: |
| Impl()->UnlockThreadStack(); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "GetAndLogThreadTopologyFull failed: %s", ErrorStr(err)); |
| } |
| #endif // CHIP_PROGRESS_LOGGING |
| return err; |
| } |
| #else // CHIP_DEVICE_CONFIG_THREAD_FTD |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetAndLogThreadTopologyFull() |
| { |
| return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; |
| } |
| #endif |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetPrimary802154MACAddress(uint8_t * buf) |
| { |
| const otExtAddress * extendedAddr = otLinkGetExtendedAddress(mOTInst); |
| memcpy(buf, extendedAddr, sizeof(otExtAddress)); |
| return CHIP_NO_ERROR; |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetExternalIPv6Address(chip::Inet::IPAddress & addr) |
| { |
| const otNetifAddress * otAddresses = otIp6GetUnicastAddresses(mOTInst); |
| |
| // Look only for the global unicast addresses, not internally assigned by Thread. |
| for (const otNetifAddress * otAddress = otAddresses; otAddress != nullptr; otAddress = otAddress->mNext) |
| { |
| if (otAddress->mValid) |
| { |
| switch (otAddress->mAddressOrigin) |
| { |
| case OT_ADDRESS_ORIGIN_THREAD: |
| break; |
| case OT_ADDRESS_ORIGIN_SLAAC: |
| case OT_ADDRESS_ORIGIN_DHCPV6: |
| case OT_ADDRESS_ORIGIN_MANUAL: |
| addr = ToIPAddress(otAddress->mAddress); |
| return CHIP_NO_ERROR; |
| default: |
| break; |
| } |
| } |
| } |
| |
| return CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; |
| } |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::_ResetThreadNetworkDiagnosticsCounts(void) |
| { |
| // Based on the spec, only OverrunCount should be resetted. |
| mOverrunCount = 0; |
| } |
| /* |
| * @brief Get runtime value from the thread network based on the given attribute ID. |
| * The info is encoded via the AttributeValueEncoder. |
| * |
| * @param attributeId Id of the attribute for the requested info. |
| * @param aEncoder Encoder to encode the attribute value. |
| * |
| * @return CHIP_NO_ERROR = Succes. |
| * CHIP_ERROR_NOT_IMPLEMENTED = Runtime value for this attribute to yet available to send as reply |
| * Use standard read. |
| * CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE = Is not a Runtime readable attribute. Use standard read |
| * All other errors should be treated as a read error and reported as such. |
| */ |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_WriteThreadNetworkDiagnosticAttributeToTlv( |
| AttributeId attributeId, app::AttributeValueEncoder & encoder) |
| { |
| CHIP_ERROR err; |
| |
| namespace ThreadNetworkDiagnostics = app::Clusters::ThreadNetworkDiagnostics; |
| |
| switch (attributeId) |
| { |
| case ThreadNetworkDiagnostics::Attributes::Channel::Id: { |
| uint16_t channel = otLinkGetChannel(mOTInst); |
| err = encoder.Encode(channel); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RoutingRole::Id: { |
| ThreadNetworkDiagnostics::RoutingRole routingRole; |
| otDeviceRole otRole = otThreadGetDeviceRole(mOTInst); |
| |
| if (otRole == OT_DEVICE_ROLE_DISABLED) |
| { |
| routingRole = EMBER_ZCL_ROUTING_ROLE_UNSPECIFIED; |
| } |
| else if (otRole == OT_DEVICE_ROLE_DETACHED) |
| { |
| routingRole = EMBER_ZCL_ROUTING_ROLE_UNASSIGNED; |
| } |
| else if (otRole == OT_DEVICE_ROLE_ROUTER) |
| { |
| routingRole = EMBER_ZCL_ROUTING_ROLE_ROUTER; |
| } |
| else if (otRole == OT_DEVICE_ROLE_LEADER) |
| { |
| routingRole = EMBER_ZCL_ROUTING_ROLE_LEADER; |
| } |
| else if (otRole == OT_DEVICE_ROLE_CHILD) |
| { |
| otLinkModeConfig linkMode = otThreadGetLinkMode(mOTInst); |
| |
| if (linkMode.mRxOnWhenIdle) |
| { |
| routingRole = EMBER_ZCL_ROUTING_ROLE_END_DEVICE; |
| #if CHIP_DEVICE_CONFIG_THREAD_FTD |
| if (otThreadIsRouterEligible(mOTInst)) |
| { |
| routingRole = EMBER_ZCL_ROUTING_ROLE_REED; |
| } |
| #endif |
| } |
| else |
| { |
| routingRole = EMBER_ZCL_ROUTING_ROLE_SLEEPY_END_DEVICE; |
| } |
| } |
| |
| err = encoder.Encode(routingRole); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::NetworkName::Id: { |
| const char * networkName = otThreadGetNetworkName(mOTInst); |
| err = encoder.Encode(CharSpan::fromCharString(networkName)); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::PanId::Id: { |
| uint16_t panId = otLinkGetPanId(mOTInst); |
| err = encoder.Encode(panId); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::ExtendedPanId::Id: { |
| const otExtendedPanId * pExtendedPanid = otThreadGetExtendedPanId(mOTInst); |
| err = encoder.Encode(Encoding::BigEndian::Get64(pExtendedPanid->m8)); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::MeshLocalPrefix::Id: { |
| uint8_t meshLocaPrefix[OT_MESH_LOCAL_PREFIX_SIZE + 1] = { 0 }; // + 1 to encode prefix Len in the octstr |
| |
| const otMeshLocalPrefix * pMeshLocalPrefix = otThreadGetMeshLocalPrefix(mOTInst); |
| meshLocaPrefix[0] = OT_IP6_PREFIX_BITSIZE; |
| |
| memcpy(&meshLocaPrefix[1], pMeshLocalPrefix->m8, OT_MESH_LOCAL_PREFIX_SIZE); |
| err = encoder.Encode(ByteSpan(meshLocaPrefix)); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::OverrunCount::Id: { |
| uint64_t overrunCount = mOverrunCount; |
| err = encoder.Encode(overrunCount); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::NeighborTableList::Id: { |
| err = encoder.EncodeList([this](const auto & aEncoder) -> CHIP_ERROR { |
| otNeighborInfo neighInfo; |
| otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT; |
| |
| while (otThreadGetNextNeighborInfo(mOTInst, &iterator, &neighInfo) == OT_ERROR_NONE) |
| { |
| ThreadNetworkDiagnostics::Structs::NeighborTable::Type neighborTable; |
| app::DataModel::Nullable<int8_t> averageRssi; |
| app::DataModel::Nullable<int8_t> lastRssi; |
| |
| averageRssi.SetNonNull(neighInfo.mAverageRssi); |
| lastRssi.SetNonNull(neighInfo.mLastRssi); |
| |
| neighborTable.averageRssi = averageRssi; |
| neighborTable.lastRssi = lastRssi; |
| neighborTable.extAddress = Encoding::BigEndian::Get64(neighInfo.mExtAddress.m8); |
| neighborTable.age = neighInfo.mAge; |
| neighborTable.rloc16 = neighInfo.mRloc16; |
| neighborTable.linkFrameCounter = neighInfo.mLinkFrameCounter; |
| neighborTable.mleFrameCounter = neighInfo.mMleFrameCounter; |
| neighborTable.lqi = neighInfo.mLinkQualityIn; |
| neighborTable.frameErrorRate = neighInfo.mFrameErrorRate; |
| neighborTable.messageErrorRate = neighInfo.mMessageErrorRate; |
| neighborTable.rxOnWhenIdle = neighInfo.mRxOnWhenIdle; |
| neighborTable.fullThreadDevice = neighInfo.mFullThreadDevice; |
| neighborTable.fullNetworkData = neighInfo.mFullNetworkData; |
| neighborTable.isChild = neighInfo.mIsChild; |
| |
| ReturnErrorOnFailure(aEncoder.Encode(neighborTable)); |
| } |
| |
| return CHIP_NO_ERROR; |
| }); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RouteTableList::Id: { |
| err = encoder.EncodeList([this](const auto & aEncoder) -> CHIP_ERROR { |
| otRouterInfo routerInfo; |
| |
| #if CHIP_DEVICE_CONFIG_THREAD_FTD |
| uint8_t maxRouterId = otThreadGetMaxRouterId(mOTInst); |
| CHIP_ERROR chipErr = CHIP_ERROR_INCORRECT_STATE; |
| |
| for (uint8_t i = 0; i <= maxRouterId; i++) |
| { |
| if (otThreadGetRouterInfo(mOTInst, i, &routerInfo) == OT_ERROR_NONE) |
| { |
| ThreadNetworkDiagnostics::Structs::RouteTable::Type routeTable; |
| |
| routeTable.extAddress = Encoding::BigEndian::Get64(routerInfo.mExtAddress.m8); |
| routeTable.rloc16 = routerInfo.mRloc16; |
| routeTable.routerId = routerInfo.mRouterId; |
| routeTable.nextHop = routerInfo.mNextHop; |
| routeTable.pathCost = routerInfo.mPathCost; |
| routeTable.LQIIn = routerInfo.mLinkQualityIn; |
| routeTable.LQIOut = routerInfo.mLinkQualityOut; |
| routeTable.age = routerInfo.mAge; |
| routeTable.allocated = routerInfo.mAllocated; |
| routeTable.linkEstablished = routerInfo.mLinkEstablished; |
| |
| ReturnErrorOnFailure(aEncoder.Encode(routeTable)); |
| chipErr = CHIP_NO_ERROR; |
| } |
| } |
| |
| return chipErr; |
| |
| #else // OPENTHREAD_MTD |
| otError otErr = otThreadGetParentInfo(mOTInst, &routerInfo); |
| ReturnErrorOnFailure(MapOpenThreadError(otErr)); |
| |
| ThreadNetworkDiagnostics::Structs::RouteTable::Type routeTable; |
| |
| routeTable.extAddress = Encoding::BigEndian::Get64(routerInfo.mExtAddress.m8); |
| routeTable.rloc16 = routerInfo.mRloc16; |
| routeTable.routerId = routerInfo.mRouterId; |
| routeTable.nextHop = routerInfo.mNextHop; |
| routeTable.pathCost = routerInfo.mPathCost; |
| routeTable.LQIIn = routerInfo.mLinkQualityIn; |
| routeTable.LQIOut = routerInfo.mLinkQualityOut; |
| routeTable.age = routerInfo.mAge; |
| routeTable.allocated = routerInfo.mAllocated; |
| routeTable.linkEstablished = routerInfo.mLinkEstablished; |
| |
| ReturnErrorOnFailure(aEncoder.Encode(routeTable)); |
| return CHIP_NO_ERROR; |
| #endif |
| }); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::PartitionId::Id: { |
| uint32_t partitionId = otThreadGetPartitionId(mOTInst); |
| err = encoder.Encode(partitionId); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::Weighting::Id: { |
| uint8_t weight = otThreadGetLeaderWeight(mOTInst); |
| err = encoder.Encode(weight); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::DataVersion::Id: { |
| uint8_t dataVersion = otNetDataGetVersion(mOTInst); |
| err = encoder.Encode(dataVersion); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::StableDataVersion::Id: { |
| uint8_t stableVersion = otNetDataGetStableVersion(mOTInst); |
| err = encoder.Encode(stableVersion); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::LeaderRouterId::Id: { |
| uint8_t leaderRouterId = otThreadGetLeaderRouterId(mOTInst); |
| err = encoder.Encode(leaderRouterId); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::DetachedRoleCount::Id: { |
| uint16_t detachedRole = otThreadGetMleCounters(mOTInst)->mDetachedRole; |
| err = encoder.Encode(detachedRole); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::ChildRoleCount::Id: { |
| uint16_t childRole = otThreadGetMleCounters(mOTInst)->mChildRole; |
| err = encoder.Encode(childRole); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RouterRoleCount::Id: { |
| uint16_t routerRole = otThreadGetMleCounters(mOTInst)->mRouterRole; |
| err = encoder.Encode(routerRole); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::LeaderRoleCount::Id: { |
| uint16_t leaderRole = otThreadGetMleCounters(mOTInst)->mLeaderRole; |
| err = encoder.Encode(leaderRole); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::AttachAttemptCount::Id: { |
| uint16_t attachAttempts = otThreadGetMleCounters(mOTInst)->mAttachAttempts; |
| err = encoder.Encode(attachAttempts); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::PartitionIdChangeCount::Id: { |
| uint16_t partitionIdChanges = otThreadGetMleCounters(mOTInst)->mPartitionIdChanges; |
| err = encoder.Encode(partitionIdChanges); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::BetterPartitionAttachAttemptCount::Id: { |
| uint16_t betterPartitionAttachAttempts = otThreadGetMleCounters(mOTInst)->mBetterPartitionAttachAttempts; |
| err = encoder.Encode(betterPartitionAttachAttempts); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::ParentChangeCount::Id: { |
| uint16_t parentChanges = otThreadGetMleCounters(mOTInst)->mParentChanges; |
| err = encoder.Encode(parentChanges); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxTotalCount::Id: { |
| uint32_t txTotal = otLinkGetCounters(mOTInst)->mTxTotal; |
| err = encoder.Encode(txTotal); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxUnicastCount::Id: { |
| uint32_t txUnicast = otLinkGetCounters(mOTInst)->mTxUnicast; |
| err = encoder.Encode(txUnicast); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxBroadcastCount::Id: { |
| uint32_t txBroadcast = otLinkGetCounters(mOTInst)->mTxBroadcast; |
| err = encoder.Encode(txBroadcast); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxAckRequestedCount::Id: { |
| uint32_t txAckRequested = otLinkGetCounters(mOTInst)->mTxAckRequested; |
| err = encoder.Encode(txAckRequested); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxAckedCount::Id: { |
| uint32_t txAcked = otLinkGetCounters(mOTInst)->mTxAcked; |
| err = encoder.Encode(txAcked); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxNoAckRequestedCount::Id: { |
| uint32_t txNoAckRequested = otLinkGetCounters(mOTInst)->mTxNoAckRequested; |
| err = encoder.Encode(txNoAckRequested); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxDataCount::Id: { |
| uint32_t txData = otLinkGetCounters(mOTInst)->mTxData; |
| err = encoder.Encode(txData); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxDataPollCount::Id: { |
| uint32_t txDataPoll = otLinkGetCounters(mOTInst)->mTxDataPoll; |
| err = encoder.Encode(txDataPoll); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxBeaconCount::Id: { |
| uint32_t txBeacon = otLinkGetCounters(mOTInst)->mTxBeacon; |
| err = encoder.Encode(txBeacon); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxBeaconRequestCount::Id: { |
| uint32_t txBeaconRequest = otLinkGetCounters(mOTInst)->mTxBeaconRequest; |
| err = encoder.Encode(txBeaconRequest); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxOtherCount::Id: { |
| uint32_t txOther = otLinkGetCounters(mOTInst)->mTxOther; |
| err = encoder.Encode(txOther); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxRetryCount::Id: { |
| uint32_t txRetry = otLinkGetCounters(mOTInst)->mTxRetry; |
| err = encoder.Encode(txRetry); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxDirectMaxRetryExpiryCount::Id: { |
| uint32_t txDirectMaxRetryExpiry = otLinkGetCounters(mOTInst)->mTxDirectMaxRetryExpiry; |
| err = encoder.Encode(txDirectMaxRetryExpiry); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxIndirectMaxRetryExpiryCount::Id: { |
| uint32_t txIndirectMaxRetryExpiry = otLinkGetCounters(mOTInst)->mTxIndirectMaxRetryExpiry; |
| err = encoder.Encode(txIndirectMaxRetryExpiry); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxErrCcaCount::Id: { |
| uint32_t txErrCca = otLinkGetCounters(mOTInst)->mTxErrCca; |
| err = encoder.Encode(txErrCca); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxErrAbortCount::Id: { |
| uint32_t TxErrAbort = otLinkGetCounters(mOTInst)->mTxErrAbort; |
| err = encoder.Encode(TxErrAbort); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::TxErrBusyChannelCount::Id: { |
| uint32_t TxErrBusyChannel = otLinkGetCounters(mOTInst)->mTxErrBusyChannel; |
| err = encoder.Encode(TxErrBusyChannel); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxTotalCount::Id: { |
| uint32_t rxTotal = otLinkGetCounters(mOTInst)->mRxTotal; |
| err = encoder.Encode(rxTotal); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxUnicastCount::Id: { |
| uint32_t rxUnicast = otLinkGetCounters(mOTInst)->mRxUnicast; |
| err = encoder.Encode(rxUnicast); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxBroadcastCount::Id: { |
| uint32_t rxBroadcast = otLinkGetCounters(mOTInst)->mRxBroadcast; |
| err = encoder.Encode(rxBroadcast); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxDataCount::Id: { |
| uint32_t rxData = otLinkGetCounters(mOTInst)->mRxData; |
| err = encoder.Encode(rxData); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxDataPollCount::Id: { |
| uint32_t rxDataPoll = otLinkGetCounters(mOTInst)->mRxDataPoll; |
| err = encoder.Encode(rxDataPoll); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxBeaconCount::Id: { |
| uint32_t rxBeacon = otLinkGetCounters(mOTInst)->mRxBeacon; |
| err = encoder.Encode(rxBeacon); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxBeaconRequestCount::Id: { |
| uint32_t rxBeaconRequest = otLinkGetCounters(mOTInst)->mRxBeaconRequest; |
| err = encoder.Encode(rxBeaconRequest); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxOtherCount::Id: { |
| uint32_t rxOther = otLinkGetCounters(mOTInst)->mRxOther; |
| err = encoder.Encode(rxOther); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxAddressFilteredCount::Id: { |
| uint32_t rxAddressFiltered = otLinkGetCounters(mOTInst)->mRxAddressFiltered; |
| err = encoder.Encode(rxAddressFiltered); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxDestAddrFilteredCount::Id: { |
| uint32_t rxDestAddrFiltered = otLinkGetCounters(mOTInst)->mRxDestAddrFiltered; |
| err = encoder.Encode(rxDestAddrFiltered); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxDuplicatedCount::Id: { |
| uint32_t rxDuplicated = otLinkGetCounters(mOTInst)->mRxDuplicated; |
| err = encoder.Encode(rxDuplicated); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxErrNoFrameCount::Id: { |
| uint32_t rxErrNoFrame = otLinkGetCounters(mOTInst)->mRxErrNoFrame; |
| err = encoder.Encode(rxErrNoFrame); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxErrUnknownNeighborCount::Id: { |
| uint32_t rxErrUnknownNeighbor = otLinkGetCounters(mOTInst)->mRxErrUnknownNeighbor; |
| err = encoder.Encode(rxErrUnknownNeighbor); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxErrInvalidSrcAddrCount::Id: { |
| uint32_t rxErrInvalidSrcAddr = otLinkGetCounters(mOTInst)->mRxErrInvalidSrcAddr; |
| err = encoder.Encode(rxErrInvalidSrcAddr); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxErrSecCount::Id: { |
| uint32_t rxErrSec = otLinkGetCounters(mOTInst)->mRxErrSec; |
| err = encoder.Encode(rxErrSec); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxErrFcsCount::Id: { |
| uint32_t rxErrFcs = otLinkGetCounters(mOTInst)->mRxErrFcs; |
| err = encoder.Encode(rxErrFcs); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::RxErrOtherCount::Id: { |
| uint32_t rxErrOther = otLinkGetCounters(mOTInst)->mRxErrOther; |
| err = encoder.Encode(rxErrOther); |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::ActiveTimestamp::Id: { |
| err = CHIP_ERROR_INCORRECT_STATE; |
| if (otDatasetIsCommissioned(mOTInst)) |
| { |
| otOperationalDataset activeDataset; |
| otError otErr = otDatasetGetActive(mOTInst, &activeDataset); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| uint64_t activeTimestamp = activeDataset.mPendingTimestamp; |
| err = encoder.Encode(activeTimestamp); |
| } |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::PendingTimestamp::Id: { |
| err = CHIP_ERROR_INCORRECT_STATE; |
| if (otDatasetIsCommissioned(mOTInst)) |
| { |
| otOperationalDataset activeDataset; |
| otError otErr = otDatasetGetActive(mOTInst, &activeDataset); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| uint64_t pendingTimestamp = activeDataset.mPendingTimestamp; |
| err = encoder.Encode(pendingTimestamp); |
| } |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::Delay::Id: { |
| err = CHIP_ERROR_INCORRECT_STATE; |
| if (otDatasetIsCommissioned(mOTInst)) |
| { |
| otOperationalDataset activeDataset; |
| otError otErr = otDatasetGetActive(mOTInst, &activeDataset); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| uint32_t delay = activeDataset.mDelay; |
| err = encoder.Encode(delay); |
| } |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::SecurityPolicy::Id: { |
| err = CHIP_ERROR_INCORRECT_STATE; |
| |
| if (otDatasetIsCommissioned(mOTInst)) |
| { |
| otOperationalDataset activeDataset; |
| otError otErr = otDatasetGetActive(mOTInst, &activeDataset); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| ThreadNetworkDiagnostics::Structs::SecurityPolicy::Type securityPolicy; |
| static_assert(sizeof(securityPolicy) == sizeof(activeDataset.mSecurityPolicy), |
| "securityPolicy Struct do not match otSecurityPolicy"); |
| uint16_t policyAsInts[2]; |
| static_assert(sizeof(policyAsInts) == sizeof(activeDataset.mSecurityPolicy), |
| "We're missing some members of otSecurityPolicy?"); |
| memcpy(&policyAsInts, &activeDataset.mSecurityPolicy, sizeof(policyAsInts)); |
| securityPolicy.rotationTime = policyAsInts[0]; |
| securityPolicy.flags = policyAsInts[1]; |
| |
| err = encoder.EncodeList([securityPolicy](const auto & aEncoder) -> CHIP_ERROR { |
| ReturnErrorOnFailure(aEncoder.Encode(securityPolicy)); |
| return CHIP_NO_ERROR; |
| }); |
| } |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::ChannelMask::Id: { |
| err = CHIP_ERROR_INCORRECT_STATE; |
| if (otDatasetIsCommissioned(mOTInst)) |
| { |
| otOperationalDataset activeDataset; |
| otError otErr = otDatasetGetActive(mOTInst, &activeDataset); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| // In the resultant Octet string, the most significant bit of the left-most byte indicates channel 0 |
| // We have to bitswap the entire uint32_t before converting to octet string |
| uint32_t bitSwappedChannelMask = 0; |
| for (int i = 0, j = 31; i < 32; i++, j--) |
| { |
| bitSwappedChannelMask |= ((activeDataset.mChannelMask >> j) & 1) << i; |
| } |
| |
| uint8_t buffer[sizeof(uint32_t)] = { 0 }; |
| Encoding::BigEndian::Put32(buffer, bitSwappedChannelMask); |
| err = encoder.Encode(ByteSpan(buffer)); |
| } |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::OperationalDatasetComponents::Id: { |
| err = CHIP_ERROR_INCORRECT_STATE; |
| if (otDatasetIsCommissioned(mOTInst)) |
| { |
| otOperationalDataset activeDataset; |
| otError otErr = otDatasetGetActive(mOTInst, &activeDataset); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| ThreadNetworkDiagnostics::Structs::OperationalDatasetComponents::Type OpDatasetComponents; |
| |
| OpDatasetComponents.activeTimestampPresent = activeDataset.mComponents.mIsActiveTimestampPresent; |
| OpDatasetComponents.pendingTimestampPresent = activeDataset.mComponents.mIsPendingTimestampPresent; |
| OpDatasetComponents.masterKeyPresent = activeDataset.mComponents.mIsNetworkKeyPresent; |
| OpDatasetComponents.networkNamePresent = activeDataset.mComponents.mIsNetworkNamePresent; |
| OpDatasetComponents.extendedPanIdPresent = activeDataset.mComponents.mIsExtendedPanIdPresent; |
| OpDatasetComponents.meshLocalPrefixPresent = activeDataset.mComponents.mIsMeshLocalPrefixPresent; |
| OpDatasetComponents.delayPresent = activeDataset.mComponents.mIsDelayPresent; |
| OpDatasetComponents.panIdPresent = activeDataset.mComponents.mIsPanIdPresent; |
| OpDatasetComponents.channelPresent = activeDataset.mComponents.mIsChannelPresent; |
| OpDatasetComponents.pskcPresent = activeDataset.mComponents.mIsPskcPresent; |
| OpDatasetComponents.securityPolicyPresent = activeDataset.mComponents.mIsSecurityPolicyPresent; |
| OpDatasetComponents.channelMaskPresent = activeDataset.mComponents.mIsChannelMaskPresent; |
| |
| err = encoder.EncodeList([OpDatasetComponents](const auto & aEncoder) -> CHIP_ERROR { |
| ReturnErrorOnFailure(aEncoder.Encode(OpDatasetComponents)); |
| return CHIP_NO_ERROR; |
| }); |
| } |
| } |
| break; |
| |
| case ThreadNetworkDiagnostics::Attributes::ActiveNetworkFaultsList::Id: { |
| err = encoder.EncodeList([](const auto & aEncoder) -> CHIP_ERROR { |
| // TODO activeNetworkFaultsList isn't tracked. Encode the list of 4 entries at 0 none the less |
| ThreadNetworkDiagnostics::NetworkFault activeNetworkFaultsList[4] = { ThreadNetworkDiagnostics::NetworkFault(0) }; |
| for (auto fault : activeNetworkFaultsList) |
| { |
| ReturnErrorOnFailure(aEncoder.Encode(fault)); |
| } |
| |
| return CHIP_NO_ERROR; |
| }); |
| } |
| break; |
| |
| default: { |
| err = CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; |
| } |
| break; |
| } |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "_WriteThreadNetworkDiagnosticAttributeToTlv failed: %s", ErrorStr(err)); |
| } |
| return err; |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetPollPeriod(uint32_t & buf) |
| { |
| Impl()->LockThreadStack(); |
| buf = otLinkGetPollPeriod(mOTInst); |
| Impl()->UnlockThreadStack(); |
| return CHIP_NO_ERROR; |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::DoInit(otInstance * otInst) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| otError otErr = OT_ERROR_NONE; |
| |
| // Arrange for OpenThread errors to be translated to text. |
| RegisterOpenThreadErrorFormatter(); |
| |
| mOTInst = NULL; |
| |
| // If an OpenThread instance hasn't been supplied, call otInstanceInitSingle() to |
| // create or acquire a singleton instance of OpenThread. |
| if (otInst == NULL) |
| { |
| otInst = otInstanceInitSingle(); |
| VerifyOrExit(otInst != NULL, err = MapOpenThreadError(OT_ERROR_FAILED)); |
| } |
| |
| #if !defined(PW_RPC_ENABLED) && CHIP_DEVICE_CONFIG_THREAD_ENABLE_CLI |
| otAppCliInit(otInst); |
| #endif |
| |
| mOTInst = otInst; |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_SED |
| ConnectivityManager::SEDIntervalsConfig sedIntervalsConfig; |
| using namespace System::Clock::Literals; |
| sedIntervalsConfig.ActiveIntervalMS = CHIP_DEVICE_CONFIG_SED_ACTIVE_INTERVAL; |
| sedIntervalsConfig.IdleIntervalMS = CHIP_DEVICE_CONFIG_SED_IDLE_INTERVAL; |
| err = _SetSEDIntervalsConfig(sedIntervalsConfig); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(DeviceLayer, "Failed to set sleepy end device intervals: %s", ErrorStr(err)); |
| } |
| SuccessOrExit(err); |
| #endif |
| |
| // Arrange for OpenThread to call the OnOpenThreadStateChange method whenever a |
| // state change occurs. Note that we reference the OnOpenThreadStateChange method |
| // on the concrete implementation class so that that class can override the default |
| // method implementation if it chooses to. |
| otErr = otSetStateChangedCallback(otInst, ImplClass::OnOpenThreadStateChange, mOTInst); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| // Enable automatic assignment of Thread advertised addresses. |
| #if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE |
| otIp6SetSlaacEnabled(otInst, true); |
| #endif |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT |
| otSrpClientSetCallback(mOTInst, &OnSrpClientNotification, nullptr); |
| otSrpClientEnableAutoStartMode(mOTInst, &OnSrpClientStateChange, nullptr); |
| memset(&mSrpClient, 0, sizeof(mSrpClient)); |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT |
| |
| // If the Thread stack has been provisioned, but is not currently enabled, enable it now. |
| if (otThreadGetDeviceRole(mOTInst) == OT_DEVICE_ROLE_DISABLED && otDatasetIsCommissioned(otInst)) |
| { |
| // Enable the Thread IPv6 interface. |
| otErr = otIp6SetEnabled(otInst, true); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| otErr = otThreadSetEnabled(otInst, true); |
| VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr)); |
| |
| ChipLogProgress(DeviceLayer, "OpenThread ifconfig up and thread start"); |
| } |
| |
| initNetworkCommissioningThreadDriver(); |
| |
| exit: |
| |
| ChipLogProgress(DeviceLayer, "OpenThread started: %s", otThreadErrorToString(otErr)); |
| return err; |
| } |
| |
| template <class ImplClass> |
| bool GenericThreadStackManagerImpl_OpenThread<ImplClass>::IsThreadAttachedNoLock(void) |
| { |
| otDeviceRole curRole = otThreadGetDeviceRole(mOTInst); |
| return (curRole != OT_DEVICE_ROLE_DISABLED && curRole != OT_DEVICE_ROLE_DETACHED); |
| } |
| |
| template <class ImplClass> |
| bool GenericThreadStackManagerImpl_OpenThread<ImplClass>::IsThreadInterfaceUpNoLock(void) |
| { |
| return otIp6IsEnabled(mOTInst); |
| } |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_SED |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetSEDIntervalsConfig( |
| ConnectivityManager::SEDIntervalsConfig & intervalsConfig) |
| { |
| intervalsConfig = mIntervalsConfig; |
| return CHIP_NO_ERROR; |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_SetSEDIntervalsConfig( |
| const ConnectivityManager::SEDIntervalsConfig & intervalsConfig) |
| { |
| using namespace System::Clock::Literals; |
| if ((intervalsConfig.IdleIntervalMS < intervalsConfig.ActiveIntervalMS) || (intervalsConfig.IdleIntervalMS == 0_ms32) || |
| (intervalsConfig.ActiveIntervalMS == 0_ms32)) |
| { |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| mIntervalsConfig = intervalsConfig; |
| |
| CHIP_ERROR err = SetSEDIntervalMode(mIntervalsMode); |
| |
| if (err == CHIP_NO_ERROR) |
| { |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kSEDIntervalChange; |
| err = chip::DeviceLayer::PlatformMgr().PostEvent(&event); |
| } |
| |
| return err; |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR |
| GenericThreadStackManagerImpl_OpenThread<ImplClass>::SetSEDIntervalMode(ConnectivityManager::SEDIntervalMode intervalType) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| System::Clock::Milliseconds32 interval; |
| |
| if (intervalType == ConnectivityManager::SEDIntervalMode::Idle) |
| { |
| interval = mIntervalsConfig.IdleIntervalMS; |
| } |
| else if (intervalType == ConnectivityManager::SEDIntervalMode::Active) |
| { |
| interval = mIntervalsConfig.ActiveIntervalMS; |
| } |
| else |
| { |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| mIntervalsMode = intervalType; |
| |
| Impl()->LockThreadStack(); |
| |
| // For Thread devices, the intervals are defined as: |
| // * poll period for SED devices that poll the parent for data |
| // * CSL period for SSED devices that listen for messages in scheduled time slots. |
| #if CHIP_DEVICE_CONFIG_THREAD_SSED |
| // Get CSL period in units of 10 symbols, convert it to microseconds and divide by 1000 to get milliseconds. |
| uint32_t curIntervalMS = otLinkCslGetPeriod(mOTInst) * OT_US_PER_TEN_SYMBOLS / 1000; |
| #else |
| uint32_t curIntervalMS = otLinkGetPollPeriod(mOTInst); |
| #endif |
| |
| if (interval.count() != curIntervalMS) |
| { |
| #if CHIP_DEVICE_CONFIG_THREAD_SSED |
| // Set CSL period in units of 10 symbols, convert it to microseconds and divide by 1000 to get milliseconds. |
| otError otErr = otLinkCslSetPeriod(mOTInst, interval.count() * 1000 / OT_US_PER_TEN_SYMBOLS); |
| #else |
| otError otErr = otLinkSetPollPeriod(mOTInst, interval.count()); |
| #endif |
| err = MapOpenThreadError(otErr); |
| } |
| |
| Impl()->UnlockThreadStack(); |
| |
| if (interval.count() != curIntervalMS) |
| { |
| ChipLogProgress(DeviceLayer, "OpenThread SED interval set to %" PRId32 "ms", interval.count()); |
| } |
| |
| return err; |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_RequestSEDActiveMode(bool onOff) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| ConnectivityManager::SEDIntervalMode mode; |
| |
| if (onOff) |
| { |
| mActiveModeConsumers++; |
| } |
| else |
| { |
| if (mActiveModeConsumers > 0) |
| mActiveModeConsumers--; |
| } |
| |
| mode = mActiveModeConsumers > 0 ? ConnectivityManager::SEDIntervalMode::Active : ConnectivityManager::SEDIntervalMode::Idle; |
| |
| if (mIntervalsMode != mode) |
| err = SetSEDIntervalMode(mode); |
| |
| return err; |
| } |
| #endif |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::_ErasePersistentInfo(void) |
| { |
| ChipLogProgress(DeviceLayer, "Erasing Thread persistent info..."); |
| Impl()->LockThreadStack(); |
| otThreadSetEnabled(mOTInst, false); |
| otInstanceErasePersistentInfo(mOTInst); |
| Impl()->UnlockThreadStack(); |
| } |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::OnJoinerComplete(otError aError, void * aContext) |
| { |
| static_cast<GenericThreadStackManagerImpl_OpenThread *>(aContext)->OnJoinerComplete(aError); |
| } |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::OnJoinerComplete(otError aError) |
| { |
| #if CHIP_PROGRESS_LOGGING |
| |
| ChipLogProgress(DeviceLayer, "Join Thread network: %s", otThreadErrorToString(aError)); |
| |
| if (aError == OT_ERROR_NONE) |
| { |
| otError error = otThreadSetEnabled(mOTInst, true); |
| |
| ChipLogProgress(DeviceLayer, "Start Thread network: %s", otThreadErrorToString(error)); |
| } |
| #endif // CHIP_PROGRESS_LOGGING |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_JoinerStart(void) |
| { |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| |
| Impl()->LockThreadStack(); |
| VerifyOrExit(!otDatasetIsCommissioned(mOTInst) && otThreadGetDeviceRole(mOTInst) == OT_DEVICE_ROLE_DISABLED, |
| error = MapOpenThreadError(OT_ERROR_INVALID_STATE)); |
| VerifyOrExit(otJoinerGetState(mOTInst) == OT_JOINER_STATE_IDLE, error = MapOpenThreadError(OT_ERROR_BUSY)); |
| |
| if (!otIp6IsEnabled(mOTInst)) |
| { |
| SuccessOrExit(error = MapOpenThreadError(otIp6SetEnabled(mOTInst, true))); |
| } |
| |
| { |
| otJoinerDiscerner discerner; |
| // This is dead code to remove, so the placeholder value is OK. |
| // See ThreadStackManagerImpl. |
| uint16_t discriminator = 3840; |
| |
| discerner.mLength = 12; |
| discerner.mValue = discriminator; |
| |
| ChipLogProgress(DeviceLayer, "Joiner Discerner: %u", discriminator); |
| otJoinerSetDiscerner(mOTInst, &discerner); |
| } |
| |
| { |
| otJoinerPskd pskd; |
| // This is dead code to remove, so the placeholder value is OK.d |
| // See ThreadStackManagerImpl. |
| uint32_t pincode = 20202021; |
| |
| snprintf(pskd.m8, sizeof(pskd.m8) - 1, "%09" PRIu32, pincode); |
| |
| ChipLogProgress(DeviceLayer, "Joiner PSKd: %s", pskd.m8); |
| error = MapOpenThreadError(otJoinerStart(mOTInst, pskd.m8, NULL, NULL, NULL, NULL, NULL, |
| &GenericThreadStackManagerImpl_OpenThread::OnJoinerComplete, this)); |
| } |
| |
| exit: |
| Impl()->UnlockThreadStack(); |
| |
| ChipLogProgress(DeviceLayer, "Joiner start: %s", chip::ErrorStr(error)); |
| |
| return error; |
| } |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::_UpdateNetworkStatus() |
| { |
| // Thread is not enabled, then we are not trying to connect to the network. |
| VerifyOrReturn(ThreadStackMgrImpl().IsThreadEnabled() && mpStatusChangeCallback != nullptr); |
| |
| ByteSpan datasetTLV; |
| Thread::OperationalDataset dataset; |
| ByteSpan extpanid; |
| |
| // If we have not provisioned any Thread network, return the status from last network scan, |
| // If we have provisioned a network, we assume the ot-br-posix is activitely connecting to that network. |
| ReturnOnFailure(ThreadStackMgrImpl().GetThreadProvision(dataset)); |
| // The Thread network is not enabled, but has a different extended pan id. |
| ReturnOnFailure(dataset.GetExtendedPanIdAsByteSpan(extpanid)); |
| // If we don't have a valid dataset, we are not attempting to connect the network. |
| |
| // We have already connected to the network, thus return success. |
| if (ThreadStackMgrImpl().IsThreadAttached()) |
| { |
| mpStatusChangeCallback->OnNetworkingStatusChange(NetworkCommissioning::Status::kSuccess, MakeOptional(extpanid), |
| NullOptional); |
| } |
| else |
| { |
| mpStatusChangeCallback->OnNetworkingStatusChange(NetworkCommissioning::Status::kNetworkNotFound, MakeOptional(extpanid), |
| MakeOptional(static_cast<int32_t>(OT_ERROR_DETACHED))); |
| } |
| } |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT |
| |
| static_assert(OPENTHREAD_API_VERSION >= 156, "SRP Client requires a more recent OpenThread version"); |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::OnSrpClientNotification(otError aError, |
| const otSrpClientHostInfo * aHostInfo, |
| const otSrpClientService * aServices, |
| const otSrpClientService * aRemovedServices, |
| void * aContext) |
| { |
| switch (aError) |
| { |
| case OT_ERROR_NONE: { |
| ChipLogDetail(DeviceLayer, "OnSrpClientNotification: Last requested operation completed successfully"); |
| |
| if (aHostInfo) |
| { |
| if (aHostInfo->mState == OT_SRP_CLIENT_ITEM_STATE_REMOVED) |
| { |
| // Clear memory for removed host |
| memset(ThreadStackMgrImpl().mSrpClient.mHostName, 0, sizeof(ThreadStackMgrImpl().mSrpClient.mHostName)); |
| |
| ThreadStackMgrImpl().mSrpClient.mIsInitialized = true; |
| ThreadStackMgrImpl().mSrpClient.mInitializedCallback(ThreadStackMgrImpl().mSrpClient.mCallbackContext, |
| CHIP_NO_ERROR); |
| } |
| } |
| |
| if (aRemovedServices) |
| { |
| otSrpClientService * otService = const_cast<otSrpClientService *>(aRemovedServices); |
| otSrpClientService * next = nullptr; |
| using Service = typename SrpClient::Service; |
| |
| // Clear memory for all removed services. |
| do |
| { |
| next = otService->mNext; |
| auto service = reinterpret_cast<Service *>(reinterpret_cast<size_t>(otService) - offsetof(Service, mService)); |
| memset(service, 0, sizeof(Service)); |
| otService = next; |
| } while (otService); |
| } |
| break; |
| } |
| case OT_ERROR_PARSE: |
| ChipLogError(DeviceLayer, "OnSrpClientNotification: Parsing operaton failed"); |
| break; |
| case OT_ERROR_NOT_FOUND: |
| ChipLogError(DeviceLayer, "OnSrpClientNotification: Domain name or RRset does not exist"); |
| break; |
| case OT_ERROR_NOT_IMPLEMENTED: |
| ChipLogError(DeviceLayer, "OnSrpClientNotification: Server does not support query type"); |
| break; |
| case OT_ERROR_SECURITY: |
| ChipLogError(DeviceLayer, "OnSrpClientNotification: Operation refused for security reasons"); |
| break; |
| case OT_ERROR_DUPLICATED: |
| ChipLogError(DeviceLayer, "OnSrpClientNotification: Domain name or RRset is duplicated"); |
| break; |
| case OT_ERROR_RESPONSE_TIMEOUT: |
| ChipLogError(DeviceLayer, "OnSrpClientNotification: Timed out waiting on server response"); |
| break; |
| case OT_ERROR_INVALID_ARGS: |
| ChipLogError(DeviceLayer, "OnSrpClientNotification: Invalid service structure detected"); |
| break; |
| case OT_ERROR_NO_BUFS: |
| ChipLogError(DeviceLayer, "OnSrpClientNotification: Insufficient buffer to handle message"); |
| break; |
| case OT_ERROR_FAILED: |
| ChipLogError(DeviceLayer, "OnSrpClientNotification: Internal server error occurred"); |
| break; |
| default: |
| ChipLogError(DeviceLayer, "OnSrpClientNotification: Unknown error occurred"); |
| break; |
| } |
| } |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::OnSrpClientStateChange(const otSockAddr * aServerSockAddr, |
| void * aContext) |
| { |
| if (aServerSockAddr) |
| { |
| ChipLogProgress(DeviceLayer, "SRP Client was started, detected server: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", |
| Encoding::BigEndian::HostSwap16(aServerSockAddr->mAddress.mFields.m16[0]), |
| Encoding::BigEndian::HostSwap16(aServerSockAddr->mAddress.mFields.m16[1]), |
| Encoding::BigEndian::HostSwap16(aServerSockAddr->mAddress.mFields.m16[2]), |
| Encoding::BigEndian::HostSwap16(aServerSockAddr->mAddress.mFields.m16[3]), |
| Encoding::BigEndian::HostSwap16(aServerSockAddr->mAddress.mFields.m16[4]), |
| Encoding::BigEndian::HostSwap16(aServerSockAddr->mAddress.mFields.m16[5]), |
| Encoding::BigEndian::HostSwap16(aServerSockAddr->mAddress.mFields.m16[6]), |
| Encoding::BigEndian::HostSwap16(aServerSockAddr->mAddress.mFields.m16[7])); |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT |
| // Set DNS server config to be set at the SRP server address |
| otDnsQueryConfig dnsConfig = *otDnsClientGetDefaultConfig(ThreadStackMgrImpl().OTInstance()); |
| dnsConfig.mServerSockAddr.mAddress = aServerSockAddr->mAddress; |
| otDnsClientSetDefaultConfig(ThreadStackMgrImpl().OTInstance(), &dnsConfig); |
| #endif |
| } |
| else |
| { |
| ChipLogProgress(DeviceLayer, "SRP Client was stopped, because current server is no longer detected."); |
| } |
| } |
| |
| template <class ImplClass> |
| bool GenericThreadStackManagerImpl_OpenThread<ImplClass>::SrpClient::Service::Matches(const char * aInstanceName, |
| const char * aName) const |
| { |
| return IsUsed() && (strcmp(mService.mInstanceName, aInstanceName) == 0) && (strcmp(mService.mName, aName) == 0); |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_AddSrpService(const char * aInstanceName, const char * aName, |
| uint16_t aPort, |
| const Span<const char * const> & aSubTypes, |
| const Span<const Dnssd::TextEntry> & aTxtEntries, |
| uint32_t aLeaseInterval, uint32_t aKeyLeaseInterval) |
| { |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| typename SrpClient::Service * srpService = nullptr; |
| size_t entryId = 0; |
| FixedBufferAllocator alloc; |
| |
| Impl()->LockThreadStack(); |
| |
| VerifyOrExit(mSrpClient.mIsInitialized, error = CHIP_ERROR_WELL_UNINITIALIZED); |
| VerifyOrExit(aInstanceName, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(aName, error = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| // Try to find an empty slot in array for the new service and |
| // remove the possible existing entry from anywhere in the list |
| for (typename SrpClient::Service & service : mSrpClient.mServices) |
| { |
| // Remove possible existing entry |
| if (service.Matches(aInstanceName, aName)) |
| { |
| SuccessOrExit(error = MapOpenThreadError(otSrpClientClearService(mOTInst, &service.mService))); |
| // Clear memory immediately, as OnSrpClientNotification will not be called. |
| memset(&service, 0, sizeof(service)); |
| } |
| |
| if ((srpService == nullptr) && !service.IsUsed()) |
| { |
| // Assign first empty slot found in array for a new service. |
| srpService = &service; |
| |
| // Keep looping to remove possible existing entry further in the list |
| } |
| } |
| |
| // Verify there is a slot found for the new service. |
| VerifyOrExit(srpService != nullptr, error = CHIP_ERROR_BUFFER_TOO_SMALL); |
| alloc.Init(srpService->mServiceBuffer); |
| |
| otSrpClientSetLeaseInterval(mOTInst, aLeaseInterval); |
| otSrpClientSetKeyLeaseInterval(mOTInst, aKeyLeaseInterval); |
| |
| srpService->mService.mInstanceName = alloc.Clone(aInstanceName); |
| srpService->mService.mName = alloc.Clone(aName); |
| srpService->mService.mPort = aPort; |
| |
| VerifyOrExit(aSubTypes.size() < ArraySize(srpService->mSubTypes), error = CHIP_ERROR_BUFFER_TOO_SMALL); |
| entryId = 0; |
| |
| for (const char * subType : aSubTypes) |
| { |
| srpService->mSubTypes[entryId++] = alloc.Clone(subType); |
| } |
| |
| srpService->mSubTypes[entryId] = nullptr; |
| srpService->mService.mSubTypeLabels = srpService->mSubTypes; |
| |
| // Initialize TXT entries |
| VerifyOrExit(aTxtEntries.size() <= ArraySize(srpService->mTxtEntries), error = CHIP_ERROR_BUFFER_TOO_SMALL); |
| entryId = 0; |
| |
| for (const chip::Dnssd::TextEntry & entry : aTxtEntries) |
| { |
| using OtTxtValueLength = decltype(srpService->mTxtEntries[entryId].mValueLength); |
| static_assert(SrpClient::kServiceBufferSize <= std::numeric_limits<OtTxtValueLength>::max(), |
| "DNS TXT value length may not fit in otDnsTxtEntry structure"); |
| |
| // TXT entry keys are constants, so they don't need to be cloned |
| srpService->mTxtEntries[entryId].mKey = entry.mKey; |
| srpService->mTxtEntries[entryId].mValue = alloc.Clone(entry.mData, entry.mDataSize); |
| srpService->mTxtEntries[entryId].mValueLength = static_cast<OtTxtValueLength>(entry.mDataSize); |
| ++entryId; |
| } |
| |
| using OtNumTxtEntries = decltype(srpService->mService.mNumTxtEntries); |
| static_assert(ArraySize(srpService->mTxtEntries) <= std::numeric_limits<OtNumTxtEntries>::max(), |
| "Number of DNS TXT entries may not fit in otSrpClientService structure"); |
| srpService->mService.mNumTxtEntries = static_cast<OtNumTxtEntries>(aTxtEntries.size()); |
| srpService->mService.mTxtEntries = srpService->mTxtEntries; |
| |
| VerifyOrExit(!alloc.AnyAllocFailed(), error = CHIP_ERROR_BUFFER_TOO_SMALL); |
| |
| ChipLogProgress(DeviceLayer, "advertising srp service: %s.%s", srpService->mService.mInstanceName, srpService->mService.mName); |
| error = MapOpenThreadError(otSrpClientAddService(mOTInst, &(srpService->mService))); |
| |
| exit: |
| if (srpService != nullptr && error != CHIP_NO_ERROR) |
| { |
| memset(srpService, 0, sizeof(*srpService)); |
| } |
| |
| Impl()->UnlockThreadStack(); |
| return error; |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_RemoveSrpService(const char * aInstanceName, const char * aName) |
| { |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| typename SrpClient::Service * srpService = nullptr; |
| |
| VerifyOrExit(mSrpClient.mIsInitialized, error = CHIP_ERROR_WELL_UNINITIALIZED); |
| |
| Impl()->LockThreadStack(); |
| |
| VerifyOrExit(aInstanceName, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(aName, error = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| // Check if service to remove exists. |
| for (typename SrpClient::Service & service : mSrpClient.mServices) |
| { |
| if (service.Matches(aInstanceName, aName)) |
| { |
| srpService = &service; |
| break; |
| } |
| } |
| |
| VerifyOrExit(srpService, error = MapOpenThreadError(OT_ERROR_NOT_FOUND)); |
| |
| ChipLogProgress(DeviceLayer, "removing srp service: %s.%s", aInstanceName, aName); |
| error = MapOpenThreadError(otSrpClientRemoveService(mOTInst, &(srpService->mService))); |
| |
| exit: |
| Impl()->UnlockThreadStack(); |
| |
| return error; |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_InvalidateAllSrpServices() |
| { |
| Impl()->LockThreadStack(); |
| |
| for (typename SrpClient::Service & service : mSrpClient.mServices) |
| { |
| if (service.IsUsed()) |
| { |
| service.mIsInvalid = true; |
| } |
| } |
| |
| Impl()->UnlockThreadStack(); |
| return CHIP_NO_ERROR; |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_RemoveInvalidSrpServices() |
| { |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| |
| VerifyOrExit(mSrpClient.mIsInitialized, error = CHIP_ERROR_WELL_UNINITIALIZED); |
| |
| Impl()->LockThreadStack(); |
| |
| for (typename SrpClient::Service & service : mSrpClient.mServices) |
| { |
| if (service.IsUsed() && service.mIsInvalid) |
| { |
| ChipLogProgress(DeviceLayer, "removing srp service: %s.%s", service.mService.mInstanceName, service.mService.mName); |
| error = MapOpenThreadError(otSrpClientRemoveService(mOTInst, &service.mService)); |
| SuccessOrExit(error); |
| } |
| } |
| |
| exit: |
| Impl()->UnlockThreadStack(); |
| |
| return error; |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_SetupSrpHost(const char * aHostName) |
| { |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| Inet::IPAddress hostAddress; |
| |
| VerifyOrExit(mSrpClient.mIsInitialized, error = CHIP_ERROR_WELL_UNINITIALIZED); |
| |
| Impl()->LockThreadStack(); |
| |
| VerifyOrExit(aHostName, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(strlen(aHostName) <= Dnssd::kHostNameMaxLength, error = CHIP_ERROR_INVALID_STRING_LENGTH); |
| |
| // Avoid adding the same host name multiple times |
| if (strcmp(mSrpClient.mHostName, aHostName) != 0) |
| { |
| strcpy(mSrpClient.mHostName, aHostName); |
| error = MapOpenThreadError(otSrpClientSetHostName(mOTInst, mSrpClient.mHostName)); |
| SuccessOrExit(error); |
| } |
| |
| // Check if device has any external IPv6 assigned. If not, host will be set without IPv6 addresses |
| // and updated later on. |
| if (ThreadStackMgr().GetExternalIPv6Address(hostAddress) == CHIP_NO_ERROR) |
| { |
| memcpy(&mSrpClient.mHostAddress.mFields.m32, hostAddress.Addr, sizeof(hostAddress.Addr)); |
| error = MapOpenThreadError(otSrpClientSetHostAddresses(mOTInst, &mSrpClient.mHostAddress, 1)); |
| } |
| |
| exit: |
| Impl()->UnlockThreadStack(); |
| |
| return error; |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_ClearSrpHost(const char * aHostName) |
| { |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| |
| Impl()->LockThreadStack(); |
| |
| VerifyOrExit(aHostName, error = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(strlen(aHostName) <= Dnssd::kHostNameMaxLength, error = CHIP_ERROR_INVALID_STRING_LENGTH); |
| VerifyOrExit(mSrpClient.mInitializedCallback, error = CHIP_ERROR_INCORRECT_STATE); |
| |
| // Add host and remove it with notifying SRP server to clean old information related to the host. |
| // Avoid adding the same host name multiple times |
| if (strcmp(mSrpClient.mHostName, aHostName) != 0) |
| { |
| strcpy(mSrpClient.mHostName, aHostName); |
| error = MapOpenThreadError(otSrpClientSetHostName(mOTInst, mSrpClient.mHostName)); |
| SuccessOrExit(error); |
| } |
| error = MapOpenThreadError(otSrpClientRemoveHostAndServices(mOTInst, false, true)); |
| |
| exit: |
| Impl()->UnlockThreadStack(); |
| |
| return error; |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_SetSrpDnsCallbacks(DnsAsyncReturnCallback aInitCallback, |
| DnsAsyncReturnCallback aErrorCallback, |
| void * aContext) |
| { |
| mSrpClient.mInitializedCallback = aInitCallback; |
| mSrpClient.mCallbackContext = aContext; |
| return CHIP_NO_ERROR; |
| } |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::FromOtDnsResponseToMdnsData( |
| otDnsServiceInfo & serviceInfo, const char * serviceType, chip::Dnssd::DnssdService & mdnsService, |
| DnsServiceTxtEntries & serviceTxtEntries) |
| { |
| char protocol[chip::Dnssd::kDnssdProtocolTextMaxSize + 1]; |
| |
| if (strchr(serviceInfo.mHostNameBuffer, '.') == nullptr) |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| |
| // Extract from the <hostname>.<domain-name>. the <hostname> part. |
| size_t substringSize = strchr(serviceInfo.mHostNameBuffer, '.') - serviceInfo.mHostNameBuffer; |
| if (substringSize >= ArraySize(mdnsService.mHostName)) |
| { |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| strncpy(mdnsService.mHostName, serviceInfo.mHostNameBuffer, substringSize); |
| // Append string terminating character. |
| mdnsService.mHostName[substringSize] = '\0'; |
| |
| if (strchr(serviceType, '.') == nullptr) |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| |
| // Extract from the <type>.<protocol>.<domain-name>. the <type> part. |
| substringSize = strchr(serviceType, '.') - serviceType; |
| if (substringSize >= ArraySize(mdnsService.mType)) |
| { |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| strncpy(mdnsService.mType, serviceType, substringSize); |
| // Append string terminating character. |
| mdnsService.mType[substringSize] = '\0'; |
| |
| // Extract from the <type>.<protocol>.<domain-name>. the <protocol> part. |
| const char * protocolSubstringStart = serviceType + substringSize + 1; |
| |
| if (strchr(protocolSubstringStart, '.') == nullptr) |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| |
| substringSize = strchr(protocolSubstringStart, '.') - protocolSubstringStart; |
| if (substringSize >= ArraySize(protocol)) |
| { |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| strncpy(protocol, protocolSubstringStart, substringSize); |
| // Append string terminating character. |
| protocol[substringSize] = '\0'; |
| |
| if (strncmp(protocol, "_udp", chip::Dnssd::kDnssdProtocolTextMaxSize) == 0) |
| { |
| mdnsService.mProtocol = chip::Dnssd::DnssdServiceProtocol::kDnssdProtocolUdp; |
| } |
| else if (strncmp(protocol, "_tcp", chip::Dnssd::kDnssdProtocolTextMaxSize) == 0) |
| { |
| mdnsService.mProtocol = chip::Dnssd::DnssdServiceProtocol::kDnssdProtocolTcp; |
| } |
| else |
| { |
| mdnsService.mProtocol = chip::Dnssd::DnssdServiceProtocol::kDnssdProtocolUnknown; |
| } |
| mdnsService.mPort = serviceInfo.mPort; |
| mdnsService.mInterface = Inet::InterfaceId::Null(); |
| mdnsService.mAddressType = Inet::IPAddressType::kIPv6; |
| mdnsService.mAddress = chip::Optional<chip::Inet::IPAddress>(ToIPAddress(serviceInfo.mHostAddress)); |
| |
| otDnsTxtEntryIterator iterator; |
| otDnsInitTxtEntryIterator(&iterator, serviceInfo.mTxtData, serviceInfo.mTxtDataSize); |
| |
| otDnsTxtEntry txtEntry; |
| FixedBufferAllocator alloc(serviceTxtEntries.mBuffer); |
| |
| uint8_t entryIndex = 0; |
| while ((otDnsGetNextTxtEntry(&iterator, &txtEntry) == OT_ERROR_NONE) && entryIndex < kMaxDnsServiceTxtEntriesNumber) |
| { |
| if (txtEntry.mKey == nullptr || txtEntry.mValue == nullptr) |
| continue; |
| |
| serviceTxtEntries.mTxtEntries[entryIndex].mKey = alloc.Clone(txtEntry.mKey); |
| serviceTxtEntries.mTxtEntries[entryIndex].mData = alloc.Clone(txtEntry.mValue, txtEntry.mValueLength); |
| serviceTxtEntries.mTxtEntries[entryIndex].mDataSize = txtEntry.mValueLength; |
| entryIndex++; |
| } |
| |
| ReturnErrorCodeIf(alloc.AnyAllocFailed(), CHIP_ERROR_BUFFER_TOO_SMALL); |
| |
| mdnsService.mTextEntries = serviceTxtEntries.mTxtEntries; |
| mdnsService.mTextEntrySize = entryIndex; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::DispatchResolve(intptr_t context) |
| { |
| auto * dnsResult = reinterpret_cast<DnsResult *>(context); |
| ThreadStackMgrImpl().mDnsResolveCallback(dnsResult->context, &(dnsResult->mMdnsService), Span<Inet::IPAddress>(), |
| dnsResult->error); |
| Platform::Delete<DnsResult>(dnsResult); |
| } |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::DispatchBrowseEmpty(intptr_t context) |
| { |
| auto * dnsResult = reinterpret_cast<DnsResult *>(context); |
| ThreadStackMgrImpl().mDnsBrowseCallback(dnsResult->context, nullptr, 0, dnsResult->error); |
| Platform::Delete<DnsResult>(dnsResult); |
| } |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::DispatchBrowse(intptr_t context) |
| { |
| auto * dnsResult = reinterpret_cast<DnsResult *>(context); |
| ThreadStackMgrImpl().mDnsBrowseCallback(dnsResult->context, &dnsResult->mMdnsService, 1, dnsResult->error); |
| Platform::Delete<DnsResult>(dnsResult); |
| } |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::OnDnsBrowseResult(otError aError, const otDnsBrowseResponse * aResponse, |
| void * aContext) |
| { |
| CHIP_ERROR error; |
| // type buffer size is kDnssdTypeAndProtocolMaxSize + . + kMaxDomainNameSize + . + termination character |
| char type[Dnssd::kDnssdTypeAndProtocolMaxSize + SrpClient::kMaxDomainNameSize + 3]; |
| // hostname buffer size is kHostNameMaxLength + . + kMaxDomainNameSize + . + termination character |
| char hostname[Dnssd::kHostNameMaxLength + SrpClient::kMaxDomainNameSize + 3]; |
| // secure space for the raw TXT data in the worst-case scenario relevant for Matter: |
| // each entry consists of txt_entry_size (1B) + txt_entry_key + "=" + txt_entry_data |
| uint8_t txtBuffer[kMaxDnsServiceTxtEntriesNumber + kTotalDnsServiceTxtBufferSize]; |
| otDnsServiceInfo serviceInfo; |
| uint16_t index = 0; |
| bool wasAnythingBrowsed = false; |
| |
| if (ThreadStackMgrImpl().mDnsBrowseCallback == nullptr) |
| { |
| ChipLogError(DeviceLayer, "Invalid dns browse callback"); |
| return; |
| } |
| |
| VerifyOrExit(aError == OT_ERROR_NONE, error = MapOpenThreadError(aError)); |
| |
| error = MapOpenThreadError(otDnsBrowseResponseGetServiceName(aResponse, type, sizeof(type))); |
| |
| VerifyOrExit(error == CHIP_NO_ERROR, ); |
| |
| char serviceName[Dnssd::Common::kInstanceNameMaxLength + 1]; |
| while (otDnsBrowseResponseGetServiceInstance(aResponse, index, serviceName, sizeof(serviceName)) == OT_ERROR_NONE) |
| { |
| serviceInfo.mHostNameBuffer = hostname; |
| serviceInfo.mHostNameBufferSize = sizeof(hostname); |
| serviceInfo.mTxtData = txtBuffer; |
| serviceInfo.mTxtDataSize = sizeof(txtBuffer); |
| |
| error = MapOpenThreadError(otDnsBrowseResponseGetServiceInfo(aResponse, serviceName, &serviceInfo)); |
| |
| VerifyOrExit(error == CHIP_NO_ERROR, ); |
| |
| DnsResult * dnsResult = Platform::New<DnsResult>(aContext, MapOpenThreadError(aError)); |
| error = FromOtDnsResponseToMdnsData(serviceInfo, type, dnsResult->mMdnsService, dnsResult->mServiceTxtEntry); |
| if (CHIP_NO_ERROR == error) |
| { |
| // Invoke callback for every service one by one instead of for the whole list due to large memory size needed to |
| // allocate on |
| // stack. |
| static_assert(ArraySize(dnsResult->mMdnsService.mName) >= ArraySize(serviceName), |
| "The target buffer must be big enough"); |
| Platform::CopyString(dnsResult->mMdnsService.mName, serviceName); |
| DeviceLayer::PlatformMgr().ScheduleWork(DispatchBrowse, reinterpret_cast<intptr_t>(dnsResult)); |
| wasAnythingBrowsed = true; |
| } |
| else |
| { |
| Platform::Delete<DnsResult>(dnsResult); |
| } |
| index++; |
| } |
| |
| exit: |
| |
| // In case no service was found invoke callback to notify about failure. In other case it was already called before. |
| if (!wasAnythingBrowsed) |
| { |
| DnsResult * dnsResult = Platform::New<DnsResult>(aContext, MapOpenThreadError(aError)); |
| DeviceLayer::PlatformMgr().ScheduleWork(DispatchBrowseEmpty, reinterpret_cast<intptr_t>(dnsResult)); |
| } |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_DnsBrowse(const char * aServiceName, DnsBrowseCallback aCallback, |
| void * aContext) |
| { |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| |
| Impl()->LockThreadStack(); |
| |
| VerifyOrExit(aServiceName, error = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| mDnsBrowseCallback = aCallback; |
| |
| // Append default SRP domain name to the service name. |
| // fullServiceName buffer size is kDnssdFullTypeAndProtocolMaxSize + . + kDefaultDomainNameSize + null-terminator. |
| char fullServiceName[Dnssd::kDnssdFullTypeAndProtocolMaxSize + 1 + SrpClient::kDefaultDomainNameSize + 1]; |
| snprintf(fullServiceName, sizeof(fullServiceName), "%s.%s", aServiceName, SrpClient::kDefaultDomainName); |
| |
| error = MapOpenThreadError(otDnsClientBrowse(mOTInst, fullServiceName, OnDnsBrowseResult, aContext, /* config */ nullptr)); |
| |
| exit: |
| |
| Impl()->UnlockThreadStack(); |
| |
| return error; |
| } |
| |
| template <class ImplClass> |
| void GenericThreadStackManagerImpl_OpenThread<ImplClass>::OnDnsResolveResult(otError aError, const otDnsServiceResponse * aResponse, |
| void * aContext) |
| { |
| CHIP_ERROR error; |
| DnsResult * dnsResult = Platform::New<DnsResult>(aContext, MapOpenThreadError(aError)); |
| // type buffer size is kDnssdTypeAndProtocolMaxSize + . + kMaxDomainNameSize + . + termination character |
| char type[Dnssd::kDnssdTypeAndProtocolMaxSize + SrpClient::kMaxDomainNameSize + 3]; |
| // hostname buffer size is kHostNameMaxLength + . + kMaxDomainNameSize + . + termination character |
| char hostname[Dnssd::kHostNameMaxLength + SrpClient::kMaxDomainNameSize + 3]; |
| // secure space for the raw TXT data in the worst-case scenario relevant for Matter: |
| // each entry consists of txt_entry_size (1B) + txt_entry_key + "=" + txt_entry_data |
| uint8_t txtBuffer[kMaxDnsServiceTxtEntriesNumber + kTotalDnsServiceTxtBufferSize]; |
| otDnsServiceInfo serviceInfo; |
| |
| if (ThreadStackMgrImpl().mDnsResolveCallback == nullptr) |
| { |
| ChipLogError(DeviceLayer, "Invalid dns browse callback"); |
| return; |
| } |
| |
| VerifyOrExit(aError == OT_ERROR_NONE, error = MapOpenThreadError(aError)); |
| |
| error = MapOpenThreadError(otDnsServiceResponseGetServiceName(aResponse, dnsResult->mMdnsService.mName, |
| sizeof(dnsResult->mMdnsService.mName), type, sizeof(type))); |
| |
| VerifyOrExit(error == CHIP_NO_ERROR, ); |
| |
| serviceInfo.mHostNameBuffer = hostname; |
| serviceInfo.mHostNameBufferSize = sizeof(hostname); |
| serviceInfo.mTxtData = txtBuffer; |
| serviceInfo.mTxtDataSize = sizeof(txtBuffer); |
| |
| error = MapOpenThreadError(otDnsServiceResponseGetServiceInfo(aResponse, &serviceInfo)); |
| |
| VerifyOrExit(error == CHIP_NO_ERROR, ); |
| |
| error = FromOtDnsResponseToMdnsData(serviceInfo, type, dnsResult->mMdnsService, dnsResult->mServiceTxtEntry); |
| |
| exit: |
| |
| dnsResult->error = error; |
| DeviceLayer::PlatformMgr().ScheduleWork(DispatchResolve, reinterpret_cast<intptr_t>(dnsResult)); |
| } |
| |
| template <class ImplClass> |
| CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_DnsResolve(const char * aServiceName, const char * aInstanceName, |
| DnsResolveCallback aCallback, void * aContext) |
| { |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| |
| Impl()->LockThreadStack(); |
| const otDnsQueryConfig * defaultConfig = otDnsClientGetDefaultConfig(mOTInst); |
| |
| VerifyOrExit(aServiceName && aInstanceName, error = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| mDnsResolveCallback = aCallback; |
| |
| // Append default SRP domain name to the service name. |
| // fullServiceName buffer size is kDnssdTypeAndProtocolMaxSize + . separator + kDefaultDomainNameSize + termination character. |
| char fullServiceName[Dnssd::kDnssdTypeAndProtocolMaxSize + 1 + SrpClient::kDefaultDomainNameSize + 1]; |
| snprintf(fullServiceName, sizeof(fullServiceName), "%s.%s", aServiceName, SrpClient::kDefaultDomainName); |
| |
| error = MapOpenThreadError( |
| otDnsClientResolveService(mOTInst, aInstanceName, fullServiceName, OnDnsResolveResult, aContext, defaultConfig)); |
| |
| exit: |
| |
| Impl()->UnlockThreadStack(); |
| |
| return error; |
| } |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT |
| |
| // Fully instantiate the generic implementation class in whatever compilation unit includes this file. |
| // NB: This must come after all templated class members are defined. |
| template class GenericThreadStackManagerImpl_OpenThread<ThreadStackManagerImpl>; |
| |
| } // namespace Internal |
| } // namespace DeviceLayer |
| } // namespace chip |
| |
| #endif // GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_IPP |