face+fw: store and return PitToken from downstream

refs #4532

Change-Id: I97b95232a242186601459622f17983d1bef806d6
diff --git a/core/common.hpp b/core/common.hpp
index efc9856..4280dc7 100644
--- a/core/common.hpp
+++ b/core/common.hpp
@@ -124,6 +124,7 @@
 namespace signal = ndn::util::signal;
 namespace time = ndn::time;
 using namespace ndn::time_literals;
+using ndn::operator""_block;
 
 } // namespace nfd
 
diff --git a/daemon/face/generic-link-service.cpp b/daemon/face/generic-link-service.cpp
index 5a3664c..2ab9f7e 100644
--- a/daemon/face/generic-link-service.cpp
+++ b/daemon/face/generic-link-service.cpp
@@ -25,6 +25,7 @@
 
 #include "generic-link-service.hpp"
 
+#include <ndn-cxx/lp/pit-token.hpp>
 #include <ndn-cxx/lp/tags.hpp>
 
 #include <cmath>
@@ -129,28 +130,33 @@
 GenericLinkService::encodeLpFields(const ndn::PacketBase& netPkt, lp::Packet& lpPacket)
 {
   if (m_options.allowLocalFields) {
-    shared_ptr<lp::IncomingFaceIdTag> incomingFaceIdTag = netPkt.getTag<lp::IncomingFaceIdTag>();
+    auto incomingFaceIdTag = netPkt.getTag<lp::IncomingFaceIdTag>();
     if (incomingFaceIdTag != nullptr) {
       lpPacket.add<lp::IncomingFaceIdField>(*incomingFaceIdTag);
     }
   }
 
-  shared_ptr<lp::CongestionMarkTag> congestionMarkTag = netPkt.getTag<lp::CongestionMarkTag>();
+  auto congestionMarkTag = netPkt.getTag<lp::CongestionMarkTag>();
   if (congestionMarkTag != nullptr) {
     lpPacket.add<lp::CongestionMarkField>(*congestionMarkTag);
   }
 
   if (m_options.allowSelfLearning) {
-    shared_ptr<lp::NonDiscoveryTag> nonDiscoveryTag = netPkt.getTag<lp::NonDiscoveryTag>();
+    auto nonDiscoveryTag = netPkt.getTag<lp::NonDiscoveryTag>();
     if (nonDiscoveryTag != nullptr) {
       lpPacket.add<lp::NonDiscoveryField>(*nonDiscoveryTag);
     }
 
-    shared_ptr<lp::PrefixAnnouncementTag> prefixAnnouncementTag = netPkt.getTag<lp::PrefixAnnouncementTag>();
+    auto prefixAnnouncementTag = netPkt.getTag<lp::PrefixAnnouncementTag>();
     if (prefixAnnouncementTag != nullptr) {
       lpPacket.add<lp::PrefixAnnouncementField>(*prefixAnnouncementTag);
     }
   }
+
+  auto pitToken = netPkt.getTag<lp::PitToken>();
+  if (pitToken != nullptr) {
+    lpPacket.add<lp::PitTokenField>(*pitToken);
+  }
 }
 
 void
@@ -388,6 +394,10 @@
     return;
   }
 
+  if (firstPkt.has<lp::PitTokenField>()) {
+    interest->setTag(make_shared<lp::PitToken>(firstPkt.get<lp::PitTokenField>()));
+  }
+
   this->receiveInterest(*interest, endpointId);
 }
 
diff --git a/daemon/fw/strategy.cpp b/daemon/fw/strategy.cpp
index b4ccda8..b0b9362 100644
--- a/daemon/fw/strategy.cpp
+++ b/daemon/fw/strategy.cpp
@@ -27,6 +27,8 @@
 #include "forwarder.hpp"
 #include "common/logger.hpp"
 
+#include <ndn-cxx/lp/pit-token.hpp>
+
 #include <boost/range/adaptor/map.hpp>
 #include <boost/range/algorithm/copy.hpp>
 
@@ -192,15 +194,40 @@
 }
 
 void
