/* * Copyright (c) 2023, 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 Border Router. */ #include "cli_br.hpp" #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE #include #include "cli/cli.hpp" namespace ot { namespace Cli { /** * @cli br init * @code * br init 2 1 * Done * @endcode * @cparam br init @ca{infrastructure-network-index} @ca{is-running} * @par * Initializes the Border Routing Manager. * @sa otBorderRoutingInit */ template <> otError Br::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; uint32_t ifIndex; bool isRunning; SuccessOrExit(error = aArgs[0].ParseAsUint32(ifIndex)); SuccessOrExit(error = aArgs[1].ParseAsBool(isRunning)); VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); error = otBorderRoutingInit(GetInstancePtr(), ifIndex, isRunning); exit: return error; } /** * @cli br enable * @code * br enable * Done * @endcode * @par * Enables the Border Routing Manager. * @sa otBorderRoutingSetEnabled */ template <> otError Br::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); error = otBorderRoutingSetEnabled(GetInstancePtr(), true); exit: return error; } /** * @cli br disable * @code * br disable * Done * @endcode * @par * Disables the Border Routing Manager. * @sa otBorderRoutingSetEnabled */ template <> otError Br::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); error = otBorderRoutingSetEnabled(GetInstancePtr(), false); exit: return error; } /** * @cli br state * @code * br state * running * @endcode * @par api_copy * #otBorderRoutingGetState */ template <> otError Br::Process(Arg aArgs[]) { static const char *const kStateStrings[] = { "uninitialized", // (0) OT_BORDER_ROUTING_STATE_UNINITIALIZED "disabled", // (1) OT_BORDER_ROUTING_STATE_DISABLED "stopped", // (2) OT_BORDER_ROUTING_STATE_STOPPED "running", // (3) OT_BORDER_ROUTING_STATE_RUNNING }; otError error = OT_ERROR_NONE; static_assert(0 == OT_BORDER_ROUTING_STATE_UNINITIALIZED, "STATE_UNINITIALIZED value is incorrect"); static_assert(1 == OT_BORDER_ROUTING_STATE_DISABLED, "STATE_DISABLED value is incorrect"); static_assert(2 == OT_BORDER_ROUTING_STATE_STOPPED, "STATE_STOPPED value is incorrect"); static_assert(3 == OT_BORDER_ROUTING_STATE_RUNNING, "STATE_RUNNING value is incorrect"); VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); OutputLine("%s", Stringify(otBorderRoutingGetState(GetInstancePtr()), kStateStrings)); exit: return error; } otError Br::ParsePrefixTypeArgs(Arg aArgs[], PrefixType &aFlags) { otError error = OT_ERROR_NONE; aFlags = 0; if (aArgs[0].IsEmpty()) { aFlags = kPrefixTypeFavored | kPrefixTypeLocal; ExitNow(); } if (aArgs[0] == "local") { aFlags = kPrefixTypeLocal; } else if (aArgs[0] == "favored") { aFlags = kPrefixTypeFavored; } else { ExitNow(error = OT_ERROR_INVALID_ARGS); } VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); exit: return error; } /** * @cli br omrprefix * @code * br omrprefix * Local: fdfc:1ff5:1512:5622::/64 * Favored: fdfc:1ff5:1512:5622::/64 prf:low * Done * @endcode * @par * Outputs both local and favored OMR prefix. * @sa otBorderRoutingGetOmrPrefix * @sa otBorderRoutingGetFavoredOmrPrefix */ template <> otError Br::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; PrefixType outputPrefixTypes; SuccessOrExit(error = ParsePrefixTypeArgs(aArgs, outputPrefixTypes)); /** * @cli br omrprefix local * @code * br omrprefix local * fdfc:1ff5:1512:5622::/64 * Done * @endcode * @par api_copy * #otBorderRoutingGetOmrPrefix */ if (outputPrefixTypes & kPrefixTypeLocal) { otIp6Prefix local; SuccessOrExit(error = otBorderRoutingGetOmrPrefix(GetInstancePtr(), &local)); OutputFormat("%s", outputPrefixTypes == kPrefixTypeLocal ? "" : "Local: "); OutputIp6PrefixLine(local); } /** * @cli br omrprefix favored * @code * br omrprefix favored * fdfc:1ff5:1512:5622::/64 prf:low * Done * @endcode * @par api_copy * #otBorderRoutingGetFavoredOmrPrefix */ if (outputPrefixTypes & kPrefixTypeFavored) { otIp6Prefix favored; otRoutePreference preference; SuccessOrExit(error = otBorderRoutingGetFavoredOmrPrefix(GetInstancePtr(), &favored, &preference)); OutputFormat("%s", outputPrefixTypes == kPrefixTypeFavored ? "" : "Favored: "); OutputIp6Prefix(favored); OutputLine(" prf:%s", PreferenceToString(preference)); } exit: return error; } /** * @cli br onlinkprefix * @code * br onlinkprefix * Local: fd41:2650:a6f5:0::/64 * Favored: 2600::0:1234:da12::/64 * Done * @endcode * @par * Outputs both local and favored on-link prefixes. * @sa otBorderRoutingGetOnLinkPrefix * @sa otBorderRoutingGetFavoredOnLinkPrefix */ template <> otError Br::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; PrefixType outputPrefixTypes; #if OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE if (aArgs[0] == "test") { otIp6Prefix prefix; SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix)); otBorderRoutingSetOnLinkPrefix(GetInstancePtr(), &prefix); ExitNow(); } #endif error = ParsePrefixTypeArgs(aArgs, outputPrefixTypes); SuccessOrExit(error); /** * @cli br onlinkprefix local * @code * br onlinkprefix local * fd41:2650:a6f5:0::/64 * Done * @endcode * @par api_copy * #otBorderRoutingGetOnLinkPrefix */ if (outputPrefixTypes & kPrefixTypeLocal) { otIp6Prefix local; SuccessOrExit(error = otBorderRoutingGetOnLinkPrefix(GetInstancePtr(), &local)); OutputFormat("%s", outputPrefixTypes == kPrefixTypeLocal ? "" : "Local: "); OutputIp6PrefixLine(local); } /** * @cli br onlinkprefix favored * @code * br onlinkprefix favored * 2600::0:1234:da12::/64 * Done * @endcode * @par api_copy * #otBorderRoutingGetFavoredOnLinkPrefix */ if (outputPrefixTypes & kPrefixTypeFavored) { otIp6Prefix favored; SuccessOrExit(error = otBorderRoutingGetFavoredOnLinkPrefix(GetInstancePtr(), &favored)); OutputFormat("%s", outputPrefixTypes == kPrefixTypeFavored ? "" : "Favored: "); OutputIp6PrefixLine(favored); } exit: return error; } #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE /** * @cli br nat64prefix * @code * br nat64prefix * Local: fd14:1078:b3d5:b0b0:0:0::/96 * Favored: fd14:1078:b3d5:b0b0:0:0::/96 prf:low * Done * @endcode * @par * Outputs both local and favored NAT64 prefixes. * @sa otBorderRoutingGetNat64Prefix * @sa otBorderRoutingGetFavoredNat64Prefix */ template <> otError Br::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; PrefixType outputPrefixTypes; SuccessOrExit(error = ParsePrefixTypeArgs(aArgs, outputPrefixTypes)); /** * @cli br nat64prefix local * @code * br nat64prefix local * fd14:1078:b3d5:b0b0:0:0::/96 * Done * @endcode * @par api_copy * #otBorderRoutingGetNat64Prefix */ if (outputPrefixTypes & kPrefixTypeLocal) { otIp6Prefix local; SuccessOrExit(error = otBorderRoutingGetNat64Prefix(GetInstancePtr(), &local)); OutputFormat("%s", outputPrefixTypes == kPrefixTypeLocal ? "" : "Local: "); OutputIp6PrefixLine(local); } /** * @cli br nat64prefix favored * @code * br nat64prefix favored * fd14:1078:b3d5:b0b0:0:0::/96 prf:low * Done * @endcode * @par api_copy * #otBorderRoutingGetFavoredNat64Prefix */ if (outputPrefixTypes & kPrefixTypeFavored) { otIp6Prefix favored; otRoutePreference preference; SuccessOrExit(error = otBorderRoutingGetFavoredNat64Prefix(GetInstancePtr(), &favored, &preference)); OutputFormat("%s", outputPrefixTypes == kPrefixTypeFavored ? "" : "Favored: "); OutputIp6Prefix(favored); OutputLine(" prf:%s", PreferenceToString(preference)); } exit: return error; } #endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE #if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE template <> otError Br::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; /** * @cli br peers * @code * br peers * rloc16:0x5c00 age:00:00:49 * rloc16:0xf800 age:00:01:51 * Done * @endcode * @par * Get the list of peer BRs found in Network Data entries. * `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE` is required. * Peer BRs are other devices within the Thread mesh that provide external IP connectivity. A device is considered * to provide external IP connectivity if at least one of the following conditions is met regarding its Network * Data entries: * - It has added at least one external route entry. * - It has added at least one prefix entry with both the default-route and on-mesh flags set. * - It has added at least one domain prefix (with both the domain and on-mesh flags set). * The list of peer BRs specifically excludes the current device, even if its is itself acting as a BR. * Info per BR entry: * - RLOC16 of the BR * - Age as the duration interval since this BR appeared in Network Data. It is formatted as `{hh}:{mm}:{ss}` for * hours, minutes, seconds, if the duration is less than 24 hours. If the duration is 24 hours or more, the * format is `{dd}d.{hh}:{mm}:{ss}` for days, hours, minutes, seconds. * @sa otBorderRoutingGetNextPrefixTableEntry */ if (aArgs[0].IsEmpty()) { otBorderRoutingPrefixTableIterator iterator; otBorderRoutingPeerBorderRouterEntry peerBrEntry; char ageString[OT_DURATION_STRING_SIZE]; otBorderRoutingPrefixTableInitIterator(GetInstancePtr(), &iterator); while (otBorderRoutingGetNextPeerBrEntry(GetInstancePtr(), &iterator, &peerBrEntry) == OT_ERROR_NONE) { otConvertDurationInSecondsToString(peerBrEntry.mAge, ageString, sizeof(ageString)); OutputLine("rloc16:0x%04x age:%s", peerBrEntry.mRloc16, ageString); } } /** * @cli br peers count * @code * br peers count * 2 min-age:00:00:47 * Done * @endcode * @par api_copy * #otBorderRoutingCountPeerBrs */ else if (aArgs[0] == "count") { uint32_t minAge; uint16_t count; char ageString[OT_DURATION_STRING_SIZE]; VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); count = otBorderRoutingCountPeerBrs(GetInstancePtr(), &minAge); otConvertDurationInSecondsToString(minAge, ageString, sizeof(ageString)); OutputLine("%u min-age:%s", count, ageString); } else { error = OT_ERROR_INVALID_ARGS; } exit: return error; } #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE /** * @cli br prefixtable * @code * br prefixtable * prefix:fd00:1234:5678:0::/64, on-link:no, ms-since-rx:29526, lifetime:1800, route-prf:med, * router:ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) * prefix:1200:abba:baba:0::/64, on-link:yes, ms-since-rx:29527, lifetime:1800, preferred:1800, * router:ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) * Done * @endcode * @par * Get the discovered prefixes by Border Routing Manager on the infrastructure link. * Info per prefix entry: * - The prefix * - Whether the prefix is on-link or route * - Milliseconds since last received Router Advertisement containing this prefix * - Prefix lifetime in seconds * - Preferred lifetime in seconds only if prefix is on-link * - Route preference (low, med, high) only if prefix is route (not on-link) * - The router IPv6 address which advertising this prefix * - Flags in received Router Advertisement header: * - M: Managed Address Config flag * - O: Other Config flag * - Stub: Stub Router flag (indicates whether the router is a stub router) * @sa otBorderRoutingGetNextPrefixTableEntry */ template <> otError Br::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; otBorderRoutingPrefixTableIterator iterator; otBorderRoutingPrefixTableEntry entry; VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); otBorderRoutingPrefixTableInitIterator(GetInstancePtr(), &iterator); while (otBorderRoutingGetNextPrefixTableEntry(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE) { char string[OT_IP6_PREFIX_STRING_SIZE]; otIp6PrefixToString(&entry.mPrefix, string, sizeof(string)); OutputFormat("prefix:%s, on-link:%s, ms-since-rx:%lu, lifetime:%lu, ", string, entry.mIsOnLink ? "yes" : "no", ToUlong(entry.mMsecSinceLastUpdate), ToUlong(entry.mValidLifetime)); if (entry.mIsOnLink) { OutputFormat("preferred:%lu, ", ToUlong(entry.mPreferredLifetime)); } else { OutputFormat("route-prf:%s, ", PreferenceToString(entry.mRoutePreference)); } OutputFormat("router:"); OutputRouterInfo(entry.mRouter, kShortVersion); } exit: return error; } #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE template <> otError Br::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; /** * @cli br pd (enable,disable) * @code * br pd enable * Done * @endcode * @code * br pd disable * Done * @endcode * @cparam br pd @ca{enable|disable} * @par api_copy * #otBorderRoutingDhcp6PdSetEnabled * */ if (ProcessEnableDisable(aArgs, otBorderRoutingDhcp6PdSetEnabled) == OT_ERROR_NONE) { } /** * @cli br pd state * @code * br pd state * running * Done * @endcode * @par api_copy * #otBorderRoutingDhcp6PdGetState */ else if (aArgs[0] == "state") { static const char *const kDhcpv6PdStateStrings[] = { "disabled", // (0) OT_BORDER_ROUTING_DHCP6_PD_STATE_DISABLED "stopped", // (1) OT_BORDER_ROUTING_DHCP6_PD_STATE_STOPPED "running", // (2) OT_BORDER_ROUTING_DHCP6_PD_STATE_RUNNING }; static_assert(0 == OT_BORDER_ROUTING_DHCP6_PD_STATE_DISABLED, "OT_BORDER_ROUTING_DHCP6_PD_STATE_DISABLED value is not expected!"); static_assert(1 == OT_BORDER_ROUTING_DHCP6_PD_STATE_STOPPED, "OT_BORDER_ROUTING_DHCP6_PD_STATE_STOPPED value is not expected!"); static_assert(2 == OT_BORDER_ROUTING_DHCP6_PD_STATE_RUNNING, "OT_BORDER_ROUTING_DHCP6_PD_STATE_RUNNING value is not expected!"); OutputLine("%s", Stringify(otBorderRoutingDhcp6PdGetState(GetInstancePtr()), kDhcpv6PdStateStrings)); } /** * @cli br pd omrprefix * @code * br pd omrprefix * 2001:db8:cafe:0:0/64 lifetime:1800 preferred:1800 * Done * @endcode * @par api_copy * #otBorderRoutingGetPdOmrPrefix */ else if (aArgs[0] == "omrprefix") { otBorderRoutingPrefixTableEntry entry; SuccessOrExit(error = otBorderRoutingGetPdOmrPrefix(GetInstancePtr(), &entry)); OutputIp6Prefix(entry.mPrefix); OutputLine(" lifetime:%lu preferred:%lu", ToUlong(entry.mValidLifetime), ToUlong(entry.mPreferredLifetime)); } else { ExitNow(error = OT_ERROR_INVALID_COMMAND); } exit: return error; } #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE /** * @cli br routers * @code * br routers * ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) ms-since-rx:1505 reachable:yes age:00:18:13 * Done * @endcode * @par * Get the list of discovered routers by Border Routing Manager on the infrastructure link. * Info per router: * - The router IPv6 address * - Flags in received Router Advertisement header: * - M: Managed Address Config flag * - O: Other Config flag * - Stub: Stub Router flag (indicates whether the router is a stub router) * - Milliseconds since last received message from this router * - Reachability flag: A router is marked as unreachable if it fails to respond to multiple Neighbor Solicitation * probes. * - Age: Duration interval since this router was first discovered. It is formatted as `{hh}:{mm}:{ss}` for hours, * minutes, seconds, if the duration is less than 24 hours. If the duration is 24 hours or more, the format is * `{dd}d.{hh}:{mm}:{ss}` for days, hours, minutes, seconds. * - `(this BR)` is appended when the router is the local device itself. * - `(peer BR)` is appended when the router is likely a peer BR connected to the same Thread mesh. This requires * `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE`. * @sa otBorderRoutingGetNextRouterEntry */ template <> otError Br::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; otBorderRoutingPrefixTableIterator iterator; otBorderRoutingRouterEntry entry; VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); otBorderRoutingPrefixTableInitIterator(GetInstancePtr(), &iterator); while (otBorderRoutingGetNextRouterEntry(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE) { OutputRouterInfo(entry, kLongVersion); } exit: return error; } void Br::OutputRouterInfo(const otBorderRoutingRouterEntry &aEntry, RouterOutputMode aMode) { OutputIp6Address(aEntry.mAddress); OutputFormat(" (M:%u O:%u Stub:%u)", aEntry.mManagedAddressConfigFlag, aEntry.mOtherConfigFlag, aEntry.mStubRouterFlag); if (aMode == kLongVersion) { char ageString[OT_DURATION_STRING_SIZE]; otConvertDurationInSecondsToString(aEntry.mAge, ageString, sizeof(ageString)); OutputFormat(" ms-since-rx:%lu reachable:%s age:%s", ToUlong(aEntry.mMsecSinceLastUpdate), aEntry.mIsReachable ? "yes" : "no", ageString); if (aEntry.mIsLocalDevice) { OutputFormat(" (this BR)"); } #if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE if (aEntry.mIsPeerBr) { OutputFormat(" (peer BR)"); } #endif } OutputNewLine(); } template <> otError Br::Process(Arg aArgs[]) { static constexpr uint16_t kMaxExtraOptions = 800; otError error = OT_ERROR_NONE; uint8_t options[kMaxExtraOptions]; uint16_t length; /** * @cli br raoptions (set,clear) * @code * br raoptions 0400ff00020001 * Done * @endcode * @code * br raoptions clear * Done * @endcode * @cparam br raoptions @ca{options|clear} * `br raoptions clear` passes a `nullptr` to #otBorderRoutingSetExtraRouterAdvertOptions. * Otherwise, you can pass the `options` byte as hex data. * @par api_copy * #otBorderRoutingSetExtraRouterAdvertOptions */ if (aArgs[0] == "clear") { length = 0; } else { length = sizeof(options); SuccessOrExit(error = aArgs[0].ParseAsHexString(length, options)); } error = otBorderRoutingSetExtraRouterAdvertOptions(GetInstancePtr(), length > 0 ? options : nullptr, length); exit: return error; } template <> otError Br::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; /** * @cli br rioprf * @code * br rioprf * med * Done * @endcode * @par api_copy * #otBorderRoutingGetRouteInfoOptionPreference */ if (aArgs[0].IsEmpty()) { OutputLine("%s", PreferenceToString(otBorderRoutingGetRouteInfoOptionPreference(GetInstancePtr()))); } /** * @cli br rioprf clear * @code * br rioprf clear * Done * @endcode * @par api_copy * #otBorderRoutingClearRouteInfoOptionPreference */ else if (aArgs[0] == "clear") { otBorderRoutingClearRouteInfoOptionPreference(GetInstancePtr()); } /** * @cli br rioprf (high,med,low) * @code * br rioprf low * Done * @endcode * @cparam br rioprf [@ca{high}|@ca{med}|@ca{low}] * @par api_copy * #otBorderRoutingSetRouteInfoOptionPreference */ else { otRoutePreference preference; SuccessOrExit(error = Interpreter::ParsePreference(aArgs[0], preference)); otBorderRoutingSetRouteInfoOptionPreference(GetInstancePtr(), preference); } exit: return error; } template <> otError Br::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; /** * @cli br routeprf * @code * br routeprf * med * Done * @endcode * @par api_copy * #otBorderRoutingGetRoutePreference */ if (aArgs[0].IsEmpty()) { OutputLine("%s", PreferenceToString(otBorderRoutingGetRoutePreference(GetInstancePtr()))); } /** * @cli br routeprf clear * @code * br routeprf clear * Done * @endcode * @par api_copy * #otBorderRoutingClearRoutePreference */ else if (aArgs[0] == "clear") { otBorderRoutingClearRoutePreference(GetInstancePtr()); } /** * @cli br routeprf (high,med,low) * @code * br routeprf low * Done * @endcode * @cparam br routeprf [@ca{high}|@ca{med}|@ca{low}] * @par api_copy * #otBorderRoutingSetRoutePreference */ else { otRoutePreference preference; SuccessOrExit(error = Interpreter::ParsePreference(aArgs[0], preference)); otBorderRoutingSetRoutePreference(GetInstancePtr(), preference); } exit: return error; } #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE /** * @cli br counters * @code * br counters * Inbound Unicast: Packets 4 Bytes 320 * Inbound Multicast: Packets 0 Bytes 0 * Outbound Unicast: Packets 2 Bytes 160 * Outbound Multicast: Packets 0 Bytes 0 * RA Rx: 4 * RA TxSuccess: 2 * RA TxFailed: 0 * RS Rx: 0 * RS TxSuccess: 2 * RS TxFailed: 0 * Done * @endcode * @par api_copy * #otIp6GetBorderRoutingCounters */ template <> otError Br::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); Interpreter::GetInterpreter().OutputBorderRouterCounters(); exit: return error; } #endif // OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE otError Br::Process(Arg aArgs[]) { #define CmdEntry(aCommandString) \ { \ aCommandString, &Br::Process \ } static constexpr Command kCommands[] = { #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE CmdEntry("counters"), #endif CmdEntry("disable"), CmdEntry("enable"), CmdEntry("init"), #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE CmdEntry("nat64prefix"), #endif CmdEntry("omrprefix"), CmdEntry("onlinkprefix"), #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE CmdEntry("pd"), #endif #if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE CmdEntry("peers"), #endif CmdEntry("prefixtable"), CmdEntry("raoptions"), CmdEntry("rioprf"), CmdEntry("routeprf"), CmdEntry("routers"), CmdEntry("state"), }; #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_BORDER_ROUTING_ENABLE