face: add best-effort link-layer reliability

refs #3931

Change-Id: I009fabe000f4dd4ceb62acab6b0c735c13112430
diff --git a/daemon/face/generic-link-service.cpp b/daemon/face/generic-link-service.cpp
index 2a7f527..9d1d0fc 100644
--- a/daemon/face/generic-link-service.cpp
+++ b/daemon/face/generic-link-service.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -48,12 +48,47 @@
   , m_options(options)
   , m_fragmenter(m_options.fragmenterOptions, this)
   , m_reassembler(m_options.reassemblerOptions, this)
+  , m_reliability(m_options.reliabilityOptions, this)
   , m_lastSeqNo(-2)
 {
   m_reassembler.beforeTimeout.connect(bind([this] { ++this->nReassemblyTimeouts; }));
 }
 
 void
+GenericLinkService::setOptions(const GenericLinkService::Options& options)
+{
+  m_options = options;
+  m_fragmenter.setOptions(m_options.fragmenterOptions);
+  m_reassembler.setOptions(m_options.reassemblerOptions);
+  m_reliability.setOptions(m_options.reliabilityOptions);
+}
+
+void
+GenericLinkService::requestIdlePacket()
+{
+  // No need to request Acks to attach to this packet from LpReliability, as they are already
+  // attached in sendLpPacket
+  this->sendLpPacket({});
+}
+
+void
+GenericLinkService::sendLpPacket(lp::Packet&& pkt)
+{
+  const ssize_t mtu = this->getTransport()->getMtu();
+  if (m_options.reliabilityOptions.isEnabled) {
+    m_reliability.piggyback(pkt, mtu);
+  }
+
+  Transport::Packet tp(pkt.wireEncode());
+  if (mtu != MTU_UNLIMITED && tp.packet.size() > static_cast<size_t>(mtu)) {
+    ++this->nOutOverMtu;
+    NFD_LOG_FACE_WARN("attempted to send packet over MTU limit");
+    return;
+  }
+  this->sendPacket(std::move(tp));
+}
+
+void
 GenericLinkService::doSendInterest(const Interest& interest)
 {
   lp::Packet lpPacket(interest.wireEncode());
@@ -115,29 +150,30 @@
     }
   }
   else {
-    frags.push_back(pkt);
+    frags.push_back(std::move(pkt));
   }
 
-  if (frags.size() > 1) {
-    // sequence is needed only if packet is fragmented
-    this->assignSequences(frags);
-  }
-  else {
+  if (frags.size() == 1) {
     // even if indexed fragmentation is enabled, the fragmenter should not
     // fragment the packet if it can fit in MTU
-    BOOST_ASSERT(frags.size() > 0);
     BOOST_ASSERT(!frags.front().has<lp::FragIndexField>());
     BOOST_ASSERT(!frags.front().has<lp::FragCountField>());
   }
 
-  for (const lp::Packet& frag : frags) {
-    Transport::Packet tp(frag.wireEncode());
-    if (mtu != MTU_UNLIMITED && tp.packet.size() > static_cast<size_t>(mtu)) {
-      ++this->nOutOverMtu;
-      NFD_LOG_FACE_WARN("attempt to send packet over MTU limit");
-      continue;
-    }
-    this->sendPacket(std::move(tp));
+  // Only assign sequences to fragments if reliability enabled and packet contains a fragment,
+  // or there is more than 1 fragment
+  if ((m_options.reliabilityOptions.isEnabled && frags.front().has<lp::FragmentField>()) ||
+      frags.size() > 1) {
+    // Assign sequences to all fragments
+    this->assignSequences(frags);
+  }
+
+  if (m_options.reliabilityOptions.isEnabled && frags.front().has<lp::FragmentField>()) {
+    m_reliability.observeOutgoing(frags);
+  }
+
+  for (lp::Packet& frag : frags) {
+    this->sendLpPacket(std::move(frag));
   }
 }
 
@@ -159,6 +195,10 @@
   try {
     lp::Packet pkt(packet.packet);
 
+    if (m_options.reliabilityOptions.isEnabled) {
+      m_reliability.processIncomingPacket(pkt);
+    }
+
     if (!pkt.has<lp::FragmentField>()) {
       NFD_LOG_FACE_TRACE("received IDLE packet: DROP");
       return;