+Strategy::sendInterest(const shared_ptr<pit::Entry>& pitEntry,
+                       const FaceEndpoint& egress, const Interest& interest)
+{
+  if (interest.getTag<lp::PitToken>() != nullptr) {
+    Interest interest2 = interest; // make a copy to preserve tag on original packet
+    interest2.removeTag<lp::PitToken>();
+    m_forwarder.onOutgoingInterest(pitEntry, egress, interest2);
+    return;
+  }
+  m_forwarder.onOutgoingInterest(pitEntry, egress, interest);
+}
+
+void
 Strategy::sendData(const shared_ptr<pit::Entry>& pitEntry, const Data& data,
                    const FaceEndpoint& egress)
 {
   BOOST_ASSERT(pitEntry->getInterest().matchesData(data));
 
+  shared_ptr<lp::PitToken> pitToken;
+  auto inRecord = pitEntry->getInRecord(egress.face);
+  if (inRecord != pitEntry->in_end()) {
+    pitToken = inRecord->getInterest().getTag<lp::PitToken>();
+  }
+
   // delete the PIT entry's in-record based on egress,
   // since Data is sent to face and endpoint from which the Interest was received
   pitEntry->deleteInRecord(egress.face);
 
+  if (pitToken != nullptr) {
+    Data data2 = data; // make a copy so each downstream can get a different PIT token
+    data2.setTag(pitToken);
+    m_forwarder.onOutgoingData(data2, egress);
+    return;
+  }
   m_forwarder.onOutgoingData(data, egress);
 }
 
diff --git a/daemon/fw/strategy.hpp b/daemon/fw/strategy.hpp
index cc1b2b8..8c62329 100644
--- a/daemon/fw/strategy.hpp
+++ b/daemon/fw/strategy.hpp
@@ -241,10 +241,7 @@
    */
   VIRTUAL_WITH_TESTS void
   sendInterest(const shared_ptr<pit::Entry>& pitEntry,
-               const FaceEndpoint& egress, const Interest& interest)
-  {
-    m_forwarder.onOutgoingInterest(pitEntry, egress, interest);
-  }
+               const FaceEndpoint& egress, const Interest& interest);
 
   /** \brief send \p data to \p egress
    *  \param pitEntry PIT entry
diff --git a/tests/daemon/fw/pit-token.t.cpp b/tests/daemon/fw/pit-token.t.cpp
new file mode 100644
index 0000000..aa735ed
--- /dev/null
+++ b/tests/daemon/fw/pit-token.t.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tests/daemon/global-io-fixture.hpp"
+#include "topology-tester.hpp"
+
+#include <ndn-cxx/lp/packet.hpp>
+#include <ndn-cxx/lp/pit-token.hpp>
+
+namespace nfd {
+namespace fw {
+namespace tests {
+
+using namespace nfd::tests;
+
+BOOST_AUTO_TEST_SUITE(Fw)
+BOOST_AUTO_TEST_SUITE(TestPitToken)
+
+// Downstream requires PIT token.
+BOOST_FIXTURE_TEST_CASE(Downstream, GlobalIoTimeFixture)
+{
+  TopologyTester topo;
+  TopologyNode nodeR = topo.addForwarder("R");
+  auto linkC = topo.addBareLink("C", nodeR, ndn::nfd::FACE_SCOPE_NON_LOCAL);
+  auto linkS = topo.addBareLink("S", nodeR, ndn::nfd::FACE_SCOPE_NON_LOCAL);
+  topo.registerPrefix(nodeR, linkS->getForwarderFace(), "/U", 5);
+  // Client --- Router --- Server
+  // Client requires PIT token; Router supports PIT token; Server disallows PIT token.
+
+  // C sends Interest /U/0 with PIT token
+  lp::Packet lppI("6414 pit-token=6206A0A1A2A3A4A5 payload=500A interest=0508 0706080155080130"_block);
+  lp::PitToken tokenI(lppI.get<lp::PitTokenField>());
+  linkC->receivePacket(lppI.wireEncode());
+  advanceClocks(5_ms, 30_ms);
+
+  // S should receive Interest without PIT token
+  BOOST_REQUIRE_EQUAL(linkS->sentPackets.size(), 1);
+  lp::Packet lppS(linkS->sentPackets.front());
+  BOOST_CHECK_EQUAL(lppS.count<lp::PitTokenField>(), 0);
+
+  // S responds Data
+  linkS->receivePacket(makeData("/U/0")->wireEncode());
+  advanceClocks(5_ms, 30_ms);
+
+  // C should receive Data with same PIT token
+  BOOST_REQUIRE_EQUAL(linkC->sentPackets.size(), 1);
+  lp::Packet lppD(linkC->sentPackets.front());
+  lp::PitToken tokenD(lppD.get<lp::PitTokenField>());
+  BOOST_CHECK(tokenD == tokenI);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestPitToken
+BOOST_AUTO_TEST_SUITE_END() // Fw
+
+} // namespace tests
+} // namespace fw
+} // namespace nfd
diff --git a/tests/daemon/fw/topology-tester.cpp b/tests/daemon/fw/topology-tester.cpp
index 3007f38..6a70ead 100644
--- a/tests/daemon/fw/topology-tester.cpp
+++ b/tests/daemon/fw/topology-tester.cpp
@@ -104,9 +104,14 @@
   }
 }
 
-TopologyAppLink::TopologyAppLink(shared_ptr<Face> forwarderFace)
+TopologySingleLink::TopologySingleLink(shared_ptr<Face> forwarderFace)
   : m_face(std::move(forwarderFace))
   , m_forwarderTransport(static_cast<InternalForwarderTransport*>(m_face->getTransport()))
+{
+}
+
+TopologyAppLink::TopologyAppLink(shared_ptr<Face> forwarderFace)
+  : TopologySingleLink(std::move(forwarderFace))
   , m_clientTransport(make_shared<InternalClientTransport>())
   , m_client(make_shared<ndn::Face>(m_clientTransport, getGlobalIoService()))
 {
@@ -125,6 +130,38 @@
   m_clientTransport->connectToForwarder(m_forwarderTransport);
 }
 
+class TopologyBareLink::Observer : public face::InternalTransportBase
+{
+public:
+  explicit
+  Observer(std::vector<Block>& receivedPackets)
+    : m_receivedPackets(receivedPackets)
+  {
+  }
+
+  void
+  receivePacket(const Block& packet) final
+  {
+    m_receivedPackets.push_back(packet);
+  }
+
+private:
+  std::vector<Block>& m_receivedPackets;
+};
+
+TopologyBareLink::TopologyBareLink(shared_ptr<Face> forwarderFace)
+  : TopologySingleLink(std::move(forwarderFace))
+  , m_observer(make_unique<Observer>(sentPackets))
+{
+  m_forwarderTransport->setPeer(m_observer.get());
+}
+
+void
+TopologyBareLink::receivePacket(const Block& packet)
+{
+  m_forwarderTransport->receivePacket(packet);
+}
+
 class TopologyPcapLinkService : public GenericLinkService
                               , public TopologyPcap
 {
@@ -166,6 +203,19 @@
   return i;
 }
 
+shared_ptr<Face>
+TopologyTester::makeFace(TopologyNode i, const FaceUri& localUri, const FaceUri& remoteUri,
+                         ndn::nfd::FaceScope scope, ndn::nfd::LinkType linkType)
+{
+  Forwarder& forwarder = this->getForwarder(i);
+  unique_ptr<GenericLinkService> service = m_wantPcap ? make_unique<TopologyPcapLinkService>() :
+                                                        make_unique<GenericLinkService>();
+  auto transport = make_unique<InternalForwarderTransport>(localUri, remoteUri, scope, linkType);
+  auto face = make_shared<Face>(std::move(service), std::move(transport));
+  forwarder.addFace(face);
+  return face;
+}
+
 shared_ptr<TopologyLink>
 TopologyTester::addLink(const std::string& label, time::nanoseconds delay,
                         std::initializer_list<TopologyNode> forwarders,
@@ -180,16 +230,8 @@
   BOOST_ASSERT(forwarders.size() <= 2 || linkType != ndn::nfd::LINK_TYPE_POINT_TO_POINT);
 
   for (TopologyNode i : forwarders) {
-    Forwarder& forwarder = this->getForwarder(i);
     FaceUri localUri("topology://" + m_forwarderLabels.at(i) + "/" + label);
-
-    unique_ptr<GenericLinkService> service = m_wantPcap ? make_unique<TopologyPcapLinkService>() :
-                                                          make_unique<GenericLinkService>();
-    auto transport = make_unique<InternalForwarderTransport>(localUri, remoteUri,
-                     ndn::nfd::FACE_SCOPE_NON_LOCAL, linkType);
-    auto face = make_shared<Face>(std::move(service), std::move(transport));
-
-    forwarder.addFace(face);
+    auto face = makeFace(i, localUri, remoteUri, ndn::nfd::FACE_SCOPE_NON_LOCAL, linkType);
     link->addFace(i, std::move(face));
   }
 
@@ -200,17 +242,9 @@
 shared_ptr<TopologyAppLink>
 TopologyTester::addAppFace(const std::string& label, TopologyNode i)
 {
-  Forwarder& forwarder = this->getForwarder(i);
   FaceUri localUri("topology://" + m_forwarderLabels.at(i) + "/local/" + label);
   FaceUri remoteUri("topology://" + m_forwarderLabels.at(i) + "/app/" + label);
-
-  unique_ptr<GenericLinkService> service = m_wantPcap ? make_unique<TopologyPcapLinkService>() :
-                                                        make_unique<GenericLinkService>();
-  auto transport = make_unique<InternalForwarderTransport>(localUri, remoteUri,
-                   ndn::nfd::FACE_SCOPE_LOCAL, ndn::nfd::LINK_TYPE_POINT_TO_POINT);
-  auto face = make_shared<Face>(std::move(service), std::move(transport));
-
-  forwarder.addFace(face);
+  auto face = makeFace(i, localUri, remoteUri, ndn::nfd::FACE_SCOPE_LOCAL, ndn::nfd::LINK_TYPE_POINT_TO_POINT);
 
   auto al = make_shared<TopologyAppLink>(std::move(face));
   m_appLinks.push_back(al); // keep a shared_ptr so callers don't have to
@@ -225,6 +259,19 @@
   return al;
 }
 
+shared_ptr<TopologyBareLink>
+TopologyTester::addBareLink(const std::string& label, TopologyNode i, ndn::nfd::FaceScope scope,
+                            ndn::nfd::LinkType linkType)
+{
+  FaceUri localUri("topology://" + m_forwarderLabels.at(i) + "/local/" + label);
+  FaceUri remoteUri("topology://" + m_forwarderLabels.at(i) + "/bare/" + label);
+  auto face = makeFace(i, localUri, remoteUri, scope, linkType);
+
+  auto bl = make_shared<TopologyBareLink>(std::move(face));
+  m_bareLinks.push_back(bl); // keep a shared_ptr so callers don't have to
+  return bl;
+}
+
 void
 TopologyTester::enablePcap(bool isEnabled)
 {
diff --git a/tests/daemon/fw/topology-tester.hpp b/tests/daemon/fw/topology-tester.hpp
index 233c216..9243476 100644
--- a/tests/daemon/fw/topology-tester.hpp
+++ b/tests/daemon/fw/topology-tester.hpp
@@ -150,9 +150,33 @@
   std::unordered_map<TopologyNode, NodeTransport> m_transports;
 };
 
+/** \brief represents a link on a single forwarder
+ */
+class TopologySingleLink : noncopyable
+{
+public:
+  /** \brief constructor
+   *  \param forwarderFace a Face with InternalForwarderTransport
+   */
+  explicit
+  TopologySingleLink(shared_ptr<Face> forwarderFace);
+
+  /** \return face on forwarder side
+   */
+  Face&
+  getForwarderFace()
+  {
+    return *m_face;
+  }
+
+protected:
+  shared_ptr<Face> m_face;
+  face::InternalForwarderTransport* m_forwarderTransport;
+};
+
 /** \brief represents a link to a local application
  */
