ndnping: recognize and trace Nack

Change-Id: If47877892c75ae0849375f36430a66e02fb7a608
refs: #3335
diff --git a/tests/ping/client/ping.t.cpp b/tests/ping/client/ping.t.cpp
index 9f9eeb9..e39c65a 100644
--- a/tests/ping/client/ping.t.cpp
+++ b/tests/ping/client/ping.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Arizona Board of Regents.
+ * Copyright (c) 2014-2016,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -31,86 +31,64 @@
 
 BOOST_AUTO_TEST_SUITE(PingClientPing)
 
-class SequenceNumberIncrementFixture : public UnitTestTimeFixture
+BOOST_FIXTURE_TEST_CASE(Basic, UnitTestTimeFixture)
 {
-protected:
-  SequenceNumberIncrementFixture()
-    : face(util::makeDummyClientFace(io, {false, true}))
-    , pingOptions(makeOptions())
-    , ping(*face, pingOptions)
-    , numPings(0)
-    , lastPingSeq(pingOptions.startSeq)
-  {
-    ping.afterResponse.connect(bind(&SequenceNumberIncrementFixture::onResponse, this, _1));
-    ping.afterTimeout.connect(bind(&SequenceNumberIncrementFixture::onTimeout, this, _1));
-  }
-
-public:
-  void
-  onResponse(uint64_t seq)
-  {
-    numPings++;
-    lastPingSeq = seq;
-    if (numPings == maxPings) {
-      face->shutdown();
-      io.stop();
-    }
-  }
-
-  void
-  onTimeout(uint64_t seq)
-  {
-    numPings++;
-    lastPingSeq = seq;
-    if (numPings == maxPings) {
-      face->shutdown();
-      io.stop();
-    }
-  }
-
-private:
-  static Options
-  makeOptions()
-  {
-    Options opt;
-    opt.prefix = "ndn:/test-prefix";
-    opt.shouldAllowStaleData = false;
-    opt.shouldGenerateRandomSeq = false;
-    opt.shouldPrintTimestamp = false;
-    opt.nPings = 4;
-    opt.interval = time::milliseconds(100);
-    opt.timeout = time::milliseconds(1000);
-    opt.startSeq = 1000;
-    return opt;
-  }
-
-protected:
-  boost::asio::io_service io;
-  shared_ptr<util::DummyClientFace> face;
   Options pingOptions;
-  Ping ping;
-  uint32_t numPings;
-  uint32_t maxPings;
-  uint64_t lastPingSeq;
-  KeyChain keyChain;
-};
+  pingOptions.prefix = "ndn:/test-prefix";
+  pingOptions.shouldAllowStaleData = false;
+  pingOptions.shouldGenerateRandomSeq = false;
+  pingOptions.shouldPrintTimestamp = false;
+  pingOptions.nPings = 4;
+  pingOptions.interval = time::milliseconds(100);
+  pingOptions.timeout = time::milliseconds(2000);
+  pingOptions.startSeq = 1000;
 
-BOOST_FIXTURE_TEST_CASE(SequenceNumberIncrement, SequenceNumberIncrementFixture)
-{
-  maxPings = 4;
+  boost::asio::io_service io;
+  util::DummyClientFace face(io, {true, true});
+  Ping ping(face, pingOptions);
+
+  int nFinishSignals = 0;
+  std::vector<uint64_t> dataSeqs;
+  std::vector<uint64_t> nackSeqs;
+  std::vector<uint64_t> timeoutSeqs;
+
+  ping.afterData.connect(bind([&] (uint64_t seq) { dataSeqs.push_back(seq); }, _1));
+  ping.afterNack.connect(bind([&] (uint64_t seq) { nackSeqs.push_back(seq); }, _1));
+  ping.afterTimeout.connect(bind([&] (uint64_t seq) { timeoutSeqs.push_back(seq); }, _1));
+  ping.afterFinish.connect(bind([&] {
+    BOOST_REQUIRE_EQUAL(dataSeqs.size(), 2);
+    BOOST_REQUIRE_EQUAL(nackSeqs.size(), 1);
+    BOOST_REQUIRE_EQUAL(timeoutSeqs.size(), 1);
+
+    BOOST_CHECK_EQUAL(dataSeqs[0], 1000);
+    BOOST_CHECK_EQUAL(nackSeqs[0], 1001);
+    BOOST_CHECK_EQUAL(dataSeqs[1], 1002);
+    BOOST_CHECK_EQUAL(timeoutSeqs[0], 1003);
+
+    nFinishSignals++;
+  }));
+
   ping.start();
 
-  this->advanceClocks(io, time::milliseconds(1), 400);
+  this->advanceClocks(io, time::milliseconds(1), 500);
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 4);
 
-  face->receive(*makeData("ndn:/test-prefix/ping/1000"));
-  face->receive(*makeData("ndn:/test-prefix/ping/1001"));
-  face->receive(*makeData("ndn:/test-prefix/ping/1002"));
-  face->receive(*makeData("ndn:/test-prefix/ping/1003"));
+  face.receive(*makeData("ndn:/test-prefix/ping/1000"));
 
-  io.run();
+  lp::Nack nack(face.sentInterests[1]);
+  nack.setReason(lp::NackReason::DUPLICATE);
+  face.receive(nack);
 
-  BOOST_REQUIRE_EQUAL(1003, lastPingSeq);
-  BOOST_REQUIRE_EQUAL(4, numPings);
+  face.receive(*makeData("ndn:/test-prefix/ping/1002"));
+
+  this->advanceClocks(io, time::milliseconds(100), 20);
+
+  // ndn:/test-prefix/ping/1003 is unanswered and will timeout
+
+  BOOST_CHECK_EQUAL(nFinishSignals, 1);
+
+  face.shutdown();
+  io.stop();
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/ping/client/statistics-collector.t.cpp b/tests/ping/client/statistics-collector.t.cpp
index 5dcf941..a1cc177 100644
--- a/tests/ping/client/statistics-collector.t.cpp
+++ b/tests/ping/client/statistics-collector.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Arizona Board of Regents.
+ * Copyright (c) 2014-2016,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -67,7 +67,7 @@
 
 BOOST_AUTO_TEST_CASE(Resp50msResp50ms)
 {
-  sc.recordResponse(time::milliseconds(50));
+  sc.recordData(time::milliseconds(50));
 
   Statistics stats1 = sc.computeStatistics();
   BOOST_CHECK_EQUAL(stats1.prefix, pingOptions.prefix);
@@ -80,7 +80,7 @@
   BOOST_CHECK_CLOSE(stats1.avgRtt, 50.0, 0.001);
   BOOST_CHECK_CLOSE(stats1.stdDevRtt, 0.0, 0.001);
 
-  sc.recordResponse(time::milliseconds(50));
+  sc.recordData(time::milliseconds(50));
 
   Statistics stats2 = sc.computeStatistics();
   BOOST_CHECK_EQUAL(stats2.prefix, pingOptions.prefix);
@@ -96,8 +96,8 @@
 
 BOOST_AUTO_TEST_CASE(Resp50msResp100ms)
 {
-  sc.recordResponse(time::milliseconds(50));
-  sc.recordResponse(time::milliseconds(100));
+  sc.recordData(time::milliseconds(50));
+  sc.recordData(time::milliseconds(100));
 
   Statistics stats = sc.computeStatistics();
   BOOST_CHECK_EQUAL(stats.prefix, pingOptions.prefix);
@@ -125,7 +125,7 @@
 
 BOOST_AUTO_TEST_CASE(Resp50msLoss)
 {
-  sc.recordResponse(time::milliseconds(50));
+  sc.recordData(time::milliseconds(50));
   sc.recordTimeout();
 
   Statistics stats = sc.computeStatistics();
@@ -140,6 +140,51 @@
   BOOST_CHECK_CLOSE(stats.stdDevRtt, 0.0, 0.001);
 }
 
+BOOST_AUTO_TEST_CASE(NackNack)
+{
+  sc.recordNack();
+  sc.recordNack();
+
+  Statistics stats = sc.computeStatistics();
+  BOOST_CHECK_EQUAL(stats.prefix, pingOptions.prefix);
+  BOOST_CHECK_EQUAL(stats.nSent, 2);
+  BOOST_CHECK_EQUAL(stats.nNacked, 2);
+  BOOST_CHECK_EQUAL(stats.nReceived, 0);
+  BOOST_CHECK_CLOSE(stats.packetNackedRate, 1.0, 0.001);
+}
+
+BOOST_AUTO_TEST_CASE(Resp50msNack)
+{
+  sc.recordData(time::milliseconds(50));
+  sc.recordNack();
+
+  Statistics stats = sc.computeStatistics();
+  BOOST_CHECK_EQUAL(stats.prefix, pingOptions.prefix);
+  BOOST_CHECK_EQUAL(stats.nSent, 2);
+  BOOST_CHECK_EQUAL(stats.nReceived, 1);
+  BOOST_CHECK_CLOSE(stats.minRtt, 50.0, 0.001);
+  BOOST_CHECK_CLOSE(stats.maxRtt, 50.0, 0.001);
+  BOOST_CHECK_CLOSE(stats.sumRtt, 50.0, 0.001);
+  BOOST_CHECK_CLOSE(stats.avgRtt, 50.0, 0.001);
+  BOOST_CHECK_CLOSE(stats.stdDevRtt, 0.0, 0.001);
+  BOOST_CHECK_EQUAL(stats.nNacked, 1);
+  BOOST_CHECK_CLOSE(stats.packetNackedRate, 0.5, 0.001);
+}
+
+BOOST_AUTO_TEST_CASE(NackLoss)
+{
+  sc.recordNack();
+  sc.recordTimeout();
+
+  Statistics stats = sc.computeStatistics();
+  BOOST_CHECK_EQUAL(stats.prefix, pingOptions.prefix);
+  BOOST_CHECK_EQUAL(stats.nSent, 2);
+  BOOST_CHECK_EQUAL(stats.nReceived, 0);
+  BOOST_CHECK_EQUAL(stats.nNacked, 1);
+  BOOST_CHECK_CLOSE(stats.packetNackedRate, 0.5, 0.001);
+  BOOST_CHECK_CLOSE(stats.packetLossRate, 0.5, 0.001);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests
diff --git a/tests/ping/integrated.t.cpp b/tests/ping/integrated.t.cpp
index b8e2164..aec1896 100644
--- a/tests/ping/integrated.t.cpp
+++ b/tests/ping/integrated.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Arizona Board of Regents.
+ * Copyright (c) 2014-2016,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -36,7 +36,6 @@
   PingIntegratedFixture()
     : serverFace(util::makeDummyClientFace(io, {false, true}))
     , clientFace(util::makeDummyClientFace(io, {false, true}))
-    , numResponses(0)
     , wantLoss(false)
   {
     serverFace->onSendInterest.connect([this] (const Interest& interest) {
@@ -53,24 +52,11 @@
     });
   }
 
-  void onTimeout(uint64_t seq)
+  void onFinish()
   {
-    numResponses++;
-    if (numResponses == maxResponses) {
-      serverFace->shutdown();
-      clientFace->shutdown();
-      io.stop();
-    }
-  }
-
-  void onData(uint64_t seq)
-  {
-    numResponses++;
-    if (numResponses == maxResponses) {
-      serverFace->shutdown();
-      clientFace->shutdown();
-      io.stop();
-    }
+    serverFace->shutdown();
+    clientFace->shutdown();
+    io.stop();
   }
 
 public:
@@ -79,8 +65,6 @@
   shared_ptr<util::DummyClientFace> clientFace;
   std::unique_ptr<server::PingServer> server;
   std::unique_ptr<client::Ping> client;
-  int maxResponses;
-  int numResponses;
   bool wantLoss;
 };
 
@@ -108,9 +92,7 @@
   clientOpts.timeout = time::milliseconds(2000);
   clientOpts.startSeq = 1000;
   client.reset(new client::Ping(*clientFace, clientOpts));
-  client->afterResponse.connect(bind(&PingIntegratedFixture::onData, this, _1));
-  client->afterTimeout.connect(bind(&PingIntegratedFixture::onTimeout, this, _1));
-  maxResponses = 4;
+  client->afterFinish.connect(bind(&PingIntegratedFixture::onFinish, this));
   client->start();
 
   this->advanceClocks(io, time::milliseconds(1), 400);
@@ -143,10 +125,7 @@
   clientOpts.timeout = time::milliseconds(500);
   clientOpts.startSeq = 1000;
   client.reset(new client::Ping(*clientFace, clientOpts));
-  numResponses = 0;
-  maxResponses = 4;
-  client->afterResponse.connect(bind(&PingIntegratedFixture::onData, this, _1));
-  client->afterTimeout.connect(bind(&PingIntegratedFixture::onTimeout, this, _1));
+  client->afterFinish.connect(bind(&PingIntegratedFixture::onFinish, this));
   client->start();
 
   this->advanceClocks(io, time::milliseconds(1), 1000);
diff --git a/tools/ping/client/ping.cpp b/tools/ping/client/ping.cpp
index b60be65..966d29e 100644
--- a/tools/ping/client/ping.cpp
+++ b/tools/ping/client/ping.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Arizona Board of Regents.
+ * Copyright (c) 2014-2016,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -18,6 +18,7 @@
  *
  * @author: Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
  * @author: Eric Newberry <enewberry@email.arizona.edu>
+ * @author: Teng Liang <philoliang@email.arizona.edu>
  */
 
 #include "ping.hpp"
@@ -63,8 +64,10 @@
   interest.setMustBeFresh(!m_options.shouldAllowStaleData);
   interest.setInterestLifetime(m_options.timeout);
 
+  auto now = time::steady_clock::now();
   m_face.expressInterest(interest,
-                         bind(&Ping::onData, this, _1, _2, m_nextSeq, time::steady_clock::now()),
+                         bind(&Ping::onData, this, _1, _2, m_nextSeq, now),
+                         bind(&Ping::onNack, this, _1, _2, m_nextSeq, now),
                          bind(&Ping::onTimeout, this, _1, m_nextSeq));
 
   ++m_nSent;
@@ -80,11 +83,27 @@
 }
 
 void
-Ping::onData(const Interest& interest, Data& data, uint64_t seq, const time::steady_clock::TimePoint& sendTime)
+Ping::onData(const Interest& interest,
+             const Data& data,
+             uint64_t seq,
+             const time::steady_clock::TimePoint& sendTime)
 {
   time::nanoseconds rtt = time::steady_clock::now() - sendTime;
 
-  afterResponse(seq, rtt);
+  afterData(seq, rtt);
+
+  finish();
+}
+
+void
+Ping::onNack(const Interest& interest,
+             const lp::Nack& nack,
+             uint64_t seq,
+             const time::steady_clock::TimePoint& sendTime)
+{
+  time::nanoseconds rtt = time::steady_clock::now() - sendTime;
+
+  afterNack(seq, rtt, nack.getHeader());
 
   finish();
 }
diff --git a/tools/ping/client/ping.hpp b/tools/ping/client/ping.hpp
index 49ac492..ce4119d 100644
--- a/tools/ping/client/ping.hpp
+++ b/tools/ping/client/ping.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2015,  Arizona Board of Regents.
+ * Copyright (c) 2015-2016,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -18,6 +18,7 @@
  *
  * @author: Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
  * @author: Eric Newberry <enewberry@email.arizona.edu>
+ * @author: Teng Liang <philoliang@email.arizona.edu>
  */
 
 #ifndef NDN_TOOLS_PING_CLIENT_PING_HPP
@@ -56,12 +57,21 @@
   Ping(Face& face, const Options& options);
 
   /**
-   * @brief Signals on the successful return of a packet
+   * @brief Signals on the successful return of a Data packet
    *
    * @param seq ping sequence number
    * @param rtt round trip time
    */
-  signal::Signal<Ping, uint64_t, Rtt> afterResponse;
+  signal::Signal<Ping, uint64_t, Rtt> afterData;
+
+  /**
+   * @brief Signals on the return of a Nack
+   *
+   * @param seq ping sequence number
+   * @param rtt round trip time
+   * @param header the received Network NACK header
+   */
+  signal::Signal<Ping, uint64_t, Rtt, lp::NackHeader> afterNack;
 
   /**
    * @brief Signals on timeout of a packet
@@ -109,7 +119,7 @@
   performPing();
 
   /**
-   * @brief Called when ping returned successfully
+   * @brief Called when a Data packet is received in response to a ping
    *
    * @param interest NDN interest
    * @param data returned data
@@ -117,7 +127,24 @@
    * @param sendTime time ping sent
    */
   void
-  onData(const Interest& interest, Data& data, uint64_t seq, const time::steady_clock::TimePoint& sendTime);
+  onData(const Interest& interest,
+         const Data& data,
+         uint64_t seq,
+         const time::steady_clock::TimePoint& sendTime);
+
+  /**
+   * @brief Called when a Nack is received in response to a ping
+   *
+   * @param interest NDN interest
+   * @param nack returned nack
+   * @param seq ping sequence number
+   * @param sendTime time ping sent
+   */
+  void
+  onNack(const Interest& interest,
+         const lp::Nack& nack,
+         uint64_t seq,
+         const time::steady_clock::TimePoint& sendTime);
 
   /**
    * @brief Called when ping timed out
diff --git a/tools/ping/client/statistics-collector.cpp b/tools/ping/client/statistics-collector.cpp
index bc1956e..0e7ad2a 100644
--- a/tools/ping/client/statistics-collector.cpp
+++ b/tools/ping/client/statistics-collector.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2015,  Arizona Board of Regents.
+ * Copyright (c) 2015-2016,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -18,6 +18,7 @@
  *
  * @author: Eric Newberry <enewberry@email.arizona.edu>
  * @author: Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
+ * @author: Teng Liang <philoliang@email.arizona.edu>
  */
 
 #include "statistics-collector.hpp"
@@ -31,18 +32,20 @@
   , m_options(options)
   , m_nSent(0)
   , m_nReceived(0)
+  , m_nNacked(0)
   , m_pingStartTime(time::steady_clock::now())
   , m_minRtt(std::numeric_limits<double>::max())
   , m_maxRtt(0.0)
   , m_sumRtt(0.0)
   , m_sumRttSquared(0.0)
 {
-  m_ping.afterResponse.connect(bind(&StatisticsCollector::recordResponse, this, _2));
+  m_ping.afterData.connect(bind(&StatisticsCollector::recordData, this, _2));
+  m_ping.afterNack.connect(bind(&StatisticsCollector::recordNack, this));
   m_ping.afterTimeout.connect(bind(&StatisticsCollector::recordTimeout, this));
 }
 
 void
-StatisticsCollector::recordResponse(Rtt rtt)
+StatisticsCollector::recordData(Rtt rtt)
 {
   m_nSent++;
   m_nReceived++;
@@ -58,6 +61,13 @@
 }
 
 void
+StatisticsCollector::recordNack()
+{
+  m_nSent++;
+  m_nNacked++;
+}
+
+void
 StatisticsCollector::recordTimeout()
 {
   m_nSent++;
@@ -71,10 +81,12 @@
   statistics.prefix = m_options.prefix;
   statistics.nSent = m_nSent;
   statistics.nReceived = m_nReceived;
+  statistics.nNacked = m_nNacked;
   statistics.pingStartTime = m_pingStartTime;
   statistics.minRtt = m_minRtt;
   statistics.maxRtt = m_maxRtt;
-  statistics.packetLossRate = (double)(m_nSent - m_nReceived) / (double)m_nSent;
+  statistics.packetLossRate = static_cast<double>(m_nSent - m_nReceived - m_nNacked) / static_cast<double>(m_nSent);
+  statistics.packetNackedRate = static_cast<double>(m_nNacked) / static_cast<double>(m_nSent);
   statistics.sumRtt = m_sumRtt;
   statistics.avgRtt = m_sumRtt / m_nReceived;
   statistics.stdDevRtt = std::sqrt((m_sumRttSquared / m_nReceived) - (statistics.avgRtt * statistics.avgRtt));
@@ -99,7 +111,9 @@
   os << "--- " << statistics.prefix <<" ping statistics ---\n";
   os << statistics.nSent << " packets transmitted";
   os << ", " << statistics.nReceived << " received";
+  os << ", " << statistics.nNacked << " nacked";
   os << ", " << statistics.packetLossRate * 100.0 << "% packet loss";
+  os << ", " << statistics.packetNackedRate * 100.0 << "% nacked";
   os << ", time " << statistics.sumRtt << " ms";
   if (statistics.nReceived > 0) {
     os << "\n";
diff --git a/tools/ping/client/statistics-collector.hpp b/tools/ping/client/statistics-collector.hpp
index 2ced820..ee67f80 100644
--- a/tools/ping/client/statistics-collector.hpp
+++ b/tools/ping/client/statistics-collector.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2015,  Arizona Board of Regents.
+ * Copyright (c) 2015-2016,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -18,6 +18,7 @@
  *
  * @author: Eric Newberry <enewberry@email.arizona.edu>
  * @author: Jerald Paul Abraham <jeraldabraham@email.arizona.edu>
+ * @author: Teng Liang <philoliang@email.arizona.edu>
  */
 
 #ifndef NDN_TOOLS_PING_CLIENT_STATISTICS_COLLECTOR_HPP
@@ -39,10 +40,12 @@
   Name prefix;                                  //!< prefix pinged
   int nSent;                                    //!< number of pings sent
   int nReceived;                                //!< number of pings received
+  int nNacked;                                  //!< number of nacks received
   time::steady_clock::TimePoint pingStartTime;  //!< time pings started
   double minRtt;                                //!< minimum round trip time
   double maxRtt;                                //!< maximum round trip time
   double packetLossRate;                        //!< packet loss rate
+  double packetNackedRate;                      //!< packet nacked rate
   double sumRtt;                                //!< sum of round trip times
   double avgRtt;                                //!< average round trip time
   double stdDevRtt;                             //!< std dev of round trip time
@@ -71,12 +74,18 @@
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   /**
-   * @brief Called on ping response received
+   * @brief Called when a Data packet is received
    *
    * @param rtt round trip time
    */
   void
-  recordResponse(Rtt rtt);
+  recordData(Rtt rtt);
+
+  /**
+   * @brief Called when a Nack is received
+   */
+  void
+  recordNack();
 
   /**
    * @brief Called on ping timeout
@@ -89,6 +98,7 @@
   const Options& m_options;
   int m_nSent;
   int m_nReceived;
+  int m_nNacked;
   time::steady_clock::TimePoint m_pingStartTime;
   double m_minRtt;
   double m_maxRtt;
diff --git a/tools/ping/client/tracer.cpp b/tools/ping/client/tracer.cpp
index c521b20..9510af8 100644
--- a/tools/ping/client/tracer.cpp
+++ b/tools/ping/client/tracer.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2015,  Arizona Board of Regents.
+ * Copyright (c) 2015-2016,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -17,6 +17,7 @@
  * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  *
  * @author: Eric Newberry <enewberry@email.arizona.edu>
+ * @author: Teng Liang <philoliang@email.arizona.edu>
  */
 
 #include "tracer.hpp"
@@ -28,12 +29,13 @@
 Tracer::Tracer(Ping& ping, const Options& options)
   : m_options(options)
 {
-  ping.afterResponse.connect(bind(&Tracer::onResponse, this, _1, _2));
+  ping.afterData.connect(bind(&Tracer::onData, this, _1, _2));
+  ping.afterNack.connect(bind(&Tracer::onNack, this, _1, _2, _3));
   ping.afterTimeout.connect(bind(&Tracer::onTimeout, this, _1));
 }
 
 void
-Tracer::onResponse(uint64_t seq, Rtt rtt)
+Tracer::onData(uint64_t seq, Rtt rtt)
 {
   if (m_options.shouldPrintTimestamp) {
     std::cout << time::toIsoString(time::system_clock::now()) << " - ";
@@ -44,6 +46,17 @@
 }
 
 void
+Tracer::onNack(uint64_t seq, Rtt rtt, const lp::NackHeader& header)
+{
+  if (m_options.shouldPrintTimestamp) {
+    std::cout << time::toIsoString(time::system_clock::now()) << " - ";
+  }
+
+  std::cout << "nack from " << m_options.prefix << ": seq=" << seq << " time="
+            << rtt.count() << " ms" << " reason=" << header.getReason() << std::endl;
+}
+
+void
 Tracer::onTimeout(uint64_t seq)
 {
   if (m_options.shouldPrintTimestamp) {
diff --git a/tools/ping/client/tracer.hpp b/tools/ping/client/tracer.hpp
index f62b9b8..ca75fb5 100644
--- a/tools/ping/client/tracer.hpp
+++ b/tools/ping/client/tracer.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2015,  Arizona Board of Regents.
+ * Copyright (c) 2015-2016,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -17,6 +17,7 @@
  * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  *
  * @author: Eric Newberry <enewberry@email.arizona.edu>
+ * @author: Teng Liang <philoliang@email.arizona.edu>
  */
 
 #ifndef NDN_TOOLS_PING_CLIENT_TRACER_HPP
@@ -43,13 +44,23 @@
   Tracer(Ping& ping, const Options& options);
 
   /**
-   * @brief Prints ping results when response received
+   * @brief Prints ping results when a Data packet is received
    *
    * @param seq ping sequence number
    * @param rtt round trip time
    */
   void
-  onResponse(uint64_t seq, Rtt rtt);
+  onData(uint64_t seq, Rtt rtt);
+
+  /**
+   * @brief Prints NackReason when a Nack is received
+   *
+   * @param seq ping sequence number
+   * @param rtt round trip time
+   * @param header the header of Nack
+   */
+  void
+  onNack(uint64_t seq, Rtt rtt, const lp::NackHeader& header);
 
   /**
    * @brief Prints ping results when timed out