| /* |
| * |
| * Copyright (c) 2020-2022 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 |
| * Utility functions for working with OpenThread. |
| */ |
| |
| #include "OpenThreadUtils.h" |
| |
| #include <inet/IPAddress.h> |
| #include <lib/core/CHIPEncoding.h> |
| #include <lib/support/ErrorStr.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| #include <platform/internal/CHIPDeviceLayerInternal.h> |
| |
| #include <openthread/error.h> |
| |
| #include <cstdio> |
| |
| namespace chip { |
| namespace DeviceLayer { |
| namespace Internal { |
| |
| /** |
| * Map an OpenThread error into the CHIP error space. |
| */ |
| CHIP_ERROR MapOpenThreadError(otError otErr) |
| { |
| return (otErr == OT_ERROR_NONE) ? CHIP_NO_ERROR : CHIP_ERROR(ChipError::Range::kOpenThread, static_cast<unsigned int>(otErr)); |
| } |
| |
| /** |
| * Given an CHIP 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, CHIP_ERROR err) |
| { |
| if (!err.IsRange(ChipError::Range::kOpenThread)) |
| { |
| return false; |
| } |
| |
| #if CHIP_CONFIG_SHORT_ERROR_STR |
| const char * desc = NULL; |
| #else // CHIP_CONFIG_SHORT_ERROR_STR |
| otError otErr = (otError) err.GetValue(); |
| const char * desc = otThreadErrorToString(otErr); |
| #endif // CHIP_CONFIG_SHORT_ERROR_STR |
| |
| chip::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 CHIP_DETAIL_LOGGING |
| |
| #if OPENTHREAD_API_VERSION >= 126 |
| const uint32_t kParamsChanged = (OT_CHANGED_THREAD_NETWORK_NAME | OT_CHANGED_THREAD_PANID | OT_CHANGED_THREAD_EXT_PANID | |
| OT_CHANGED_THREAD_CHANNEL | OT_CHANGED_NETWORK_KEY | OT_CHANGED_PSKC); |
| #else |
| 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); |
| #endif |
| |
| static char strBuf[64]; |
| |
| ChipLogDetail(DeviceLayer, "OpenThread State Changed (Flags: 0x%08" PRIx32 ")", flags); |
| if ((flags & OT_CHANGED_THREAD_ROLE) != 0) |
| { |
| ChipLogDetail(DeviceLayer, " Device Role: %s", OpenThreadRoleToStr(otThreadGetDeviceRole(otInst))); |
| } |
| if ((flags & kParamsChanged) != 0) |
| { |
| ChipLogDetail(DeviceLayer, " Network Name: %s", otThreadGetNetworkName(otInst)); |
| ChipLogDetail(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]); |
| ChipLogDetail(DeviceLayer, " Extended PAN Id: %s", strBuf); |
| } |
| ChipLogDetail(DeviceLayer, " Channel: %d", otLinkGetChannel(otInst)); |
| { |
| const otMeshLocalPrefix * otMeshPrefix = otThreadGetMeshLocalPrefix(otInst); |
| chip::Inet::IPAddress meshPrefix; |
| memset(meshPrefix.Addr, 0, sizeof(meshPrefix.Addr)); |
| memcpy(meshPrefix.Addr, otMeshPrefix->m8, sizeof(otMeshPrefix->m8)); |
| meshPrefix.ToString(strBuf); |
| ChipLogDetail(DeviceLayer, " Mesh Prefix: %s/64", strBuf); |
| } |
| #if CHIP_CONFIG_SECURITY_TEST_MODE |
| { |
| #if OPENTHREAD_API_VERSION >= 126 |
| const otNetworkKey * otKey = otThreadGetNetworkKey(otInst); |
| for (int i = 0; i < OT_NETWORK_KEY_SIZE; i++) |
| #else |
| const otMasterKey * otKey = otThreadGetMasterKey(otInst); |
| for (int i = 0; i < OT_MASTER_KEY_SIZE; i++) |
| #endif |
| snprintf(&strBuf[i * 2], 3, "%02X", otKey->m8[i]); |
| ChipLogDetail(DeviceLayer, " Network Key: %s", strBuf); |
| } |
| #endif // CHIP_CONFIG_SECURITY_TEST_MODE |
| } |
| if ((flags & OT_CHANGED_THREAD_PARTITION_ID) != 0) |
| { |
| ChipLogDetail(DeviceLayer, " Partition Id: 0x%" PRIX32, otThreadGetPartitionId(otInst)); |
| } |
| if ((flags & (OT_CHANGED_IP6_ADDRESS_ADDED | OT_CHANGED_IP6_ADDRESS_REMOVED)) != 0) |
| { |
| ChipLogDetail(DeviceLayer, " Thread Unicast Addresses:"); |
| for (const otNetifAddress * addr = otIp6GetUnicastAddresses(otInst); addr != NULL; addr = addr->mNext) |
| { |
| chip::Inet::IPAddress ipAddr; |
| memcpy(ipAddr.Addr, addr->mAddress.mFields.m32, sizeof(ipAddr.Addr)); |
| |
| ipAddr.ToString(strBuf); |
| |
| ChipLogDetail(DeviceLayer, " %s/%d%s%s%s", strBuf, addr->mPrefixLength, addr->mValid ? " valid" : "", |
| addr->mPreferred ? " preferred" : "", addr->mRloc ? " rloc" : ""); |
| } |
| } |
| |
| #endif // CHIP_DETAIL_LOGGING |
| } |
| |
| void LogOpenThreadPacket(const char * titleStr, otMessage * pkt) |
| { |
| #if CHIP_DETAIL_LOGGING |
| |
| char srcStr[50], destStr[50], typeBuf[20]; |
| const char * type = typeBuf; |
| Inet::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); |
| |
| memcpy(addr.Addr, IPv6_DestAddr, 16); |
| addr.ToString(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 %u,%u", ICMPv6_Type, ICMPv6_Code); |
| } |
| } |
| else |
| { |
| snprintf(typeBuf, sizeof(typeBuf), "IP proto %u", IPv6_NextHeader); |
| } |
| |
| if (IPv6_NextHeader == kIPProto_UDP || IPv6_NextHeader == kIPProto_TCP) |
| { |
| snprintf(srcStr + strlen(srcStr), 13, ", port %u", Encoding::BigEndian::Get16(IPv6_SrcPort)); |
| snprintf(destStr + strlen(destStr), 13, ", port %u", Encoding::BigEndian::Get16(IPv6_DestPort)); |
| } |
| |
| ChipLogDetail(DeviceLayer, "%s: %s, len %u", StringOrNullMarker(titleStr), type, pktLen); |
| ChipLogDetail(DeviceLayer, " src %s", srcStr); |
| ChipLogDetail(DeviceLayer, " dest %s", destStr); |
| } |
| else |
| { |
| ChipLogDetail(DeviceLayer, "%s: %s, len %u", StringOrNullMarker(titleStr), "(decode error)", pktLen); |
| } |
| |
| #endif // CHIP_DETAIL_LOGGING |
| } |
| |
| bool IsOpenThreadMeshLocalAddress(otInstance * otInst, const Inet::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 chip |