| /* |
| * Copyright (c) 2016, 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 the CLI interpreter. |
| */ |
| |
| #include "cli.hpp" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include "mac/channel_mask.hpp" |
| #include "utils/parse_cmdline.hpp" |
| |
| #include <openthread/icmp6.h> |
| #include <openthread/link.h> |
| #include <openthread/ncp.h> |
| #include <openthread/netdata.h> |
| #include <openthread/thread.h> |
| #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE |
| #include <openthread/network_time.h> |
| #endif |
| |
| #if OPENTHREAD_FTD |
| #include <openthread/dataset_ftd.h> |
| #include <openthread/thread_ftd.h> |
| #endif |
| |
| #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE |
| #include <openthread/border_router.h> |
| #endif |
| #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE |
| #include <openthread/server.h> |
| #endif |
| |
| #include <openthread/diag.h> |
| #include <openthread/icmp6.h> |
| #include <openthread/logging.h> |
| #include <openthread/platform/uart.h> |
| #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE |
| #include <openthread/platform/misc.h> |
| #endif |
| |
| #include "common/new.hpp" |
| #include "net/ip6.hpp" |
| #include "utils/otns.hpp" |
| |
| #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) |
| #include <openthread/backbone_router.h> |
| #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE |
| #include <openthread/backbone_router_ftd.h> |
| #endif |
| #endif |
| |
| #include "cli_dataset.hpp" |
| |
| #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD |
| #include <openthread/channel_manager.h> |
| #endif |
| |
| #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE |
| #include <openthread/channel_monitor.h> |
| #endif |
| |
| #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX |
| #include <openthread/platform/debug_uart.h> |
| #endif |
| |
| #include "common/encoding.hpp" |
| #include "common/string.hpp" |
| |
| using ot::Encoding::BigEndian::HostSwap16; |
| using ot::Encoding::BigEndian::HostSwap32; |
| |
| #define INDENT_SIZE (4) |
| |
| namespace ot { |
| |
| namespace Cli { |
| |
| const struct Command Interpreter::sCommands[] = { |
| #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) |
| {"bbr", &Interpreter::ProcessBackboneRouter}, |
| #endif |
| {"bufferinfo", &Interpreter::ProcessBufferInfo}, |
| {"channel", &Interpreter::ProcessChannel}, |
| #if OPENTHREAD_FTD |
| {"child", &Interpreter::ProcessChild}, |
| {"childip", &Interpreter::ProcessChildIp}, |
| {"childmax", &Interpreter::ProcessChildMax}, |
| #endif |
| {"childtimeout", &Interpreter::ProcessChildTimeout}, |
| #if OPENTHREAD_CONFIG_COAP_API_ENABLE |
| {"coap", &Interpreter::ProcessCoap}, |
| #endif |
| #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE |
| {"coaps", &Interpreter::ProcessCoapSecure}, |
| #endif |
| #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE |
| {"coex", &Interpreter::ProcessCoexMetrics}, |
| #endif |
| #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD |
| {"commissioner", &Interpreter::ProcessCommissioner}, |
| #endif |
| #if OPENTHREAD_FTD |
| {"contextreusedelay", &Interpreter::ProcessContextIdReuseDelay}, |
| #endif |
| {"counters", &Interpreter::ProcessCounters}, |
| {"dataset", &Interpreter::ProcessDataset}, |
| #if OPENTHREAD_FTD |
| {"delaytimermin", &Interpreter::ProcessDelayTimerMin}, |
| #endif |
| #if OPENTHREAD_CONFIG_DIAG_ENABLE |
| {"diag", &Interpreter::ProcessDiag}, |
| #endif |
| {"discover", &Interpreter::ProcessDiscover}, |
| #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE |
| {"dns", &Interpreter::ProcessDns}, |
| #endif |
| #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) |
| {"domainname", &Interpreter::ProcessDomainName}, |
| #endif |
| #if OPENTHREAD_CONFIG_DUA_ENABLE |
| {"dua", &Interpreter::ProcessDua}, |
| #endif |
| #if OPENTHREAD_FTD |
| {"eidcache", &Interpreter::ProcessEidCache}, |
| #endif |
| {"eui64", &Interpreter::ProcessEui64}, |
| #if OPENTHREAD_POSIX |
| {"exit", &Interpreter::ProcessExit}, |
| #endif |
| {"log", &Interpreter::ProcessLog}, |
| {"extaddr", &Interpreter::ProcessExtAddress}, |
| {"extpanid", &Interpreter::ProcessExtPanId}, |
| {"factoryreset", &Interpreter::ProcessFactoryReset}, |
| {"help", &Interpreter::ProcessHelp}, |
| {"ifconfig", &Interpreter::ProcessIfconfig}, |
| {"ipaddr", &Interpreter::ProcessIpAddr}, |
| {"ipmaddr", &Interpreter::ProcessIpMulticastAddr}, |
| #if OPENTHREAD_CONFIG_JOINER_ENABLE |
| {"joiner", &Interpreter::ProcessJoiner}, |
| #endif |
| #if OPENTHREAD_FTD |
| {"joinerport", &Interpreter::ProcessJoinerPort}, |
| #endif |
| {"keysequence", &Interpreter::ProcessKeySequence}, |
| {"leaderdata", &Interpreter::ProcessLeaderData}, |
| #if OPENTHREAD_FTD |
| {"leaderpartitionid", &Interpreter::ProcessLeaderPartitionId}, |
| {"leaderweight", &Interpreter::ProcessLeaderWeight}, |
| #endif |
| {"mac", &Interpreter::ProcessMac}, |
| #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE |
| {"macfilter", &Interpreter::ProcessMacFilter}, |
| #endif |
| {"masterkey", &Interpreter::ProcessMasterKey}, |
| {"mode", &Interpreter::ProcessMode}, |
| #if OPENTHREAD_FTD |
| {"neighbor", &Interpreter::ProcessNeighbor}, |
| #endif |
| {"netdata", &Interpreter::ProcessNetworkData}, |
| #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE |
| {"netdataregister", &Interpreter::ProcessNetworkDataRegister}, |
| #endif |
| {"netdatashow", &Interpreter::ProcessNetworkDataShow}, |
| #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE |
| {"netif", &Interpreter::ProcessNetif}, |
| #endif |
| {"netstat", &Interpreter::ProcessNetstat}, |
| #if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE |
| {"networkdiagnostic", &Interpreter::ProcessNetworkDiagnostic}, |
| #endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE |
| #if OPENTHREAD_FTD |
| {"networkidtimeout", &Interpreter::ProcessNetworkIdTimeout}, |
| #endif |
| {"networkname", &Interpreter::ProcessNetworkName}, |
| #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE |
| {"networktime", &Interpreter::ProcessNetworkTime}, |
| #endif |
| {"panid", &Interpreter::ProcessPanId}, |
| {"parent", &Interpreter::ProcessParent}, |
| #if OPENTHREAD_FTD |
| {"parentpriority", &Interpreter::ProcessParentPriority}, |
| #endif |
| {"ping", &Interpreter::ProcessPing}, |
| {"pollperiod", &Interpreter::ProcessPollPeriod}, |
| {"promiscuous", &Interpreter::ProcessPromiscuous}, |
| #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE |
| {"prefix", &Interpreter::ProcessPrefix}, |
| #endif |
| #if OPENTHREAD_FTD |
| {"preferrouterid", &Interpreter::ProcessPreferRouterId}, |
| {"pskc", &Interpreter::ProcessPskc}, |
| #endif |
| {"rcp", &Interpreter::ProcessRcp}, |
| #if OPENTHREAD_FTD |
| {"releaserouterid", &Interpreter::ProcessReleaseRouterId}, |
| #endif |
| {"reset", &Interpreter::ProcessReset}, |
| {"rloc16", &Interpreter::ProcessRloc16}, |
| #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE |
| {"route", &Interpreter::ProcessRoute}, |
| #endif |
| #if OPENTHREAD_FTD |
| {"router", &Interpreter::ProcessRouter}, |
| {"routerdowngradethreshold", &Interpreter::ProcessRouterDowngradeThreshold}, |
| {"routereligible", &Interpreter::ProcessRouterEligible}, |
| {"routerselectionjitter", &Interpreter::ProcessRouterSelectionJitter}, |
| {"routerupgradethreshold", &Interpreter::ProcessRouterUpgradeThreshold}, |
| #endif |
| {"scan", &Interpreter::ProcessScan}, |
| #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE |
| {"service", &Interpreter::ProcessService}, |
| #endif |
| {"singleton", &Interpreter::ProcessSingleton}, |
| #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE |
| {"sntp", &Interpreter::ProcessSntp}, |
| #endif |
| {"state", &Interpreter::ProcessState}, |
| {"thread", &Interpreter::ProcessThread}, |
| {"txpower", &Interpreter::ProcessTxPower}, |
| {"udp", &Interpreter::ProcessUdp}, |
| {"unsecureport", &Interpreter::ProcessUnsecurePort}, |
| {"version", &Interpreter::ProcessVersion}, |
| }; |
| |
| Interpreter *Interpreter::sInterpreter = nullptr; |
| |
| Interpreter::Interpreter(Instance *aInstance) |
| : mUserCommands(nullptr) |
| , mUserCommandsLength(0) |
| , mPingLength(kDefaultPingLength) |
| , mPingCount(kDefaultPingCount) |
| , mPingInterval(kDefaultPingInterval) |
| , mPingHopLimit(0) |
| , mPingAllowZeroHopLimit(false) |
| , mPingIdentifier(0) |
| , mPingTimer(*aInstance, Interpreter::HandlePingTimer, this) |
| #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE |
| , mResolvingInProgress(0) |
| #endif |
| #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE |
| , mSntpQueryingInProgress(false) |
| #endif |
| , mUdp(*this) |
| , mDataset(*this) |
| #if OPENTHREAD_CONFIG_COAP_API_ENABLE |
| , mCoap(*this) |
| #endif |
| #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE |
| , mCoapSecure(*this) |
| #endif |
| #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD |
| , mCommissioner(*this) |
| #endif |
| #if OPENTHREAD_CONFIG_JOINER_ENABLE |
| , mJoiner(*this) |
| #endif |
| , mInstance(aInstance) |
| { |
| #if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE |
| otThreadSetReceiveDiagnosticGetCallback(mInstance, &Interpreter::HandleDiagnosticGetResponse, this); |
| #endif |
| #if OPENTHREAD_FTD |
| otThreadSetDiscoveryRequestCallback(mInstance, &Interpreter::HandleDiscoveryRequest, this); |
| #endif |
| |
| mIcmpHandler.mReceiveCallback = Interpreter::HandleIcmpReceive; |
| mIcmpHandler.mContext = this; |
| IgnoreError(otIcmp6RegisterHandler(mInstance, &mIcmpHandler)); |
| |
| #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE |
| memset(mResolvingHostname, 0, sizeof(mResolvingHostname)); |
| #endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE |
| } |
| |
| int Interpreter::Hex2Bin(const char *aHex, uint8_t *aBin, uint16_t aBinLength, bool aAllowTruncate) |
| { |
| size_t hexLength = strlen(aHex); |
| const char *hexEnd = aHex + hexLength; |
| uint8_t * cur = aBin; |
| uint8_t numChars = hexLength & 1; |
| uint8_t byte = 0; |
| int len = 0; |
| int rval; |
| |
| if (!aAllowTruncate) |
| { |
| VerifyOrExit((hexLength + 1) / 2 <= aBinLength, rval = -1); |
| } |
| |
| while (aHex < hexEnd) |
| { |
| if ('A' <= *aHex && *aHex <= 'F') |
| { |
| byte |= 10 + (*aHex - 'A'); |
| } |
| else if ('a' <= *aHex && *aHex <= 'f') |
| { |
| byte |= 10 + (*aHex - 'a'); |
| } |
| else if ('0' <= *aHex && *aHex <= '9') |
| { |
| byte |= *aHex - '0'; |
| } |
| else |
| { |
| ExitNow(rval = -1); |
| } |
| |
| aHex++; |
| numChars++; |
| |
| if (numChars >= 2) |
| { |
| numChars = 0; |
| *cur++ = byte; |
| byte = 0; |
| len++; |
| |
| if (len == aBinLength) |
| { |
| ExitNow(rval = len); |
| } |
| } |
| else |
| { |
| byte <<= 4; |
| } |
| } |
| |
| rval = len; |
| |
| exit: |
| return rval; |
| } |
| |
| void Interpreter::AppendResult(otError aError) |
| { |
| if (aError == OT_ERROR_NONE) |
| { |
| OutputFormat("Done\r\n"); |
| } |
| else |
| { |
| OutputFormat("Error %d: %s\r\n", aError, otThreadErrorToString(aError)); |
| } |
| } |
| |
| void Interpreter::OutputBytes(const uint8_t *aBytes, uint8_t aLength) |
| { |
| for (int i = 0; i < aLength; i++) |
| { |
| OutputFormat("%02x", aBytes[i]); |
| } |
| } |
| |
| int Interpreter::OutputIp6Address(const otIp6Address &aAddress) |
| { |
| return OutputFormat( |
| "%x:%x:%x:%x:%x:%x:%x:%x", HostSwap16(aAddress.mFields.m16[0]), HostSwap16(aAddress.mFields.m16[1]), |
| HostSwap16(aAddress.mFields.m16[2]), HostSwap16(aAddress.mFields.m16[3]), HostSwap16(aAddress.mFields.m16[4]), |
| HostSwap16(aAddress.mFields.m16[5]), HostSwap16(aAddress.mFields.m16[6]), HostSwap16(aAddress.mFields.m16[7])); |
| } |
| |
| otError Interpreter::ParseLong(char *aString, long &aLong) |
| { |
| char *endptr; |
| aLong = strtol(aString, &endptr, 0); |
| return (*endptr == '\0') ? OT_ERROR_NONE : OT_ERROR_INVALID_ARGS; |
| } |
| |
| otError Interpreter::ParseUnsignedLong(char *aString, unsigned long &aUnsignedLong) |
| { |
| char *endptr; |
| aUnsignedLong = strtoul(aString, &endptr, 0); |
| return (*endptr == '\0') ? OT_ERROR_NONE : OT_ERROR_INVALID_ARGS; |
| } |
| |
| otError Interpreter::ParseJoinerDiscerner(char *aString, otJoinerDiscerner &aDiscerner) |
| { |
| otError error = OT_ERROR_NONE; |
| char * separator = strstr(aString, "/"); |
| unsigned long length; |
| |
| VerifyOrExit(separator != nullptr, error = OT_ERROR_NOT_FOUND); |
| |
| SuccessOrExit(error = ParseUnsignedLong(separator + 1, length)); |
| VerifyOrExit(length > 0 && length <= 64, error = OT_ERROR_INVALID_ARGS); |
| |
| { |
| char * end; |
| unsigned long long value = strtoull(aString, &end, 0); |
| aDiscerner.mValue = value; |
| VerifyOrExit(end == separator, error = OT_ERROR_INVALID_ARGS); |
| } |
| |
| aDiscerner.mLength = static_cast<uint8_t>(length); |
| |
| exit: |
| return error; |
| } |
| |
| otError Interpreter::ParsePingInterval(const char *aString, uint32_t &aInterval) |
| { |
| otError error = OT_ERROR_NONE; |
| const uint32_t msFactor = 1000; |
| uint32_t factor = msFactor; |
| |
| aInterval = 0; |
| |
| while (*aString) |
| { |
| if ('0' <= *aString && *aString <= '9') |
| { |
| // In the case of seconds, change the base of already calculated value. |
| if (factor == msFactor) |
| { |
| aInterval *= 10; |
| } |
| |
| aInterval += static_cast<uint32_t>(*aString - '0') * factor; |
| |
| // In the case of milliseconds, change the multiplier factor. |
| if (factor != msFactor) |
| { |
| factor /= 10; |
| } |
| } |
| else if (*aString == '.') |
| { |
| // Accept only one dot character. |
| VerifyOrExit(factor == msFactor, error = OT_ERROR_INVALID_ARGS); |
| |
| // Start analyzing hundreds of milliseconds. |
| factor /= 10; |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| |
| aString++; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| void Interpreter::ProcessHelp(uint8_t aArgsLength, char *aArgs[]) |
| { |
| OT_UNUSED_VARIABLE(aArgsLength); |
| OT_UNUSED_VARIABLE(aArgs); |
| |
| for (const Command &command : sCommands) |
| { |
| OutputFormat("%s\r\n", command.mName); |
| } |
| |
| for (uint8_t i = 0; i < mUserCommandsLength; i++) |
| { |
| OutputFormat("%s\r\n", mUserCommands[i].mName); |
| } |
| |
| AppendResult(OT_ERROR_NONE); |
| } |
| |
| #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) |
| void Interpreter::ProcessBackboneRouter(uint8_t aArgsLength, char *aArgs[]) |
| { |
| OT_UNUSED_VARIABLE(aArgs); |
| otError error = OT_ERROR_INVALID_COMMAND; |
| otBackboneRouterConfig config; |
| |
| if (aArgsLength == 0) |
| { |
| if (otBackboneRouterGetPrimary(mInstance, &config) == OT_ERROR_NONE) |
| { |
| OutputFormat("BBR Primary:\r\n"); |
| OutputFormat("server16: 0x%04X\r\n", config.mServer16); |
| OutputFormat("seqno: %d\r\n", config.mSequenceNumber); |
| OutputFormat("delay: %d secs\r\n", config.mReregistrationDelay); |
| OutputFormat("timeout: %d secs\r\n", config.mMlrTimeout); |
| } |
| else |
| { |
| OutputFormat("BBR Primary: None\r\n"); |
| } |
| |
| error = OT_ERROR_NONE; |
| } |
| #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE |
| else |
| { |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
| if (strcmp(aArgs[0], "mgmt") == 0) |
| { |
| unsigned long value; |
| |
| VerifyOrExit((aArgsLength == 3 || aArgsLength == 4), error = OT_ERROR_INVALID_ARGS); |
| |
| if (strcmp(aArgs[1], "dua") == 0) |
| { |
| otIp6InterfaceIdentifier *mlIid = nullptr; |
| otIp6InterfaceIdentifier iid; |
| |
| SuccessOrExit(error = ParseUnsignedLong(aArgs[2], value)); |
| |
| if (aArgsLength == 4) |
| { |
| VerifyOrExit(Hex2Bin(aArgs[3], iid.mFields.m8, sizeof(iid)) == sizeof(iid), |
| error = OT_ERROR_INVALID_ARGS); |
| mlIid = &iid; |
| } |
| |
| otBackboneRouterConfigNextDuaRegistrationResponse(mInstance, mlIid, static_cast<uint8_t>(value)); |
| ExitNow(); |
| } |
| } |
| #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
| SuccessOrExit(error = ProcessBackboneRouterLocal(aArgsLength, aArgs)); |
| } |
| |
| exit: |
| #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE |
| |
| AppendResult(error); |
| } |
| |
| #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE |
| otError Interpreter::ProcessBackboneRouterLocal(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| otBackboneRouterConfig config; |
| unsigned long value; |
| |
| if (strcmp(aArgs[0], "disable") == 0) |
| { |
| otBackboneRouterSetEnabled(mInstance, false); |
| } |
| else if (strcmp(aArgs[0], "enable") == 0) |
| { |
| otBackboneRouterSetEnabled(mInstance, true); |
| } |
| else if (strcmp(aArgs[0], "jitter") == 0) |
| { |
| if (aArgsLength == 1) |
| { |
| OutputFormat("%d\r\n", otBackboneRouterGetRegistrationJitter(mInstance)); |
| } |
| else if (aArgsLength == 2) |
| { |
| SuccessOrExit(error = ParseUnsignedLong(aArgs[1], value)); |
| otBackboneRouterSetRegistrationJitter(mInstance, static_cast<uint8_t>(value)); |
| } |
| } |
| else if (strcmp(aArgs[0], "register") == 0) |
| { |
| SuccessOrExit(error = otBackboneRouterRegister(mInstance)); |
| } |
| else if (strcmp(aArgs[0], "state") == 0) |
| { |
| switch (otBackboneRouterGetState(mInstance)) |
| { |
| case OT_BACKBONE_ROUTER_STATE_DISABLED: |
| OutputFormat("Disabled\r\n"); |
| break; |
| case OT_BACKBONE_ROUTER_STATE_SECONDARY: |
| OutputFormat("Secondary\r\n"); |
| break; |
| case OT_BACKBONE_ROUTER_STATE_PRIMARY: |
| OutputFormat("Primary\r\n"); |
| break; |
| } |
| } |
| else if (strcmp(aArgs[0], "config") == 0) |
| { |
| otBackboneRouterGetConfig(mInstance, &config); |
| |
| if (aArgsLength == 1) |
| { |
| OutputFormat("seqno: %d\r\n", config.mSequenceNumber); |
| OutputFormat("delay: %d secs\r\n", config.mReregistrationDelay); |
| OutputFormat("timeout: %d secs\r\n", config.mMlrTimeout); |
| } |
| else |
| { |
| // Set local Backbone Router configuration. |
| for (int argCur = 1; argCur < aArgsLength; argCur++) |
| { |
| VerifyOrExit(argCur + 1 < aArgsLength, error = OT_ERROR_INVALID_ARGS); |
| |
| if (strcmp(aArgs[argCur], "seqno") == 0) |
| { |
| SuccessOrExit(error = ParseUnsignedLong(aArgs[++argCur], value)); |
| config.mSequenceNumber = static_cast<uint8_t>(value); |
| } |
| else if (strcmp(aArgs[argCur], "delay") == 0) |
| { |
| SuccessOrExit(error = ParseUnsignedLong(aArgs[++argCur], value)); |
| config.mReregistrationDelay = static_cast<uint16_t>(value); |
| } |
| else if (strcmp(aArgs[argCur], "timeout") == 0) |
| { |
| SuccessOrExit(error = ParseUnsignedLong(aArgs[++argCur], value)); |
| config.mMlrTimeout = static_cast<uint32_t>(value); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| } |
| |
| SuccessOrExit(error = otBackboneRouterSetConfig(mInstance, &config)); |
| } |
| } |
| else |
| { |
| error = OT_ERROR_INVALID_COMMAND; |
| } |
| |
| exit: |
| return error; |
| } |
| #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE |
| |
| void Interpreter::ProcessDomainName(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aArgsLength == 0) |
| { |
| const char *domainName = otThreadGetDomainName(mInstance); |
| OutputFormat("%s\r\n", static_cast<const char *>(domainName)); |
| } |
| else |
| { |
| SuccessOrExit(error = otThreadSetDomainName(mInstance, aArgs[0])); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| #if OPENTHREAD_CONFIG_DUA_ENABLE |
| void Interpreter::ProcessDua(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(aArgsLength >= 1 && strcmp(aArgs[0], "iid") == 0, error = OT_ERROR_INVALID_COMMAND); |
| |
| switch (aArgsLength) |
| { |
| case 1: |
| { |
| const otIp6InterfaceIdentifier *iid = otThreadGetFixedDuaInterfaceIdentifier(mInstance); |
| |
| if (iid != nullptr) |
| { |
| OutputBytes(iid->mFields.m8, sizeof(otIp6InterfaceIdentifier)); |
| OutputFormat("\r\n"); |
| } |
| break; |
| } |
| case 2: |
| if (strcmp(aArgs[1], "clear") == 0) |
| { |
| SuccessOrExit(error = otThreadSetFixedDuaInterfaceIdentifier(mInstance, nullptr)); |
| } |
| else |
| { |
| otIp6InterfaceIdentifier iid; |
| |
| VerifyOrExit(Hex2Bin(aArgs[1], iid.mFields.m8, sizeof(otIp6InterfaceIdentifier)) == |
| sizeof(otIp6InterfaceIdentifier), |
| error = OT_ERROR_INVALID_ARGS); |
| SuccessOrExit(error = otThreadSetFixedDuaInterfaceIdentifier(mInstance, &iid)); |
| } |
| break; |
| default: |
| error = OT_ERROR_INVALID_ARGS; |
| break; |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| #endif // OPENTHREAD_CONFIG_DUA_ENABLE |
| |
| #endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) |
| |
| void Interpreter::ProcessBufferInfo(uint8_t aArgsLength, char *aArgs[]) |
| { |
| OT_UNUSED_VARIABLE(aArgsLength); |
| OT_UNUSED_VARIABLE(aArgs); |
| |
| otBufferInfo bufferInfo; |
| |
| otMessageGetBufferInfo(mInstance, &bufferInfo); |
| |
| OutputFormat("total: %d\r\n", bufferInfo.mTotalBuffers); |
| OutputFormat("free: %d\r\n", bufferInfo.mFreeBuffers); |
| OutputFormat("6lo send: %d %d\r\n", bufferInfo.m6loSendMessages, bufferInfo.m6loSendBuffers); |
| OutputFormat("6lo reas: %d %d\r\n", bufferInfo.m6loReassemblyMessages, bufferInfo.m6loReassemblyBuffers); |
| OutputFormat("ip6: %d %d\r\n", bufferInfo.mIp6Messages, bufferInfo.mIp6Buffers); |
| OutputFormat("mpl: %d %d\r\n", bufferInfo.mMplMessages, bufferInfo.mMplBuffers); |
| OutputFormat("mle: %d %d\r\n", bufferInfo.mMleMessages, bufferInfo.mMleBuffers); |
| OutputFormat("arp: %d %d\r\n", bufferInfo.mArpMessages, bufferInfo.mArpBuffers); |
| OutputFormat("coap: %d %d\r\n", bufferInfo.mCoapMessages, bufferInfo.mCoapBuffers); |
| OutputFormat("coap secure: %d %d\r\n", bufferInfo.mCoapSecureMessages, bufferInfo.mCoapSecureBuffers); |
| OutputFormat("application coap: %d %d\r\n", bufferInfo.mApplicationCoapMessages, |
| bufferInfo.mApplicationCoapBuffers); |
| |
| AppendResult(OT_ERROR_NONE); |
| } |
| |
| void Interpreter::ProcessChannel(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| long value; |
| |
| if (aArgsLength == 0) |
| { |
| OutputFormat("%d\r\n", otLinkGetChannel(mInstance)); |
| } |
| else if (strcmp(aArgs[0], "supported") == 0) |
| { |
| OutputFormat("0x%x\r\n", otPlatRadioGetSupportedChannelMask(mInstance)); |
| } |
| else if (strcmp(aArgs[0], "preferred") == 0) |
| { |
| OutputFormat("0x%x\r\n", otPlatRadioGetPreferredChannelMask(mInstance)); |
| } |
| #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE |
| else if (strcmp(aArgs[0], "monitor") == 0) |
| { |
| if (aArgsLength == 1) |
| { |
| OutputFormat("enabled: %d\r\n", otChannelMonitorIsEnabled(mInstance)); |
| if (otChannelMonitorIsEnabled(mInstance)) |
| { |
| uint32_t channelMask = otLinkGetSupportedChannelMask(mInstance); |
| uint8_t channelNum = sizeof(channelMask) * CHAR_BIT; |
| |
| OutputFormat("interval: %u\r\n", otChannelMonitorGetSampleInterval(mInstance)); |
| OutputFormat("threshold: %d\r\n", otChannelMonitorGetRssiThreshold(mInstance)); |
| OutputFormat("window: %u\r\n", otChannelMonitorGetSampleWindow(mInstance)); |
| OutputFormat("count: %u\r\n", otChannelMonitorGetSampleCount(mInstance)); |
| |
| OutputFormat("occupancies:\r\n"); |
| for (uint8_t channel = 0; channel < channelNum; channel++) |
| { |
| uint32_t occupancy = 0; |
| |
| if (!((1UL << channel) & channelMask)) |
| { |
| continue; |
| } |
| |
| occupancy = otChannelMonitorGetChannelOccupancy(mInstance, channel); |
| |
| OutputFormat("ch %d (0x%04x) ", channel, occupancy); |
| occupancy = (occupancy * 10000) / 0xffff; |
| OutputFormat("%2d.%02d%% busy\r\n", occupancy / 100, occupancy % 100); |
| } |
| OutputFormat("\r\n"); |
| } |
| } |
| else if (strcmp(aArgs[1], "start") == 0) |
| { |
| error = otChannelMonitorSetEnabled(mInstance, true); |
| } |
| else if (strcmp(aArgs[1], "stop") == 0) |
| { |
| error = otChannelMonitorSetEnabled(mInstance, false); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| } |
| #endif |
| #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD |
| else if (strcmp(aArgs[0], "manager") == 0) |
| { |
| if (aArgsLength == 1) |
| { |
| OutputFormat("channel: %d\r\n", otChannelManagerGetRequestedChannel(mInstance)); |
| OutputFormat("auto: %d\r\n", otChannelManagerGetAutoChannelSelectionEnabled(mInstance)); |
| |
| if (otChannelManagerGetAutoChannelSelectionEnabled(mInstance)) |
| { |
| Mac::ChannelMask supportedMask(otChannelManagerGetSupportedChannels(mInstance)); |
| Mac::ChannelMask favoredMask(otChannelManagerGetFavoredChannels(mInstance)); |
| |
| OutputFormat("delay: %d\r\n", otChannelManagerGetDelay(mInstance)); |
| OutputFormat("interval: %lu\r\n", otChannelManagerGetAutoChannelSelectionInterval(mInstance)); |
| OutputFormat("supported: %s\r\n", supportedMask.ToString().AsCString()); |
| OutputFormat("favored: %s\r\n", supportedMask.ToString().AsCString()); |
| } |
| } |
| else if (strcmp(aArgs[1], "change") == 0) |
| { |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); |
| SuccessOrExit(error = ParseLong(aArgs[2], value)); |
| otChannelManagerRequestChannelChange(mInstance, static_cast<uint8_t>(value)); |
| } |
| #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE |
| else if (strcmp(aArgs[1], "select") == 0) |
| { |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); |
| SuccessOrExit(error = ParseLong(aArgs[2], value)); |
| error = otChannelManagerRequestChannelSelect(mInstance, (value != 0) ? true : false); |
| } |
| #endif |
| else if (strcmp(aArgs[1], "auto") == 0) |
| { |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); |
| SuccessOrExit(error = ParseLong(aArgs[2], value)); |
| otChannelManagerSetAutoChannelSelectionEnabled(mInstance, (value != 0) ? true : false); |
| } |
| else if (strcmp(aArgs[1], "delay") == 0) |
| { |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); |
| SuccessOrExit(error = ParseLong(aArgs[2], value)); |
| error = otChannelManagerSetDelay(mInstance, static_cast<uint8_t>(value)); |
| } |
| else if (strcmp(aArgs[1], "interval") == 0) |
| { |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); |
| SuccessOrExit(error = ParseLong(aArgs[2], value)); |
| error = otChannelManagerSetAutoChannelSelectionInterval(mInstance, static_cast<uint32_t>(value)); |
| } |
| else if (strcmp(aArgs[1], "supported") == 0) |
| { |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); |
| SuccessOrExit(error = ParseLong(aArgs[2], value)); |
| otChannelManagerSetSupportedChannels(mInstance, static_cast<uint32_t>(value)); |
| } |
| else if (strcmp(aArgs[1], "favored") == 0) |
| { |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); |
| SuccessOrExit(error = ParseLong(aArgs[2], value)); |
| otChannelManagerSetFavoredChannels(mInstance, static_cast<uint32_t>(value)); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| } |
| #endif |
| else |
| { |
| SuccessOrExit(error = ParseLong(aArgs[0], value)); |
| error = otLinkSetChannel(mInstance, static_cast<uint8_t>(value)); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| #if OPENTHREAD_FTD |
| void Interpreter::ProcessChild(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| otChildInfo childInfo; |
| long value; |
| bool isTable; |
| |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); |
| |
| isTable = (strcmp(aArgs[0], "table") == 0); |
| |
| if (isTable || strcmp(aArgs[0], "list") == 0) |
| { |
| uint16_t maxChildren; |
| |
| if (isTable) |
| { |
| OutputFormat("| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|S|D|N| Extended MAC |\r\n"); |
| OutputFormat("+-----+--------+------------+------------+-------+------+-+-+-+-+------------------+\r\n"); |
| } |
| |
| maxChildren = otThreadGetMaxAllowedChildren(mInstance); |
| |
| for (uint16_t i = 0; i < maxChildren; i++) |
| { |
| if ((otThreadGetChildInfoByIndex(mInstance, i, &childInfo) != OT_ERROR_NONE) || childInfo.mIsStateRestoring) |
| { |
| continue; |
| } |
| |
| if (isTable) |
| { |
| OutputFormat("| %3d ", childInfo.mChildId); |
| OutputFormat("| 0x%04x ", childInfo.mRloc16); |
| OutputFormat("| %10d ", childInfo.mTimeout); |
| OutputFormat("| %10d ", childInfo.mAge); |
| OutputFormat("| %5d ", childInfo.mLinkQualityIn); |
| OutputFormat("| %4d ", childInfo.mNetworkDataVersion); |
| OutputFormat("|%1d", childInfo.mRxOnWhenIdle); |
| OutputFormat("|%1d", childInfo.mSecureDataRequest); |
| OutputFormat("|%1d", childInfo.mFullThreadDevice); |
| OutputFormat("|%1d", childInfo.mFullNetworkData); |
| OutputFormat("| "); |
| |
| for (uint8_t b : childInfo.mExtAddress.m8) |
| { |
| OutputFormat("%02x", b); |
| } |
| |
| OutputFormat(" |\r\n"); |
| } |
| else |
| { |
| OutputFormat("%d ", childInfo.mChildId); |
| } |
| } |
| |
| OutputFormat("\r\n"); |
| ExitNow(); |
| } |
| |
| SuccessOrExit(error = ParseLong(aArgs[0], value)); |
| SuccessOrExit(error = otThreadGetChildInfoById(mInstance, static_cast<uint16_t>(value), &childInfo)); |
| |
| OutputFormat("Child ID: %d\r\n", childInfo.mChildId); |
| OutputFormat("Rloc: %04x\r\n", childInfo.mRloc16); |
| OutputFormat("Ext Addr: "); |
| |
| for (uint8_t b : childInfo.mExtAddress.m8) |
| { |
| OutputFormat("%02x", b); |
| } |
| |
| OutputFormat("\r\n"); |
| OutputFormat("Mode: "); |
| |
| if (childInfo.mRxOnWhenIdle) |
| { |
| OutputFormat("r"); |
| } |
| |
| if (childInfo.mSecureDataRequest) |
| { |
| OutputFormat("s"); |
| } |
| |
| if (childInfo.mFullThreadDevice) |
| { |
| OutputFormat("d"); |
| } |
| |
| if (childInfo.mFullNetworkData) |
| { |
| OutputFormat("n"); |
| } |
| |
| OutputFormat("\r\n"); |
| |
| OutputFormat("Net Data: %d\r\n", childInfo.mNetworkDataVersion); |
| OutputFormat("Timeout: %d\r\n", childInfo.mTimeout); |
| OutputFormat("Age: %d\r\n", childInfo.mAge); |
| OutputFormat("Link Quality In: %d\r\n", childInfo.mLinkQualityIn); |
| OutputFormat("RSSI: %d\r\n", childInfo.mAverageRssi); |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| void Interpreter::ProcessChildIp(uint8_t aArgsLength, char *aArgs[]) |
| { |
| OT_UNUSED_VARIABLE(aArgs); |
| otError error = OT_ERROR_NONE; |
| |
| if (aArgsLength == 0) |
| { |
| uint16_t maxChildren = otThreadGetMaxAllowedChildren(mInstance); |
| |
| for (uint16_t childIndex = 0; childIndex < maxChildren; childIndex++) |
| { |
| otChildIp6AddressIterator iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT; |
| otIp6Address ip6Address; |
| otChildInfo childInfo; |
| |
| if ((otThreadGetChildInfoByIndex(mInstance, childIndex, &childInfo) != OT_ERROR_NONE) || |
| childInfo.mIsStateRestoring) |
| { |
| continue; |
| } |
| |
| iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT; |
| |
| while (otThreadGetChildNextIp6Address(mInstance, childIndex, &iterator, &ip6Address) == OT_ERROR_NONE) |
| { |
| OutputFormat("%04x: ", childInfo.mRloc16); |
| OutputIp6Address(ip6Address); |
| OutputFormat("\r\n"); |
| } |
| } |
| } |
| else if (strcmp(aArgs[0], "max") == 0) |
| { |
| if (aArgsLength == 1) |
| { |
| OutputFormat("%d\r\n", otThreadGetMaxChildIpAddresses(mInstance)); |
| } |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
| else if (aArgsLength == 2) |
| { |
| unsigned long value; |
| SuccessOrExit(error = ParseUnsignedLong(aArgs[1], value)); |
| SuccessOrExit(error = otThreadSetMaxChildIpAddresses(mInstance, static_cast<uint8_t>(value))); |
| } |
| #endif |
| else |
| { |
| error = OT_ERROR_INVALID_ARGS; |
| } |
| } |
| else |
| { |
| error = OT_ERROR_INVALID_COMMAND; |
| } |
| |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
| exit: |
| #endif |
| AppendResult(error); |
| } |
| |
| void Interpreter::ProcessChildMax(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| long value; |
| |
| if (aArgsLength == 0) |
| { |
| OutputFormat("%d\r\n", otThreadGetMaxAllowedChildren(mInstance)); |
| } |
| else |
| { |
| SuccessOrExit(error = ParseLong(aArgs[0], value)); |
| SuccessOrExit(error = otThreadSetMaxAllowedChildren(mInstance, static_cast<uint16_t>(value))); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| #endif // OPENTHREAD_FTD |
| |
| void Interpreter::ProcessChildTimeout(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| long value; |
| |
| if (aArgsLength == 0) |
| { |
| OutputFormat("%d\r\n", otThreadGetChildTimeout(mInstance)); |
| } |
| else |
| { |
| SuccessOrExit(error = ParseLong(aArgs[0], value)); |
| otThreadSetChildTimeout(mInstance, static_cast<uint32_t>(value)); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| #if OPENTHREAD_CONFIG_COAP_API_ENABLE |
| |
| void Interpreter::ProcessCoap(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error; |
| error = mCoap.Process(aArgsLength, aArgs); |
| AppendResult(error); |
| } |
| |
| #endif // OPENTHREAD_CONFIG_COAP_API_ENABLE |
| |
| #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE |
| |
| void Interpreter::ProcessCoapSecure(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error; |
| error = mCoapSecure.Process(aArgsLength, aArgs); |
| AppendResult(error); |
| } |
| |
| #endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE |
| |
| #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE |
| void Interpreter::ProcessCoexMetrics(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aArgsLength == 0) |
| { |
| OutputFormat("%s\r\n", otPlatRadioIsCoexEnabled(mInstance) ? "Enabled" : "Disabled"); |
| } |
| else if (strcmp(aArgs[0], "enable") == 0) |
| { |
| error = otPlatRadioSetCoexEnabled(mInstance, true); |
| } |
| else if (strcmp(aArgs[0], "disable") == 0) |
| { |
| error = otPlatRadioSetCoexEnabled(mInstance, false); |
| } |
| else if (strcmp(aArgs[0], "metrics") == 0) |
| { |
| otRadioCoexMetrics metrics; |
| |
| SuccessOrExit(error = otPlatRadioGetCoexMetrics(mInstance, &metrics)); |
| |
| OutputFormat("Stopped: %s\r\n", metrics.mStopped ? "true" : "false"); |
| OutputFormat("Grant Glitch: %u\r\n", metrics.mNumGrantGlitch); |
| OutputFormat("Transmit metrics\r\n"); |
| OutputFormat(" Request: %u\r\n", metrics.mNumTxRequest); |
| OutputFormat(" Grant Immediate: %u\r\n", metrics.mNumTxGrantImmediate); |
| OutputFormat(" Grant Wait: %u\r\n", metrics.mNumTxGrantWait); |
| OutputFormat(" Grant Wait Activated: %u\r\n", metrics.mNumTxGrantWaitActivated); |
| OutputFormat(" Grant Wait Timeout: %u\r\n", metrics.mNumTxGrantWaitTimeout); |
| OutputFormat(" Grant Deactivated During Request: %u\r\n", metrics.mNumTxGrantDeactivatedDuringRequest); |
| OutputFormat(" Delayed Grant: %u\r\n", metrics.mNumTxDelayedGrant); |
| OutputFormat(" Average Request To Grant Time: %u\r\n", metrics.mAvgTxRequestToGrantTime); |
| OutputFormat("Receive metrics\r\n"); |
| OutputFormat(" Request: %u\r\n", metrics.mNumRxRequest); |
| OutputFormat(" Grant Immediate: %u\r\n", metrics.mNumRxGrantImmediate); |
| OutputFormat(" Grant Wait: %u\r\n", metrics.mNumRxGrantWait); |
| OutputFormat(" Grant Wait Activated: %u\r\n", metrics.mNumRxGrantWaitActivated); |
| OutputFormat(" Grant Wait Timeout: %u\r\n", metrics.mNumRxGrantWaitTimeout); |
| OutputFormat(" Grant Deactivated During Request: %u\r\n", metrics.mNumRxGrantDeactivatedDuringRequest); |
| OutputFormat(" Delayed Grant: %u\r\n", metrics.mNumRxDelayedGrant); |
| OutputFormat(" Average Request To Grant Time: %u\r\n", metrics.mAvgRxRequestToGrantTime); |
| OutputFormat(" Grant None: %u\r\n", metrics.mNumRxGrantNone); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| #endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE |
| |
| #if OPENTHREAD_FTD |
| void Interpreter::ProcessContextIdReuseDelay(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| long value; |
| |
| if (aArgsLength == 0) |
| { |
| OutputFormat("%d\r\n", otThreadGetContextIdReuseDelay(mInstance)); |
| } |
| else |
| { |
| SuccessOrExit(ParseLong(aArgs[0], value)); |
| otThreadSetContextIdReuseDelay(mInstance, static_cast<uint32_t>(value)); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| #endif // OPENTHREAD_FTD |
| |
| void Interpreter::ProcessCounters(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aArgsLength == 0) |
| { |
| OutputFormat("mac\r\n"); |
| OutputFormat("mle\r\n"); |
| } |
| else if (strcmp(aArgs[0], "mac") == 0) |
| { |
| if (aArgsLength == 1) |
| { |
| const otMacCounters *macCounters = otLinkGetCounters(mInstance); |
| |
| OutputFormat("TxTotal: %d\r\n", macCounters->mTxTotal); |
| OutputFormat(" TxUnicast: %d\r\n", macCounters->mTxUnicast); |
| OutputFormat(" TxBroadcast: %d\r\n", macCounters->mTxBroadcast); |
| OutputFormat(" TxAckRequested: %d\r\n", macCounters->mTxAckRequested); |
| OutputFormat(" TxAcked: %d\r\n", macCounters->mTxAcked); |
| OutputFormat(" TxNoAckRequested: %d\r\n", macCounters->mTxNoAckRequested); |
| OutputFormat(" TxData: %d\r\n", macCounters->mTxData); |
| OutputFormat(" TxDataPoll: %d\r\n", macCounters->mTxDataPoll); |
| OutputFormat(" TxBeacon: %d\r\n", macCounters->mTxBeacon); |
| OutputFormat(" TxBeaconRequest: %d\r\n", macCounters->mTxBeaconRequest); |
| OutputFormat(" TxOther: %d\r\n", macCounters->mTxOther); |
| OutputFormat(" TxRetry: %d\r\n", macCounters->mTxRetry); |
| OutputFormat(" TxErrCca: %d\r\n", macCounters->mTxErrCca); |
| OutputFormat(" TxErrBusyChannel: %d\r\n", macCounters->mTxErrBusyChannel); |
| OutputFormat("RxTotal: %d\r\n", macCounters->mRxTotal); |
| OutputFormat(" RxUnicast: %d\r\n", macCounters->mRxUnicast); |
| OutputFormat(" RxBroadcast: %d\r\n", macCounters->mRxBroadcast); |
| OutputFormat(" RxData: %d\r\n", macCounters->mRxData); |
| OutputFormat(" RxDataPoll: %d\r\n", macCounters->mRxDataPoll); |
| OutputFormat(" RxBeacon: %d\r\n", macCounters->mRxBeacon); |
| OutputFormat(" RxBeaconRequest: %d\r\n", macCounters->mRxBeaconRequest); |
| OutputFormat(" RxOther: %d\r\n", macCounters->mRxOther); |
| OutputFormat(" RxAddressFiltered: %d\r\n", macCounters->mRxAddressFiltered); |
| OutputFormat(" RxDestAddrFiltered: %d\r\n", macCounters->mRxDestAddrFiltered); |
| OutputFormat(" RxDuplicated: %d\r\n", macCounters->mRxDuplicated); |
| OutputFormat(" RxErrNoFrame: %d\r\n", macCounters->mRxErrNoFrame); |
| OutputFormat(" RxErrNoUnknownNeighbor: %d\r\n", macCounters->mRxErrUnknownNeighbor); |
| OutputFormat(" RxErrInvalidSrcAddr: %d\r\n", macCounters->mRxErrInvalidSrcAddr); |
| OutputFormat(" RxErrSec: %d\r\n", macCounters->mRxErrSec); |
| OutputFormat(" RxErrFcs: %d\r\n", macCounters->mRxErrFcs); |
| OutputFormat(" RxErrOther: %d\r\n", macCounters->mRxErrOther); |
| } |
| else if ((aArgsLength == 2) && (strcmp(aArgs[1], "reset") == 0)) |
| { |
| otLinkResetCounters(mInstance); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| } |
| else if (strcmp(aArgs[0], "mle") == 0) |
| { |
| if (aArgsLength == 1) |
| { |
| const otMleCounters *mleCounters = otThreadGetMleCounters(mInstance); |
| |
| OutputFormat("Role Disabled: %d\r\n", mleCounters->mDisabledRole); |
| OutputFormat("Role Detached: %d\r\n", mleCounters->mDetachedRole); |
| OutputFormat("Role Child: %d\r\n", mleCounters->mChildRole); |
| OutputFormat("Role Router: %d\r\n", mleCounters->mRouterRole); |
| OutputFormat("Role Leader: %d\r\n", mleCounters->mLeaderRole); |
| OutputFormat("Attach Attempts: %d\r\n", mleCounters->mAttachAttempts); |
| OutputFormat("Partition Id Changes: %d\r\n", mleCounters->mPartitionIdChanges); |
| OutputFormat("Better Partition Attach Attempts: %d\r\n", mleCounters->mBetterPartitionAttachAttempts); |
| OutputFormat("Parent Changes: %d\r\n", mleCounters->mParentChanges); |
| } |
| else if ((aArgsLength == 2) && (strcmp(aArgs[1], "reset") == 0)) |
| { |
| otThreadResetMleCounters(mInstance); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| #if OPENTHREAD_FTD |
| void Interpreter::ProcessDelayTimerMin(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aArgsLength == 0) |
| { |
| OutputFormat("%d\r\n", (otDatasetGetDelayTimerMinimal(mInstance) / 1000)); |
| } |
| else if (aArgsLength == 1) |
| { |
| unsigned long value; |
| SuccessOrExit(error = ParseUnsignedLong(aArgs[0], value)); |
| SuccessOrExit(error = otDatasetSetDelayTimerMinimal(mInstance, static_cast<uint32_t>(value * 1000))); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| #endif |
| |
| void Interpreter::ProcessDiscover(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| uint32_t scanChannels = 0; |
| long value; |
| |
| if (aArgsLength > 0) |
| { |
| SuccessOrExit(error = ParseLong(aArgs[0], value)); |
| VerifyOrExit((0 <= value) && (value < static_cast<long>(sizeof(scanChannels) * CHAR_BIT)), |
| error = OT_ERROR_INVALID_ARGS); |
| scanChannels = 1 << value; |
| } |
| |
| SuccessOrExit(error = otThreadDiscover(mInstance, scanChannels, OT_PANID_BROADCAST, false, false, |
| &Interpreter::HandleActiveScanResult, this)); |
| OutputFormat("| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI |\r\n"); |
| OutputFormat("+---+------------------+------------------+------+------------------+----+-----+-----+\r\n"); |
| |
| exit: |
| if (error != OT_ERROR_NONE) |
| { |
| AppendResult(error); |
| } |
| } |
| |
| #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE |
| void Interpreter::ProcessDns(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| long port = OT_DNS_DEFAULT_SERVER_PORT; |
| Ip6::MessageInfo messageInfo; |
| otDnsQuery query; |
| |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); |
| |
| if (strcmp(aArgs[0], "resolve") == 0) |
| { |
| VerifyOrExit(!mResolvingInProgress, error = OT_ERROR_BUSY); |
| VerifyOrExit(aArgsLength > 1, error = OT_ERROR_INVALID_ARGS); |
| VerifyOrExit(strlen(aArgs[1]) < OT_DNS_MAX_HOSTNAME_LENGTH, error = OT_ERROR_INVALID_ARGS); |
| |
| strcpy(mResolvingHostname, aArgs[1]); |
| |
| if (aArgsLength > 2) |
| { |
| SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(aArgs[2])); |
| } |
| else |
| { |
| // Use IPv6 address of default DNS server. |
| SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(OT_DNS_DEFAULT_SERVER_IP)); |
| } |
| |
| if (aArgsLength > 3) |
| { |
| SuccessOrExit(error = ParseLong(aArgs[3], port)); |
| } |
| |
| messageInfo.SetPeerPort(static_cast<uint16_t>(port)); |
| |
| query.mHostname = mResolvingHostname; |
| query.mMessageInfo = static_cast<const otMessageInfo *>(&messageInfo); |
| query.mNoRecursion = false; |
| |
| SuccessOrExit(error = otDnsClientQuery(mInstance, &query, &Interpreter::HandleDnsResponse, this)); |
| |
| mResolvingInProgress = true; |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); |
| } |
| |
| exit: |
| if (error != OT_ERROR_NONE) |
| { |
| AppendResult(error); |
| } |
| } |
| |
| void Interpreter::HandleDnsResponse(void * aContext, |
| const char * aHostname, |
| const otIp6Address *aAddress, |
| uint32_t aTtl, |
| otError aResult) |
| { |
| static_cast<Interpreter *>(aContext)->HandleDnsResponse(aHostname, static_cast<const Ip6::Address *>(aAddress), |
| aTtl, aResult); |
| } |
| |
| void Interpreter::HandleDnsResponse(const char *aHostname, const Ip6::Address *aAddress, uint32_t aTtl, otError aResult) |
| { |
| OutputFormat("DNS response for %s - ", aHostname); |
| |
| if (aResult == OT_ERROR_NONE) |
| { |
| if (aAddress != nullptr) |
| { |
| OutputIp6Address(*aAddress); |
| } |
| OutputFormat(" TTL: %d\r\n", aTtl); |
| } |
| |
| AppendResult(aResult); |
| |
| mResolvingInProgress = false; |
| } |
| #endif |
| |
| #if OPENTHREAD_FTD |
| void Interpreter::ProcessEidCache(uint8_t aArgsLength, char *aArgs[]) |
| { |
| OT_UNUSED_VARIABLE(aArgsLength); |
| OT_UNUSED_VARIABLE(aArgs); |
| |
| otCacheEntryIterator iterator; |
| otCacheEntryInfo entry; |
| |
| memset(&iterator, 0, sizeof(iterator)); |
| |
| for (uint8_t i = 0;; i++) |
| { |
| SuccessOrExit(otThreadGetNextCacheEntry(mInstance, &entry, &iterator)); |
| |
| OutputIp6Address(entry.mTarget); |
| OutputFormat(" %04x\r\n", entry.mRloc16); |
| } |
| |
| exit: |
| AppendResult(OT_ERROR_NONE); |
| } |
| #endif // OPENTHREAD_FTD |
| |
| void Interpreter::ProcessEui64(uint8_t aArgsLength, char *aArgs[]) |
| { |
| OT_UNUSED_VARIABLE(aArgs); |
| |
| otError error = OT_ERROR_NONE; |
| otExtAddress extAddress; |
| |
| VerifyOrExit(aArgsLength == 0, error = OT_ERROR_INVALID_ARGS); |
| |
| otLinkGetFactoryAssignedIeeeEui64(mInstance, &extAddress); |
| OutputBytes(extAddress.m8, OT_EXT_ADDRESS_SIZE); |
| OutputFormat("\r\n"); |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| void Interpreter::ProcessExtAddress(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aArgsLength == 0) |
| { |
| const uint8_t *extAddress = reinterpret_cast<const uint8_t *>(otLinkGetExtendedAddress(mInstance)); |
| OutputBytes(extAddress, OT_EXT_ADDRESS_SIZE); |
| OutputFormat("\r\n"); |
| } |
| else |
| { |
| otExtAddress extAddress; |
| |
| memset(&extAddress, 0, sizeof(extAddress)); |
| VerifyOrExit(Hex2Bin(aArgs[0], extAddress.m8, sizeof(extAddress.m8)) == sizeof(extAddress.m8), |
| error = OT_ERROR_INVALID_ARGS); |
| |
| error = otLinkSetExtendedAddress(mInstance, &extAddress); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| #if OPENTHREAD_POSIX |
| void Interpreter::ProcessExit(uint8_t aArgsLength, char *aArgs[]) |
| { |
| OT_UNUSED_VARIABLE(aArgsLength); |
| OT_UNUSED_VARIABLE(aArgs); |
| |
| exit(EXIT_SUCCESS); |
| } |
| #endif |
| |
| void Interpreter::ProcessLog(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(aArgsLength >= 1, error = OT_ERROR_INVALID_ARGS); |
| |
| if (!strcmp(aArgs[0], "level")) |
| { |
| if (aArgsLength == 1) |
| { |
| OutputFormat("%d\r\n", otLoggingGetLevel()); |
| } |
| #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE |
| else if (aArgsLength == 2) |
| { |
| long level; |
| |
| SuccessOrExit(error = ParseLong(aArgs[1], level)); |
| SuccessOrExit(error = otLoggingSetLevel(static_cast<otLogLevel>(level))); |
| } |
| #endif |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| } |
| #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX |
| else if (!strcmp(aArgs[0], "filename")) |
| { |
| VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS); |
| SuccessOrExit(error = otPlatDebugUart_logfile(aArgs[1])); |
| } |
| #endif |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| void Interpreter::ProcessExtPanId(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aArgsLength == 0) |
| { |
| const uint8_t *extPanId = reinterpret_cast<const uint8_t *>(otThreadGetExtendedPanId(mInstance)); |
| OutputBytes(extPanId, OT_EXT_PAN_ID_SIZE); |
| OutputFormat("\r\n"); |
| } |
| else |
| { |
| otExtendedPanId extPanId; |
| |
| VerifyOrExit(Hex2Bin(aArgs[0], extPanId.m8, sizeof(extPanId)) == sizeof(extPanId), |
| error = OT_ERROR_INVALID_ARGS); |
| |
| error = otThreadSetExtendedPanId(mInstance, &extPanId); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| void Interpreter::ProcessFactoryReset(uint8_t aArgsLength, char *aArgs[]) |
| { |
| OT_UNUSED_VARIABLE(aArgsLength); |
| OT_UNUSED_VARIABLE(aArgs); |
| |
| otInstanceFactoryReset(mInstance); |
| } |
| |
| void Interpreter::ProcessIfconfig(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aArgsLength == 0) |
| { |
| if (otIp6IsEnabled(mInstance)) |
| { |
| OutputFormat("up\r\n"); |
| } |
| else |
| { |
| OutputFormat("down\r\n"); |
| } |
| } |
| else if (strcmp(aArgs[0], "up") == 0) |
| { |
| SuccessOrExit(error = otIp6SetEnabled(mInstance, true)); |
| } |
| else if (strcmp(aArgs[0], "down") == 0) |
| { |
| SuccessOrExit(error = otIp6SetEnabled(mInstance, false)); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| otError Interpreter::ProcessIpAddrAdd(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error; |
| otNetifAddress aAddress; |
| |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); |
| |
| SuccessOrExit(error = otIp6AddressFromString(aArgs[0], &aAddress.mAddress)); |
| aAddress.mPrefixLength = 64; |
| aAddress.mPreferred = true; |
| aAddress.mValid = true; |
| aAddress.mAddressOrigin = OT_ADDRESS_ORIGIN_MANUAL; |
| error = otIp6AddUnicastAddress(mInstance, &aAddress); |
| |
| exit: |
| return error; |
| } |
| |
| otError Interpreter::ProcessIpAddrDel(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error; |
| struct otIp6Address address; |
| |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); |
| |
| SuccessOrExit(error = otIp6AddressFromString(aArgs[0], &address)); |
| error = otIp6RemoveUnicastAddress(mInstance, &address); |
| |
| exit: |
| return error; |
| } |
| |
| void Interpreter::ProcessIpAddr(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aArgsLength == 0) |
| { |
| const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(mInstance); |
| |
| for (const otNetifAddress *addr = unicastAddrs; addr; addr = addr->mNext) |
| { |
| OutputIp6Address(addr->mAddress); |
| OutputFormat("\r\n"); |
| } |
| } |
| else |
| { |
| if (strcmp(aArgs[0], "add") == 0) |
| { |
| SuccessOrExit(error = ProcessIpAddrAdd(aArgsLength - 1, aArgs + 1)); |
| } |
| else if (strcmp(aArgs[0], "del") == 0) |
| { |
| SuccessOrExit(error = ProcessIpAddrDel(aArgsLength - 1, aArgs + 1)); |
| } |
| else if (strcmp(aArgs[0], "linklocal") == 0) |
| { |
| OutputIp6Address(*otThreadGetLinkLocalIp6Address(mInstance)); |
| OutputFormat("\r\n"); |
| } |
| else if (strcmp(aArgs[0], "rloc") == 0) |
| { |
| OutputIp6Address(*otThreadGetRloc(mInstance)); |
| OutputFormat("\r\n"); |
| } |
| else if (strcmp(aArgs[0], "mleid") == 0) |
| { |
| OutputIp6Address(*otThreadGetMeshLocalEid(mInstance)); |
| OutputFormat("\r\n"); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); |
| } |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| otError Interpreter::ProcessIpMulticastAddrAdd(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error; |
| struct otIp6Address address; |
| |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); |
| |
| SuccessOrExit(error = otIp6AddressFromString(aArgs[0], &address)); |
| error = otIp6SubscribeMulticastAddress(mInstance, &address); |
| |
| exit: |
| return error; |
| } |
| |
| otError Interpreter::ProcessIpMulticastAddrDel(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error; |
| struct otIp6Address address; |
| |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); |
| |
| SuccessOrExit(error = otIp6AddressFromString(aArgs[0], &address)); |
| error = otIp6UnsubscribeMulticastAddress(mInstance, &address); |
| |
| exit: |
| return error; |
| } |
| |
| otError Interpreter::ProcessMulticastPromiscuous(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aArgsLength == 0) |
| { |
| if (otIp6IsMulticastPromiscuousEnabled(mInstance)) |
| { |
| OutputFormat("Enabled\r\n"); |
| } |
| else |
| { |
| OutputFormat("Disabled\r\n"); |
| } |
| } |
| else |
| { |
| if (strcmp(aArgs[0], "enable") == 0) |
| { |
| otIp6SetMulticastPromiscuousEnabled(mInstance, true); |
| } |
| else if (strcmp(aArgs[0], "disable") == 0) |
| { |
| otIp6SetMulticastPromiscuousEnabled(mInstance, false); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| void Interpreter::ProcessIpMulticastAddr(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aArgsLength == 0) |
| { |
| for (const otNetifMulticastAddress *addr = otIp6GetMulticastAddresses(mInstance); addr; addr = addr->mNext) |
| { |
| OutputIp6Address(addr->mAddress); |
| OutputFormat("\r\n"); |
| } |
| } |
| else |
| { |
| if (strcmp(aArgs[0], "add") == 0) |
| { |
| SuccessOrExit(error = ProcessIpMulticastAddrAdd(aArgsLength - 1, aArgs + 1)); |
| } |
| else if (strcmp(aArgs[0], "del") == 0) |
| { |
| SuccessOrExit(error = ProcessIpMulticastAddrDel(aArgsLength - 1, aArgs + 1)); |
| } |
| else if (strcmp(aArgs[0], "promiscuous") == 0) |
| { |
| SuccessOrExit(error = ProcessMulticastPromiscuous(aArgsLength - 1, aArgs + 1)); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); |
| } |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| void Interpreter::ProcessKeySequence(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| long value; |
| |
| VerifyOrExit(aArgsLength == 1 || aArgsLength == 2, error = OT_ERROR_INVALID_ARGS); |
| |
| if (strcmp(aArgs[0], "counter") == 0) |
| { |
| if (aArgsLength == 1) |
| { |
| OutputFormat("%d\r\n", otThreadGetKeySequenceCounter(mInstance)); |
| } |
| else |
| { |
| SuccessOrExit(error = ParseLong(aArgs[1], value)); |
| otThreadSetKeySequenceCounter(mInstance, static_cast<uint32_t>(value)); |
| } |
| } |
| else if (strcmp(aArgs[0], "guardtime") == 0) |
| { |
| if (aArgsLength == 1) |
| { |
| OutputFormat("%d\r\n", otThreadGetKeySwitchGuardTime(mInstance)); |
| } |
| else |
| { |
| SuccessOrExit(error = ParseLong(aArgs[1], value)); |
| otThreadSetKeySwitchGuardTime(mInstance, static_cast<uint32_t>(value)); |
| } |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| void Interpreter::ProcessLeaderData(uint8_t aArgsLength, char *aArgs[]) |
| { |
| OT_UNUSED_VARIABLE(aArgsLength); |
| OT_UNUSED_VARIABLE(aArgs); |
| |
| otError error; |
| otLeaderData leaderData; |
| |
| SuccessOrExit(error = otThreadGetLeaderData(mInstance, &leaderData)); |
| |
| OutputFormat("Partition ID: %u\r\n", leaderData.mPartitionId); |
| OutputFormat("Weighting: %d\r\n", leaderData.mWeighting); |
| OutputFormat("Data Version: %d\r\n", leaderData.mDataVersion); |
| OutputFormat("Stable Data Version: %d\r\n", leaderData.mStableDataVersion); |
| OutputFormat("Leader Router ID: %d\r\n", leaderData.mLeaderRouterId); |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| #if OPENTHREAD_FTD |
| void Interpreter::ProcessLeaderPartitionId(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| unsigned long value; |
| |
| if (aArgsLength == 0) |
| { |
| OutputFormat("%u\r\n", otThreadGetLocalLeaderPartitionId(mInstance)); |
| } |
| else |
| { |
| SuccessOrExit(error = ParseUnsignedLong(aArgs[0], value)); |
| otThreadSetLocalLeaderPartitionId(mInstance, static_cast<uint32_t>(value)); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| void Interpreter::ProcessLeaderWeight(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| long value; |
| |
| if (aArgsLength == 0) |
| { |
| OutputFormat("%d\r\n", otThreadGetLocalLeaderWeight(mInstance)); |
| } |
| else |
| { |
| SuccessOrExit(error = ParseLong(aArgs[0], value)); |
| otThreadSetLocalLeaderWeight(mInstance, static_cast<uint8_t>(value)); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| #endif // OPENTHREAD_FTD |
| |
| #if OPENTHREAD_FTD |
| void Interpreter::ProcessPskc(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aArgsLength == 0) |
| { |
| const otPskc *pskc = otThreadGetPskc(mInstance); |
| |
| for (uint8_t b : pskc->m8) |
| { |
| OutputFormat("%02x", b); |
| } |
| |
| OutputFormat("\r\n"); |
| } |
| else |
| { |
| otPskc pskc; |
| |
| if (aArgsLength == 1) |
| { |
| VerifyOrExit(Hex2Bin(aArgs[0], pskc.m8, sizeof(pskc)) == sizeof(pskc), error = OT_ERROR_INVALID_ARGS); |
| } |
| else if (!strcmp(aArgs[0], "-p")) |
| { |
| SuccessOrExit(error = otDatasetGeneratePskc( |
| aArgs[1], reinterpret_cast<const otNetworkName *>(otThreadGetNetworkName(mInstance)), |
| otThreadGetExtendedPanId(mInstance), &pskc)); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| |
| SuccessOrExit(error = otThreadSetPskc(mInstance, &pskc)); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| #endif |
| |
| void Interpreter::ProcessMasterKey(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aArgsLength == 0) |
| { |
| const uint8_t *key = reinterpret_cast<const uint8_t *>(otThreadGetMasterKey(mInstance)); |
| |
| for (int i = 0; i < OT_MASTER_KEY_SIZE; i++) |
| { |
| OutputFormat("%02x", key[i]); |
| } |
| |
| OutputFormat("\r\n"); |
| } |
| else |
| { |
| otMasterKey key; |
| |
| VerifyOrExit(Hex2Bin(aArgs[0], key.m8, sizeof(key.m8)) == OT_MASTER_KEY_SIZE, error = OT_ERROR_INVALID_ARGS); |
| SuccessOrExit(error = otThreadSetMasterKey(mInstance, &key)); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| void Interpreter::ProcessMode(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| otLinkModeConfig linkMode; |
| |
| memset(&linkMode, 0, sizeof(otLinkModeConfig)); |
| |
| if (aArgsLength == 0) |
| { |
| linkMode = otThreadGetLinkMode(mInstance); |
| |
| if (linkMode.mRxOnWhenIdle) |
| { |
| OutputFormat("r"); |
| } |
| |
| if (linkMode.mSecureDataRequests) |
| { |
| OutputFormat("s"); |
| } |
| |
| if (linkMode.mDeviceType) |
| { |
| OutputFormat("d"); |
| } |
| |
| if (linkMode.mNetworkData) |
| { |
| OutputFormat("n"); |
| } |
| |
| OutputFormat("\r\n"); |
| } |
| else |
| { |
| for (char *arg = aArgs[0]; *arg != '\0'; arg++) |
| { |
| switch (*arg) |
| { |
| case 'r': |
| linkMode.mRxOnWhenIdle = 1; |
| break; |
| |
| case 's': |
| linkMode.mSecureDataRequests = 1; |
| break; |
| |
| case 'd': |
| linkMode.mDeviceType = 1; |
| break; |
| |
| case 'n': |
| linkMode.mNetworkData = 1; |
| break; |
| |
| default: |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| } |
| |
| SuccessOrExit(error = otThreadSetLinkMode(mInstance, linkMode)); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| #if OPENTHREAD_FTD |
| void Interpreter::ProcessNeighbor(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| otNeighborInfo neighborInfo; |
| bool isTable; |
| otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT; |
| |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); |
| |
| isTable = (strcmp(aArgs[0], "table") == 0); |
| |
| if (isTable || strcmp(aArgs[0], "list") == 0) |
| { |
| if (isTable) |
| { |
| OutputFormat("| Role | RLOC16 | Age | Avg RSSI | Last RSSI |R|S|D|N| Extended MAC |\r\n"); |
| OutputFormat("+------+--------+-----+----------+-----------+-+-+-+-+------------------+\r\n"); |
| } |
| |
| while (otThreadGetNextNeighborInfo(mInstance, &iterator, &neighborInfo) == OT_ERROR_NONE) |
| { |
| if (isTable) |
| { |
| OutputFormat("| %3c ", neighborInfo.mIsChild ? 'C' : 'R'); |
| OutputFormat("| 0x%04x ", neighborInfo.mRloc16); |
| OutputFormat("| %3d ", neighborInfo.mAge); |
| OutputFormat("| %8d ", neighborInfo.mAverageRssi); |
| OutputFormat("| %9d ", neighborInfo.mLastRssi); |
| OutputFormat("|%1d", neighborInfo.mRxOnWhenIdle); |
| OutputFormat("|%1d", neighborInfo.mSecureDataRequest); |
| OutputFormat("|%1d", neighborInfo.mFullThreadDevice); |
| OutputFormat("|%1d", neighborInfo.mFullNetworkData); |
| OutputFormat("| "); |
| |
| for (uint8_t b : neighborInfo.mExtAddress.m8) |
| { |
| OutputFormat("%02x", b); |
| } |
| |
| OutputFormat(" |\r\n"); |
| } |
| else |
| { |
| OutputFormat("0x%04x ", neighborInfo.mRloc16); |
| } |
| } |
| |
| OutputFormat("\r\n"); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| #endif |
| |
| void Interpreter::ProcessNetworkDataShow(uint8_t aArgsLength, char *aArgs[]) |
| { |
| OT_UNUSED_VARIABLE(aArgsLength); |
| OT_UNUSED_VARIABLE(aArgs); |
| |
| otError error = OT_ERROR_NONE; |
| uint8_t data[255]; |
| uint8_t len = sizeof(data); |
| |
| SuccessOrExit(error = otNetDataGet(mInstance, false, data, &len)); |
| |
| OutputBytes(data, static_cast<uint8_t>(len)); |
| OutputFormat("\r\n"); |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE |
| void Interpreter::ProcessNetif(uint8_t aArgsLength, char *aArgs[]) |
| { |
| OT_UNUSED_VARIABLE(aArgsLength); |
| OT_UNUSED_VARIABLE(aArgs); |
| |
| otError error = OT_ERROR_NONE; |
| const char * netif = nullptr; |
| unsigned int netifidx = 0; |
| |
| SuccessOrExit(error = otPlatGetNetif(mInstance, &netif, &netifidx)); |
| |
| OutputFormat("%s:%u\r\n", netif ? netif : "(null)", netifidx); |
| |
| exit: |
| AppendResult(error); |
| } |
| #endif |
| |
| void Interpreter::ProcessNetstat(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otUdpSocket *socket = otUdpGetSockets(mInstance); |
| |
| OT_UNUSED_VARIABLE(aArgsLength); |
| OT_UNUSED_VARIABLE(aArgs); |
| |
| OutputFormat("| Local Address | Peer Address |\n"); |
| OutputFormat("+-----------------------------------------------+-----------------------------------------------+\n"); |
| |
| while (socket) |
| { |
| constexpr int kMaxOutputLength = 45; |
| int outputLength; |
| |
| OutputFormat("| "); |
| |
| outputLength = OutputSocketAddress(socket->mSockName); |
| for (int i = outputLength; 0 <= i && i < kMaxOutputLength; ++i) |
| { |
| OutputFormat(" "); |
| } |
| OutputFormat(" | "); |
| |
| outputLength = OutputSocketAddress(socket->mPeerName); |
| for (int i = outputLength; 0 <= i && i < kMaxOutputLength; ++i) |
| { |
| OutputFormat(" "); |
| } |
| OutputFormat(" |\n"); |
| |
| socket = socket->mNext; |
| } |
| |
| AppendResult(OT_ERROR_NONE); |
| } |
| |
| int Interpreter::OutputSocketAddress(const otSockAddr &aAddress) |
| { |
| int outputLength; |
| int result = 0; |
| |
| VerifyOrExit((outputLength = OutputIp6Address(aAddress.mAddress)) >= 0, result = -1); |
| result += outputLength; |
| |
| VerifyOrExit((outputLength = OutputFormat(":")) >= 0, result = -1); |
| result += outputLength; |
| if (aAddress.mPort == 0) |
| { |
| VerifyOrExit((outputLength = OutputFormat("*")) >= 0, result = -1); |
| result += outputLength; |
| } |
| else |
| { |
| VerifyOrExit((outputLength = OutputFormat("%d", aAddress.mPort)) >= 0, result = -1); |
| result += outputLength; |
| } |
| |
| exit: |
| return result; |
| } |
| |
| #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE |
| void Interpreter::ProcessService(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); |
| |
| if (strcmp(aArgs[0], "add") == 0) |
| { |
| otServiceConfig cfg; |
| long enterpriseNumber; |
| size_t length; |
| |
| VerifyOrExit(aArgsLength > 3, error = OT_ERROR_INVALID_ARGS); |
| |
| SuccessOrExit(error = ParseLong(aArgs[1], enterpriseNumber)); |
| cfg.mEnterpriseNumber = static_cast<uint32_t>(enterpriseNumber); |
| |
| length = strlen(aArgs[2]); |
| VerifyOrExit(length <= sizeof(cfg.mServiceData), error = OT_ERROR_NO_BUFS); |
| cfg.mServiceDataLength = static_cast<uint8_t>(length); |
| memcpy(cfg.mServiceData, aArgs[2], cfg.mServiceDataLength); |
| |
| length = strlen(aArgs[3]); |
| VerifyOrExit(length <= sizeof(cfg.mServerConfig.mServerData), error = OT_ERROR_NO_BUFS); |
| cfg.mServerConfig.mServerDataLength = static_cast<uint8_t>(length); |
| memcpy(cfg.mServerConfig.mServerData, aArgs[3], cfg.mServerConfig.mServerDataLength); |
| |
| cfg.mServerConfig.mStable = true; |
| |
| SuccessOrExit(error = otServerAddService(mInstance, &cfg)); |
| } |
| else if (strcmp(aArgs[0], "remove") == 0) |
| { |
| long enterpriseNumber = 0; |
| |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); |
| |
| SuccessOrExit(error = ParseLong(aArgs[1], enterpriseNumber)); |
| |
| SuccessOrExit(error = otServerRemoveService(mInstance, static_cast<uint32_t>(enterpriseNumber), |
| reinterpret_cast<uint8_t *>(aArgs[2]), |
| static_cast<uint8_t>(strlen(aArgs[2])))); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| #endif |
| |
| void Interpreter::ProcessNetworkData(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error; |
| |
| if (aArgsLength > 2 && strcmp(aArgs[0], "steeringdata") == 0) |
| { |
| if (strcmp(aArgs[1], "check") == 0) |
| { |
| otExtAddress addr; |
| otJoinerDiscerner discerner; |
| |
| discerner.mLength = 0; |
| |
| error = Interpreter::ParseJoinerDiscerner(aArgs[2], discerner); |
| |
| if (error == OT_ERROR_NOT_FOUND) |
| { |
| VerifyOrExit(Interpreter::Hex2Bin(aArgs[2], addr.m8, sizeof(addr)) == sizeof(addr), |
| error = OT_ERROR_INVALID_ARGS); |
| } |
| else if (error != OT_ERROR_NONE) |
| { |
| ExitNow(); |
| } |
| |
| if (discerner.mLength) |
| { |
| ExitNow(error = otNetDataSteeringDataCheckJoinerWithDiscerner(mInstance, &discerner)); |
| } |
| else |
| { |
| ExitNow(error = otNetDataSteeringDataCheckJoiner(mInstance, &addr)); |
| } |
| } |
| } |
| |
| error = OT_ERROR_INVALID_COMMAND; |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE |
| void Interpreter::ProcessNetworkDataRegister(uint8_t aArgsLength, char *aArgs[]) |
| { |
| OT_UNUSED_VARIABLE(aArgsLength); |
| OT_UNUSED_VARIABLE(aArgs); |
| |
| otError error = OT_ERROR_NONE; |
| #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE |
| SuccessOrExit(error = otBorderRouterRegister(mInstance)); |
| #else |
| SuccessOrExit(error = otServerRegister(mInstance)); |
| #endif |
| |
| exit: |
| AppendResult(error); |
| } |
| #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE |
| |
| #if OPENTHREAD_FTD |
| void Interpreter::ProcessNetworkIdTimeout(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| long value; |
| |
| if (aArgsLength == 0) |
| { |
| OutputFormat("%d\r\n", otThreadGetNetworkIdTimeout(mInstance)); |
| } |
| else |
| { |
| SuccessOrExit(error = ParseLong(aArgs[0], value)); |
| otThreadSetNetworkIdTimeout(mInstance, static_cast<uint8_t>(value)); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| #endif // OPENTHREAD_FTD |
| |
| void Interpreter::ProcessNetworkName(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aArgsLength == 0) |
| { |
| const char *networkName = otThreadGetNetworkName(mInstance); |
| OutputFormat("%s\r\n", static_cast<const char *>(networkName)); |
| } |
| else |
| { |
| SuccessOrExit(error = otThreadSetNetworkName(mInstance, aArgs[0])); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE |
| void Interpreter::ProcessNetworkTime(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| long value; |
| |
| if (aArgsLength == 0) |
| { |
| uint64_t time; |
| otNetworkTimeStatus networkTimeStatus; |
| |
| networkTimeStatus = otNetworkTimeGet(mInstance, &time); |
| |
| OutputFormat("Network Time: %luus", time); |
| |
| switch (networkTimeStatus) |
| { |
| case OT_NETWORK_TIME_UNSYNCHRONIZED: |
| OutputFormat(" (unsynchronized)\r\n"); |
| break; |
| |
| case OT_NETWORK_TIME_RESYNC_NEEDED: |
| OutputFormat(" (resync needed)\r\n"); |
| break; |
| |
| case OT_NETWORK_TIME_SYNCHRONIZED: |
| OutputFormat(" (synchronized)\r\n"); |
| break; |
| |
| default: |
| break; |
| } |
| |
| OutputFormat("Time Sync Period: %ds\r\n", otNetworkTimeGetSyncPeriod(mInstance)); |
| OutputFormat("XTAL Threshold: %dppm\r\n", otNetworkTimeGetXtalThreshold(mInstance)); |
| } |
| else if (aArgsLength == 2) |
| { |
| SuccessOrExit(error = ParseLong(aArgs[0], value)); |
| SuccessOrExit(error = otNetworkTimeSetSyncPeriod(mInstance, static_cast<uint16_t>(value))); |
| |
| SuccessOrExit(error = ParseLong(aArgs[1], value)); |
| SuccessOrExit(error = otNetworkTimeSetXtalThreshold(mInstance, static_cast<uint16_t>(value))); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE |
| |
| void Interpreter::ProcessPanId(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| long value; |
| |
| if (aArgsLength == 0) |
| { |
| OutputFormat("0x%04x\r\n", otLinkGetPanId(mInstance)); |
| } |
| else |
| { |
| SuccessOrExit(error = ParseLong(aArgs[0], value)); |
| error = otLinkSetPanId(mInstance, static_cast<otPanId>(value)); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| void Interpreter::ProcessParent(uint8_t aArgsLength, char *aArgs[]) |
| { |
| OT_UNUSED_VARIABLE(aArgsLength); |
| OT_UNUSED_VARIABLE(aArgs); |
| |
| otError error = OT_ERROR_NONE; |
| otRouterInfo parentInfo; |
| |
| SuccessOrExit(error = otThreadGetParentInfo(mInstance, &parentInfo)); |
| OutputFormat("Ext Addr: "); |
| |
| for (uint8_t b : parentInfo.mExtAddress.m8) |
| { |
| OutputFormat("%02x", b); |
| } |
| |
| OutputFormat("\r\n"); |
| |
| OutputFormat("Rloc: %x\r\n", parentInfo.mRloc16); |
| OutputFormat("Link Quality In: %d\r\n", parentInfo.mLinkQualityIn); |
| OutputFormat("Link Quality Out: %d\r\n", parentInfo.mLinkQualityOut); |
| OutputFormat("Age: %d\r\n", parentInfo.mAge); |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| #if OPENTHREAD_FTD |
| void Interpreter::ProcessParentPriority(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| long value; |
| |
| if (aArgsLength == 0) |
| { |
| OutputFormat("%d\r\n", otThreadGetParentPriority(mInstance)); |
| } |
| else |
| { |
| SuccessOrExit(error = ParseLong(aArgs[0], value)); |
| error = otThreadSetParentPriority(mInstance, static_cast<int8_t>(value)); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| #endif |
| |
| void Interpreter::HandleIcmpReceive(void * aContext, |
| otMessage * aMessage, |
| const otMessageInfo *aMessageInfo, |
| const otIcmp6Header *aIcmpHeader) |
| { |
| static_cast<Interpreter *>(aContext)->HandleIcmpReceive(aMessage, aMessageInfo, aIcmpHeader); |
| } |
| |
| void Interpreter::HandleIcmpReceive(otMessage * aMessage, |
| const otMessageInfo *aMessageInfo, |
| const otIcmp6Header *aIcmpHeader) |
| { |
| uint32_t timestamp = 0; |
| uint16_t dataSize; |
| |
| VerifyOrExit(aIcmpHeader->mType == OT_ICMP6_TYPE_ECHO_REPLY, OT_NOOP); |
| VerifyOrExit((mPingIdentifier != 0) && (mPingIdentifier == HostSwap16(aIcmpHeader->mData.m16[0])), OT_NOOP); |
| |
| dataSize = otMessageGetLength(aMessage) - otMessageGetOffset(aMessage); |
| OutputFormat("%u bytes from ", dataSize + static_cast<uint16_t>(sizeof(otIcmp6Header))); |
| |
| OutputIp6Address(aMessageInfo->mPeerAddr); |
| |
| OutputFormat(": icmp_seq=%d hlim=%d", HostSwap16(aIcmpHeader->mData.m16[1]), aMessageInfo->mHopLimit); |
| |
| if (otMessageRead(aMessage, otMessageGetOffset(aMessage), ×tamp, sizeof(uint32_t)) == sizeof(uint32_t)) |
| { |
| OutputFormat(" time=%dms", TimerMilli::GetNow().GetValue() - HostSwap32(timestamp)); |
| } |
| |
| OutputFormat("\r\n"); |
| |
| SignalPingReply(static_cast<const Ip6::MessageInfo *>(aMessageInfo)->GetPeerAddr(), dataSize, HostSwap32(timestamp), |
| aMessageInfo->mHopLimit); |
| |
| exit: |
| return; |
| } |
| |
| void Interpreter::ProcessPing(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| uint8_t index = 1; |
| long value; |
| uint32_t interval; |
| |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); |
| |
| if (strcmp(aArgs[0], "stop") == 0) |
| { |
| mPingIdentifier = 0; |
| VerifyOrExit(mPingTimer.IsRunning(), error = OT_ERROR_INVALID_STATE); |
| mPingTimer.Stop(); |
| ExitNow(); |
| } |
| |
| VerifyOrExit(!mPingTimer.IsRunning(), error = OT_ERROR_BUSY); |
| |
| SuccessOrExit(error = otIp6AddressFromString(aArgs[0], &mPingDestAddress)); |
| |
| mPingLength = kDefaultPingLength; |
| mPingCount = kDefaultPingCount; |
| mPingInterval = kDefaultPingInterval; |
| mPingHopLimit = 0; |
| mPingAllowZeroHopLimit = false; |
| |
| while (index < aArgsLength) |
| { |
| switch (index) |
| { |
| case 1: |
| SuccessOrExit(error = ParseLong(aArgs[index], value)); |
| mPingLength = static_cast<uint16_t>(value); |
| break; |
| |
| case 2: |
| SuccessOrExit(error = ParseLong(aArgs[index], value)); |
| mPingCount = static_cast<uint16_t>(value); |
| break; |
| |
| case 3: |
| SuccessOrExit(error = ParsePingInterval(aArgs[index], interval)); |
| VerifyOrExit(0 < interval && interval <= Timer::kMaxDelay, error = OT_ERROR_INVALID_ARGS); |
| mPingInterval = interval; |
| break; |
| |
| case 4: |
| SuccessOrExit(error = ParseLong(aArgs[index], value)); |
| VerifyOrExit(0 <= value && value <= 255, error = OT_ERROR_INVALID_ARGS); |
| mPingHopLimit = static_cast<uint8_t>(value); |
| mPingAllowZeroHopLimit = (mPingHopLimit == 0); |
| break; |
| |
| default: |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| |
| index++; |
| } |
| |
| mPingIdentifier++; |
| |
| if (mPingIdentifier == 0) |
| { |
| mPingIdentifier++; |
| } |
| |
| SendPing(); |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| void Interpreter::HandlePingTimer(Timer &aTimer) |
| { |
| GetOwner(aTimer).SendPing(); |
| } |
| |
| void Interpreter::SendPing(void) |
| { |
| uint32_t timestamp = HostSwap32(TimerMilli::GetNow().GetValue()); |
| otMessage * message = nullptr; |
| otMessageInfo messageInfo; |
| |
| memset(&messageInfo, 0, sizeof(messageInfo)); |
| messageInfo.mPeerAddr = mPingDestAddress; |
| messageInfo.mHopLimit = mPingHopLimit; |
| messageInfo.mAllowZeroHopLimit = mPingAllowZeroHopLimit; |
| |
| message = otIp6NewMessage(mInstance, nullptr); |
| VerifyOrExit(message != nullptr, OT_NOOP); |
| |
| SuccessOrExit(otMessageAppend(message, ×tamp, sizeof(timestamp))); |
| SuccessOrExit(otMessageSetLength(message, mPingLength)); |
| SuccessOrExit(otIcmp6SendEchoRequest(mInstance, message, &messageInfo, mPingIdentifier)); |
| |
| SignalPingRequest(static_cast<Ip6::MessageInfo *>(&messageInfo)->GetPeerAddr(), mPingLength, HostSwap32(timestamp), |
| messageInfo.mHopLimit); |
| |
| message = nullptr; |
| |
| exit: |
| if (message != nullptr) |
| { |
| otMessageFree(message); |
| } |
| |
| if (--mPingCount) |
| { |
| mPingTimer.Start(mPingInterval); |
| } |
| } |
| |
| void Interpreter::ProcessPollPeriod(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| long value; |
| |
| if (aArgsLength == 0) |
| { |
| OutputFormat("%d\r\n", otLinkGetPollPeriod(mInstance)); |
| } |
| else |
| { |
| SuccessOrExit(error = ParseLong(aArgs[0], value)); |
| error = otLinkSetPollPeriod(mInstance, static_cast<uint32_t>(value)); |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| void Interpreter::ProcessPromiscuous(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aArgsLength == 0) |
| { |
| if (otLinkIsPromiscuous(mInstance) && otPlatRadioGetPromiscuous(mInstance)) |
| { |
| OutputFormat("Enabled\r\n"); |
| } |
| else |
| { |
| OutputFormat("Disabled\r\n"); |
| } |
| } |
| else |
| { |
| if (strcmp(aArgs[0], "enable") == 0) |
| { |
| SuccessOrExit(error = otLinkSetPromiscuous(mInstance, true)); |
| otLinkSetPcapCallback(mInstance, &HandleLinkPcapReceive, this); |
| } |
| else if (strcmp(aArgs[0], "disable") == 0) |
| { |
| otLinkSetPcapCallback(mInstance, nullptr, nullptr); |
| SuccessOrExit(error = otLinkSetPromiscuous(mInstance, false)); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| } |
| |
| exit: |
| AppendResult(error); |
| } |
| |
| void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx, void *aContext) |
| { |
| static_cast<Interpreter *>(aContext)->HandleLinkPcapReceive(aFrame, aIsTx); |
| } |
| |
| void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx) |
| { |
| OT_UNUSED_VARIABLE(aIsTx); |
| |
| OutputFormat("\r\n"); |
| |
| for (size_t i = 0; i < 44; i++) |
| { |
| OutputFormat("="); |
| } |
| |
| OutputFormat("[len = %3u]", aFrame->mLength); |
| |
| for (size_t i = 0; i < 28; i++) |
| { |
| OutputFormat("="); |
| } |
| |
| OutputFormat("\r\n"); |
| |
| for (size_t i = 0; i < aFrame->mLength; i += 16) |
| { |
| OutputFormat("|"); |
| |
| for (size_t j = 0; j < 16; j++) |
| { |
| if (i + j < aFrame->mLength) |
| { |
| OutputFormat(" %02X", aFrame->mPsdu[i + j]); |
| } |
| else |
| { |
| OutputFormat(" .."); |
| } |
| } |
| |
| OutputFormat("|"); |
| |
|