blob: 5d0908e299734982993688a3cb984c0aebd5acfb [file] [log] [blame]
Junxiao Shi1688ded2015-03-29 10:09:26 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014-2015, Arizona Board of Regents.
4 *
5 * This file is part of ndn-tools (Named Data Networking Essential Tools).
6 * See AUTHORS.md for complete list of ndn-tools authors and contributors.
7 *
8 * ndn-tools is free software: you can redistribute it and/or modify it under the terms
9 * of the GNU General Public License as published by the Free Software Foundation,
10 * either version 3 of the License, or (at your option) any later version.
11 *
12 * ndn-tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * ndn-tools, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
18 */
Junxiao Shi11b2ae92015-03-21 11:29:19 -070019/**
20 * Copyright (C) 2014 Arizona Board of Regents
21 *
22 * This file is part of ndn-tlv-ping (Ping Application for Named Data Networking).
23 *
24 * ndn-tlv-ping is a free software: you can redistribute it and/or modify it under
25 * the terms of the GNU General Public License as published by the Free Software
26 * Foundation, either version 3 of the License, or (at your option) any later version.
27 *
28 * ndn-tlv-ping is distributed in the hope that it will be useful, but WITHOUT ANY
29 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
30 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License along with
33 * ndn-tlv-ping, e.g., in LICENSE file. If not, see <http://www.gnu.org/licenses/>.
34 *
35 * @author: Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
36 */
37
Junxiao Shi1753afb2015-04-17 20:59:50 -070038#include "core/version.hpp"
Junxiao Shi11b2ae92015-03-21 11:29:19 -070039
Junxiao Shi11b2ae92015-03-21 11:29:19 -070040#include <boost/date_time/posix_time/posix_time.hpp>
Junxiao Shi11b2ae92015-03-21 11:29:19 -070041
42namespace ndn {
Junxiao Shi0c75f992015-03-24 21:39:47 -070043namespace ping {
Junxiao Shi11b2ae92015-03-21 11:29:19 -070044
Junxiao Shi0c75f992015-03-24 21:39:47 -070045class NdnPing : boost::noncopyable
Junxiao Shi11b2ae92015-03-21 11:29:19 -070046{
47public:
Junxiao Shi11b2ae92015-03-21 11:29:19 -070048 explicit
Junxiao Shi0c75f992015-03-24 21:39:47 -070049 NdnPing(char* programName)
Junxiao Shi11b2ae92015-03-21 11:29:19 -070050 : m_programName(programName)
51 , m_isAllowCachingSet(false)
52 , m_isPrintTimestampSet(false)
53 , m_hasError(false)
54 , m_totalPings(-1)
55 , m_startPingNumber(-1)
56 , m_pingsSent(0)
57 , m_pingsReceived(0)
58 , m_pingInterval(getPingMinimumInterval())
59 , m_clientIdentifier(0)
60 , m_pingTimeoutThreshold(getPingTimeoutThreshold())
61 , m_outstanding(0)
62 , m_face(m_ioService)
63 {
64 }
65
66 class PingStatistics : boost::noncopyable
67 {
68 public:
Junxiao Shi11b2ae92015-03-21 11:29:19 -070069 explicit
70 PingStatistics()
71 : m_sentPings(0)
72 , m_receivedPings(0)
73 , m_pingStartTime(time::steady_clock::now())
74 , m_minimumRoundTripTime(std::numeric_limits<double>::max())
75 , m_averageRoundTripTimeData(0)
76 , m_standardDeviationRoundTripTimeData(0)
77 {
78 }
79
80 void
81 addToPingStatistics(time::nanoseconds roundTripNanoseconds)
82 {
83 double roundTripTime = roundTripNanoseconds.count() / 1000000.0;
84 if (roundTripTime < m_minimumRoundTripTime)
85 m_minimumRoundTripTime = roundTripTime;
86 if (roundTripTime > m_maximumRoundTripTime)
87 m_maximumRoundTripTime = roundTripTime;
Junxiao Shi0c75f992015-03-24 21:39:47 -070088
Junxiao Shi11b2ae92015-03-21 11:29:19 -070089 m_averageRoundTripTimeData += roundTripTime;
Junxiao Shi0c75f992015-03-24 21:39:47 -070090 m_standardDeviationRoundTripTimeData += roundTripTime * roundTripTime;
Junxiao Shi11b2ae92015-03-21 11:29:19 -070091 }
92
Junxiao Shi0c75f992015-03-24 21:39:47 -070093 public:
Junxiao Shi11b2ae92015-03-21 11:29:19 -070094 int m_sentPings;
95 int m_receivedPings;
96 time::steady_clock::TimePoint m_pingStartTime;
97 double m_minimumRoundTripTime;
98 double m_maximumRoundTripTime;
99 double m_averageRoundTripTimeData;
100 double m_standardDeviationRoundTripTimeData;
101
102 };
103
104 void
105 usage()
106 {
107 std::cout << "\n Usage:\n " << m_programName << " ndn:/name/prefix [options]\n"
108 " Ping a NDN name prefix using Interests with name"
109 " ndn:/name/prefix/ping/number.\n"
110 " The numbers in the Interests are randomly generated unless specified.\n"
111 " [-i interval] - set ping interval in seconds (minimum "
112 << getPingMinimumInterval().count() << " milliseconds)\n"
113 " [-c count] - set total number of pings\n"
114 " [-n number] - set the starting number, the number is incremented by 1"
115 " after each Interest\n"
116 " [-p identifier] - add identifier to the Interest names before the"
117 " numbers to avoid conflict\n"
118 " [-a] - allow routers to return stale Data from cache\n"
119 " [-t] - print timestamp with messages\n"
120 " [-h] - print this message and exit\n\n";
121 exit(1);
122 }
123
124 time::milliseconds
125 getPingMinimumInterval()
126 {
127 return time::milliseconds(1000);
128 }
129
130 time::milliseconds
131 getPingTimeoutThreshold()
132 {
133 return time::milliseconds(4000);
134 }
135
136 void
137 setTotalPings(int totalPings)
138 {
139 if (totalPings <= 0)
140 usage();
Junxiao Shi0c75f992015-03-24 21:39:47 -0700141
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700142 m_totalPings = totalPings;
143 }
144
145 void
146 setPingInterval(int pingInterval)
147 {
148 if (pingInterval < getPingMinimumInterval().count())
149 usage();
Junxiao Shi0c75f992015-03-24 21:39:47 -0700150
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700151 m_pingInterval = time::milliseconds(pingInterval);
152 }
153
154 void
155 setStartPingNumber(int64_t startPingNumber)
156 {
157 if (startPingNumber < 0)
158 usage();
Junxiao Shi0c75f992015-03-24 21:39:47 -0700159
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700160 m_startPingNumber = startPingNumber;
161 }
162
163 void
164 setAllowCaching()
165 {
166 m_isAllowCachingSet = true;
167 }
168
169 void
170 setPrintTimestamp()
171 {
172 m_isPrintTimestampSet = true;
173 }
174
175 void
176 setClientIdentifier(char* clientIdentifier)
177 {
178 m_clientIdentifier = clientIdentifier;
Junxiao Shi0c75f992015-03-24 21:39:47 -0700179
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700180 if (strlen(clientIdentifier) == 0)
181 usage();
Junxiao Shi0c75f992015-03-24 21:39:47 -0700182
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700183 while (*clientIdentifier != '\0') {
Junxiao Shi0c75f992015-03-24 21:39:47 -0700184 if (isalnum(*clientIdentifier) == 0)
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700185 usage();
186 clientIdentifier++;
187 }
188 }
189
190 void
191 setPrefix(char* prefix)
192 {
193 m_prefix = prefix;
194 }
195
196 bool
197 hasError() const
198 {
199 return m_hasError;
200 }
201
202
203 void
204 onData(const ndn::Interest& interest,
205 Data& data,
206 time::steady_clock::TimePoint timePoint)
207 {
Junxiao Shi0c75f992015-03-24 21:39:47 -0700208 std::string pingReference = interest.getName().toUri();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700209 m_pingsReceived++;
210 m_pingStatistics.m_receivedPings++;
Junxiao Shi0c75f992015-03-24 21:39:47 -0700211 time::nanoseconds roundTripTime = time::steady_clock::now() - timePoint;
212
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700213 if (m_isPrintTimestampSet)
214 std::cout << time::toIsoString(time::system_clock::now()) << " - ";
215 std::cout << "Content From " << m_prefix;
216 std::cout << " - Ping Reference = " <<
217 interest.getName().getSubName(interest.getName().size()-1).toUri().substr(1);
218 std::cout << " \t- Round Trip Time = " <<
219 roundTripTime.count() / 1000000.0 << " ms" << std::endl;
Junxiao Shi0c75f992015-03-24 21:39:47 -0700220
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700221 m_pingStatistics.addToPingStatistics(roundTripTime);
222 this->finish();
223 }
224
225 void
226 onTimeout(const ndn::Interest& interest)
227 {
228 if (m_isPrintTimestampSet)
229 std::cout << time::toIsoString(time::system_clock::now()) << " - ";
230 std::cout << "Timeout From " << m_prefix;
231 std::cout << " - Ping Reference = " <<
232 interest.getName().getSubName(interest.getName().size()-1).toUri().substr(1);
233 std::cout << std::endl;
Junxiao Shi0c75f992015-03-24 21:39:47 -0700234
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700235 this->finish();
236 }
237
238 void
239 printPingStatistics()
240 {
241 std::cout << "\n\n=== " << " Ping Statistics For "<< m_prefix <<" ===" << std::endl;
242 std::cout << "Sent=" << m_pingStatistics.m_sentPings;
243 std::cout << ", Received=" << m_pingStatistics.m_receivedPings;
244 double packetLossRate = m_pingStatistics.m_sentPings - m_pingStatistics.m_receivedPings;
245 packetLossRate /= m_pingStatistics.m_sentPings;
246 std::cout << ", Packet Loss=" << packetLossRate * 100.0 << "%";
247 if (m_pingStatistics.m_sentPings != m_pingStatistics.m_receivedPings)
248 m_hasError = true;
249 std::cout << ", Total Time=" << m_pingStatistics.m_averageRoundTripTimeData << " ms\n";
250 if (m_pingStatistics.m_receivedPings > 0) {
251 double averageRoundTripTime =
252 m_pingStatistics.m_averageRoundTripTimeData / m_pingStatistics.m_receivedPings;
253 double standardDeviationRoundTripTime =
254 m_pingStatistics.m_standardDeviationRoundTripTimeData / m_pingStatistics.m_receivedPings;
255 standardDeviationRoundTripTime -= averageRoundTripTime * averageRoundTripTime;
256 standardDeviationRoundTripTime = std::sqrt(standardDeviationRoundTripTime);
257 std::cout << "Round Trip Time (Min/Max/Avg/MDev) = (";
258 std::cout << m_pingStatistics.m_minimumRoundTripTime << "/";
259 std::cout << m_pingStatistics.m_maximumRoundTripTime << "/";
260 std::cout << averageRoundTripTime << "/";
261 std::cout << standardDeviationRoundTripTime << ") ms\n";
262 }
263 std::cout << std::endl;
264 }
265
266 void
267 performPing(boost::asio::deadline_timer* deadlineTimer)
268 {
Junxiao Shi0c75f992015-03-24 21:39:47 -0700269 if ((m_totalPings < 0) || (m_pingsSent < m_totalPings)) {
270 m_pingsSent++;
271 m_pingStatistics.m_sentPings++;
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700272
Junxiao Shi0c75f992015-03-24 21:39:47 -0700273 //Perform Ping
274 char pingNumberString[20];
275 Name pingPacketName(m_prefix);
276 pingPacketName.append("ping");
277 if (m_clientIdentifier != 0)
278 pingPacketName.append(m_clientIdentifier);
279 std::memset(pingNumberString, 0, 20);
280 if (m_startPingNumber < 0)
281 m_startPingNumber = std::rand();
282 sprintf(pingNumberString, "%lld", static_cast<long long int>(m_startPingNumber));
283 pingPacketName.append(pingNumberString);
284
285 ndn::Interest interest(pingPacketName);
286
287 if (m_isAllowCachingSet)
288 interest.setMustBeFresh(false);
289 else
290 interest.setMustBeFresh(true);
291
292 interest.setInterestLifetime(m_pingTimeoutThreshold);
293 interest.setNonce(m_startPingNumber);
294
295 m_startPingNumber++;
296
297 try {
298 m_face.expressInterest(interest,
299 std::bind(&NdnPing::onData, this, _1, _2,
300 time::steady_clock::now()),
301 std::bind(&NdnPing::onTimeout, this, _1));
302 deadlineTimer->expires_at(deadlineTimer->expires_at() +
303 boost::posix_time::millisec(m_pingInterval.count()));
304 deadlineTimer->async_wait(bind(&NdnPing::performPing,
305 this,
306 deadlineTimer));
307 }
308 catch (std::exception& e) {
309 std::cerr << "ERROR: " << e.what() << std::endl;
310 }
311 ++m_outstanding;
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700312 }
313 else {
314 this->finish();
315 }
316 }
317
318 void
319 finish()
320 {
321 if (--m_outstanding >= 0) {
322 return;
323 }
324 m_face.shutdown();
325 printPingStatistics();
326 m_ioService.stop();
327 }
328
329 void
330 signalHandler()
331 {
332 m_face.shutdown();
333 printPingStatistics();
334 exit(1);
335 }
336
337 void
338 run()
339 {
340 std::cout << "\n=== Pinging " << m_prefix << " ===\n" <<std::endl;
341
342 boost::asio::signal_set signalSet(m_ioService, SIGINT, SIGTERM);
Junxiao Shi0c75f992015-03-24 21:39:47 -0700343 signalSet.async_wait(bind(&NdnPing::signalHandler, this));
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700344
345 boost::asio::deadline_timer deadlineTimer(m_ioService,
346 boost::posix_time::millisec(0));
347
Junxiao Shi0c75f992015-03-24 21:39:47 -0700348 deadlineTimer.async_wait(bind(&NdnPing::performPing,
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700349 this,
350 &deadlineTimer));
351 try {
352 m_face.processEvents();
353 }
354 catch (std::exception& e) {
355 std::cerr << "ERROR: " << e.what() << std::endl;
356 m_hasError = true;
357 m_ioService.stop();
358 }
359 }
360
361private:
362 char* m_programName;
363 bool m_isAllowCachingSet;
364 bool m_isPrintTimestampSet;
365 bool m_hasError;
366 int m_totalPings;
367 int64_t m_startPingNumber;
368 int m_pingsSent;
369 int m_pingsReceived;
370 time::milliseconds m_pingInterval;
371 char* m_clientIdentifier;
372 time::milliseconds m_pingTimeoutThreshold;
373 char* m_prefix;
374 PingStatistics m_pingStatistics;
375 ssize_t m_outstanding;
376
377 boost::asio::io_service m_ioService;
378 Face m_face;
379};
380
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700381int
382main(int argc, char* argv[])
383{
384 std::srand(::time(0));
385 int res;
386
Junxiao Shi0c75f992015-03-24 21:39:47 -0700387 NdnPing program(argv[0]);
Junxiao Shi1753afb2015-04-17 20:59:50 -0700388 while ((res = getopt(argc, argv, "htai:c:n:p:V")) != -1)
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700389 {
390 switch (res) {
391 case 'a':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700392 program.setAllowCaching();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700393 break;
394 case 'c':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700395 program.setTotalPings(atoi(optarg));
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700396 break;
397 case 'h':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700398 program.usage();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700399 break;
400 case 'i':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700401 program.setPingInterval(atoi(optarg));
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700402 break;
403 case 'n':
404 try {
Junxiao Shi0c75f992015-03-24 21:39:47 -0700405 program.setStartPingNumber(boost::lexical_cast<int64_t>(optarg));
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700406 }
407 catch (boost::bad_lexical_cast&) {
Junxiao Shi0c75f992015-03-24 21:39:47 -0700408 program.usage();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700409 }
410 break;
411 case 'p':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700412 program.setClientIdentifier(optarg);
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700413 break;
414 case 't':
Junxiao Shi0c75f992015-03-24 21:39:47 -0700415 program.setPrintTimestamp();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700416 break;
Junxiao Shi1753afb2015-04-17 20:59:50 -0700417 case 'V':
418 std::cout << "ndnping " << tools::VERSION << std::endl;
419 return 0;
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700420 default:
Junxiao Shi0c75f992015-03-24 21:39:47 -0700421 program.usage();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700422 break;
423 }
424 }
425
426 argc -= optind;
427 argv += optind;
428
429 if (argv[0] == 0)
Junxiao Shi0c75f992015-03-24 21:39:47 -0700430 program.usage();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700431
Junxiao Shi0c75f992015-03-24 21:39:47 -0700432 program.setPrefix(argv[0]);
433 program.run();
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700434
435 std::cout << std::endl;
436
Junxiao Shi0c75f992015-03-24 21:39:47 -0700437 if (program.hasError())
Junxiao Shi11b2ae92015-03-21 11:29:19 -0700438 return 1;
439 else
440 return 0;
441}
Junxiao Shi0c75f992015-03-24 21:39:47 -0700442
443} // namespace ping
444} // namespace ndn
445
446int
447main(int argc, char** argv)
448{
449 return ndn::ping::main(argc, argv);
450}