blob: 3252c9a7fcebb5fca90c6632533de3e676e107e1 [file] [log] [blame]
/*
*
* 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
* Utility functions for working with OpenThread.
*/
#include <Weave/DeviceLayer/internal/WeaveDeviceLayerInternal.h>
#if WEAVE_DEVICE_CONFIG_ENABLE_THREAD
#include <Weave/DeviceLayer/OpenThread/OpenThreadUtils.h>
#include <openthread/error.h>
namespace nl {
namespace Weave {
namespace DeviceLayer {
namespace Internal {
/**
* Map an OpenThread error into the OpenWeave error space.
*/
WEAVE_ERROR MapOpenThreadError(otError otErr)
{
return (otErr == OT_ERROR_NONE) ? WEAVE_NO_ERROR : WEAVE_CONFIG_OPEN_THREAD_ERROR_MIN + (WEAVE_ERROR)otErr;
}
/**
* Given an OpenWeave error value that represents an OpenThread error, returns a
* human-readable NULL-terminated C string describing the error.
*
* @param[in] buf Buffer into which the error string will be placed.
* @param[in] bufSize Size of the supplied buffer in bytes.
* @param[in] err The error to be described.
*
* @return true If a description string was written into the supplied buffer.
* @return false If the supplied error was not an OpenThread error.
*
*/
bool FormatOpenThreadError(char * buf, uint16_t bufSize, int32_t err)
{
if (err < WEAVE_CONFIG_OPEN_THREAD_ERROR_MIN || err > WEAVE_CONFIG_OPEN_THREAD_ERROR_MAX)
{
return false;
}
#if WEAVE_CONFIG_SHORT_ERROR_STR
const char * desc = NULL;
#else // WEAVE_CONFIG_SHORT_ERROR_STR
otError otErr = (otError)(err - WEAVE_CONFIG_OPEN_THREAD_ERROR_MIN);
const char * desc = otThreadErrorToString(otErr);
#endif // WEAVE_CONFIG_SHORT_ERROR_STR
nl::FormatError(buf, bufSize, "OpenThread", err, desc);
return true;
}
/**
* Register a text error formatter for OpenThread errors.
*/
void RegisterOpenThreadErrorFormatter(void)
{
static ErrorFormatter sOpenThreadErrorFormatter =
{
FormatOpenThreadError,
NULL
};
RegisterErrorFormatter(&sOpenThreadErrorFormatter);
}
/**
* Log information related to a state change in the OpenThread stack.
*
* NB: This function *must* be called with the Thread stack lock held.
*/
void LogOpenThreadStateChange(otInstance * otInst, uint32_t flags)
{
#if WEAVE_DETAIL_LOGGING
const uint32_t kParamsChanged = (OT_CHANGED_THREAD_NETWORK_NAME |
OT_CHANGED_THREAD_PANID |
OT_CHANGED_THREAD_EXT_PANID |
OT_CHANGED_THREAD_CHANNEL |
OT_CHANGED_MASTER_KEY |
OT_CHANGED_PSKC);
static char strBuf[64];
WeaveLogDetail(DeviceLayer, "OpenThread State Changed (Flags: 0x%08x)", flags);
if ((flags & OT_CHANGED_THREAD_ROLE) != 0)
{
WeaveLogDetail(DeviceLayer, " Device Role: %s", OpenThreadRoleToStr(otThreadGetDeviceRole(otInst)));
}
if ((flags & kParamsChanged) != 0)
{
WeaveLogDetail(DeviceLayer, " Network Name: %s", otThreadGetNetworkName(otInst));
WeaveLogDetail(DeviceLayer, " PAN Id: 0x%04X", otLinkGetPanId(otInst));
{
const otExtendedPanId * exPanId = otThreadGetExtendedPanId(otInst);
snprintf(strBuf, sizeof(strBuf), "0x%02X%02X%02X%02X%02X%02X%02X%02X",
exPanId->m8[0], exPanId->m8[1], exPanId->m8[2], exPanId->m8[3],
exPanId->m8[4], exPanId->m8[5], exPanId->m8[6], exPanId->m8[7]);
WeaveLogDetail(DeviceLayer, " Extended PAN Id: %s", strBuf);
}
WeaveLogDetail(DeviceLayer, " Channel: %d", otLinkGetChannel(otInst));
{
const otMeshLocalPrefix * otMeshPrefix = otThreadGetMeshLocalPrefix(otInst);
nl::Inet::IPAddress meshPrefix;
memset(meshPrefix.Addr, 0, sizeof(meshPrefix.Addr));
memcpy(meshPrefix.Addr, otMeshPrefix->m8, sizeof(otMeshPrefix->m8));
meshPrefix.ToString(strBuf, sizeof(strBuf));
WeaveLogDetail(DeviceLayer, " Mesh Prefix: %s/64", strBuf);
}
#if WEAVE_CONFIG_SECURITY_TEST_MODE
{
const otMasterKey * otKey = otThreadGetMasterKey(otInst);
for (int i = 0; i < OT_MASTER_KEY_SIZE; i++)
snprintf(&strBuf[i*2], 3, "%02X", otKey->m8[i]);
WeaveLogDetail(DeviceLayer, " Master Key: %s", strBuf);
}
#endif // WEAVE_CONFIG_SECURITY_TEST_MODE
}
if ((flags & OT_CHANGED_THREAD_PARTITION_ID) != 0)
{
WeaveLogDetail(DeviceLayer, " Partition Id: 0x%" PRIX32, otThreadGetPartitionId(otInst));
}
if ((flags & (OT_CHANGED_IP6_ADDRESS_ADDED|OT_CHANGED_IP6_ADDRESS_REMOVED)) != 0)
{
WeaveLogDetail(DeviceLayer, " Thread Unicast Addresses:");
for (const otNetifAddress * addr = otIp6GetUnicastAddresses(otInst); addr != NULL; addr = addr->mNext)
{
nl::Inet::IPAddress ipAddr;
memcpy(ipAddr.Addr, addr->mAddress.mFields.m32, sizeof(ipAddr.Addr));
ipAddr.ToString(strBuf, sizeof(strBuf));
WeaveLogDetail(DeviceLayer, " %s/%d%s%s%s", strBuf,
addr->mPrefixLength,
addr->mValid ? " valid" : "",
addr->mPreferred ? " preferred" : "",
addr->mRloc ? " rloc" : "");
}
}
#endif // WEAVE_DETAIL_LOGGING
}
void LogOpenThreadPacket(const char * titleStr, otMessage * pkt)
{
#if WEAVE_DETAIL_LOGGING
char srcStr[50], destStr[50], typeBuf[20];
const char * type = typeBuf;
IPAddress addr;
uint8_t headerData[44];
uint16_t pktLen;
const uint8_t & IPv6_NextHeader = headerData[6];
const uint8_t * const IPv6_SrcAddr = headerData + 8;
const uint8_t * const IPv6_DestAddr = headerData + 24;
const uint8_t * const IPv6_SrcPort = headerData + 40;
const uint8_t * const IPv6_DestPort = headerData + 42;
const uint8_t & ICMPv6_Type = headerData[40];
const uint8_t & ICMPv6_Code = headerData[41];
constexpr uint8_t kIPProto_UDP = 17;
constexpr uint8_t kIPProto_TCP = 6;
constexpr uint8_t kIPProto_ICMPv6 = 58;
constexpr uint8_t kICMPType_EchoRequest = 128;
constexpr uint8_t kICMPType_EchoResponse = 129;
pktLen = otMessageGetLength(pkt);
if (pktLen >= sizeof(headerData))
{
otMessageRead(pkt, 0, headerData, sizeof(headerData));
memcpy(addr.Addr, IPv6_SrcAddr, 16);
addr.ToString(srcStr, sizeof(srcStr));
memcpy(addr.Addr, IPv6_DestAddr, 16);
addr.ToString(destStr, sizeof(destStr));
if (IPv6_NextHeader == kIPProto_UDP)
{
type = "UDP";
}
else if (IPv6_NextHeader == kIPProto_TCP)
{
type = "TCP";
}
else if (IPv6_NextHeader == kIPProto_ICMPv6)
{
if (ICMPv6_Type == kICMPType_EchoRequest)
{
type = "ICMPv6 Echo Request";
}
else if (ICMPv6_Type == kICMPType_EchoResponse)
{
type = "ICMPv6 Echo Response";
}
else
{
snprintf(typeBuf, sizeof(typeBuf), "ICMPv6 %" PRIu8 ",%" PRIu8, ICMPv6_Type, ICMPv6_Code);
}
}
else
{
snprintf(typeBuf, sizeof(typeBuf), "IP proto %" PRIu8, IPv6_NextHeader);
}
if (IPv6_NextHeader == kIPProto_UDP || IPv6_NextHeader == kIPProto_TCP)
{
snprintf(srcStr + strlen(srcStr), 13, ", port %" PRIu16, Encoding::BigEndian::Get16(IPv6_SrcPort));
snprintf(destStr + strlen(destStr), 13, ", port %" PRIu16, Encoding::BigEndian::Get16(IPv6_DestPort));
}
WeaveLogDetail(DeviceLayer, "%s: %s, len %" PRIu16, titleStr, type, pktLen);
WeaveLogDetail(DeviceLayer, " src %s", srcStr);
WeaveLogDetail(DeviceLayer, " dest %s", destStr);
}
else
{
WeaveLogDetail(DeviceLayer, "%s: %s, len %" PRIu16, titleStr, "(decode error)", pktLen);
}
#endif // WEAVE_DETAIL_LOGGING
}
bool IsOpenThreadMeshLocalAddress(otInstance * otInst, const IPAddress & addr)
{
const otMeshLocalPrefix * otMeshPrefix = otThreadGetMeshLocalPrefix(otInst);
return otMeshPrefix != NULL && memcmp(otMeshPrefix->m8, addr.Addr, OT_MESH_LOCAL_PREFIX_SIZE) == 0;
}
const char * OpenThreadRoleToStr(otDeviceRole role)
{
switch (role)
{
case OT_DEVICE_ROLE_DISABLED:
return "DISABLED";
case OT_DEVICE_ROLE_DETACHED:
return "DETACHED";
case OT_DEVICE_ROLE_CHILD:
return "CHILD";
case OT_DEVICE_ROLE_ROUTER:
return "ROUTER";
case OT_DEVICE_ROLE_LEADER:
return "LEADER";
default:
return "(unknown)";
}
}
} // namespace Internal
} // namespace DeviceLayer
} // namespace Weave
} // namespace nl
#endif // WEAVE_DEVICE_CONFIG_ENABLE_THREAD