ping: import code from ndn-tlv-ping repository
This is an exact copy of code from
ndn-tlv-ping:commit:d3675f528af468d59b2c505057acd21b56210c5c
refs #2525
Change-Id: I4cd93087cf28737fa109183e83aa3bcf60c0f6ff
diff --git a/tools/ping/ndn-ping-server.cpp b/tools/ping/ndn-ping-server.cpp
new file mode 100644
index 0000000..dfe6538
--- /dev/null
+++ b/tools/ping/ndn-ping-server.cpp
@@ -0,0 +1,226 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/**
+ * Copyright (C) 2014 Arizona Board of Regents
+ *
+ * This file is part of ndn-tlv-ping (Ping Application for Named Data Networking).
+ *
+ * ndn-tlv-ping is a free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-tlv-ping is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ndn-tlv-ping, e.g., in LICENSE file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author: Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
+ */
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/name.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+
+#include <boost/asio.hpp>
+#include <boost/noncopyable.hpp>
+
+namespace ndn {
+
+class NdnTlvPingServer : boost::noncopyable
+{
+public:
+
+ explicit
+ NdnTlvPingServer(char* programName)
+ : m_programName(programName)
+ , m_hasError(false)
+ , m_isPrintTimestampSet(false)
+ , m_freshnessPeriod(getMinimumFreshnessPeriod())
+ , m_maximumPings(-1)
+ , m_totalPings(0)
+ , m_face(m_ioService)
+ {
+ }
+
+ void
+ usage()
+ {
+ std::cout << "\n Usage:\n " << m_programName << " ndn:/name/prefix [options]\n"
+ " Starts a NDN ping server that responds to Interests with name"
+ " ndn:/name/prefix/ping/number.\n"
+ " [-x freshness] - set FreshnessSeconds\n"
+ " [-p] - specify number of pings to be satisfied (>=1)\n"
+ " [-t] - print timestamp\n"
+ " [-h] - print this message and exit\n\n";
+ exit(1);
+ }
+
+ time::milliseconds
+ getMinimumFreshnessPeriod()
+ {
+ return time::milliseconds(1000);
+ }
+
+ void
+ setFreshnessPeriod(int freshnessPeriod)
+ {
+ if (freshnessPeriod <= 0)
+
+ m_freshnessPeriod = time::milliseconds(freshnessPeriod);
+ }
+
+ void
+ setMaximumPings(int maximumPings)
+ {
+ if (maximumPings <= 0)
+ usage();
+ m_maximumPings = maximumPings;
+ }
+
+ void
+ setPrefix(char* prefix)
+ {
+ m_prefix = prefix;
+ }
+
+ void
+ setPrintTimestamp()
+ {
+ m_isPrintTimestampSet = true;
+ }
+
+ bool
+ hasError() const
+ {
+ return m_hasError;
+ }
+
+ void
+ onInterest(const Name& name, const Interest& interest)
+ {
+ Name interestName = interest.getName();
+ if (m_isPrintTimestampSet)
+ std::cout << time::toIsoString(time::system_clock::now()) << " - ";
+ std::cout << "Interest Received - Ping Reference = "
+ << interestName.at(-1).toUri() << std::endl;
+ char responseContent[] = "NDN TLV Ping Response";
+ shared_ptr<Data> data = make_shared<Data>(interestName);
+ data->setFreshnessPeriod(m_freshnessPeriod);
+ data->setContent(reinterpret_cast<const uint8_t*>(responseContent),
+ sizeof(responseContent));
+ m_keyChain.sign(*data);
+ m_face.put(*data);
+ ++m_totalPings;
+ if (m_maximumPings > 0 && m_maximumPings == m_totalPings) {
+ std::cout << "\n\nTotal Ping Interests Processed = " << m_totalPings << std::endl;
+ std::cout << "Shutting Down Ping Server (" << m_name << ").\n" << std::endl;
+ m_face.shutdown();
+ m_ioService.stop();
+ }
+ }
+
+ void
+ onRegisterFailed(const ndn::Name& prefix, const std::string& reason)
+ {
+ std::cerr << "ERROR: Failed to register prefix in local hub's daemon" << std::endl;
+ std::cerr << "REASON: " << reason << std::endl;
+ m_hasError = true;
+ m_face.shutdown();
+ m_ioService.stop();
+ }
+
+ void
+ signalHandler()
+ {
+ std::cout << "\n\nTotal Ping Interests Processed = " << m_totalPings << std::endl;
+ std::cout << "Shutting Down Ping Server (" << m_name.toUri() << ").\n" << std::endl;
+ m_face.shutdown();
+ exit(1);
+ }
+
+ void
+ run()
+ {
+ std::cout << "\n=== Ping Server " << m_prefix <<" ===\n" << std::endl;
+
+ boost::asio::signal_set signalSet(m_ioService, SIGINT, SIGTERM);
+ signalSet.async_wait(bind([this]() { signalHandler(); }));
+ m_name = m_prefix;
+ m_name.append("ping");
+ m_face.setInterestFilter(m_name,
+ bind(&NdnTlvPingServer::onInterest,
+ this, _1, _2),
+ bind(&NdnTlvPingServer::onRegisterFailed,
+ this, _1,_2));
+ try {
+ m_face.processEvents();
+ }
+ catch (std::exception& e) {
+ std::cerr << "ERROR: " << e.what() << std::endl;
+ m_hasError = true;
+ m_ioService.stop();
+ }
+ }
+
+private:
+ char* m_programName;
+ bool m_hasError;
+ bool m_isPrintTimestampSet;
+ time::milliseconds m_freshnessPeriod;
+ int m_maximumPings;
+ int m_totalPings;
+
+ char* m_prefix;
+ Name m_name;
+
+ boost::asio::io_service m_ioService;
+ KeyChain m_keyChain;
+ Face m_face;
+};
+
+}
+
+int
+main(int argc, char* argv[])
+{
+ int res;
+
+ ndn::NdnTlvPingServer ndnTlvPingServer(argv[0]);
+ while ((res = getopt(argc, argv, "hdtp:x:")) != -1)
+ {
+ switch (res) {
+ case 'h':
+ ndnTlvPingServer.usage();
+ break;
+ case 'p':
+ ndnTlvPingServer.setMaximumPings(atoi(optarg));
+ break;
+ case 'x':
+ ndnTlvPingServer.setFreshnessPeriod(atoi(optarg));
+ break;
+ case 't':
+ ndnTlvPingServer.setPrintTimestamp();
+ break;
+ default:
+ ndnTlvPingServer.usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argv[0] == 0)
+ ndnTlvPingServer.usage();
+
+ ndnTlvPingServer.setPrefix(argv[0]);
+ ndnTlvPingServer.run();
+
+ std::cout << std::endl;
+
+ if (ndnTlvPingServer.hasError())
+ return 1;
+ else
+ return 0;
+}
diff --git a/tools/ping/ndn-ping.cpp b/tools/ping/ndn-ping.cpp
new file mode 100644
index 0000000..3479e73
--- /dev/null
+++ b/tools/ping/ndn-ping.cpp
@@ -0,0 +1,417 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/**
+ * Copyright (C) 2014 Arizona Board of Regents
+ *
+ * This file is part of ndn-tlv-ping (Ping Application for Named Data Networking).
+ *
+ * ndn-tlv-ping is a free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-tlv-ping is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ndn-tlv-ping, e.g., in LICENSE file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author: Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
+ */
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/name.hpp>
+
+#include <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/noncopyable.hpp>
+
+namespace ndn {
+
+class NdnTlvPing : boost::noncopyable
+{
+public:
+
+ explicit
+ NdnTlvPing(char* programName)
+ : m_programName(programName)
+ , m_isAllowCachingSet(false)
+ , m_isPrintTimestampSet(false)
+ , m_hasError(false)
+ , m_totalPings(-1)
+ , m_startPingNumber(-1)
+ , m_pingsSent(0)
+ , m_pingsReceived(0)
+ , m_pingInterval(getPingMinimumInterval())
+ , m_clientIdentifier(0)
+ , m_pingTimeoutThreshold(getPingTimeoutThreshold())
+ , m_outstanding(0)
+ , m_face(m_ioService)
+ {
+ }
+
+ class PingStatistics : boost::noncopyable
+ {
+ public:
+
+ explicit
+ PingStatistics()
+ : m_sentPings(0)
+ , m_receivedPings(0)
+ , m_pingStartTime(time::steady_clock::now())
+ , m_minimumRoundTripTime(std::numeric_limits<double>::max())
+ , m_averageRoundTripTimeData(0)
+ , m_standardDeviationRoundTripTimeData(0)
+ {
+ }
+
+ void
+ addToPingStatistics(time::nanoseconds roundTripNanoseconds)
+ {
+ double roundTripTime = roundTripNanoseconds.count() / 1000000.0;
+ if (roundTripTime < m_minimumRoundTripTime)
+ m_minimumRoundTripTime = roundTripTime;
+ if (roundTripTime > m_maximumRoundTripTime)
+ m_maximumRoundTripTime = roundTripTime;
+ m_averageRoundTripTimeData += roundTripTime;
+ m_standardDeviationRoundTripTimeData += roundTripTime*roundTripTime;
+ }
+
+ int m_sentPings;
+ int m_receivedPings;
+ time::steady_clock::TimePoint m_pingStartTime;
+ double m_minimumRoundTripTime;
+ double m_maximumRoundTripTime;
+ double m_averageRoundTripTimeData;
+ double m_standardDeviationRoundTripTimeData;
+
+ };
+
+ void
+ usage()
+ {
+ std::cout << "\n Usage:\n " << m_programName << " ndn:/name/prefix [options]\n"
+ " Ping a NDN name prefix using Interests with name"
+ " ndn:/name/prefix/ping/number.\n"
+ " The numbers in the Interests are randomly generated unless specified.\n"
+ " [-i interval] - set ping interval in seconds (minimum "
+ << getPingMinimumInterval().count() << " milliseconds)\n"
+ " [-c count] - set total number of pings\n"
+ " [-n number] - set the starting number, the number is incremented by 1"
+ " after each Interest\n"
+ " [-p identifier] - add identifier to the Interest names before the"
+ " numbers to avoid conflict\n"
+ " [-a] - allow routers to return stale Data from cache\n"
+ " [-t] - print timestamp with messages\n"
+ " [-h] - print this message and exit\n\n";
+ exit(1);
+ }
+
+ time::milliseconds
+ getPingMinimumInterval()
+ {
+ return time::milliseconds(1000);
+ }
+
+ time::milliseconds
+ getPingTimeoutThreshold()
+ {
+ return time::milliseconds(4000);
+ }
+
+ void
+ setTotalPings(int totalPings)
+ {
+ if (totalPings <= 0)
+ usage();
+ m_totalPings = totalPings;
+ }
+
+ void
+ setPingInterval(int pingInterval)
+ {
+ if (pingInterval < getPingMinimumInterval().count())
+ usage();
+ m_pingInterval = time::milliseconds(pingInterval);
+ }
+
+ void
+ setStartPingNumber(int64_t startPingNumber)
+ {
+ if (startPingNumber < 0)
+ usage();
+ m_startPingNumber = startPingNumber;
+ }
+
+ void
+ setAllowCaching()
+ {
+ m_isAllowCachingSet = true;
+ }
+
+ void
+ setPrintTimestamp()
+ {
+ m_isPrintTimestampSet = true;
+ }
+
+ void
+ setClientIdentifier(char* clientIdentifier)
+ {
+ m_clientIdentifier = clientIdentifier;
+ if (strlen(clientIdentifier) == 0)
+ usage();
+ while (*clientIdentifier != '\0') {
+ if( isalnum(*clientIdentifier) == 0 )
+ usage();
+ clientIdentifier++;
+ }
+ }
+
+ void
+ setPrefix(char* prefix)
+ {
+ m_prefix = prefix;
+ }
+
+ bool
+ hasError() const
+ {
+ return m_hasError;
+ }
+
+
+ void
+ onData(const ndn::Interest& interest,
+ Data& data,
+ time::steady_clock::TimePoint timePoint)
+ {
+ std::string pingReference;
+ time::nanoseconds roundTripTime;
+ pingReference = interest.getName().toUri();
+ m_pingsReceived++;
+ m_pingStatistics.m_receivedPings++;
+ roundTripTime = time::steady_clock::now() - timePoint;
+ if (m_isPrintTimestampSet)
+ std::cout << time::toIsoString(time::system_clock::now()) << " - ";
+ std::cout << "Content From " << m_prefix;
+ std::cout << " - Ping Reference = " <<
+ interest.getName().getSubName(interest.getName().size()-1).toUri().substr(1);
+ std::cout << " \t- Round Trip Time = " <<
+ roundTripTime.count() / 1000000.0 << " ms" << std::endl;
+ m_pingStatistics.addToPingStatistics(roundTripTime);
+ this->finish();
+ }
+
+ void
+ onTimeout(const ndn::Interest& interest)
+ {
+ if (m_isPrintTimestampSet)
+ std::cout << time::toIsoString(time::system_clock::now()) << " - ";
+ std::cout << "Timeout From " << m_prefix;
+ std::cout << " - Ping Reference = " <<
+ interest.getName().getSubName(interest.getName().size()-1).toUri().substr(1);
+ std::cout << std::endl;
+ this->finish();
+ }
+
+ void
+ printPingStatistics()
+ {
+ std::cout << "\n\n=== " << " Ping Statistics For "<< m_prefix <<" ===" << std::endl;
+ std::cout << "Sent=" << m_pingStatistics.m_sentPings;
+ std::cout << ", Received=" << m_pingStatistics.m_receivedPings;
+ double packetLossRate = m_pingStatistics.m_sentPings - m_pingStatistics.m_receivedPings;
+ packetLossRate /= m_pingStatistics.m_sentPings;
+ std::cout << ", Packet Loss=" << packetLossRate * 100.0 << "%";
+ if (m_pingStatistics.m_sentPings != m_pingStatistics.m_receivedPings)
+ m_hasError = true;
+ std::cout << ", Total Time=" << m_pingStatistics.m_averageRoundTripTimeData << " ms\n";
+ if (m_pingStatistics.m_receivedPings > 0) {
+ double averageRoundTripTime =
+ m_pingStatistics.m_averageRoundTripTimeData / m_pingStatistics.m_receivedPings;
+ double standardDeviationRoundTripTime =
+ m_pingStatistics.m_standardDeviationRoundTripTimeData / m_pingStatistics.m_receivedPings;
+ standardDeviationRoundTripTime -= averageRoundTripTime * averageRoundTripTime;
+ standardDeviationRoundTripTime = std::sqrt(standardDeviationRoundTripTime);
+ std::cout << "Round Trip Time (Min/Max/Avg/MDev) = (";
+ std::cout << m_pingStatistics.m_minimumRoundTripTime << "/";
+ std::cout << m_pingStatistics.m_maximumRoundTripTime << "/";
+ std::cout << averageRoundTripTime << "/";
+ std::cout << standardDeviationRoundTripTime << ") ms\n";
+ }
+ std::cout << std::endl;
+ }
+
+ void
+ performPing(boost::asio::deadline_timer* deadlineTimer)
+ {
+ if ((m_totalPings < 0) || (m_pingsSent < m_totalPings))
+ {
+ m_pingsSent++;
+ m_pingStatistics.m_sentPings++;
+
+ //Perform Ping
+ char pingNumberString[20];
+ Name pingPacketName(m_prefix);
+ pingPacketName.append("ping");
+ if(m_clientIdentifier != 0)
+ pingPacketName.append(m_clientIdentifier);
+ std::memset(pingNumberString, 0, 20);
+ if (m_startPingNumber < 0)
+ m_startPingNumber = std::rand();
+ sprintf(pingNumberString, "%lld", static_cast<long long int>(m_startPingNumber));
+ pingPacketName.append(pingNumberString);
+ ndn::Interest interest(pingPacketName);
+ if (m_isAllowCachingSet)
+ interest.setMustBeFresh(false);
+ else
+ interest.setMustBeFresh(true);
+ interest.setInterestLifetime(m_pingTimeoutThreshold);
+ interest.setNonce(m_startPingNumber);
+ m_startPingNumber++;
+ try {
+ m_face.expressInterest(interest,
+ std::bind(&NdnTlvPing::onData, this, _1, _2,
+ time::steady_clock::now()),
+ std::bind(&NdnTlvPing::onTimeout, this, _1));
+ deadlineTimer->expires_at(deadlineTimer->expires_at() +
+ boost::posix_time::millisec(m_pingInterval.count()));
+ deadlineTimer->async_wait(bind(&NdnTlvPing::performPing,
+ this,
+ deadlineTimer));
+ }
+ catch (std::exception& e) {
+ std::cerr << "ERROR: " << e.what() << std::endl;
+ }
+ ++m_outstanding;
+ }
+ else {
+ this->finish();
+ }
+ }
+
+ void
+ finish()
+ {
+ if (--m_outstanding >= 0) {
+ return;
+ }
+ m_face.shutdown();
+ printPingStatistics();
+ m_ioService.stop();
+ }
+
+ void
+ signalHandler()
+ {
+ m_face.shutdown();
+ printPingStatistics();
+ exit(1);
+ }
+
+ void
+ run()
+ {
+ std::cout << "\n=== Pinging " << m_prefix << " ===\n" <<std::endl;
+
+ boost::asio::signal_set signalSet(m_ioService, SIGINT, SIGTERM);
+ signalSet.async_wait(bind(&NdnTlvPing::signalHandler, this));
+
+ boost::asio::deadline_timer deadlineTimer(m_ioService,
+ boost::posix_time::millisec(0));
+
+ deadlineTimer.async_wait(bind(&NdnTlvPing::performPing,
+ this,
+ &deadlineTimer));
+ try {
+ m_face.processEvents();
+ }
+ catch (std::exception& e) {
+ std::cerr << "ERROR: " << e.what() << std::endl;
+ m_hasError = true;
+ m_ioService.stop();
+ }
+ }
+
+private:
+ char* m_programName;
+ bool m_isAllowCachingSet;
+ bool m_isPrintTimestampSet;
+ bool m_hasError;
+ int m_totalPings;
+ int64_t m_startPingNumber;
+ int m_pingsSent;
+ int m_pingsReceived;
+ time::milliseconds m_pingInterval;
+ char* m_clientIdentifier;
+ time::milliseconds m_pingTimeoutThreshold;
+ char* m_prefix;
+ PingStatistics m_pingStatistics;
+ ssize_t m_outstanding;
+
+ boost::asio::io_service m_ioService;
+ Face m_face;
+};
+
+}
+
+
+int
+main(int argc, char* argv[])
+{
+ std::srand(::time(0));
+ int res;
+
+ ndn::NdnTlvPing ndnTlvPing(argv[0]);
+ while ((res = getopt(argc, argv, "htai:c:n:p:")) != -1)
+ {
+ switch (res) {
+ case 'a':
+ ndnTlvPing.setAllowCaching();
+ break;
+ case 'c':
+ ndnTlvPing.setTotalPings(atoi(optarg));
+ break;
+ case 'h':
+ ndnTlvPing.usage();
+ break;
+ case 'i':
+ ndnTlvPing.setPingInterval(atoi(optarg));
+ break;
+ case 'n':
+ try {
+ ndnTlvPing.setStartPingNumber(boost::lexical_cast<int64_t>(optarg));
+ }
+ catch (boost::bad_lexical_cast&) {
+ ndnTlvPing.usage();
+ }
+ break;
+ case 'p':
+ ndnTlvPing.setClientIdentifier(optarg);
+ break;
+ case 't':
+ ndnTlvPing.setPrintTimestamp();
+ break;
+ default:
+ ndnTlvPing.usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argv[0] == 0)
+ ndnTlvPing.usage();
+
+ ndnTlvPing.setPrefix(argv[0]);
+ ndnTlvPing.run();
+
+ std::cout << std::endl;
+
+ if (ndnTlvPing.hasError())
+ return 1;
+ else
+ return 0;
+}
diff --git a/tools/ping/wscript b/tools/ping/wscript
new file mode 100644
index 0000000..406537b
--- /dev/null
+++ b/tools/ping/wscript
@@ -0,0 +1,17 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+top = '../..'
+
+def build(bld):
+ bld.program(
+ features='cxx',
+ target='../../bin/ndnping',
+ source='ndn-ping.cpp',
+ use='core-objects',
+ )
+
+ bld.program(
+ features='cxx',
+ target='../../bin/ndnpingserver',
+ source='ndn-ping-server.cpp',
+ use='core-objects',
+ )