| /* |
| * Copyright (c) 2021, The OpenThread Authors. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holder nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @file |
| * This file implements CLI for the History Tracker. |
| */ |
| |
| #include "cli_history.hpp" |
| |
| #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE |
| |
| #include <string.h> |
| |
| #include "cli/cli.hpp" |
| |
| namespace ot { |
| namespace Cli { |
| |
| constexpr History::Command History::sCommands[]; |
| |
| static const char *const kSimpleEventStrings[] = { |
| "Added", // (0) OT_HISTORY_TRACKER_{NET_DATA_ENTRY/ADDRESSS_EVENT}_ADDED |
| "Removed" // (1) OT_HISTORY_TRACKER_{NET_DATA_ENTRY/ADDRESS_EVENT}_REMOVED |
| }; |
| |
| otError History::ProcessHelp(Arg aArgs[]) |
| { |
| OT_UNUSED_VARIABLE(aArgs); |
| |
| for (const Command &command : sCommands) |
| { |
| OutputLine(command.mName); |
| } |
| |
| return OT_ERROR_NONE; |
| } |
| |
| otError History::ParseArgs(Arg aArgs[], bool &aIsList, uint16_t &aNumEntries) const |
| { |
| if (*aArgs == "list") |
| { |
| aArgs++; |
| aIsList = true; |
| } |
| else |
| { |
| aIsList = false; |
| } |
| |
| if (aArgs->ParseAsUint16(aNumEntries) == OT_ERROR_NONE) |
| { |
| aArgs++; |
| } |
| else |
| { |
| aNumEntries = 0; |
| } |
| |
| return aArgs[0].IsEmpty() ? OT_ERROR_NONE : OT_ERROR_INVALID_ARGS; |
| } |
| |
| otError History::ProcessIpAddr(Arg aArgs[]) |
| { |
| otError error; |
| bool isList; |
| uint16_t numEntries; |
| otHistoryTrackerIterator iterator; |
| const otHistoryTrackerUnicastAddressInfo *info; |
| uint32_t entryAge; |
| char ageString[OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE]; |
| char addressString[OT_IP6_ADDRESS_STRING_SIZE + 4]; |
| |
| static_assert(0 == OT_HISTORY_TRACKER_ADDRESS_EVENT_ADDED, "ADDRESS_EVENT_ADDED is incorrect"); |
| static_assert(1 == OT_HISTORY_TRACKER_ADDRESS_EVENT_REMOVED, "ADDRESS_EVENT_REMOVED is incorrect"); |
| |
| SuccessOrExit(error = ParseArgs(aArgs, isList, numEntries)); |
| |
| if (!isList) |
| { |
| // | Age | Event | Address / PrefixLen /123 | Origin |Scope| P | V | R | |
| // +----------------------+---------+---------------------------------------------+--------+-----+---+---+---+ |
| |
| static const char *const kUnicastAddrInfoTitles[] = { |
| "Age", "Event", "Address / PrefixLength", "Origin", "Scope", "P", "V", "R"}; |
| |
| static const uint8_t kUnicastAddrInfoColumnWidths[] = {22, 9, 45, 8, 5, 3, 3, 3}; |
| |
| OutputTableHeader(kUnicastAddrInfoTitles, kUnicastAddrInfoColumnWidths); |
| } |
| |
| otHistoryTrackerInitIterator(&iterator); |
| |
| for (uint16_t index = 0; (numEntries == 0) || (index < numEntries); index++) |
| { |
| info = otHistoryTrackerIterateUnicastAddressHistory(GetInstancePtr(), &iterator, &entryAge); |
| VerifyOrExit(info != nullptr); |
| |
| otHistoryTrackerEntryAgeToString(entryAge, ageString, sizeof(ageString)); |
| otIp6AddressToString(&info->mAddress, addressString, sizeof(addressString)); |
| |
| if (!isList) |
| { |
| sprintf(&addressString[strlen(addressString)], "/%d", info->mPrefixLength); |
| |
| OutputLine("| %20s | %-7s | %-43s | %-6s | %3d | %c | %c | %c |", ageString, |
| Stringify(info->mEvent, kSimpleEventStrings), addressString, |
| Interpreter::AddressOriginToString(info->mAddressOrigin), info->mScope, |
| info->mPreferred ? 'Y' : 'N', info->mValid ? 'Y' : 'N', info->mRloc ? 'Y' : 'N'); |
| } |
| else |
| { |
| OutputLine("%s -> event:%s address:%s prefixlen:%d origin:%s scope:%d preferred:%s valid:%s rloc:%s", |
| ageString, Stringify(info->mEvent, kSimpleEventStrings), addressString, info->mPrefixLength, |
| Interpreter::AddressOriginToString(info->mAddressOrigin), info->mScope, |
| info->mPreferred ? "yes" : "no", info->mValid ? "yes" : "no", info->mRloc ? "yes" : "no"); |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError History::ProcessIpMulticastAddr(Arg aArgs[]) |
| { |
| static const char *const kEventStrings[] = { |
| "Subscribed", // (0) OT_HISTORY_TRACKER_ADDRESS_EVENT_ADDED |
| "Unsubscribed" // (1) OT_HISTORY_TRACKER_ADDRESS_EVENT_REMOVED |
| }; |
| |
| otError error; |
| bool isList; |
| uint16_t numEntries; |
| otHistoryTrackerIterator iterator; |
| const otHistoryTrackerMulticastAddressInfo *info; |
| uint32_t entryAge; |
| char ageString[OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE]; |
| char addressString[OT_IP6_ADDRESS_STRING_SIZE]; |
| |
| static_assert(0 == OT_HISTORY_TRACKER_ADDRESS_EVENT_ADDED, "ADDRESS_EVENT_ADDED is incorrect"); |
| static_assert(1 == OT_HISTORY_TRACKER_ADDRESS_EVENT_REMOVED, "ADDRESS_EVENT_REMOVED is incorrect"); |
| |
| SuccessOrExit(error = ParseArgs(aArgs, isList, numEntries)); |
| |
| if (!isList) |
| { |
| // | Age | Event | Multicast Address | Origin | |
| // +----------------------+--------------+-----------------------------------------+--------+ |
| |
| static const char *const kMulticastAddrInfoTitles[] = { |
| "Age", |
| "Event", |
| "Multicast Address", |
| "Origin", |
| }; |
| |
| static const uint8_t kMulticastAddrInfoColumnWidths[] = {22, 14, 42, 8}; |
| |
| OutputTableHeader(kMulticastAddrInfoTitles, kMulticastAddrInfoColumnWidths); |
| } |
| |
| otHistoryTrackerInitIterator(&iterator); |
| |
| for (uint16_t index = 0; (numEntries == 0) || (index < numEntries); index++) |
| { |
| info = otHistoryTrackerIterateMulticastAddressHistory(GetInstancePtr(), &iterator, &entryAge); |
| VerifyOrExit(info != nullptr); |
| |
| otHistoryTrackerEntryAgeToString(entryAge, ageString, sizeof(ageString)); |
| otIp6AddressToString(&info->mAddress, addressString, sizeof(addressString)); |
| |
| OutputLine(isList ? "%s -> event:%s address:%s origin:%s" : "| %20s | %-12s | %-39s | %-6s |", ageString, |
| Stringify(info->mEvent, kEventStrings), addressString, |
| Interpreter::AddressOriginToString(info->mAddressOrigin)); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError History::ProcessNeighbor(Arg aArgs[]) |
| { |
| static const char *const kEventString[] = { |
| /* (0) OT_HISTORY_TRACKER_NEIGHBOR_EVENT_ADDED -> */ "Added", |
| /* (1) OT_HISTORY_TRACKER_NEIGHBOR_EVENT_REMOVED -> */ "Removed", |
| /* (2) OT_HISTORY_TRACKER_NEIGHBOR_EVENT_CHANGED -> */ "Changed", |
| /* (3) OT_HISTORY_TRACKER_NEIGHBOR_EVENT_RESTORING -> */ "Restoring", |
| }; |
| |
| otError error; |
| bool isList; |
| uint16_t numEntries; |
| otHistoryTrackerIterator iterator; |
| const otHistoryTrackerNeighborInfo *info; |
| uint32_t entryAge; |
| char ageString[OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE]; |
| otLinkModeConfig mode; |
| char linkModeString[Interpreter::kLinkModeStringSize]; |
| |
| static_assert(0 == OT_HISTORY_TRACKER_NEIGHBOR_EVENT_ADDED, "NEIGHBOR_EVENT_ADDED value is incorrect"); |
| static_assert(1 == OT_HISTORY_TRACKER_NEIGHBOR_EVENT_REMOVED, "NEIGHBOR_EVENT_REMOVED value is incorrect"); |
| static_assert(2 == OT_HISTORY_TRACKER_NEIGHBOR_EVENT_CHANGED, "NEIGHBOR_EVENT_CHANGED value is incorrect"); |
| static_assert(3 == OT_HISTORY_TRACKER_NEIGHBOR_EVENT_RESTORING, "NEIGHBOR_EVENT_RESTORING value is incorrect"); |
| |
| SuccessOrExit(error = ParseArgs(aArgs, isList, numEntries)); |
| |
| if (!isList) |
| { |
| // | Age | Type | Event | Extended Address | RLOC16 | Mode | Ave RSS | |
| // +----------------------+--------+-----------+------------------+--------+------+---------+ |
| |
| static const char *const kNeighborInfoTitles[] = { |
| "Age", "Type", "Event", "Extended Address", "RLOC16", "Mode", "Ave RSS", |
| }; |
| |
| static const uint8_t kNeighborInfoColumnWidths[] = {22, 8, 11, 18, 8, 6, 9}; |
| |
| OutputTableHeader(kNeighborInfoTitles, kNeighborInfoColumnWidths); |
| } |
| |
| otHistoryTrackerInitIterator(&iterator); |
| |
| for (uint16_t index = 0; (numEntries == 0) || (index < numEntries); index++) |
| { |
| info = otHistoryTrackerIterateNeighborHistory(GetInstancePtr(), &iterator, &entryAge); |
| VerifyOrExit(info != nullptr); |
| |
| otHistoryTrackerEntryAgeToString(entryAge, ageString, sizeof(ageString)); |
| |
| mode.mRxOnWhenIdle = info->mRxOnWhenIdle; |
| mode.mDeviceType = info->mFullThreadDevice; |
| mode.mNetworkData = info->mFullNetworkData; |
| Interpreter::LinkModeToString(mode, linkModeString); |
| |
| OutputFormat(isList ? "%s -> type:%s event:%s extaddr:" : "| %20s | %-6s | %-9s | ", ageString, |
| info->mIsChild ? "Child" : "Router", kEventString[info->mEvent]); |
| OutputExtAddress(info->mExtAddress); |
| OutputLine(isList ? " rloc16:0x%04x mode:%s rss:%d" : " | 0x%04x | %-4s | %7d |", info->mRloc16, linkModeString, |
| info->mAverageRssi); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError History::ProcessNetInfo(Arg aArgs[]) |
| { |
| otError error; |
| bool isList; |
| uint16_t numEntries; |
| otHistoryTrackerIterator iterator; |
| const otHistoryTrackerNetworkInfo *info; |
| uint32_t entryAge; |
| char ageString[OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE]; |
| char linkModeString[Interpreter::kLinkModeStringSize]; |
| |
| SuccessOrExit(error = ParseArgs(aArgs, isList, numEntries)); |
| |
| if (!isList) |
| { |
| // | Age | Role | Mode | RLOC16 | Partition ID | |
| // +----------------------+----------+------+--------+--------------+ |
| |
| static const char *const kNetInfoTitles[] = {"Age", "Role", "Mode", "RLOC16", "Partition ID"}; |
| static const uint8_t kNetInfoColumnWidths[] = {22, 10, 6, 8, 14}; |
| |
| OutputTableHeader(kNetInfoTitles, kNetInfoColumnWidths); |
| } |
| |
| otHistoryTrackerInitIterator(&iterator); |
| |
| for (uint16_t index = 0; (numEntries == 0) || (index < numEntries); index++) |
| { |
| info = otHistoryTrackerIterateNetInfoHistory(GetInstancePtr(), &iterator, &entryAge); |
| VerifyOrExit(info != nullptr); |
| |
| otHistoryTrackerEntryAgeToString(entryAge, ageString, sizeof(ageString)); |
| |
| OutputLine(isList ? "%s -> role:%s mode:%s rloc16:0x%04x partition-id:%u" |
| : "| %20s | %-8s | %-4s | 0x%04x | %12u |", |
| ageString, otThreadDeviceRoleToString(info->mRole), |
| Interpreter::LinkModeToString(info->mMode, linkModeString), info->mRloc16, info->mPartitionId); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError History::ProcessRx(Arg aArgs[]) |
| { |
| return ProcessRxTxHistory(kRx, aArgs); |
| } |
| |
| otError History::ProcessRxTx(Arg aArgs[]) |
| { |
| return ProcessRxTxHistory(kRxTx, aArgs); |
| } |
| |
| otError History::ProcessTx(Arg aArgs[]) |
| { |
| return ProcessRxTxHistory(kTx, aArgs); |
| } |
| |
| const char *History::MessagePriorityToString(uint8_t aPriority) |
| { |
| static const char *const kPriorityStrings[] = { |
| "low", // (0) OT_HISTORY_TRACKER_MSG_PRIORITY_LOW |
| "norm", // (1) OT_HISTORY_TRACKER_MSG_PRIORITY_NORMAL |
| "high", // (2) OT_HISTORY_TRACKER_MSG_PRIORITY_HIGH |
| "net", // (3) OT_HISTORY_TRACKER_MSG_PRIORITY_NET |
| }; |
| |
| static_assert(0 == OT_HISTORY_TRACKER_MSG_PRIORITY_LOW, "MSG_PRIORITY_LOW value is incorrect"); |
| static_assert(1 == OT_HISTORY_TRACKER_MSG_PRIORITY_NORMAL, "MSG_PRIORITY_NORMAL value is incorrect"); |
| static_assert(2 == OT_HISTORY_TRACKER_MSG_PRIORITY_HIGH, "MSG_PRIORITY_HIGH value is incorrect"); |
| static_assert(3 == OT_HISTORY_TRACKER_MSG_PRIORITY_NET, "MSG_PRIORITY_NET value is incorrect"); |
| |
| return Stringify(aPriority, kPriorityStrings, "unkn"); |
| } |
| |
| const char *History::RadioTypeToString(const otHistoryTrackerMessageInfo &aInfo) |
| { |
| const char *str = "none"; |
| |
| if (aInfo.mRadioTrelUdp6 && aInfo.mRadioIeee802154) |
| { |
| str = "all"; |
| } |
| else if (aInfo.mRadioIeee802154) |
| { |
| str = "15.4"; |
| } |
| else if (aInfo.mRadioTrelUdp6) |
| { |
| str = "trel"; |
| } |
| |
| return str; |
| } |
| |
| const char *History::MessageTypeToString(const otHistoryTrackerMessageInfo &aInfo) |
| { |
| const char *str = otIp6ProtoToString(aInfo.mIpProto); |
| |
| if (aInfo.mIpProto == OT_IP6_PROTO_ICMP6) |
| { |
| switch (aInfo.mIcmp6Type) |
| { |
| case OT_ICMP6_TYPE_DST_UNREACH: |
| str = "ICMP6(Unreach)"; |
| break; |
| case OT_ICMP6_TYPE_PACKET_TO_BIG: |
| str = "ICMP6(TooBig)"; |
| break; |
| case OT_ICMP6_TYPE_ECHO_REQUEST: |
| str = "ICMP6(EchoReqst)"; |
| break; |
| case OT_ICMP6_TYPE_ECHO_REPLY: |
| str = "ICMP6(EchoReply)"; |
| break; |
| case OT_ICMP6_TYPE_ROUTER_SOLICIT: |
| str = "ICMP6(RouterSol)"; |
| break; |
| case OT_ICMP6_TYPE_ROUTER_ADVERT: |
| str = "ICMP6(RouterAdv)"; |
| break; |
| default: |
| str = "ICMP6(Other)"; |
| break; |
| } |
| } |
| |
| return str; |
| } |
| |
| otError History::ProcessRxTxHistory(RxTx aRxTx, Arg aArgs[]) |
| { |
| otError error; |
| bool isList; |
| uint16_t numEntries; |
| otHistoryTrackerIterator rxIterator; |
| otHistoryTrackerIterator txIterator; |
| bool isRx = false; |
| const otHistoryTrackerMessageInfo *info = nullptr; |
| const otHistoryTrackerMessageInfo *rxInfo = nullptr; |
| const otHistoryTrackerMessageInfo *txInfo = nullptr; |
| uint32_t entryAge; |
| uint32_t rxEntryAge; |
| uint32_t txEntryAge; |
| |
| // | Age | Type | Len | Chksum | Sec | Prio | RSS |Dir | Neighb | Radio | |
| // +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| |
| static const char *const kTableTitles[] = {"Age", "Type", "Len", "Chksum", "Sec", |
| "Prio", "RSS", "Dir", "Neighb", "Radio"}; |
| |
| static const uint8_t kTableColumnWidths[] = {22, 18, 7, 8, 5, 6, 6, 4, 8, 7}; |
| |
| SuccessOrExit(error = ParseArgs(aArgs, isList, numEntries)); |
| |
| if (!isList) |
| { |
| OutputTableHeader(kTableTitles, kTableColumnWidths); |
| } |
| |
| otHistoryTrackerInitIterator(&txIterator); |
| otHistoryTrackerInitIterator(&rxIterator); |
| |
| for (uint16_t index = 0; (numEntries == 0) || (index < numEntries); index++) |
| { |
| switch (aRxTx) |
| { |
| case kRx: |
| info = otHistoryTrackerIterateRxHistory(GetInstancePtr(), &rxIterator, &entryAge); |
| isRx = true; |
| break; |
| |
| case kTx: |
| info = otHistoryTrackerIterateTxHistory(GetInstancePtr(), &txIterator, &entryAge); |
| isRx = false; |
| break; |
| |
| case kRxTx: |
| // Iterate through both RX and TX lists and determine the entry |
| // with earlier age. |
| |
| if (rxInfo == nullptr) |
| { |
| rxInfo = otHistoryTrackerIterateRxHistory(GetInstancePtr(), &rxIterator, &rxEntryAge); |
| } |
| |
| if (txInfo == nullptr) |
| { |
| txInfo = otHistoryTrackerIterateTxHistory(GetInstancePtr(), &txIterator, &txEntryAge); |
| } |
| |
| if ((rxInfo != nullptr) && ((txInfo == nullptr) || (rxEntryAge <= txEntryAge))) |
| { |
| info = rxInfo; |
| entryAge = rxEntryAge; |
| isRx = true; |
| rxInfo = nullptr; |
| } |
| else |
| { |
| info = txInfo; |
| entryAge = txEntryAge; |
| isRx = false; |
| txInfo = nullptr; |
| } |
| |
| break; |
| } |
| |
| VerifyOrExit(info != nullptr); |
| |
| if (isList) |
| { |
| OutputRxTxEntryListFormat(*info, entryAge, isRx); |
| } |
| else |
| { |
| if (index != 0) |
| { |
| OutputTableSeparator(kTableColumnWidths); |
| } |
| |
| OutputRxTxEntryTableFormat(*info, entryAge, isRx); |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| void History::OutputRxTxEntryListFormat(const otHistoryTrackerMessageInfo &aInfo, uint32_t aEntryAge, bool aIsRx) |
| { |
| constexpr uint8_t kIndentSize = 4; |
| |
| char ageString[OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE]; |
| |
| otHistoryTrackerEntryAgeToString(aEntryAge, ageString, sizeof(ageString)); |
| |
| OutputLine("%s", ageString); |
| OutputFormat(kIndentSize, "type:%s len:%u cheksum:0x%04x sec:%s prio:%s ", MessageTypeToString(aInfo), |
| aInfo.mPayloadLength, aInfo.mChecksum, aInfo.mLinkSecurity ? "yes" : "no", |
| MessagePriorityToString(aInfo.mPriority)); |
| if (aIsRx) |
| { |
| OutputFormat("rss:%d", aInfo.mAveRxRss); |
| } |
| else |
| { |
| OutputFormat("tx-success:%s", aInfo.mTxSuccess ? "yes" : "no"); |
| } |
| |
| OutputLine(" %s:0x%04x radio:%s", aIsRx ? "from" : "to", aInfo.mNeighborRloc16, RadioTypeToString(aInfo)); |
| |
| OutputFormat(kIndentSize, "src:"); |
| OutputSockAddrLine(aInfo.mSource); |
| |
| OutputFormat(kIndentSize, "dst:"); |
| OutputSockAddrLine(aInfo.mDestination); |
| } |
| |
| void History::OutputRxTxEntryTableFormat(const otHistoryTrackerMessageInfo &aInfo, uint32_t aEntryAge, bool aIsRx) |
| { |
| char ageString[OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE]; |
| char addrString[OT_IP6_SOCK_ADDR_STRING_SIZE]; |
| |
| otHistoryTrackerEntryAgeToString(aEntryAge, ageString, sizeof(ageString)); |
| |
| OutputFormat("| %20s | %-16.16s | %5u | 0x%04x | %3s | %4s | ", "", MessageTypeToString(aInfo), |
| aInfo.mPayloadLength, aInfo.mChecksum, aInfo.mLinkSecurity ? "yes" : "no", |
| MessagePriorityToString(aInfo.mPriority)); |
| |
| if (aIsRx) |
| { |
| OutputFormat("%4d | RX ", aInfo.mAveRxRss); |
| } |
| else |
| { |
| OutputFormat(" NA |"); |
| OutputFormat(aInfo.mTxSuccess ? " TX " : "TX-F"); |
| } |
| |
| if (aInfo.mNeighborRloc16 == kShortAddrBroadcast) |
| { |
| OutputFormat("| bcast "); |
| } |
| else if (aInfo.mNeighborRloc16 == kShortAddrInvalid) |
| { |
| OutputFormat("| unknwn "); |
| } |
| else |
| { |
| OutputFormat("| 0x%04x ", aInfo.mNeighborRloc16); |
| } |
| |
| OutputLine("| %5.5s |", RadioTypeToString(aInfo)); |
| |
| otIp6SockAddrToString(&aInfo.mSource, addrString, sizeof(addrString)); |
| OutputLine("| %20s | src: %-70s |", ageString, addrString); |
| |
| otIp6SockAddrToString(&aInfo.mDestination, addrString, sizeof(addrString)); |
| OutputLine("| %20s | dst: %-70s |", "", addrString); |
| } |
| |
| otError History::ProcessPrefix(Arg aArgs[]) |
| { |
| otError error; |
| bool isList; |
| uint16_t numEntries; |
| otHistoryTrackerIterator iterator; |
| const otHistoryTrackerOnMeshPrefixInfo *info; |
| uint32_t entryAge; |
| char ageString[OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE]; |
| char prefixString[OT_IP6_PREFIX_STRING_SIZE]; |
| NetworkData::FlagsString flagsString; |
| |
| static_assert(0 == OT_HISTORY_TRACKER_NET_DATA_ENTRY_ADDED, "NET_DATA_ENTRY_ADDED value is incorrect"); |
| static_assert(1 == OT_HISTORY_TRACKER_NET_DATA_ENTRY_REMOVED, "NET_DATA_ENTRY_REMOVED value is incorrect"); |
| |
| SuccessOrExit(error = ParseArgs(aArgs, isList, numEntries)); |
| |
| if (!isList) |
| { |
| // | Age | Event | Prefix | Flags | Pref | RLOC16 | |
| // +----------------------+---------+---------------------------------------------+-----------+------+--------+ |
| |
| static const char *const kPrefixTitles[] = {"Age", "Event", "Prefix", "Flags", "Pref", "RLOC16"}; |
| static const uint8_t kPrefixColumnWidths[] = {22, 9, 45, 11, 6, 8}; |
| |
| OutputTableHeader(kPrefixTitles, kPrefixColumnWidths); |
| } |
| |
| otHistoryTrackerInitIterator(&iterator); |
| |
| for (uint16_t index = 0; (numEntries == 0) || (index < numEntries); index++) |
| { |
| info = otHistoryTrackerIterateOnMeshPrefixHistory(GetInstancePtr(), &iterator, &entryAge); |
| VerifyOrExit(info != nullptr); |
| |
| otHistoryTrackerEntryAgeToString(entryAge, ageString, sizeof(ageString)); |
| |
| otIp6PrefixToString(&info->mPrefix.mPrefix, prefixString, sizeof(prefixString)); |
| NetworkData::PrefixFlagsToString(info->mPrefix, flagsString); |
| |
| OutputLine(isList ? "%s -> event:%s prefix:%s flags:%s pref:%s rloc16:0x%04x" |
| : "| %20s | %-7s | %-43s | %-9s | %-4s | 0x%04x |", |
| ageString, Stringify(info->mEvent, kSimpleEventStrings), prefixString, flagsString, |
| NetworkData::PreferenceToString(info->mPrefix.mPreference), info->mPrefix.mRloc16); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError History::ProcessRoute(Arg aArgs[]) |
| { |
| otError error; |
| bool isList; |
| uint16_t numEntries; |
| otHistoryTrackerIterator iterator; |
| const otHistoryTrackerExternalRouteInfo *info; |
| uint32_t entryAge; |
| char ageString[OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE]; |
| char prefixString[OT_IP6_PREFIX_STRING_SIZE]; |
| NetworkData::FlagsString flagsString; |
| |
| static_assert(0 == OT_HISTORY_TRACKER_NET_DATA_ENTRY_ADDED, "NET_DATA_ENTRY_ADDED value is incorrect"); |
| static_assert(1 == OT_HISTORY_TRACKER_NET_DATA_ENTRY_REMOVED, "NET_DATA_ENTRY_REMOVED value is incorrect"); |
| |
| SuccessOrExit(error = ParseArgs(aArgs, isList, numEntries)); |
| |
| if (!isList) |
| { |
| // | Age | Event | Route | Flags | Pref | RLOC16 | |
| // +----------------------+---------+---------------------------------------------+-----------+------+--------+ |
| |
| static const char *const kRouteTitles[] = {"Age", "Event", "Route", "Flags", "Pref", "RLOC16"}; |
| static const uint8_t kRouteColumnWidths[] = {22, 9, 45, 11, 6, 8}; |
| |
| OutputTableHeader(kRouteTitles, kRouteColumnWidths); |
| } |
| |
| otHistoryTrackerInitIterator(&iterator); |
| |
| for (uint16_t index = 0; (numEntries == 0) || (index < numEntries); index++) |
| { |
| info = otHistoryTrackerIterateExternalRouteHistory(GetInstancePtr(), &iterator, &entryAge); |
| VerifyOrExit(info != nullptr); |
| |
| otHistoryTrackerEntryAgeToString(entryAge, ageString, sizeof(ageString)); |
| |
| otIp6PrefixToString(&info->mRoute.mPrefix, prefixString, sizeof(prefixString)); |
| NetworkData::RouteFlagsToString(info->mRoute, flagsString); |
| |
| OutputLine(isList ? "%s -> event:%s route:%s flags:%s pref:%s rloc16:0x%04x" |
| : "| %20s | %-7s | %-43s | %-9s | %-4s | 0x%04x |", |
| ageString, Stringify(info->mEvent, kSimpleEventStrings), prefixString, flagsString, |
| NetworkData::PreferenceToString(info->mRoute.mPreference), info->mRoute.mRloc16); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError History::Process(Arg aArgs[]) |
| { |
| otError error = OT_ERROR_INVALID_COMMAND; |
| const Command *command; |
| |
| if (aArgs[0].IsEmpty()) |
| { |
| IgnoreError(ProcessHelp(aArgs)); |
| ExitNow(); |
| } |
| |
| command = BinarySearch::Find(aArgs[0].GetCString(), sCommands); |
| VerifyOrExit(command != nullptr); |
| |
| error = (this->*command->mHandler)(aArgs + 1); |
| |
| exit: |
| return error; |
| } |
| |
| } // namespace Cli |
| } // namespace ot |
| |
| #endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE |