• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2021, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements the ping sender module.
32  */
33 
34 #include "ping_sender.hpp"
35 
36 #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
37 
38 #include "common/as_core_type.hpp"
39 #include "common/encoding.hpp"
40 #include "common/locator_getters.hpp"
41 #include "common/random.hpp"
42 
43 namespace ot {
44 namespace Utils {
45 
46 using Encoding::BigEndian::HostSwap32;
47 
SetUnspecifiedToDefault(void)48 void PingSender::Config::SetUnspecifiedToDefault(void)
49 {
50     if (mSize == 0)
51     {
52         mSize = kDefaultSize;
53     }
54 
55     if (mCount == 0)
56     {
57         mCount = kDefaultCount;
58     }
59 
60     if (mInterval == 0)
61     {
62         mInterval = kDefaultInterval;
63     }
64 
65     if (mTimeout == 0)
66     {
67         mTimeout = kDefaultTimeout;
68     }
69 }
70 
InvokeReplyCallback(const Reply & aReply) const71 void PingSender::Config::InvokeReplyCallback(const Reply &aReply) const
72 {
73     VerifyOrExit(mReplyCallback != nullptr);
74     mReplyCallback(&aReply, mCallbackContext);
75 
76 exit:
77     return;
78 }
79 
InvokeStatisticsCallback(const Statistics & aStatistics) const80 void PingSender::Config::InvokeStatisticsCallback(const Statistics &aStatistics) const
81 {
82     VerifyOrExit(mStatisticsCallback != nullptr);
83     mStatisticsCallback(&aStatistics, mCallbackContext);
84 
85 exit:
86     return;
87 }
88 
PingSender(Instance & aInstance)89 PingSender::PingSender(Instance &aInstance)
90     : InstanceLocator(aInstance)
91     , mIdentifier(0)
92     , mTargetEchoSequence(0)
93     , mTimer(aInstance, PingSender::HandleTimer)
94     , mIcmpHandler(PingSender::HandleIcmpReceive, this)
95 {
96     IgnoreError(Get<Ip6::Icmp>().RegisterHandler(mIcmpHandler));
97 }
98 
Ping(const Config & aConfig)99 Error PingSender::Ping(const Config &aConfig)
100 {
101     Error error = kErrorNone;
102 
103     VerifyOrExit(!mTimer.IsRunning(), error = kErrorBusy);
104 
105     mConfig = aConfig;
106     mConfig.SetUnspecifiedToDefault();
107 
108     VerifyOrExit(mConfig.mInterval <= Timer::kMaxDelay, error = kErrorInvalidArgs);
109 
110     mStatistics.Clear();
111     mStatistics.mIsMulticast = AsCoreType(&mConfig.mDestination).IsMulticast();
112 
113     mIdentifier++;
114     SendPing();
115 
116 exit:
117     return error;
118 }
119 
Stop(void)120 void PingSender::Stop(void)
121 {
122     mTimer.Stop();
123     mIdentifier++;
124 }
125 
SendPing(void)126 void PingSender::SendPing(void)
127 {
128     TimeMilli        now     = TimerMilli::GetNow();
129     Message *        message = nullptr;
130     Ip6::MessageInfo messageInfo;
131 
132     messageInfo.SetSockAddr(mConfig.GetSource());
133     messageInfo.SetPeerAddr(mConfig.GetDestination());
134     messageInfo.mHopLimit          = mConfig.mHopLimit;
135     messageInfo.mAllowZeroHopLimit = mConfig.mAllowZeroHopLimit;
136 
137     message = Get<Ip6::Icmp>().NewMessage(0);
138     VerifyOrExit(message != nullptr);
139 
140     SuccessOrExit(message->Append(HostSwap32(now.GetValue())));
141 
142     if (mConfig.mSize > message->GetLength())
143     {
144         SuccessOrExit(message->SetLength(mConfig.mSize));
145     }
146 
147     mTargetEchoSequence = Get<Ip6::Icmp>().GetEchoSequence();
148     SuccessOrExit(Get<Ip6::Icmp>().SendEchoRequest(*message, messageInfo, mIdentifier));
149     mStatistics.mSentCount++;
150 
151 #if OPENTHREAD_CONFIG_OTNS_ENABLE
152     Get<Utils::Otns>().EmitPingRequest(mConfig.GetDestination(), mConfig.mSize, now.GetValue(), mConfig.mHopLimit);
153 #endif
154 
155     message = nullptr;
156 
157 exit:
158     FreeMessage(message);
159     mConfig.mCount--;
160 
161     if (mConfig.mCount > 0)
162     {
163         mTimer.Start(mConfig.mInterval);
164     }
165     else
166     {
167         mTimer.Start(mConfig.mTimeout);
168     }
169 }
170 
HandleTimer(Timer & aTimer)171 void PingSender::HandleTimer(Timer &aTimer)
172 {
173     aTimer.Get<PingSender>().HandleTimer();
174 }
175 
HandleTimer(void)176 void PingSender::HandleTimer(void)
177 {
178     if (mConfig.mCount > 0)
179     {
180         SendPing();
181     }
182     else // The last reply times out, triggering the callback to print statistics in CLI.
183     {
184         mConfig.InvokeStatisticsCallback(mStatistics);
185     }
186 }
187 
HandleIcmpReceive(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,const otIcmp6Header * aIcmpHeader)188 void PingSender::HandleIcmpReceive(void *               aContext,
189                                    otMessage *          aMessage,
190                                    const otMessageInfo *aMessageInfo,
191                                    const otIcmp6Header *aIcmpHeader)
192 {
193     reinterpret_cast<PingSender *>(aContext)->HandleIcmpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo),
194                                                                 AsCoreType(aIcmpHeader));
195 }
196 
HandleIcmpReceive(const Message & aMessage,const Ip6::MessageInfo & aMessageInfo,const Ip6::Icmp::Header & aIcmpHeader)197 void PingSender::HandleIcmpReceive(const Message &          aMessage,
198                                    const Ip6::MessageInfo & aMessageInfo,
199                                    const Ip6::Icmp::Header &aIcmpHeader)
200 {
201     Reply    reply;
202     uint32_t timestamp;
203 
204     VerifyOrExit(mTimer.IsRunning());
205     VerifyOrExit(aIcmpHeader.GetType() == Ip6::Icmp::Header::kTypeEchoReply);
206     VerifyOrExit(aIcmpHeader.GetId() == mIdentifier);
207 
208     SuccessOrExit(aMessage.Read(aMessage.GetOffset(), timestamp));
209     timestamp = HostSwap32(timestamp);
210 
211     reply.mSenderAddress = aMessageInfo.GetPeerAddr();
212     reply.mRoundTripTime =
213         static_cast<uint16_t>(OT_MIN(TimerMilli::GetNow() - TimeMilli(timestamp), NumericLimits<uint16_t>::kMax));
214     reply.mSize           = aMessage.GetLength() - aMessage.GetOffset();
215     reply.mSequenceNumber = aIcmpHeader.GetSequence();
216     reply.mHopLimit       = aMessageInfo.GetHopLimit();
217 
218     mStatistics.mReceivedCount++;
219     mStatistics.mTotalRoundTripTime += reply.mRoundTripTime;
220     mStatistics.mMaxRoundTripTime = OT_MAX(mStatistics.mMaxRoundTripTime, reply.mRoundTripTime);
221     mStatistics.mMinRoundTripTime = OT_MIN(mStatistics.mMinRoundTripTime, reply.mRoundTripTime);
222 
223 #if OPENTHREAD_CONFIG_OTNS_ENABLE
224     Get<Utils::Otns>().EmitPingReply(aMessageInfo.GetPeerAddr(), reply.mSize, timestamp, reply.mHopLimit);
225 #endif
226     // Received all ping replies, no need to wait longer.
227     if (!mStatistics.mIsMulticast && mConfig.mCount == 0 && aIcmpHeader.GetSequence() == mTargetEchoSequence)
228     {
229         mTimer.Stop();
230     }
231     mConfig.InvokeReplyCallback(reply);
232     // Received all ping replies, no need to wait longer.
233     if (!mStatistics.mIsMulticast && mConfig.mCount == 0 && aIcmpHeader.GetSequence() == mTargetEchoSequence)
234     {
235         mConfig.InvokeStatisticsCallback(mStatistics);
236     }
237 
238 exit:
239     return;
240 }
241 
242 } // namespace Utils
243 } // namespace ot
244 
245 #endif // #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
246