| /* |
| * 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 { |
| |
| static const char *const kSimpleEventStrings[] = { |
| "Added", // (0) OT_HISTORY_TRACKER_{NET_DATA_ENTRY/ADDRESS_EVENT}_ADDED |
| "Removed" // (1) OT_HISTORY_TRACKER_{NET_DATA_ENTRY/ADDRESS_EVENT}_REMOVED |
| }; |
| |
| 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; |
| } |
| |
| /** |
| * @cli history ipaddr |
| * @code |
| * history ipaddr |
| * | Age | Event | Address / Prefix Length | Origin |Scope| P | V | R | |
| * +----------------------+---------+---------------------------------------------+--------+-----+---+---+---+ |
| * | 00:00:04.991 | Removed | 2001:dead:beef:cafe:c4cb:caba:8d55:e30b/64 | slaac | 14 | Y | Y | N | |
| * | 00:00:44.647 | Added | 2001:dead:beef:cafe:c4cb:caba:8d55:e30b/64 | slaac | 14 | Y | Y | N | |
| * | 00:01:07.199 | Added | fd00:0:0:0:0:0:0:1/64 | manual | 14 | Y | Y | N | |
| * | 00:02:17.885 | Added | fdde:ad00:beef:0:0:ff:fe00:fc00/64 | thread | 3 | N | Y | N | |
| * | 00:02:17.885 | Added | fdde:ad00:beef:0:0:ff:fe00:5400/64 | thread | 3 | N | Y | Y | |
| * | 00:02:20.107 | Removed | fdde:ad00:beef:0:0:ff:fe00:5400/64 | thread | 3 | N | Y | Y | |
| * | 00:02:21.575 | Added | fdde:ad00:beef:0:0:ff:fe00:5400/64 | thread | 3 | N | Y | Y | |
| * | 00:02:21.575 | Added | fdde:ad00:beef:0:ecea:c4fc:ad96:4655/64 | thread | 3 | N | Y | N | |
| * | 00:02:23.904 | Added | fe80:0:0:0:3c12:a4d2:fbe0:31ad/64 | thread | 2 | Y | Y | N | |
| * Done |
| * @endcode |
| * @code |
| * history ipaddr list 5 |
| * 00:00:20.327 -> event:Removed address:2001:dead:beef:cafe:c4cb:caba:8d55:e30b <!-- |
| * -->prefixlen:64 origin:slaac scope:14 preferred:yes valid:yes rloc:no |
| * 00:00:59.983 -> event:Added address:2001:dead:beef:cafe:c4cb:caba:8d55:e30b <!-- |
| * -->prefixlen:64 origin:slaac scope:14 preferred:yes valid:yes rloc:no |
| * 00:01:22.535 -> event:Added address:fd00:0:0:0:0:0:0:1 prefixlen:64 <!-- |
| * -->origin:manual scope:14 preferred:yes valid:yes rloc:no |
| * 00:02:33.221 -> event:Added address:fdde:ad00:beef:0:0:ff:fe00:fc00 <!-- |
| * -->prefixlen:64 origin:thread scope:3 preferred:no valid:yes rloc:no |
| * 00:02:33.221 -> event:Added address:fdde:ad00:beef:0:0:ff:fe00:5400 <!-- |
| * -->prefixlen:64 origin:thread scope:3 preferred:no valid:yes rloc:yes |
| * Done |
| * @endcode |
| * @cparam history ipaddr [@ca{list}] [@ca{num-entries}] |
| * * Use the `list` option to display the output in list format. Otherwise, |
| * the output is shown in table format. |
| * * Use the `num-entries` option to limit the output to the number of |
| * most-recent entries specified. If this option is not used, all stored |
| * entries are shown in the output. |
| * @par |
| * Displays the unicast IPv6 address history in table or list format. |
| * @par |
| * Each table or list entry provides: |
| * * Age: Time elapsed since the command was issued, and given in the format: |
| * `hours`:`minutes`:`seconds`:`milliseconds` |
| * * Event: Possible values are `Added` or `Removed`. |
| * * Address/Prefix Length: Unicast address with its prefix length (in bits). |
| * * Origin: Possible value are `thread`, `slaac`, `dhcp6`, or `manual`. |
| * * Scope: IPv6 address scope. |
| * * P: Preferred flag. |
| * * V: Valid flag. |
| * * RLOC (R): This flag indicates if the IPv6 address is a routing locator. |
| * @note |
| * All commands under `history` require the `OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE` |
| * feature to be enabled. |
| * @sa otHistoryTrackerEntryAgeToString |
| * @sa otHistoryTrackerIterateUnicastAddressHistory |
| */ |
| template <> otError History::Process<Cmd("ipaddr")>(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) |
| { |
| size_t len = strlen(addressString); |
| |
| snprintf(&addressString[len], sizeof(addressString) - len, "/%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; |
| } |
| |
| /** |
| * @cli history ipmaddr |
| * @code |
| * history ipmaddr |
| * | Age | Event | Multicast Address | Origin | |
| * +----------------------+--------------+-----------------------------------------+--------+ |
| * | 00:00:08.592 | Unsubscribed | ff05:0:0:0:0:0:0:1 | Manual | |
| * | 00:01:25.353 | Subscribed | ff05:0:0:0:0:0:0:1 | Manual | |
| * | 00:01:54.953 | Subscribed | ff03:0:0:0:0:0:0:2 | Thread | |
| * | 00:01:54.953 | Subscribed | ff02:0:0:0:0:0:0:2 | Thread | |
| * | 00:01:59.329 | Subscribed | ff33:40:fdde:ad00:beef:0:0:1 | Thread | |
| * | 00:01:59.329 | Subscribed | ff32:40:fdde:ad00:beef:0:0:1 | Thread | |
| * | 00:02:01.129 | Subscribed | ff03:0:0:0:0:0:0:fc | Thread | |
| * | 00:02:01.129 | Subscribed | ff03:0:0:0:0:0:0:1 | Thread | |
| * | 00:02:01.129 | Subscribed | ff02:0:0:0:0:0:0:1 | Thread | |
| * Done |
| * @endcode |
| * @code |
| * history ipmaddr list |
| * 00:00:25.447 -> event:Unsubscribed address:ff05:0:0:0:0:0:0:1 origin:Manual |
| * 00:01:42.208 -> event:Subscribed address:ff05:0:0:0:0:0:0:1 origin:Manual |
| * 00:02:11.808 -> event:Subscribed address:ff03:0:0:0:0:0:0:2 origin:Thread |
| * 00:02:11.808 -> event:Subscribed address:ff02:0:0:0:0:0:0:2 origin:Thread |
| * 00:02:16.184 -> event:Subscribed address:ff33:40:fdde:ad00:beef:0:0:1 origin:Thread |
| * 00:02:16.184 -> event:Subscribed address:ff32:40:fdde:ad00:beef:0:0:1 origin:Thread |
| * 00:02:17.984 -> event:Subscribed address:ff03:0:0:0:0:0:0:fc origin:Thread |
| * 00:02:17.984 -> event:Subscribed address:ff03:0:0:0:0:0:0:1 origin:Thread |
| * 00:02:17.984 -> event:Subscribed address:ff02:0:0:0:0:0:0:1 origin:Thread |
| * Done |
| * @endcode |
| * @cparam history ipmaddr [@ca{list}] [@ca{num-entries}] |
| * * Use the `list` option to display the output in list format. Otherwise, |
| * the output is shown in table format. |
| * * Use the `num-entries` option to limit the output to the number of |
| * most-recent entries specified. If this option is not used, all stored |
| * entries are shown in the output. |
| * @par |
| * Displays the multicast IPv6 address history in table or list format. |
| * @par |
| * Each table or list entry provides: |
| * * Age: Time elapsed since the command was issued, and given in the format: |
| * `hours`:`minutes`:`seconds`:`milliseconds` |
| * * Event: Possible values are `Subscribed` or `Unsubscribed`. |
| * * Multicast Address |
| * * Origin: Possible values are `Thread` or `Manual`. |
| * @sa otHistoryTrackerEntryAgeToString |
| * @sa otHistoryTrackerIterateMulticastAddressHistory |
| */ |
| template <> otError History::Process<Cmd("ipmaddr")>(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; |
| } |
| |
| /** |
| * @cli history neighbor |
| * @code |
| * history neighbor |
| * | Age | Type | Event | Extended Address | RLOC16 | Mode | Ave RSS | |
| * +----------------------+--------+-----------+------------------+--------+------+---------+ |
| * | 00:00:29.233 | Child | Added | ae5105292f0b9169 | 0x8404 | - | -20 | |
| * | 00:01:38.368 | Child | Removed | ae5105292f0b9169 | 0x8401 | - | -20 | |
| * | 00:04:27.181 | Child | Changed | ae5105292f0b9169 | 0x8401 | - | -20 | |
| * | 00:04:51.236 | Router | Added | 865c7ca38a5fa960 | 0x9400 | rdn | -20 | |
| * | 00:04:51.587 | Child | Removed | 865c7ca38a5fa960 | 0x8402 | rdn | -20 | |
| * | 00:05:22.764 | Child | Changed | ae5105292f0b9169 | 0x8401 | rn | -20 | |
| * | 00:06:40.764 | Child | Added | 4ec99efc874a1841 | 0x8403 | r | -20 | |
| * | 00:06:44.060 | Child | Added | 865c7ca38a5fa960 | 0x8402 | rdn | -20 | |
| * | 00:06:49.515 | Child | Added | ae5105292f0b9169 | 0x8401 | - | -20 | |
| * Done |
| * @endcode |
| * @code |
| * history neighbor list |
| * 00:00:34.753 -> type:Child event:Added extaddr:ae5105292f0b9169 rloc16:0x8404 mode:- rss:-20 |
| * 00:01:43.888 -> type:Child event:Removed extaddr:ae5105292f0b9169 rloc16:0x8401 mode:- rss:-20 |
| * 00:04:32.701 -> type:Child event:Changed extaddr:ae5105292f0b9169 rloc16:0x8401 mode:- rss:-20 |
| * 00:04:56.756 -> type:Router event:Added extaddr:865c7ca38a5fa960 rloc16:0x9400 mode:rdn rss:-20 |
| * 00:04:57.107 -> type:Child event:Removed extaddr:865c7ca38a5fa960 rloc16:0x8402 mode:rdn rss:-20 |
| * 00:05:28.284 -> type:Child event:Changed extaddr:ae5105292f0b9169 rloc16:0x8401 mode:rn rss:-20 |
| * 00:06:46.284 -> type:Child event:Added extaddr:4ec99efc874a1841 rloc16:0x8403 mode:r rss:-20 |
| * 00:06:49.580 -> type:Child event:Added extaddr:865c7ca38a5fa960 rloc16:0x8402 mode:rdn rss:-20 |
| * 00:06:55.035 -> type:Child event:Added extaddr:ae5105292f0b9169 rloc16:0x8401 mode:- rss:-20 |
| * Done |
| * @endcode |
| * @cparam history neighbor [@ca{list}] [@ca{num-entries}] |
| * * Use the `list` option to display the output in list format. Otherwise, |
| * the output is shown in table format. |
| * * Use the `num-entries` option to limit the output to the number of |
| * most-recent entries specified. If this option is not used, all stored |
| * entries are shown in the output. |
| * @par |
| * Displays the neighbor history in table or list format. |
| * @par |
| * Each table or list entry provides: |
| * * Age: Time elapsed since the command was issued, and given in the format: |
| * `hours`:`minutes`:`seconds`:`milliseconds` |
| * * Type: `Child` or `Router`. |
| * * Event: Possible values are `Added`, `Removed`, or `Changed`. |
| * * Extended Address |
| * * RLOC16 |
| * * Mode: MLE link mode. Possible values: |
| * * `-`: no flags set (rx-off-when-idle, minimal Thread device, |
| * stable network data). |
| * * `r`: rx-on-when-idle |
| * * `d`: Full Thread Device. |
| * * `n`: Full Network Data |
| * * Ave RSS: Average number of frames (in dBm) received from the neighbor at the |
| * time the entry was recorded. |
| * @sa otHistoryTrackerIterateNeighborHistory |
| */ |
| template <> otError History::Process<Cmd("neighbor")>(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; |
| } |
| |
| /** |
| * @cli history router |
| * @code |
| * history router |
| * | Age | Event | ID (RLOC16) | Next Hop | Path Cost | |
| * +----------------------+----------------+-------------+-------------+------------+ |
| * | 00:00:05.258 | NextHopChanged | 7 (0x1c00) | 34 (0x8800) | inf -> 3 | |
| * | 00:00:08.604 | NextHopChanged | 34 (0x8800) | 34 (0x8800) | inf -> 2 | |
| * | 00:00:08.604 | Added | 7 (0x1c00) | none | inf -> inf | |
| * | 00:00:11.931 | Added | 34 (0x8800) | none | inf -> inf | |
| * | 00:00:14.948 | Removed | 59 (0xec00) | none | inf -> inf | |
| * | 00:00:14.948 | Removed | 54 (0xd800) | none | inf -> inf | |
| * | 00:00:14.948 | Removed | 34 (0x8800) | none | inf -> inf | |
| * | 00:00:14.948 | Removed | 7 (0x1c00) | none | inf -> inf | |
| * | 00:00:54.795 | NextHopChanged | 59 (0xec00) | 34 (0x8800) | 1 -> 5 | |
| * | 00:02:33.735 | NextHopChanged | 54 (0xd800) | none | 15 -> inf | |
| * | 00:03:10.915 | CostChanged | 54 (0xd800) | 34 (0x8800) | 13 -> 15 | |
| * | 00:03:45.716 | NextHopChanged | 54 (0xd800) | 34 (0x8800) | 15 -> 13 | |
| * | 00:03:46.188 | CostChanged | 54 (0xd800) | 59 (0xec00) | 13 -> 15 | |
| * | 00:04:19.124 | CostChanged | 54 (0xd800) | 59 (0xec00) | 11 -> 13 | |
| * | 00:04:52.008 | CostChanged | 54 (0xd800) | 59 (0xec00) | 9 -> 11 | |
| * | 00:05:23.176 | CostChanged | 54 (0xd800) | 59 (0xec00) | 7 -> 9 | |
| * | 00:05:51.081 | CostChanged | 54 (0xd800) | 59 (0xec00) | 5 -> 7 | |
| * | 00:06:48.721 | CostChanged | 54 (0xd800) | 59 (0xec00) | 3 -> 5 | |
| * | 00:07:13.792 | NextHopChanged | 54 (0xd800) | 59 (0xec00) | 1 -> 3 | |
| * | 00:09:28.681 | NextHopChanged | 7 (0x1c00) | 34 (0x8800) | inf -> 3 | |
| * | 00:09:31.882 | Added | 7 (0x1c00) | none | inf -> inf | |
| * | 00:09:51.240 | NextHopChanged | 54 (0xd800) | 54 (0xd800) | inf -> 1 | |
| * | 00:09:54.204 | Added | 54 (0xd800) | none | inf -> inf | |
| * | 00:10:20.645 | NextHopChanged | 34 (0x8800) | 34 (0x8800) | inf -> 2 | |
| * | 00:10:24.242 | NextHopChanged | 59 (0xec00) | 59 (0xec00) | inf -> 1 | |
| * | 00:10:24.242 | Added | 34 (0x8800) | none | inf -> inf | |
| * | 00:10:41.900 | NextHopChanged | 59 (0xec00) | none | 1 -> inf | |
| * | 00:10:42.480 | Added | 3 (0x0c00) | 3 (0x0c00) | inf -> inf | |
| * | 00:10:43.614 | Added | 59 (0xec00) | 59 (0xec00) | inf -> 1 | |
| * Done |
| * @endcode |
| * @code |
| * history router list 20 |
| * 00:00:06.959 -> event:NextHopChanged router:7(0x1c00) nexthop:34(0x8800) old-cost:inf new-cost:3 |
| * 00:00:10.305 -> event:NextHopChanged router:34(0x8800) nexthop:34(0x8800) old-cost:inf new-cost:2 |
| * 00:00:10.305 -> event:Added router:7(0x1c00) nexthop:none old-cost:inf new-cost:inf |
| * 00:00:13.632 -> event:Added router:34(0x8800) nexthop:none old-cost:inf new-cost:inf |
| * 00:00:16.649 -> event:Removed router:59(0xec00) nexthop:none old-cost:inf new-cost:inf |
| * 00:00:16.649 -> event:Removed router:54(0xd800) nexthop:none old-cost:inf new-cost:inf |
| * 00:00:16.649 -> event:Removed router:34(0x8800) nexthop:none old-cost:inf new-cost:inf |
| * 00:00:16.649 -> event:Removed router:7(0x1c00) nexthop:none old-cost:inf new-cost:inf |
| * 00:00:56.496 -> event:NextHopChanged router:59(0xec00) nexthop:34(0x8800) old-cost:1 new-cost:5 |
| * 00:02:35.436 -> event:NextHopChanged router:54(0xd800) nexthop:none old-cost:15 new-cost:inf |
| * 00:03:12.616 -> event:CostChanged router:54(0xd800) nexthop:34(0x8800) old-cost:13 new-cost:15 |
| * 00:03:47.417 -> event:NextHopChanged router:54(0xd800) nexthop:34(0x8800) old-cost:15 new-cost:13 |
| * 00:03:47.889 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:13 new-cost:15 |
| * 00:04:20.825 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:11 new-cost:13 |
| * 00:04:53.709 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:9 new-cost:11 |
| * 00:05:24.877 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:7 new-cost:9 |
| * 00:05:52.782 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:5 new-cost:7 |
| * 00:06:50.422 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:3 new-cost:5 |
| * 00:07:15.493 -> event:NextHopChanged router:54(0xd800) nexthop:59(0xec00) old-cost:1 new-cost:3 |
| * 00:09:30.382 -> event:NextHopChanged router:7(0x1c00) nexthop:34(0x8800) old-cost:inf new-cost:3 |
| * Done |
| * @endcode |
| * @cparam history router [@ca{list}] [@ca{num-entries}] |
| * * Use the `list` option to display the output in list format. Otherwise, |
| * the output is shown in table format. |
| * * Use the `num-entries` option to limit the output to the number of |
| * most-recent entries specified. If this option is not used, all stored |
| * entries are shown in the output. |
| * @par |
| * Displays the route-table history in table or list format. |
| * @par |
| * Each table or list entry provides: |
| * * Age: Time elapsed since the command was issued, and given in the format: |
| * `hours`:`minutes`:`seconds`:`milliseconds` |
| * * Event: Possible values are `Added`, `Removed`, `NextHopChanged`, or `CostChanged`. |
| * * ID (RLOC16): Router ID and RLOC16 of the router. |
| * * Next Hop: Router ID and RLOC16 of the next hop. If there is no next hop, |
| * `none` is shown. |
| * * Path Cost: old cost `->` new cost. A value of `inf` indicates an infinite |
| * path cost. |
| * @sa otHistoryTrackerIterateRouterHistory |
| */ |
| template <> otError History::Process<Cmd("router")>(Arg aArgs[]) |
| { |
| static const char *const kEventString[] = { |
| /* (0) OT_HISTORY_TRACKER_ROUTER_EVENT_ADDED -> */ "Added", |
| /* (1) OT_HISTORY_TRACKER_ROUTER_EVENT_REMOVED -> */ "Removed", |
| /* (2) OT_HISTORY_TRACKER_ROUTER_EVENT_NEXT_HOP_CHANGED -> */ "NextHopChanged", |
| /* (3) OT_HISTORY_TRACKER_ROUTER_EVENT_COST_CHANGED -> */ "CostChanged", |
| }; |
| |
| constexpr uint8_t kRouterIdOffset = 10; // Bit offset of Router ID in RLOC16 |
| |
| otError error; |
| bool isList; |
| uint16_t numEntries; |
| otHistoryTrackerIterator iterator; |
| const otHistoryTrackerRouterInfo *info; |
| uint32_t entryAge; |
| char ageString[OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE]; |
| |
| static_assert(0 == OT_HISTORY_TRACKER_ROUTER_EVENT_ADDED, "EVENT_ADDED is incorrect"); |
| static_assert(1 == OT_HISTORY_TRACKER_ROUTER_EVENT_REMOVED, "EVENT_REMOVED is incorrect"); |
| static_assert(2 == OT_HISTORY_TRACKER_ROUTER_EVENT_NEXT_HOP_CHANGED, "EVENT_NEXT_HOP_CHANGED is incorrect"); |
| static_assert(3 == OT_HISTORY_TRACKER_ROUTER_EVENT_COST_CHANGED, "EVENT_COST_CHANGED is incorrect"); |
| |
| SuccessOrExit(error = ParseArgs(aArgs, isList, numEntries)); |
| |
| if (!isList) |
| { |
| // | Age | Event | ID (RlOC16) | Next Hop | Path Cost | |
| // +----------------------+----------------+-------------+------------+-------------+ |
| |
| static const char *const kRouterInfoTitles[] = { |
| "Age", "Event", "ID (RLOC16)", "Next Hop", "Path Cost", |
| }; |
| |
| static const uint8_t kRouterInfoColumnWidths[] = {22, 16, 13, 13, 12}; |
| |
| OutputTableHeader(kRouterInfoTitles, kRouterInfoColumnWidths); |
| } |
| |
| otHistoryTrackerInitIterator(&iterator); |
| |
| for (uint16_t index = 0; (numEntries == 0) || (index < numEntries); index++) |
| { |
| info = otHistoryTrackerIterateRouterHistory(GetInstancePtr(), &iterator, &entryAge); |
| VerifyOrExit(info != nullptr); |
| |
| otHistoryTrackerEntryAgeToString(entryAge, ageString, sizeof(ageString)); |
| |
| OutputFormat(isList ? "%s -> event:%s router:%u(0x%04x) nexthop:" : "| %20s | %-14s | %2u (0x%04x) | ", |
| ageString, kEventString[info->mEvent], info->mRouterId, |
| static_cast<uint16_t>(info->mRouterId) << kRouterIdOffset); |
| |
| if (info->mNextHop != OT_HISTORY_TRACKER_NO_NEXT_HOP) |
| { |
| OutputFormat(isList ? "%u(0x%04x)" : "%2u (0x%04x)", info->mNextHop, |
| static_cast<uint16_t>(info->mNextHop) << kRouterIdOffset); |
| } |
| else |
| { |
| OutputFormat(isList ? "%s" : "%11s", "none"); |
| } |
| |
| if (info->mOldPathCost != OT_HISTORY_TRACKER_INFINITE_PATH_COST) |
| { |
| OutputFormat(isList ? " old-cost:%u" : " | %3u ->", info->mOldPathCost); |
| } |
| else |
| { |
| OutputFormat(isList ? " old-cost:inf" : " | inf ->"); |
| } |
| |
| if (info->mPathCost != OT_HISTORY_TRACKER_INFINITE_PATH_COST) |
| { |
| OutputLine(isList ? " new-cost:%u" : " %3u |", info->mPathCost); |
| } |
| else |
| { |
| OutputLine(isList ? " new-cost:inf" : " inf |"); |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * @cli history netinfo |
| * @code |
| * history netinfo |
| * | Age | Role | Mode | RLOC16 | Partition ID | |
| * +----------------------+----------+------+--------+--------------+ |
| * | 00:00:10.069 | router | rdn | 0x6000 | 151029327 | |
| * | 00:02:09.337 | child | rdn | 0x2001 | 151029327 | |
| * | 00:02:09.338 | child | rdn | 0x2001 | 151029327 | |
| * | 00:07:40.806 | child | - | 0x2001 | 151029327 | |
| * | 00:07:42.297 | detached | - | 0x6000 | 0 | |
| * | 00:07:42.968 | disabled | - | 0x6000 | 0 | |
| * Done |
| * @endcode |
| * @code |
| * history netinfo list |
| * 00:00:59.467 -> role:router mode:rdn rloc16:0x6000 partition-id:151029327 |
| * 00:02:58.735 -> role:child mode:rdn rloc16:0x2001 partition-id:151029327 |
| * 00:02:58.736 -> role:child mode:rdn rloc16:0x2001 partition-id:151029327 |
| * 00:08:30.204 -> role:child mode:- rloc16:0x2001 partition-id:151029327 |
| * 00:08:31.695 -> role:detached mode:- rloc16:0x6000 partition-id:0 |
| * 00:08:32.366 -> role:disabled mode:- rloc16:0x6000 partition-id:0 |
| * Done |
| * @endcode |
| * @code |
| * history netinfo 2 |
| * | Age | Role | Mode | RLOC16 | Partition ID | |
| * +----------------------+----------+------+--------+--------------+ |
| * | 00:02:05.451 | router | rdn | 0x6000 | 151029327 | |
| * | 00:04:04.719 | child | rdn | 0x2001 | 151029327 | |
| * Done |
| * @endcode |
| * @cparam history netinfo [@ca{list}] [@ca{num-entries}] |
| * * Use the `list` option to display the output in list format. Otherwise, |
| * the output is shown in table format. |
| * * Use the `num-entries` option to limit the output to the number of |
| * most-recent entries specified. If this option is not used, all stored |
| * entries are shown in the output. |
| * @par |
| * Displays the network info history in table or list format. |
| * @par |
| * Each table or list entry provides: |
| * * Age: Time elapsed since the command was issued, and given in the format: |
| * `hours`:`minutes`:`seconds`:`milliseconds` |
| * * Role: Device role. Possible values are `router`, `child`, `detached`, or `disabled`. |
| * * Mode: MLE link mode. Possible values: |
| * * `-`: no flags set (rx-off-when-idle, minimal Thread device, |
| * stable network data). |
| * * `r`: rx-on-when-idle |
| * * `d`: Full Thread Device. |
| * * `n`: Full Network Data |
| * * RLOC16 |
| * * Partition ID. |
| * @sa otHistoryTrackerIterateNetInfoHistory |
| */ |
| template <> otError History::Process<Cmd("netinfo")>(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:%lu" : "| %20s | %-8s | %-4s | 0x%04x | %12lu |", |
| ageString, otThreadDeviceRoleToString(info->mRole), |
| Interpreter::LinkModeToString(info->mMode, linkModeString), info->mRloc16, ToUlong(info->mPartitionId)); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * @cli history rx |
| * @code |
| * history rx |
| * | Age | Type | Len | Chksum | Sec | Prio | RSS |Dir | Neighb | Radio | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 50 | 0xbd26 | no | net | -20 | RX | 0x4800 | 15.4 | |
| * | 00:00:07.640 | src: [fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788 | |
| * | | dst: [ff02:0:0:0:0:0:0:1]:19788 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | HopOpts | 44 | 0x0000 | yes | norm | -20 | RX | 0x4800 | 15.4 | |
| * | 00:00:09.263 | src: [fdde:ad00:beef:0:0:ff:fe00:4800]:0 | |
| * | | dst: [ff03:0:0:0:0:0:0:2]:0 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 12 | 0x3f7d | yes | net | -20 | RX | 0x4800 | 15.4 | |
| * | 00:00:09.302 | src: [fdde:ad00:beef:0:0:ff:fe00:4800]:61631 | |
| * | | dst: [fdde:ad00:beef:0:0:ff:fe00:4801]:61631 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | ICMP6(EchoReqst) | 16 | 0x942c | yes | norm | -20 | RX | 0x4800 | 15.4 | |
| * | 00:00:09.304 | src: [fdde:ad00:beef:0:ac09:a16b:3204:dc09]:0 | |
| * | | dst: [fdde:ad00:beef:0:dc0e:d6b3:f180:b75b]:0 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | HopOpts | 44 | 0x0000 | yes | norm | -20 | RX | 0x4800 | 15.4 | |
| * | 00:00:09.304 | src: [fdde:ad00:beef:0:0:ff:fe00:4800]:0 | |
| * | | dst: [ff03:0:0:0:0:0:0:2]:0 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 50 | 0x2e37 | no | net | -20 | RX | 0x4800 | 15.4 | |
| * | 00:00:21.622 | src: [fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788 | |
| * | | dst: [ff02:0:0:0:0:0:0:1]:19788 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 50 | 0xe177 | no | net | -20 | RX | 0x4800 | 15.4 | |
| * | 00:00:26.640 | src: [fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788 | |
| * | | dst: [ff02:0:0:0:0:0:0:1]:19788 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 165 | 0x82ee | yes | net | -20 | RX | 0x4800 | 15.4 | |
| * | 00:00:30.000 | src: [fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788 | |
| * | | dst: [fe80:0:0:0:a4a5:bbac:a8e:bd07]:19788 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 93 | 0x52df | no | net | -20 | RX | unknwn | 15.4 | |
| * | 00:00:30.480 | src: [fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788 | |
| * | | dst: [fe80:0:0:0:a4a5:bbac:a8e:bd07]:19788 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 50 | 0x5ccf | no | net | -20 | RX | unknwn | 15.4 | |
| * | 00:00:30.772 | src: [fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788 | |
| * | | dst: [ff02:0:0:0:0:0:0:1]:19788 | |
| * Done |
| * @endcode |
| * @code |
| * history rx list 4 |
| * 00:00:13.368 |
| type:UDP len:50 checksum:0xbd26 sec:no prio:net rss:-20 from:0x4800 radio:15.4 |
| src:[fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788 |
| dst:[ff02:0:0:0:0:0:0:1]:19788 |
| * 00:00:14.991 |
| type:HopOpts len:44 checksum:0x0000 sec:yes prio:norm rss:-20 from:0x4800 radio:15.4 |
| src:[fdde:ad00:beef:0:0:ff:fe00:4800]:0 |
| dst:[ff03:0:0:0:0:0:0:2]:0 |
| * 00:00:15.030 |
| type:UDP len:12 checksum:0x3f7d sec:yes prio:net rss:-20 from:0x4800 radio:15.4 |
| src:[fdde:ad00:beef:0:0:ff:fe00:4800]:61631 |
| dst:[fdde:ad00:beef:0:0:ff:fe00:4801]:61631 |
| * 00:00:15.032 |
| type:ICMP6(EchoReqst) len:16 checksum:0x942c sec:yes prio:norm rss:-20 from:0x4800 radio:15.4 |
| src:[fdde:ad00:beef:0:ac09:a16b:3204:dc09]:0 |
| dst:[fdde:ad00:beef:0:dc0e:d6b3:f180:b75b]:0 |
| * Done |
| * @endcode |
| * @cparam history rx [@ca{list}] [@ca{num-entries}] |
| * * Use the `list` option to display the output in list format. Otherwise, |
| * the output is shown in table format. |
| * * Use the `num-entries` option to limit the output to the number of |
| * most-recent entries specified. If this option is not used, all stored |
| * entries are shown in the output. |
| * @par |
| * Displays the IPv6 message RX history in table or list format. |
| * @par |
| * Each table or list entry provides: |
| * * Age: Time elapsed since the command was issued, and given in the format: |
| * `hours`:`minutes`:`seconds`:`milliseconds` |
| * * Type: |
| * * IPv6 message type, such as `UDP`, `TCP`, `HopOpts`, and `ICMP6` (and its subtype). |
| * * `src`: Source IPv6 address and port number. |
| * * `dst`: Destination IPv6 address and port number (port number is valid |
| for UDP/TCP, otherwise it is 0). |
| * * Len: IPv6 payload length (excluding the IPv6 header). |
| * * Chksum: Message checksum (valid for UDP, TCP, or ICMP6 messages). |
| * * Sec: Indicates if link-layer security was used. |
| * * Prio: Message priority. Possible values are `low`, `norm`, `high`, or |
| * `net` (for Thread control messages). |
| * * RSS: Received Signal Strength (in dBm), averaged over all received fragment |
| * frames that formed the message. For TX history, `NA` (not applicable) |
| is displayed. |
| * * Dir: Shows whether the message was sent (`TX`) or received (`RX`). A failed |
| * transmission is indicated with `TX-F` in table format or |
| * `tx-success:no` in list format. Examples of a failed transmission |
| * include a `tx`getting aborted and no `ack` getting sent from the peer for |
| * any of the message fragments. |
| * * Neighb: Short address (RLOC16) of the neighbor with whom the message was |
| * sent/received. If the frame was broadcast, it is shown as |
| * `bcast` in table format or `0xffff` in list format. If the short |
| * address of the neighbor is not available, it is shown as `unknwn` in |
| * table format or `0xfffe` in list format. |
| * * Radio: Radio link on which the message was sent/received (useful when |
| `OPENTHREAD_CONFIG_MULTI_RADIO` is enabled). Can be `15.4`, `trel`, |
| or `all` (if sent on all radio links). |
| * @sa otHistoryTrackerIterateRxHistory |
| */ |
| template <> otError History::Process<Cmd("rx")>(Arg aArgs[]) { return ProcessRxTxHistory(kRx, aArgs); } |
| |
| /** |
| * @cli history rxtx |
| * @code |
| * history rxtx |
| * | Age | Type | Len | Chksum | Sec | Prio | RSS |Dir | Neighb | Radio | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | HopOpts | 44 | 0x0000 | yes | norm | -20 | RX | 0x0800 | 15.4 | |
| * | 00:00:09.267 | src: [fdde:ad00:beef:0:0:ff:fe00:800]:0 | |
| * | | dst: [ff03:0:0:0:0:0:0:2]:0 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 12 | 0x6c6b | yes | net | -20 | RX | 0x0800 | 15.4 | |
| * | 00:00:09.290 | src: [fdde:ad00:beef:0:0:ff:fe00:800]:61631 | |
| * | | dst: [fdde:ad00:beef:0:0:ff:fe00:801]:61631 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | ICMP6(EchoReqst) | 16 | 0xc6a2 | yes | norm | -20 | RX | 0x0800 | 15.4 | |
| * | 00:00:09.292 | src: [fdde:ad00:beef:0:efe8:4910:cf95:dee9]:0 | |
| * | | dst: [fdde:ad00:beef:0:af4c:3644:882a:3698]:0 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | ICMP6(EchoReply) | 16 | 0xc5a2 | yes | norm | NA | TX | 0x0800 | 15.4 | |
| * | 00:00:09.292 | src: [fdde:ad00:beef:0:af4c:3644:882a:3698]:0 | |
| * | | dst: [fdde:ad00:beef:0:efe8:4910:cf95:dee9]:0 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 50 | 0xaa0d | yes | net | NA | TX | 0x0800 | 15.4 | |
| * | 00:00:09.294 | src: [fdde:ad00:beef:0:0:ff:fe00:801]:61631 | |
| * | | dst: [fdde:ad00:beef:0:0:ff:fe00:800]:61631 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | HopOpts | 44 | 0x0000 | yes | norm | -20 | RX | 0x0800 | 15.4 | |
| * | 00:00:09.296 | src: [fdde:ad00:beef:0:0:ff:fe00:800]:0 | |
| * | | dst: [ff03:0:0:0:0:0:0:2]:0 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 50 | 0xc1d8 | no | net | -20 | RX | 0x0800 | 15.4 | |
| * | 00:00:09.569 | src: [fe80:0:0:0:54d9:5153:ffc6:df26]:19788 | |
| * | | dst: [ff02:0:0:0:0:0:0:1]:19788 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 50 | 0x3cb1 | no | net | -20 | RX | 0x0800 | 15.4 | |
| * | 00:00:16.519 | src: [fe80:0:0:0:54d9:5153:ffc6:df26]:19788 | |
| * | | dst: [ff02:0:0:0:0:0:0:1]:19788 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 50 | 0xeda0 | no | net | -20 | RX | 0x0800 | 15.4 | |
| * | 00:00:20.599 | src: [fe80:0:0:0:54d9:5153:ffc6:df26]:19788 | |
| * | | dst: [ff02:0:0:0:0:0:0:1]:19788 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 165 | 0xbdfa | yes | net | -20 | RX | 0x0800 | 15.4 | |
| * | 00:00:21.059 | src: [fe80:0:0:0:54d9:5153:ffc6:df26]:19788 | |
| * | | dst: [fe80:0:0:0:8893:c2cc:d983:1e1c]:19788 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 64 | 0x1c11 | no | net | NA | TX | 0x0800 | 15.4 | |
| * | 00:00:21.062 | src: [fe80:0:0:0:8893:c2cc:d983:1e1c]:19788 | |
| * | | dst: [fe80:0:0:0:54d9:5153:ffc6:df26]:19788 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 93 | 0xedff | no | net | -20 | RX | unknwn | 15.4 | |
| * | 00:00:21.474 | src: [fe80:0:0:0:54d9:5153:ffc6:df26]:19788 | |
| * | | dst: [fe80:0:0:0:8893:c2cc:d983:1e1c]:19788 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 44 | 0xd383 | no | net | NA | TX | bcast | 15.4 | |
| * | 00:00:21.811 | src: [fe80:0:0:0:8893:c2cc:d983:1e1c]:19788 | |
| * | | dst: [ff02:0:0:0:0:0:0:2]:19788 | |
| * Done |
| * @endcode |
| * @code |
| * history rxtx list 5 |
| * 00:00:02.100 |
| type:UDP len:50 checksum:0xd843 sec:no prio:net rss:-20 from:0x0800 radio:15.4 |
| src:[fe80:0:0:0:54d9:5153:ffc6:df26]:19788 |
| dst:[ff02:0:0:0:0:0:0:1]:19788 |
| * 00:00:15.331 |
| type:HopOpts len:44 checksum:0x0000 sec:yes prio:norm rss:-20 from:0x0800 radio:15.4 |
| src:[fdde:ad00:beef:0:0:ff:fe00:800]:0 |
| dst:[ff03:0:0:0:0:0:0:2]:0 |
| * 00:00:15.354 |
| type:UDP len:12 checksum:0x6c6b sec:yes prio:net rss:-20 from:0x0800 radio:15.4 |
| src:[fdde:ad00:beef:0:0:ff:fe00:800]:61631 |
| dst:[fdde:ad00:beef:0:0:ff:fe00:801]:61631 |
| * 00:00:15.356 |
| type:ICMP6(EchoReqst) len:16 checksum:0xc6a2 sec:yes prio:norm rss:-20 from:0x0800 radio:15.4 |
| src:[fdde:ad00:beef:0:efe8:4910:cf95:dee9]:0 |
| dst:[fdde:ad00:beef:0:af4c:3644:882a:3698]:0 |
| * 00:00:15.356 |
| type:ICMP6(EchoReply) len:16 checksum:0xc5a2 sec:yes prio:norm tx-success:yes to:0x0800 radio:15.4 |
| src:[fdde:ad00:beef:0:af4c:3644:882a:3698]:0 |
| dst:[fdde:ad00:beef:0:efe8:4910:cf95:dee9]:0 |
| * Done |
| * @endcode |
| * @cparam history rxtx [@ca{list}] [@ca{num-entries}] |
| * * Use the `list` option to display the output in list format. Otherwise, |
| * the output is shown in table format. |
| * * Use the `num-entries` option to limit the output to the number of |
| * most-recent entries specified. If this option is not used, all stored |
| * entries are shown in the output. |
| * @par |
| * Displays the combined IPv6 message RX and TX history in table or list format. |
| * @par |
| * Each table or list entry provides: |
| * * Age: Time elapsed since the command was issued, and given in the format: |
| * `hours`:`minutes`:`seconds`:`milliseconds` |
| * * Type: |
| * * IPv6 message type, such as `UDP`, `TCP`, `HopOpts`, and `ICMP6` (and its subtype). |
| * * `src`: Source IPv6 address and port number. |
| * * `dst`: Destination IPv6 address and port number (port number is valid |
| for UDP/TCP, otherwise it is 0). |
| * * Len: IPv6 payload length (excluding the IPv6 header). |
| * * Chksum: Message checksum (valid for UDP, TCP, or ICMP6 messages). |
| * * Sec: Indicates if link-layer security was used. |
| * * Prio: Message priority. Possible values are `low`, `norm`, `high`, or |
| * `net` (for Thread control messages). |
| * * RSS: Received Signal Strength (in dBm), averaged over all received fragment |
| * frames that formed the message. For TX history, `NA` (not applicable) |
| is displayed. |
| * * Dir: Shows whether the message was sent (`TX`) or received (`RX`). A failed |
| * transmission is indicated with `TX-F` in table format or |
| * `tx-success:no` in list format. Examples of a failed transmission |
| * include a `tx`getting aborted and no `ack` getting sent from the peer for |
| * any of the message fragments. |
| * * Neighb: Short address (RLOC16) of the neighbor with whom the message was |
| * sent/received. If the frame was broadcast, it is shown as |
| * `bcast` in table format or `0xffff` in list format. If the short |
| * address of the neighbor is not available, it is shown as `unknwn` in |
| * table format or `0xfffe` in list format. |
| * * Radio: Radio link on which the message was sent/received (useful when |
| `OPENTHREAD_CONFIG_MULTI_RADIO` is enabled). Can be `15.4`, `trel`, |
| or `all` (if sent on all radio links). |
| * @sa otHistoryTrackerIterateRxHistory |
| * @sa otHistoryTrackerIterateTxHistory |
| */ |
| template <> otError History::Process<Cmd("rxtx")>(Arg aArgs[]) { return ProcessRxTxHistory(kRxTx, aArgs); } |
| |
| /** |
| * @cli history tx |
| * @code |
| * history tx |
| * | Age | Type | Len | Chksum | Sec | Prio | RSS |Dir | Neighb | Radio | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | ICMP6(EchoReply) | 16 | 0x932c | yes | norm | NA | TX | 0x4800 | 15.4 | |
| * | 00:00:18.798 | src: [fdde:ad00:beef:0:dc0e:d6b3:f180:b75b]:0 | |
| * | | dst: [fdde:ad00:beef:0:ac09:a16b:3204:dc09]:0 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 50 | 0xce87 | yes | net | NA | TX | 0x4800 | 15.4 | |
| * | 00:00:18.800 | src: [fdde:ad00:beef:0:0:ff:fe00:4801]:61631 | |
| * | | dst: [fdde:ad00:beef:0:0:ff:fe00:4800]:61631 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 64 | 0xf7ba | no | net | NA | TX | 0x4800 | 15.4 | |
| * | 00:00:39.499 | src: [fe80:0:0:0:a4a5:bbac:a8e:bd07]:19788 | |
| * | | dst: [fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788 | |
| * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+ |
| * | | UDP | 44 | 0x26d4 | no | net | NA | TX | bcast | 15.4 | |
| * | 00:00:40.256 | src: [fe80:0:0:0:a4a5:bbac:a8e:bd07]:19788 | |
| * | | dst: [ff02:0:0:0:0:0:0:2]:19788 | |
| * Done |
| * @endcode |
| * @code |
| * history tx list |
| * 00:00:23.957 |
| type:ICMP6(EchoReply) len:16 checksum:0x932c sec:yes prio:norm tx-success:yes to:0x4800 radio:15.4 |
| src:[fdde:ad00:beef:0:dc0e:d6b3:f180:b75b]:0 |
| dst:[fdde:ad00:beef:0:ac09:a16b:3204:dc09]:0 |
| * 00:00:23.959 |
| type:UDP len:50 checksum:0xce87 sec:yes prio:net tx-success:yes to:0x4800 radio:15.4 |
| src:[fdde:ad00:beef:0:0:ff:fe00:4801]:61631 |
| dst:[fdde:ad00:beef:0:0:ff:fe00:4800]:61631 |
| * 00:00:44.658 |
| type:UDP len:64 checksum:0xf7ba sec:no prio:net tx-success:yes to:0x4800 radio:15.4 |
| src:[fe80:0:0:0:a4a5:bbac:a8e:bd07]:19788 |
| dst:[fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788 |
| * 00:00:45.415 |
| type:UDP len:44 checksum:0x26d4 sec:no prio:net tx-success:yes to:0xffff radio:15.4 |
| src:[fe80:0:0:0:a4a5:bbac:a8e:bd07]:19788 |
| dst:[ff02:0:0:0:0:0:0:2]:19788 |
| * Done |
| * @endcode |
| * @cparam history tx [@ca{list}] [@ca{num-entries}] |
| * * Use the `list` option to display the output in list format. Otherwise, |
| * the output is shown in table format. |
| * * Use the `num-entries` option to limit the output to the number of |
| * most-recent entries specified. If this option is not used, all stored |
| * entries are shown in the output. |
| * @par |
| * Displays the IPv6 message TX history in table or list format. |
| * @par |
| * Each table or list entry provides: |
| * * Age: Time elapsed since the command was issued, and given in the format: |
| * `hours`:`minutes`:`seconds`:`milliseconds` |
| * * Type: |
| * * IPv6 message type, such as `UDP`, `TCP`, `HopOpts`, and `ICMP6` (and its subtype). |
| * * `src`: Source IPv6 address and port number. |
| * * `dst`: Destination IPv6 address and port number (port number is valid |
| for UDP/TCP, otherwise it is 0). |
| * * Len: IPv6 payload length (excluding the IPv6 header). |
| * * Chksum: Message checksum (valid for UDP, TCP, or ICMP6 messages). |
| * * Sec: Indicates if link-layer security was used. |
| * * Prio: Message priority. Possible values are `low`, `norm`, `high`, or |
| * `net` (for Thread control messages). |
| * * RSS: Received Signal Strength (in dBm), averaged over all received fragment |
| * frames that formed the message. For TX history, `NA` (not applicable) |
| is displayed. |
| * * Dir: Shows whether the message was sent (`TX`) or received (`RX`). A failed |
| * transmission is indicated with `TX-F` in table format or |
| * `tx-success:no` in list format. Examples of a failed transmission |
| * include a `tx`getting aborted and no `ack` getting sent from the peer for |
| * any of the message fragments. |
| * * Neighb: Short address (RLOC16) of the neighbor with whom the message was |
| * sent/received. If the frame was broadcast, it is shown as |
| * `bcast` in table format or `0xffff` in list format. If the short |
| * address of the neighbor is not available, it is shown as `unknwn` in |
| * table format or `0xfffe` in list format. |
| * * Radio: Radio link on which the message was sent/received (useful when |
| `OPENTHREAD_CONFIG_MULTI_RADIO` is enabled). Can be `15.4`, `trel`, |
| or `all` (if sent on all radio links). |
| * @sa otHistoryTrackerIterateTxHistory |
| */ |
| template <> otError History::Process<Cmd("tx")>(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 checksum: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); |
| } |
| |
| /** |
| * @cli history prefix |
| * @code |
| * history prefix |
| * | Age | Event | Prefix | Flags | Pref | RLOC16 | |
| * +----------------------+---------+---------------------------------------------+-----------+------+--------+ |
| * | 00:00:10.663 | Added | fd00:1111:2222:3333::/64 | paro | med | 0x5400 | |
| * | 00:01:02.054 | Removed | fd00:dead:beef:1::/64 | paros | high | 0x5400 | |
| * | 00:01:21.136 | Added | fd00:abba:cddd:0::/64 | paos | med | 0x5400 | |
| * | 00:01:45.144 | Added | fd00:dead:beef:1::/64 | paros | high | 0x3c00 | |
| * | 00:01:50.944 | Added | fd00:dead:beef:1::/64 | paros | high | 0x5400 | |
| * | 00:01:59.887 | Added | fd00:dead:beef:1::/64 | paros | med | 0x8800 | |
| * Done |
| * @endcode |
| * @code |
| * history prefix list |
| * 00:04:12.487 -> event:Added prefix:fd00:1111:2222:3333::/64 flags:paro pref:med rloc16:0x5400 |
| * 00:05:03.878 -> event:Removed prefix:fd00:dead:beef:1::/64 flags:paros pref:high rloc16:0x5400 |
| * 00:05:22.960 -> event:Added prefix:fd00:abba:cddd:0::/64 flags:paos pref:med rloc16:0x5400 |
| * 00:05:46.968 -> event:Added prefix:fd00:dead:beef:1::/64 flags:paros pref:high rloc16:0x3c00 |
| * 00:05:52.768 -> event:Added prefix:fd00:dead:beef:1::/64 flags:paros pref:high rloc16:0x5400 |
| * 00:06:01.711 -> event:Added prefix:fd00:dead:beef:1::/64 flags:paros pref:med rloc16:0x8800 |
| * Done |
| * @endcode |
| * @cparam history prefix [@ca{list}] [@ca{num-entries}] |
| * * Use the `list` option to display the output in list format. Otherwise, |
| * the output is shown in table format. |
| * * Use the `num-entries` option to limit the output to the number of |
| * most-recent entries specified. If this option is not used, all stored |
| * entries are shown in the output. |
| * @par |
| * Displays the network data for the mesh prefix history in table or list format. |
| * @par |
| * Each table or list entry provides: |
| * * Age: Time elapsed since the command was issued, and given in the format: |
| * `hours`:`minutes`:`seconds`:`milliseconds` |
| * * Event: Possible values are `Added` or `Removed`. |
| * * Prefix |
| * * Flags/meaning: |
| * * `p`: Preferred flag |
| * * `a`: Stateless IPv6 address auto-configuration flag. |
| * * `d`: DHCPv6 IPv6 address configuration flag. |
| * * `c`: DHCPv6 other-configuration flag. |
| * * `r`: Default route flag. |
| * * `o`: On mesh flag. |
| * * `s`: Stable flag. |
| * * `n`: Nd Dns flag. |
| * * `D`: Domain prefix flag. |
| * * Pref: Preference. Values can be either `high`, `med`, or `low`. |
| * * RLOC16 |
| * @sa otHistoryTrackerIterateOnMeshPrefixHistory |
| */ |
| template <> otError History::Process<Cmd("prefix")>(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, |
| Interpreter::PreferenceToString(info->mPrefix.mPreference), info->mPrefix.mRloc16); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * @cli history route |
| * @code |
| * history route |
| * | Age | Event | Route | Flags | Pref | RLOC16 | |
| * +----------------------+---------+---------------------------------------------+-----------+------+--------+ |
| * | 00:00:05.456 | Removed | fd00:1111:0::/48 | s | med | 0x3c00 | |
| * | 00:00:29.310 | Added | fd00:1111:0::/48 | s | med | 0x3c00 | |
| * | 00:00:42.822 | Added | fd00:1111:0::/48 | s | med | 0x5400 | |
| * | 00:01:27.688 | Added | fd00:aaaa:bbbb:cccc::/64 | s | med | 0x8800 | |
| * Done |
| * @endcode |
| * @code |
| * history route list 2 |
| * 00:00:48.704 -> event:Removed route:fd00:1111:0::/48 flags:s pref:med rloc16:0x3c00 |
| * 00:01:12.558 -> event:Added route:fd00:1111:0::/48 flags:s pref:med rloc16:0x3c00 |
| * Done |
| * @endcode |
| * @cparam history route [@ca{list}] [@ca{num-entries}] |
| * * Use the `list` option to display the output in list format. Otherwise, |
| * the output is shown in table format. |
| * * Use the `num-entries` option to limit the output to the number of |
| * most-recent entries specified. If this option is not used, all stored |
| * entries are shown in the output. |
| * @par |
| * Displays the network data external-route history in table or list format. |
| * @par |
| * Each table or list entry provides: |
| * * Age: Time elapsed since the command was issued, and given in the format: |
| * `hours`:`minutes`:`seconds`:`milliseconds` |
| * * Event: Possible values are `Added` or `Removed`. |
| * * Route |
| * * Flags/meaning: |
| * * `s`: Stable flag. |
| * * `n`: NAT64 flag. |
| * * Pref: Preference. Values can be either `high`, `med`, or `low`. |
| * * RLOC16 |
| * @sa otHistoryTrackerIterateExternalRouteHistory |
| */ |
| template <> otError History::Process<Cmd("route")>(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, |
| Interpreter::PreferenceToString(info->mRoute.mPreference), info->mRoute.mRloc16); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError History::Process(Arg aArgs[]) |
| { |
| #define CmdEntry(aCommandString) \ |
| { \ |
| aCommandString, &History::Process<Cmd(aCommandString)> \ |
| } |
| |
| static constexpr Command kCommands[] = { |
| CmdEntry("ipaddr"), CmdEntry("ipmaddr"), CmdEntry("neighbor"), CmdEntry("netinfo"), CmdEntry("prefix"), |
| CmdEntry("route"), CmdEntry("router"), CmdEntry("rx"), CmdEntry("rxtx"), CmdEntry("tx"), |
| }; |
| |
| #undef CmdEntry |
| |
| static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); |
| |
| otError error = OT_ERROR_INVALID_COMMAND; |
| const Command *command; |
| |
| if (aArgs[0].IsEmpty() || (aArgs[0] == "help")) |
| { |
| OutputCommandTable(kCommands); |
| ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE); |
| } |
| |
| command = BinarySearch::Find(aArgs[0].GetCString(), kCommands); |
| VerifyOrExit(command != nullptr); |
| |
| error = (this->*command->mHandler)(aArgs + 1); |
| |
| exit: |
| return error; |
| } |
| |
| } // namespace Cli |
| } // namespace ot |
| |
| #endif // OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE |