dump: Capture and print network NACK packets

refs: #3463

Change-Id: I3aed0d3668378305404f9713cc110e13eec434e5
diff --git a/tests/dump/ndndump.t.cpp b/tests/dump/ndndump.t.cpp
new file mode 100644
index 0000000..d4b8396
--- /dev/null
+++ b/tests/dump/ndndump.t.cpp
@@ -0,0 +1,247 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016,  University of Memphis.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools is 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-tools 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-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tools/dump/ndndump.hpp"
+
+#include "tests/test-common.hpp"
+
+#include <boost/test/output_test_stream.hpp>
+
+#include <ndn-cxx/lp/packet.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/util/ethernet.hpp>
+
+namespace ndn {
+namespace dump {
+namespace tests {
+
+using namespace ndn::tests;
+
+class StdCoutRedirector
+{
+public:
+  StdCoutRedirector(std::ostream& os)
+  {
+    // Redirect std::cout to the specified output stream
+    originalBuffer = std::cout.rdbuf(os.rdbuf());
+  }
+
+  ~StdCoutRedirector()
+  {
+    // Revert state for std::cout
+    std::cout.rdbuf(originalBuffer);
+  }
+
+private:
+  std::streambuf* originalBuffer;
+};
+
+class NdnDumpFixture
+{
+protected:
+  NdnDumpFixture()
+  {
+    dump.m_dataLinkType = DLT_EN10MB;
+  }
+
+  template <typename Packet>
+  void
+  receive(const Packet& packet)
+  {
+    ndn::EncodingBuffer buffer(packet.wireEncode());
+    receive(buffer);
+  }
+
+  void
+  receive(ndn::EncodingBuffer& buffer)
+  {
+    ndn::util::ethernet::Address host;
+
+    // Ethernet header
+    uint16_t frameType = htons(ndn::util::ethernet::ETHERTYPE_NDN);
+    buffer.prependByteArray(reinterpret_cast<const uint8_t*>(&frameType),
+                            ndn::util::ethernet::TYPE_LEN);
+    buffer.prependByteArray(host.data(), host.size());
+    buffer.prependByteArray(host.data(), host.size());
+
+    pcap_pkthdr header{};
+    header.len = buffer.size();
+
+    {
+      StdCoutRedirector redirect(output);
+      dump.onCapturedPacket(&header, buffer.buf());
+    }
+  }
+
+protected:
+  Ndndump dump;
+  boost::test_tools::output_test_stream output;
+};
+
+BOOST_FIXTURE_TEST_SUITE(NdnDump, NdnDumpFixture)
+
+BOOST_AUTO_TEST_CASE(CaptureInterest)
+{
+  ndn::Interest interest("/test");
+  interest.setNonce(0);
+
+  this->receive(interest);
+
+  const std::string expectedOutput =
+    "0.000000 Tunnel Type: EthernetFrame, INTEREST: /test?ndn.Nonce=0\n";
+
+  BOOST_CHECK(output.is_equal(expectedOutput));
+}
+
+BOOST_AUTO_TEST_CASE(CaptureData)
+{
+  ndn::security::KeyChain keyChain;
+
+  ndn::Data data("/test");
+  keyChain.sign(data);
+
+  this->receive(data);
+
+  const std::string expectedOutput = "0.000000 Tunnel Type: EthernetFrame, DATA: /test\n";
+
+  BOOST_CHECK(output.is_equal(expectedOutput));
+}
+
+BOOST_AUTO_TEST_CASE(CaptureNack)
+{
+  ndn::Interest interest("/test");
+  interest.setNonce(0);
+
+  ndn::lp::Nack nack(interest);
+  nack.setReason(ndn::lp::NackReason::DUPLICATE);
+
+  lp::Packet lpPacket(interest.wireEncode());
+  lpPacket.add<lp::NackField>(nack.getHeader());
+
+  this->receive(lpPacket);
+
+  const std::string expectedOutput =
+    "0.000000 Tunnel Type: EthernetFrame, NACK: Duplicate, /test?ndn.Nonce=0\n";
+
+  BOOST_CHECK(output.is_equal(expectedOutput));
+}
+
+BOOST_AUTO_TEST_CASE(CaptureLpFragment)
+{
+  const uint8_t data[10] = {
+    0x06, 0x08, // Data packet
+        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+  };
+
+  ndn::Buffer buffer(data, 4);
+
+  lp::Packet lpPacket;
+  lpPacket.add<lp::FragmentField>(std::make_pair(buffer.begin(), buffer.end()));
+  lpPacket.add<lp::FragIndexField>(0);
+  lpPacket.add<lp::FragCountField>(2);
+  lpPacket.add<lp::SequenceField>(1000);
+
+  this->receive(lpPacket);
+
+  const std::string expectedOutput =
+    "0.000000 Tunnel Type: EthernetFrame, NDNLPv2-FRAGMENT\n";
+
+  BOOST_CHECK(output.is_equal(expectedOutput));
+}
+
+BOOST_AUTO_TEST_CASE(CaptureIdlePacket)
+{
+  lp::Packet lpPacket;
+
+  this->receive(lpPacket);
+
+  const std::string expectedOutput =
+    "0.000000 Tunnel Type: EthernetFrame, NDNLPv2-IDLE\n";
+
+  BOOST_CHECK(output.is_equal(expectedOutput));
+}
+
+BOOST_AUTO_TEST_CASE(CaptureIncompletePacket)
+{
+  const uint8_t interest[] = {
+  0x05, 0x0E, // Interest
+    0x07, 0x06, // Name
+      0x08, 0x04, // NameComponent
+        0x74, 0x65, 0x73, 0x74,
+    0x0a, 0x04, // Nonce
+      0x00, 0x00, 0x00, 0x01
+  };
+
+  EncodingBuffer buffer;
+  buffer.prependByteArray(interest, 4);
+
+  this->receive(buffer);
+
+  const std::string expectedOutput =
+    "0.000000 Tunnel Type: EthernetFrame, INCOMPLETE-PACKET, size: 4\n";
+
+  BOOST_CHECK(output.is_equal(expectedOutput));
+}
+
+BOOST_AUTO_TEST_CASE(CaptureUnknownNetworkPacket)
+{
+  EncodingBuffer buffer(ndn::encoding::makeEmptyBlock(tlv::Name));
+
+  this->receive(buffer);
+
+  const std::string expectedOutput =
+    "0.000000 Tunnel Type: EthernetFrame, UNKNOWN-NETWORK-PACKET\n";
+
+  BOOST_CHECK(output.is_equal(expectedOutput));
+}
+
+BOOST_AUTO_TEST_CASE(DumpPcapTrace)
+{
+  dump.inputFile = "tests/dump/nack.pcap";
+  dump.pcapProgram = "";
+
+  {
+    StdCoutRedirector redirect(output);
+    dump.run();
+  }
+
+  const std::string expectedOutput =
+    "1456768916.467099 From: 1.0.0.1, To: 1.0.0.2, Tunnel Type: UDP, INTEREST: /producer/nack/congestion?ndn.MustBeFresh=1&ndn.Nonce=2581361680\n"
+    "1456768916.567099 From: 1.0.0.1, To: 1.0.0.2, Tunnel Type: UDP, INTEREST: /producer/nack/duplicate?ndn.MustBeFresh=1&ndn.Nonce=4138343109\n"
+    "1456768916.667099 From: 1.0.0.1, To: 1.0.0.2, Tunnel Type: UDP, INTEREST: /producer/nack/no-reason?ndn.MustBeFresh=1&ndn.Nonce=4034910304\n"
+    "1456768916.767099 From: 1.0.0.2, To: 1.0.0.1, Tunnel Type: UDP, NACK: Congestion, /producer/nack/congestion?ndn.MustBeFresh=1&ndn.Nonce=2581361680\n"
+    "1456768916.867099 From: 1.0.0.2, To: 1.0.0.1, Tunnel Type: UDP, NACK: Duplicate, /producer/nack/duplicate?ndn.MustBeFresh=1&ndn.Nonce=4138343109\n"
+    "1456768916.967099 From: 1.0.0.2, To: 1.0.0.1, Tunnel Type: UDP, NACK: None, /producer/nack/no-reason?ndn.MustBeFresh=1&ndn.Nonce=4034910304\n"
+    "1456768917.067099 From: 1.0.0.1, To: 1.0.0.2, Tunnel Type: TCP, INTEREST: /producer/nack/congestion?ndn.MustBeFresh=1&ndn.Nonce=3192497423\n"
+    "1456768917.267099 From: 1.0.0.2, To: 1.0.0.1, Tunnel Type: TCP, NACK: Congestion, /producer/nack/congestion?ndn.MustBeFresh=1&ndn.Nonce=3192497423\n"
+    "1456768917.367099 From: 1.0.0.1, To: 1.0.0.2, Tunnel Type: TCP, INTEREST: /producer/nack/duplicate?ndn.MustBeFresh=1&ndn.Nonce=522390724\n"
+    "1456768917.567099 From: 1.0.0.2, To: 1.0.0.1, Tunnel Type: TCP, NACK: Duplicate, /producer/nack/duplicate?ndn.MustBeFresh=1&ndn.Nonce=522390724\n"
+    "1456768917.767099 From: 1.0.0.2, To: 1.0.0.1, Tunnel Type: TCP, NACK: None, /producer/nack/no-reason?ndn.MustBeFresh=1&ndn.Nonce=2002441365\n"
+    "1456768917.967099 From: 1.0.0.1, To: 1.0.0.2, Tunnel Type: TCP, INTEREST: /producer/nack/no-reason?ndn.MustBeFresh=1&ndn.Nonce=3776824408\n"
+    "1456768918.067099 From: 1.0.0.2, To: 1.0.0.1, Tunnel Type: TCP, NACK: None, /producer/nack/no-reason?ndn.MustBeFresh=1&ndn.Nonce=3776824408\n";
+
+  BOOST_CHECK(output.is_equal(expectedOutput));
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace dump
+} // namespace ndn