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',
+        )