-class TopologyAppLink : noncopyable
+class TopologyAppLink : public TopologySingleLink
 {
 public:
   /** \brief constructor
@@ -171,14 +195,6 @@
   void
   recover();
 
-  /** \return face on forwarder side
-   */
-  Face&
-  getForwarderFace()
-  {
-    return *m_face;
-  }
-
   /** \return face on application side
    */
   ndn::Face&
@@ -188,12 +204,32 @@
   }
 
 private:
-  shared_ptr<Face> m_face;
-  face::InternalForwarderTransport* m_forwarderTransport;
   shared_ptr<face::InternalClientTransport> m_clientTransport;
   shared_ptr<ndn::Face> m_client;
 };
 
+/** \brief allows the test case to inject and observe L2 packets on a link
+ */
+class TopologyBareLink : public TopologySingleLink
+{
+public:
+  /** \brief constructor
+   *  \param forwarderFace a Face with InternalForwarderTransport
+   */
+  explicit
+  TopologyBareLink(shared_ptr<Face> forwarderFace);
+
+  void
+  receivePacket(const Block& packet);
+
+public:
+  std::vector<Block> sentPackets;
+
+private:
+  class Observer;
+  unique_ptr<Observer> m_observer;
+};
+
 /** \brief captured packets on a face
  */
 class TopologyPcap : noncopyable
@@ -264,6 +300,13 @@
   shared_ptr<TopologyAppLink>
   addAppFace(const std::string& label, TopologyNode i, const Name& prefix, uint64_t cost = 0);
 
+  /** \brief makes a link that allows the test case to inject and observe L2 packets
+   */
+  shared_ptr<TopologyBareLink>
+  addBareLink(const std::string& label, TopologyNode i,
+              ndn::nfd::FaceScope scope = ndn::nfd::FACE_SCOPE_LOCAL,
+              ndn::nfd::LinkType linkType = ndn::nfd::LINK_TYPE_POINT_TO_POINT);
+
   /** \brief enables packet capture on every forwarder face
    */
   void
@@ -294,11 +337,17 @@
                       size_t n, int seq = -1);
 
 private:
+  shared_ptr<Face>
+  makeFace(TopologyNode i, const FaceUri& localUri, const FaceUri& remoteUri,
+           ndn::nfd::FaceScope scope, ndn::nfd::LinkType linkType);
+
+private:
   bool m_wantPcap = false;
   std::vector<unique_ptr<Forwarder>> m_forwarders;
   std::vector<std::string> m_forwarderLabels;
   std::vector<shared_ptr<TopologyLink>> m_links;
   std::vector<shared_ptr<TopologyAppLink>> m_appLinks;
+  std::vector<shared_ptr<TopologyBareLink>> m_bareLinks;
 };
 
 } // namespace tests