blob: 92ea4cec28f0db30aa0effc3e9f47961d26ad61c [file] [log] [blame]
/*
*
* 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 <lib/core/CHIPEncoding.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/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>
extern "C" void otSysProcessDrivers(otInstance * aInstance);
#if CHIP_DEVICE_CONFIG_THREAD_ENABLE_CLI
extern "C" void otAppCliInit(otInstance * aInstance);
#endif
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::DataModel;
using chip::Inet::IPPrefix;
namespace chip {
namespace DeviceLayer {
namespace Internal {
// Fully instantiate the generic implementation class in whatever compilation unit includes this file.
template class GenericThreadStackManagerImpl_OpenThread<ThreadStackManagerImpl>;
/**
* 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());
}
}
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 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
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>
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>
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);
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:
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;
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:
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;
const otMacCounters * macCounters;
const otIpCounters * ipCounters;
otOperationalDataset activeDataset;
otDeviceRole role;
Impl()->LockThreadStack();
role = otThreadGetDeviceRole(mOTInst);
ChipLogProgress(DeviceLayer, "Thread Role: %d\n", role);
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);
}
}
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);
exit:
Impl()->UnlockThreadStack();
return err;
}
template <class ImplClass>
CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_GetAndLogThreadTopologyMinimal(void)
{
CHIP_ERROR err = CHIP_NO_ERROR;
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"
"Extended Address: %02X%02X:%02X%02X:%02X%02X:%02X%02X\n"
"Instant RSSI: %d\n",
rloc16, routerId, leaderRouterId, parentAverageRssi, parentLastRssi, partitionId, 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());
}
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;
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];
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;
}
ChipLogProgress(DeviceLayer,
"Thread Topology:\n"
"RLOC16: %04X\n"
"Router ID: %u\n"
"Leader Router ID: %u\n"
"Leader Address: %02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X\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"
"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",
rloc16, routerId, leaderRouterId, 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], leaderWeight, leaderLocalWeight, networkDataLen,
networkDataVersion, stableNetworkDataVersion, 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: %3" PRIu8, 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"
"LinkFrameCounter: %10" PRIu32 "\n"
"MleFrameCounter: %10" PRIu32 "\n"
"RxOnWhenIdle: %c\n"
"FullFunction: %c\n"
"FullNetworkData: %c\n"
"IsChild: %c%s\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, 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));
}
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)
{
// Reset MAC counters
otLinkResetCounters(mOTInst);
otThreadResetMleCounters(mOTInst);
otThreadResetIp6Counters(mOTInst);
}
/*
* @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;
switch (attributeId)
{
case ThreadNetworkDiagnostics::Attributes::Channel::Id: {
uint16_t channel = otLinkGetChannel(mOTInst);
err = encoder.Encode(channel);
}
break;
case ThreadNetworkDiagnostics::Attributes::RoutingRole::Id: {
otDeviceRole role = otThreadGetDeviceRole(mOTInst);
err = encoder.Encode(static_cast<uint8_t>(role));
}
break;
case ThreadNetworkDiagnostics::Attributes::NetworkName::Id: {
const char * networkName = otThreadGetNetworkName(mOTInst);
err = encoder.Encode(Span<const char>(networkName, strlen(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: {
// TO DO
err = CHIP_ERROR_NOT_IMPLEMENTED;
}
break;
case ThreadNetworkDiagnostics::Attributes::NeighborTableList::Id: {
err = encoder.Encode(DataModel::List<EndpointId>());
// TO DO When list is functionnal.
// Determined limit of otNeighborInfo list
// pReadLength = sizeof(otNeighborInfo) * 20;
// buffer = static_cast<uint8_t *>(chip::Platform::MemoryAlloc(*pReadLength));
// VerifyOrExit(*buffer != NULL, err = CHIP_ERROR_NO_MEMORY);
// otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
// otNeighborInfo neighInfo;
// uint16_t remainingSize = *pReadLength;
// uint16_t offset = 0;
// while (otThreadGetNextNeighborInfo(mOTInst, &iterator, &neighInfo) == OT_ERROR_NONE)
// {
// if (remainingSize < sizeof(otNeighborInfo))
// {
// break;
// }
// memcpy(*buffer + offset, &neighInfo, sizeof(otNeighborInfo));
// remainingSize -= sizeof(otNeighborInfo);
// offset += sizeof(otNeighborInfo);
// }
}
break;
case ThreadNetworkDiagnostics::Attributes::RouteTableList::Id: {
err = encoder.Encode(DataModel::List<EndpointId>());
}
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 = encoder.Encode(DataModel::List<EndpointId>());
// Stuct type nopt yet supported
// if (otDatasetIsCommissioned(mOTInst))
// {
// otOperationalDataset activeDataset;
// otError otErr = otDatasetGetActive(mOTInst, &activeDataset);
// VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr));
// activeDataset.mSecurityPolicy
// }
}
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 = encoder.Encode(DataModel::List<EndpointId>());
// Structure not yet supported
// if (otDatasetIsCommissioned(mOTInst))
// {
// *pReadLength = sizeof(otOperationalDatasetComponents);
// *buffer = static_cast<uint8_t *>(chip::Platform::MemoryAlloc(*pReadLength));
// VerifyOrExit(*buffer != NULL, err = CHIP_ERROR_NO_MEMORY);
// otOperationalDataset activeDataset;
// otError otErr = otDatasetGetActive(mOTInst, &activeDataset);
// VerifyOrExit(otErr == OT_ERROR_NONE, err = MapOpenThreadError(otErr));
// // TODO encode TLV STRUCT with content of activeDataset.mComponents
// }
err = CHIP_ERROR_NOT_IMPLEMENTED;
}
break;
case ThreadNetworkDiagnostics::Attributes::ActiveNetworkFaultsList::Id: {
err = encoder.Encode(DataModel::List<EndpointId>());
}
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(__ZEPHYR__) && !defined(ENABLE_CHIP_SHELL) && !defined(PW_RPC_ENABLED) && CHIP_DEVICE_CONFIG_THREAD_ENABLE_CLI
otAppCliInit(otInst);
#endif
mOTInst = otInst;
#if CHIP_DEVICE_CONFIG_ENABLE_SED
ConnectivityManager::SEDPollingConfig sedPollingConfig;
mPollingConfig.Clear();
sedPollingConfig.Clear();
sedPollingConfig.FastPollingIntervalMS = CHIP_DEVICE_CONFIG_SED_FAST_POLLING_INTERVAL;
sedPollingConfig.SlowPollingIntervalMS = CHIP_DEVICE_CONFIG_SED_SLOW_POLLING_INTERVAL;
err = _SetSEDPollingConfig(sedPollingConfig);
if (err != CHIP_NO_ERROR)
{
ChipLogError(DeviceLayer, "Sleepy end device polling config set failed: %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");
}
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>::_GetSEDPollingConfig(ConnectivityManager::SEDPollingConfig & pollingConfig)
{
pollingConfig = mPollingConfig;
return CHIP_NO_ERROR;
}
template <class ImplClass>
CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_SetSEDPollingConfig(
const ConnectivityManager::SEDPollingConfig & pollingConfig)
{
if ((pollingConfig.SlowPollingIntervalMS < pollingConfig.FastPollingIntervalMS) || (pollingConfig.SlowPollingIntervalMS == 0) ||
(pollingConfig.FastPollingIntervalMS == 0))
{
return CHIP_ERROR_INVALID_ARGUMENT;
}
mPollingConfig = pollingConfig;
CHIP_ERROR err = SetSEDPollingMode(mPollingMode);
if (err == CHIP_NO_ERROR)
{
ChipDeviceEvent event;
event.Type = DeviceEventType::kSEDPollingIntervalChange;
err = chip::DeviceLayer::PlatformMgr().PostEvent(&event);
}
return err;
}
template <class ImplClass>
CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::SetSEDPollingMode(ConnectivityManager::SEDPollingMode pollingType)
{
CHIP_ERROR err = CHIP_NO_ERROR;
uint32_t interval;
if (pollingType == ConnectivityManager::SEDPollingMode::Idle)
{
interval = mPollingConfig.SlowPollingIntervalMS;
}
else if (pollingType == ConnectivityManager::SEDPollingMode::Active)
{
interval = mPollingConfig.FastPollingIntervalMS;
}
else
{
return CHIP_ERROR_INVALID_ARGUMENT;
}
mPollingMode = pollingType;
Impl()->LockThreadStack();
uint32_t curPollingIntervalMS = otLinkGetPollPeriod(mOTInst);
if (interval != curPollingIntervalMS)
{
otError otErr = otLinkSetPollPeriod(mOTInst, interval);
err = MapOpenThreadError(otErr);
}
Impl()->UnlockThreadStack();
if (interval != curPollingIntervalMS)
{
ChipLogProgress(DeviceLayer, "OpenThread polling interval set to %" PRId32 "ms", interval);
}
return err;
}
template <class ImplClass>
CHIP_ERROR GenericThreadStackManagerImpl_OpenThread<ImplClass>::_RequestSEDFastPollingMode(bool onOff)
{
CHIP_ERROR err = CHIP_NO_ERROR;
ConnectivityManager::SEDPollingMode mode;
if (onOff)
{
mFastPollingConsumers++;
}
else
{
if (mFastPollingConsumers > 0)
mFastPollingConsumers--;
}
mode = mFastPollingConsumers > 0 ? ConnectivityManager::SEDPollingMode::Active : ConnectivityManager::SEDPollingMode::Idle;
if (mPollingMode != mode)
err = SetSEDPollingMode(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)
{
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));
}
}
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;
uint16_t discriminator;
SuccessOrExit(error = ConfigurationMgr().GetSetupDiscriminator(discriminator));
discerner.mLength = 12;
discerner.mValue = discriminator;
ChipLogProgress(DeviceLayer, "Joiner Discerner: %u", discriminator);
otJoinerSetDiscerner(mOTInst, &discerner);
}
{
otJoinerPskd pskd;
uint32_t pincode;
SuccessOrExit(error = ConfigurationMgr().GetSetupPinCode(pincode));
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;
}
#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: {
ChipLogProgress(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;
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;
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;
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>::OnDnsBrowseResult(otError aError, const otDnsBrowseResponse * aResponse,
void * aContext)
{
CHIP_ERROR error;
DnsResult browseResult;
// 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, );
while (otDnsBrowseResponseGetServiceInstance(aResponse, index, browseResult.mMdnsService.mName,
sizeof(browseResult.mMdnsService.mName)) == OT_ERROR_NONE)
{
serviceInfo.mHostNameBuffer = hostname;
serviceInfo.mHostNameBufferSize = sizeof(hostname);
serviceInfo.mTxtData = txtBuffer;
serviceInfo.mTxtDataSize = sizeof(txtBuffer);
error = MapOpenThreadError(otDnsBrowseResponseGetServiceInfo(aResponse, browseResult.mMdnsService.mName, &serviceInfo));
VerifyOrExit(error == CHIP_NO_ERROR, );
if (FromOtDnsResponseToMdnsData(serviceInfo, type, browseResult.mMdnsService, browseResult.mServiceTxtEntry) ==
CHIP_NO_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.
ThreadStackMgrImpl().mDnsBrowseCallback(aContext, &browseResult.mMdnsService, 1, MapOpenThreadError(aError));
wasAnythingBrowsed = true;
}
index++;
}
exit:
// In case no service was found invoke callback to notify about failure. In other case it was already called before.
if (!wasAnythingBrowsed)
ThreadStackMgrImpl().mDnsBrowseCallback(aContext, nullptr, 0, error);
}
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 resolveResult;
// 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, resolveResult.mMdnsService.mName,
sizeof(resolveResult.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, resolveResult.mMdnsService, resolveResult.mServiceTxtEntry);
exit:
ThreadStackMgrImpl().mDnsResolveCallback(aContext, &(resolveResult.mMdnsService), error);
}
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
} // namespace Internal
} // namespace DeviceLayer
} // namespace chip
#endif // GENERIC_THREAD_STACK_MANAGER_IMPL_OPENTHREAD_IPP