face: support NonDiscovery and PrefixAnnouncement in GenericLinkService

Change-Id: I2b9577203274d28b374ebfd6a8154b5398b8ec57
Refs: #4280, #4355
diff --git a/daemon/face/generic-link-service.cpp b/daemon/face/generic-link-service.cpp
index c634484..d7ebc7c 100644
--- a/daemon/face/generic-link-service.cpp
+++ b/daemon/face/generic-link-service.cpp
@@ -43,6 +43,7 @@
   , allowCongestionMarking(false)
   , baseCongestionMarkingInterval(time::milliseconds(100)) // Interval from RFC 8289 (CoDel)
   , defaultCongestionThreshold(65536) // This default value works well for a queue capacity of 200KiB
+  , allowSelfLearning(false)
 {
 }
 
@@ -145,6 +146,18 @@
   if (congestionMarkTag != nullptr) {
     lpPacket.add<lp::CongestionMarkField>(*congestionMarkTag);
   }
+
+  if (m_options.allowSelfLearning) {
+    shared_ptr<lp::NonDiscoveryTag> nonDiscoveryTag = netPkt.getTag<lp::NonDiscoveryTag>();
+    if (nonDiscoveryTag != nullptr) {
+      lpPacket.add<lp::NonDiscoveryField>(*nonDiscoveryTag);
+    }
+
+    shared_ptr<lp::PrefixAnnouncementTag> prefixAnnouncementTag = netPkt.getTag<lp::PrefixAnnouncementTag>();
+    if (prefixAnnouncementTag != nullptr) {
+      lpPacket.add<lp::PrefixAnnouncementField>(*prefixAnnouncementTag);
+    }
+  }
 }
 
 void
@@ -366,6 +379,21 @@
     interest->setTag(make_shared<lp::CongestionMarkTag>(firstPkt.get<lp::CongestionMarkField>()));
   }
 
+  if (firstPkt.has<lp::NonDiscoveryField>()) {
+    if (m_options.allowSelfLearning) {
+      interest->setTag(make_shared<lp::NonDiscoveryTag>(firstPkt.get<lp::NonDiscoveryField>()));
+    }
+    else {
+      NFD_LOG_FACE_WARN("received NonDiscovery, but self-learning disabled: IGNORE");
+    }
+  }
+
+  if (firstPkt.has<lp::PrefixAnnouncementField>()) {
+    ++this->nInNetInvalid;
+    NFD_LOG_FACE_WARN("received PrefixAnnouncement with Interest: DROP");
+    return;
+  }
+
   this->receiveInterest(*interest);
 }
 
@@ -404,6 +432,21 @@
     data->setTag(make_shared<lp::CongestionMarkTag>(firstPkt.get<lp::CongestionMarkField>()));
   }
 
+  if (firstPkt.has<lp::NonDiscoveryField>()) {
+    ++this->nInNetInvalid;
+    NFD_LOG_FACE_WARN("received NonDiscovery with Data: DROP");
+    return;
+  }
+
+  if (firstPkt.has<lp::PrefixAnnouncementField>()) {
+    if (m_options.allowSelfLearning) {
+      data->setTag(make_shared<lp::PrefixAnnouncementTag>(firstPkt.get<lp::PrefixAnnouncementField>()));
+    }
+    else {
+      NFD_LOG_FACE_WARN("received PrefixAnnouncement, but self-learning disabled: IGNORE");
+    }
+  }
+
   this->receiveData(*data);
 }
 
@@ -436,6 +479,18 @@
     nack.setTag(make_shared<lp::CongestionMarkTag>(firstPkt.get<lp::CongestionMarkField>()));
   }
 
+  if (firstPkt.has<lp::NonDiscoveryField>()) {
+    ++this->nInNetInvalid;
+    NFD_LOG_FACE_WARN("received NonDiscovery with Nack: DROP");
+    return;
+  }
+
+  if (firstPkt.has<lp::PrefixAnnouncementField>()) {
+    ++this->nInNetInvalid;
+    NFD_LOG_FACE_WARN("received PrefixAnnouncement with Nack: DROP");
+    return;
+  }
+
   this->receiveNack(nack);
 }
 
diff --git a/daemon/face/generic-link-service.hpp b/daemon/face/generic-link-service.hpp
index d74b1f2..d0eb3dd 100644
--- a/daemon/face/generic-link-service.hpp
+++ b/daemon/face/generic-link-service.hpp
@@ -136,6 +136,10 @@
     /** \brief default congestion threshold in bytes
      */
     size_t defaultCongestionThreshold;
+
+    /** \brief enables self-learning forwarding support
+     */
+    bool allowSelfLearning;
   };
 
   /** \brief counters provided by GenericLinkService
diff --git a/tests/daemon/face/generic-link-service.t.cpp b/tests/daemon/face/generic-link-service.t.cpp
index 98801c6..3d803a1 100644
--- a/tests/daemon/face/generic-link-service.t.cpp
+++ b/tests/daemon/face/generic-link-service.t.cpp
@@ -29,6 +29,8 @@
 #include "test-common.hpp"
 #include "dummy-transport.hpp"
 
+#include <ndn-cxx/lp/empty-value.hpp>
+#include <ndn-cxx/lp/prefix-announcement.hpp>
 #include <ndn-cxx/lp/tags.hpp>
 
 namespace nfd {
@@ -37,6 +39,13 @@
 
 using namespace nfd::tests;
 
+static lp::PrefixAnnouncement
+makePrefixAnnouncement(Name announcedName)
+{
+  Name paName = Name("self-learning").append(announcedName).appendVersion();
+  return lp::PrefixAnnouncement(makeData(paName));
+}
+
 BOOST_AUTO_TEST_SUITE(Face)
 
 using nfd::Face;
@@ -1125,6 +1134,218 @@
   BOOST_CHECK_EQUAL(*tag, 1);
 }
 
+BOOST_AUTO_TEST_CASE(SendNonDiscovery)
+{
+  GenericLinkService::Options options;
+  options.allowSelfLearning = true;
+  initialize(options);
+
+  shared_ptr<Interest> interest = makeInterest("/12345678");
+  interest->setTag(make_shared<lp::NonDiscoveryTag>(lp::EmptyValue{}));
+
+  face->sendInterest(*interest);
+
+  BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 1);
+  lp::Packet sent(transport->sentPackets.back().packet);
+  BOOST_CHECK(sent.has<lp::NonDiscoveryField>());
+}
+
+BOOST_AUTO_TEST_CASE(SendNonDiscoveryDisabled)
+{
+  GenericLinkService::Options options;
+  options.allowSelfLearning = false;
+  initialize(options);
+
+  shared_ptr<Interest> interest = makeInterest("/12345678");
+  interest->setTag(make_shared<lp::NonDiscoveryTag>(lp::EmptyValue{}));
+
+  face->sendInterest(*interest);
+
+  BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 1);
+  lp::Packet sent(transport->sentPackets.back().packet);
+  BOOST_CHECK(!sent.has<lp::NonDiscoveryField>());
+}
+
+BOOST_AUTO_TEST_CASE(ReceiveNonDiscovery)
+{
+  GenericLinkService::Options options;
+  options.allowSelfLearning = true;
+  initialize(options);
+
+  shared_ptr<Interest> interest = makeInterest("/12345678");
+  lp::Packet packet(interest->wireEncode());
+  packet.set<lp::NonDiscoveryField>(lp::EmptyValue{});
+
+  transport->receivePacket(packet.wireEncode());
+
+  BOOST_REQUIRE_EQUAL(receivedInterests.size(), 1);
+  shared_ptr<lp::NonDiscoveryTag> tag = receivedInterests.back().getTag<lp::NonDiscoveryTag>();
+  BOOST_CHECK(tag != nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(ReceiveNonDiscoveryDisabled)
+{
+  GenericLinkService::Options options;
+  options.allowSelfLearning = false;
+  initialize(options);
+
+  shared_ptr<Interest> interest = makeInterest("/12345678");
+  lp::Packet packet(interest->wireEncode());
+  packet.set<lp::NonDiscoveryField>(lp::EmptyValue{});
+
+  transport->receivePacket(packet.wireEncode());
+
+  BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 0); // not an error
+  BOOST_CHECK_EQUAL(receivedInterests.size(), 1);
+
+  shared_ptr<lp::NonDiscoveryTag> tag = receivedInterests.back().getTag<lp::NonDiscoveryTag>();
+  BOOST_CHECK(tag == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(ReceiveNonDiscoveryDropData)
+{
+  GenericLinkService::Options options;
+  options.allowSelfLearning = true;
+  initialize(options);
+
+  shared_ptr<Data> data = makeData("/12345678");
+  lp::Packet packet(data->wireEncode());
+  packet.set<lp::NonDiscoveryField>(lp::EmptyValue{});
+
+  transport->receivePacket(packet.wireEncode());
+
+  BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 1);
+  BOOST_CHECK(receivedData.empty());
+}
+
+BOOST_AUTO_TEST_CASE(ReceiveNonDiscoveryDropNack)
+{
+  GenericLinkService::Options options;
+  options.allowSelfLearning = true;
+  initialize(options);
+
+  lp::Nack nack = makeNack("/localhost/test", 123, lp::NackReason::NO_ROUTE);
+  lp::Packet packet;
+  packet.set<lp::FragmentField>(std::make_pair(
+    nack.getInterest().wireEncode().begin(), nack.getInterest().wireEncode().end()));
+  packet.set<lp::NackField>(nack.getHeader());
+  packet.set<lp::NonDiscoveryField>(lp::EmptyValue{});
+
+  transport->receivePacket(packet.wireEncode());
+
+  BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 1);
+  BOOST_CHECK(receivedNacks.empty());
+}
+
+BOOST_AUTO_TEST_CASE(SendPrefixAnnouncement)
+{
+  GenericLinkService::Options options;
+  options.allowSelfLearning = true;
+  initialize(options);
+
+  shared_ptr<Data> data = makeData("/12345678");
+  lp::PrefixAnnouncement pa = makePrefixAnnouncement("/local/ndn/prefix");
+  data->setTag(make_shared<lp::PrefixAnnouncementTag>(pa));
+
+  face->sendData(*data);
+
+  BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 1);
+  lp::Packet sent(transport->sentPackets.back().packet);
+  BOOST_CHECK(sent.has<lp::PrefixAnnouncementField>());
+}
+
+BOOST_AUTO_TEST_CASE(SendPrefixAnnouncementDisabled)
+{
+  GenericLinkService::Options options;
+  options.allowSelfLearning = false;
+  initialize(options);
+
+  shared_ptr<Data> data = makeData("/12345678");
+  lp::PrefixAnnouncement pa = makePrefixAnnouncement("/local/ndn/prefix");
+  data->setTag(make_shared<lp::PrefixAnnouncementTag>(pa));
+
+  face->sendData(*data);
+
+  BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 1);
+  lp::Packet sent(transport->sentPackets.back().packet);
+  BOOST_CHECK(!sent.has<lp::PrefixAnnouncementField>());
+}
+
+BOOST_AUTO_TEST_CASE(ReceivePrefixAnnouncement)
+{
+  GenericLinkService::Options options;
+  options.allowSelfLearning = true;
+  initialize(options);
+
+  shared_ptr<Data> data = makeData("/12345678");
+  lp::PrefixAnnouncement pa = makePrefixAnnouncement("/local/ndn/prefix");
+  lp::Packet packet(data->wireEncode());
+  packet.set<lp::PrefixAnnouncementField>(pa);
+
+  transport->receivePacket(packet.wireEncode());
+
+  BOOST_REQUIRE_EQUAL(receivedData.size(), 1);
+  shared_ptr<lp::PrefixAnnouncementTag> tag = receivedData.back().getTag<lp::PrefixAnnouncementTag>();
+  BOOST_REQUIRE_EQUAL(tag->get().getAnnouncedName(), "/local/ndn/prefix");
+}
+
+BOOST_AUTO_TEST_CASE(ReceivePrefixAnnouncementDisabled)
+{
+  GenericLinkService::Options options;
+  options.allowSelfLearning = false;
+  initialize(options);
+
+  shared_ptr<Data> data = makeData("/12345678");
+  lp::PrefixAnnouncement pa = makePrefixAnnouncement("/local/ndn/prefix");
+  lp::Packet packet(data->wireEncode());
+  packet.set<lp::PrefixAnnouncementField>(pa);
+
+  transport->receivePacket(packet.wireEncode());
+
+  BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 0); // not an error
+  BOOST_CHECK_EQUAL(receivedData.size(), 1);
+
+  shared_ptr<lp::NonDiscoveryTag> tag = receivedData.back().getTag<lp::NonDiscoveryTag>();
+  BOOST_CHECK(tag == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(ReceivePrefixAnnouncementDropInterest)
+{
+  GenericLinkService::Options options;
+  options.allowSelfLearning = true;
+  initialize(options);
+
+  shared_ptr<Interest> interest = makeInterest("/12345678");
+  lp::PrefixAnnouncement pa = makePrefixAnnouncement("/local/ndn/prefix");
+  lp::Packet packet(interest->wireEncode());
+  packet.set<lp::PrefixAnnouncementField>(pa);
+
+  transport->receivePacket(packet.wireEncode());
+
+  BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 1);
+  BOOST_CHECK(receivedInterests.empty());
+}
+
+BOOST_AUTO_TEST_CASE(ReceivePrefixAnnouncementDropNack)
+{
+  GenericLinkService::Options options;
+  options.allowSelfLearning = true;
+  initialize(options);
+
+  lp::Nack nack = makeNack("/localhost/test", 123, lp::NackReason::NO_ROUTE);
+  lp::PrefixAnnouncement pa = makePrefixAnnouncement("/local/ndn/prefix");
+  lp::Packet packet;
+  packet.set<lp::FragmentField>(std::make_pair(
+    nack.getInterest().wireEncode().begin(), nack.getInterest().wireEncode().end()));
+  packet.set<lp::NackField>(nack.getHeader());
+  packet.set<lp::PrefixAnnouncementField>(pa);
+
+  transport->receivePacket(packet.wireEncode());
+
+  BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 1);
+  BOOST_CHECK(receivedNacks.empty());
+}
+
 BOOST_AUTO_TEST_SUITE_END() // LpFields
 
 BOOST_AUTO_TEST_SUITE(Malformed) // receive malformed packets