blob: 1111e3d1fb7e211b2496c2cbbee2d36f2af07660 [file] [log] [blame]
/*
* Copyright (c) 2024, 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 for Ping Sender function.
*/
#include "cli_ping.hpp"
#include <openthread/ping_sender.h>
#include "cli/cli.hpp"
#include "cli/cli_utils.hpp"
#include "common/code_utils.hpp"
#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
namespace ot {
namespace Cli {
PingSender::PingSender(otInstance *aInstance, OutputImplementer &aOutputImplementer)
: Utils(aInstance, aOutputImplementer)
, mPingIsAsync(false)
{
}
otError PingSender::Process(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
otPingSenderConfig config;
bool async = false;
bool nat64Synth;
/**
* @cli ping stop
* @code
* ping stop
* Done
* @endcode
* @par
* Stop sending ICMPv6 Echo Requests.
* @sa otPingSenderStop
*/
if (aArgs[0] == "stop")
{
otPingSenderStop(GetInstancePtr());
ExitNow();
}
else if (aArgs[0] == "async")
{
async = true;
aArgs++;
}
ClearAllBytes(config);
if (aArgs[0] == "-I")
{
SuccessOrExit(error = aArgs[1].ParseAsIp6Address(config.mSource));
#if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
VerifyOrExit(otIp6HasUnicastAddress(GetInstancePtr(), &config.mSource), error = OT_ERROR_INVALID_ARGS);
#endif
aArgs += 2;
}
if (aArgs[0] == "-m")
{
config.mMulticastLoop = true;
aArgs++;
}
SuccessOrExit(error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], config.mDestination, nat64Synth));
if (nat64Synth)
{
OutputFormat("Pinging synthesized IPv6 address: ");
OutputIp6AddressLine(config.mDestination);
}
if (!aArgs[1].IsEmpty())
{
SuccessOrExit(error = aArgs[1].ParseAsUint16(config.mSize));
}
if (!aArgs[2].IsEmpty())
{
SuccessOrExit(error = aArgs[2].ParseAsUint16(config.mCount));
}
if (!aArgs[3].IsEmpty())
{
SuccessOrExit(error = ParsePingInterval(aArgs[3], config.mInterval));
}
if (!aArgs[4].IsEmpty())
{
SuccessOrExit(error = aArgs[4].ParseAsUint8(config.mHopLimit));
config.mAllowZeroHopLimit = (config.mHopLimit == 0);
}
if (!aArgs[5].IsEmpty())
{
uint32_t timeout;
SuccessOrExit(error = ParsePingInterval(aArgs[5], timeout));
VerifyOrExit(timeout <= NumericLimits<uint16_t>::kMax, error = OT_ERROR_INVALID_ARGS);
config.mTimeout = static_cast<uint16_t>(timeout);
}
VerifyOrExit(aArgs[6].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
config.mReplyCallback = PingSender::HandlePingReply;
config.mStatisticsCallback = PingSender::HandlePingStatistics;
config.mCallbackContext = this;
SuccessOrExit(error = otPingSenderPing(GetInstancePtr(), &config));
mPingIsAsync = async;
if (!async)
{
error = OT_ERROR_PENDING;
}
exit:
return error;
}
otError PingSender::ParsePingInterval(const Arg &aArg, uint32_t &aInterval)
{
otError error = OT_ERROR_NONE;
const char *string = aArg.GetCString();
const uint32_t msFactor = 1000;
uint32_t factor = msFactor;
aInterval = 0;
while (*string)
{
if ('0' <= *string && *string <= '9')
{
// In the case of seconds, change the base of already calculated value.
if (factor == msFactor)
{
aInterval *= 10;
}
aInterval += static_cast<uint32_t>(*string - '0') * factor;
// In the case of milliseconds, change the multiplier factor.
if (factor != msFactor)
{
factor /= 10;
}
}
else if (*string == '.')
{
// 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);
}
string++;
}
exit:
return error;
}
void PingSender::HandlePingReply(const otPingSenderReply *aReply, void *aContext)
{
static_cast<PingSender *>(aContext)->HandlePingReply(aReply);
}
void PingSender::HandlePingReply(const otPingSenderReply *aReply)
{
OutputFormat("%u bytes from ", static_cast<uint16_t>(aReply->mSize + sizeof(otIcmp6Header)));
OutputIp6Address(aReply->mSenderAddress);
OutputLine(": icmp_seq=%u hlim=%u time=%ums", aReply->mSequenceNumber, aReply->mHopLimit, aReply->mRoundTripTime);
}
void PingSender::HandlePingStatistics(const otPingSenderStatistics *aStatistics, void *aContext)
{
static_cast<PingSender *>(aContext)->HandlePingStatistics(aStatistics);
}
void PingSender::HandlePingStatistics(const otPingSenderStatistics *aStatistics)
{
OutputFormat("%u packets transmitted, %u packets received.", aStatistics->mSentCount, aStatistics->mReceivedCount);
if ((aStatistics->mSentCount != 0) && !aStatistics->mIsMulticast &&
aStatistics->mReceivedCount <= aStatistics->mSentCount)
{
uint32_t packetLossRate =
1000 * (aStatistics->mSentCount - aStatistics->mReceivedCount) / aStatistics->mSentCount;
OutputFormat(" Packet loss = %lu.%u%%.", ToUlong(packetLossRate / 10),
static_cast<uint16_t>(packetLossRate % 10));
}
if (aStatistics->mReceivedCount != 0)
{
uint32_t avgRoundTripTime = 1000 * aStatistics->mTotalRoundTripTime / aStatistics->mReceivedCount;
OutputFormat(" Round-trip min/avg/max = %u/%u.%u/%u ms.", aStatistics->mMinRoundTripTime,
static_cast<uint16_t>(avgRoundTripTime / 1000), static_cast<uint16_t>(avgRoundTripTime % 1000),
aStatistics->mMaxRoundTripTime);
}
OutputNewLine();
if (!mPingIsAsync)
{
OutputResult(OT_ERROR_NONE);
}
}
void PingSender::OutputResult(otError aError) { Interpreter::GetInterpreter().OutputResult(aError); }
} // namespace Cli
} // namespace ot
#endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE