face: add best-effort link-layer reliability
refs #3931
Change-Id: I009fabe000f4dd4ceb62acab6b0c735c13112430
diff --git a/daemon/fw/rtt-estimator.cpp b/core/rtt-estimator.cpp
similarity index 81%
rename from daemon/fw/rtt-estimator.cpp
rename to core/rtt-estimator.cpp
index 97b2ab0..8de3078 100644
--- a/daemon/fw/rtt-estimator.cpp
+++ b/core/rtt-estimator.cpp
@@ -1,12 +1,12 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014 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
+ * Copyright (c) 2014-2017, 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.
@@ -48,7 +48,8 @@
m_rtt += gErr;
double difference = std::abs(err) - m_variance;
m_variance += difference * m_gain;
- } else {
+ }
+ else {
m_rtt = m;
m_variance = m;
}
diff --git a/daemon/fw/rtt-estimator.hpp b/core/rtt-estimator.hpp
similarity index 91%
rename from daemon/fw/rtt-estimator.hpp
rename to core/rtt-estimator.hpp
index b692083..2b93880 100644
--- a/daemon/fw/rtt-estimator.hpp
+++ b/core/rtt-estimator.hpp
@@ -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,
@@ -23,8 +23,8 @@
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NFD_DAEMON_FW_RTT_ESTIMATOR_HPP
-#define NFD_DAEMON_FW_RTT_ESTIMATOR_HPP
+#ifndef NFD_CORE_RTT_ESTIMATOR_HPP
+#define NFD_CORE_RTT_ESTIMATOR_HPP
#include "core/common.hpp"
@@ -80,4 +80,4 @@
} // namespace nfd
-#endif // NFD_DAEMON_FW_RTT_ESTIMATOR_HPP
+#endif // NFD_CORE_RTT_ESTIMATOR_HPP
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;
diff --git a/daemon/face/generic-link-service.hpp b/daemon/face/generic-link-service.hpp
index b675ae2..89d4a69 100644
--- a/daemon/face/generic-link-service.hpp
+++ b/daemon/face/generic-link-service.hpp
@@ -32,6 +32,7 @@
#include "link-service.hpp"
#include "lp-fragmenter.hpp"
#include "lp-reassembler.hpp"
+#include "lp-reliability.hpp"
namespace nfd {
namespace face {
@@ -71,10 +72,24 @@
/** \brief count of invalid reassembled network-layer packets dropped
*/
PacketCounter nInNetInvalid;
+
+ /** \brief count of network-layer packets that did not require retransmission of a fragment
+ */
+ PacketCounter nAcknowledged;
+
+ /** \brief count of network-layer packets that had at least one fragment retransmitted, but were
+ * eventually received in full
+ */
+ PacketCounter nRetransmitted;
+
+ /** \brief count of network-layer packets dropped because a fragment reached the maximum number
+ * of retransmissions
+ */
+ PacketCounter nRetxExhausted;
};
/** \brief GenericLinkService is a LinkService that implements the NDNLPv2 protocol
- * \sa http://redmine.named-data.net/projects/nfd/wiki/NDNLPv2
+ * \sa https://redmine.named-data.net/projects/nfd/wiki/NDNLPv2
* \todo #3941 declare GenericLinkServiceCounters as virtual inheritance
*/
class GenericLinkService : public LinkService
@@ -108,6 +123,10 @@
/** \brief options for reassembly
*/
LpReassembler::Options reassemblerOptions;
+
+ /** \brief options for reliability
+ */
+ LpReliability::Options reliabilityOptions;
};
/** \brief counters provided by GenericLinkService
@@ -131,6 +150,17 @@
getCounters() const override;
PROTECTED_WITH_TESTS_ELSE_PRIVATE: // send path
+ /** \brief request an IDLE packet to transmit pending service fields
+ */
+ void
+ requestIdlePacket();
+
+ /** \brief send an LpPacket fragment
+ * \param pkt LpPacket to send
+ */
+ void
+ sendLpPacket(lp::Packet&& pkt);
+
/** \brief send Interest
*/
void
@@ -146,7 +176,7 @@
void
doSendNack(const ndn::lp::Nack& nack) override;
-private:
+private: // send path
/** \brief encode link protocol fields from tags onto an outgoing LpPacket
* \param netPkt network-layer packet to extract tags from
* \param lpPacket LpPacket to add link protocol fields to
@@ -222,11 +252,14 @@
void
decodeNack(const Block& netPkt, const lp::Packet& firstPkt);
-private:
+PROTECTED_WITH_TESTS_ELSE_PRIVATE:
Options m_options;
LpFragmenter m_fragmenter;
LpReassembler m_reassembler;
+ LpReliability m_reliability;
lp::Sequence m_lastSeqNo;
+
+ friend class LpReliability;
};
inline const GenericLinkService::Options&
@@ -235,12 +268,6 @@
return m_options;
}
-inline void
-GenericLinkService::setOptions(const GenericLinkService::Options& options)
-{
- m_options = options;
-}
-
inline const GenericLinkService::Counters&
GenericLinkService::getCounters() const
{
diff --git a/daemon/face/lp-reliability.cpp b/daemon/face/lp-reliability.cpp
new file mode 100644
index 0000000..fb95099
--- /dev/null
+++ b/daemon/face/lp-reliability.cpp
@@ -0,0 +1,309 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2017, 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 "lp-reliability.hpp"
+#include "generic-link-service.hpp"
+#include "transport.hpp"
+
+namespace nfd {
+namespace face {
+
+LpReliability::LpReliability(const LpReliability::Options& options, GenericLinkService* linkService)
+ : m_options(options)
+ , m_linkService(linkService)
+ , m_firstUnackedFrag(m_unackedFrags.begin())
+ , m_isIdleAckTimerRunning(false)
+{
+ BOOST_ASSERT(m_linkService != nullptr);
+
+ BOOST_ASSERT(m_options.idleAckTimerPeriod > time::nanoseconds::zero());
+}
+
+void
+LpReliability::setOptions(const Options& options)
+{
+ BOOST_ASSERT(options.idleAckTimerPeriod > time::nanoseconds::zero());
+
+ if (m_options.isEnabled && !options.isEnabled) {
+ this->stopIdleAckTimer();
+ }
+
+ m_options = options;
+}
+
+const GenericLinkService*
+LpReliability::getLinkService() const
+{
+ return m_linkService;
+}
+
+void
+LpReliability::observeOutgoing(const std::vector<lp::Packet>& frags)
+{
+ BOOST_ASSERT(m_options.isEnabled);
+
+ // The sequence number of the first fragment is used to identify the NetPkt.
+ lp::Sequence netPktIdentifier = frags.at(0).get<lp::SequenceField>();
+ auto& netPkt = m_netPkts.emplace(netPktIdentifier, NetPkt{}).first->second;
+ auto unackedFragsIt = m_unackedFrags.begin();
+ auto netPktUnackedFragsIt = netPkt.unackedFrags.begin();
+
+ for (const lp::Packet& frag : frags) {
+ // Store LpPacket for future retransmissions
+ lp::Sequence seq = frag.get<lp::SequenceField>();
+ unackedFragsIt = m_unackedFrags.emplace_hint(unackedFragsIt, seq, frag);
+ unackedFragsIt->second.rtoTimer =
+ scheduler::schedule(m_rto.computeRto(), bind(&LpReliability::onLpPacketLost, this, seq));
+ unackedFragsIt->second.sendTime = time::steady_clock::now();
+ netPktUnackedFragsIt = netPkt.unackedFrags.insert(netPktUnackedFragsIt, seq);
+ if (m_unackedFrags.size() == 1) {
+ m_firstUnackedFrag = unackedFragsIt;
+ }
+ }
+}
+
+void
+LpReliability::processIncomingPacket(const lp::Packet& pkt)
+{
+ BOOST_ASSERT(m_options.isEnabled);
+
+ auto now = time::steady_clock::now();
+
+ // Extract and parse Acks
+ for (lp::Sequence ackSeq : pkt.list<lp::AckField>()) {
+ auto txFrag = m_unackedFrags.find(ackSeq);
+ if (txFrag == m_unackedFrags.end()) {
+ // Ignore an Ack for an unknown sequence number
+ continue;
+ }
+
+ // Cancel the RTO timer for the acknowledged fragment
+ txFrag->second.rtoTimer.cancel();
+
+ if (txFrag->second.retxCount == 0) {
+ // This sequence had no retransmissions, so use it to calculate the RTO
+ m_rto.addMeasurement(time::duration_cast<RttEstimator::Duration>(now - txFrag->second.sendTime));
+ }
+
+ // Look for Acks with sequence numbers < ackSeq (allowing for wraparound) and consider them lost
+ // if a configurable number of Acks containing greater sequence numbers have been received.
+ auto lostLpPackets = findLostLpPackets(ackSeq);
+
+ // Remove the fragment from the map of unacknowledged sequences and from its associated network
+ // packet (removing the network packet if it has been received in whole by remote host).
+ // Potentially increment the start of the window.
+ onLpPacketAcknowledged(txFrag, getNetPktByFrag(ackSeq));
+
+ // Resend or fail fragments considered lost. This must be done separately from the above
+ // enhanced for loop because onLpPacketLost may delete the fragment from m_unackedFrags.
+ for (const lp::Sequence& seq : lostLpPackets) {
+ this->onLpPacketLost(seq);
+ }
+ }
+
+ // If has Fragment field, extract Sequence and add to AckQueue
+ if (pkt.has<lp::FragmentField>() && pkt.has<lp::SequenceField>()) {
+ m_ackQueue.push(pkt.get<lp::SequenceField>());
+ if (!m_isIdleAckTimerRunning) {
+ this->startIdleAckTimer();
+ }
+ }
+}
+
+void
+LpReliability::piggyback(lp::Packet& pkt, ssize_t mtu)
+{
+ BOOST_ASSERT(m_options.isEnabled);
+
+ int maxAcks = std::numeric_limits<int>::max();
+ if (mtu > 0) {
+ // Ack Type (3 octets) + Ack Length (1 octet) + sizeof(lp::Sequence)
+ size_t ackSize = 3 + 1 + sizeof(lp::Sequence);
+ ndn::EncodingEstimator estimator;
+ maxAcks = (mtu - pkt.wireEncode(estimator)) / ackSize;
+ }
+
+ ssize_t nAcksInPkt = 0;
+ while (!m_ackQueue.empty() && nAcksInPkt < maxAcks) {
+ pkt.add<lp::AckField>(m_ackQueue.front());
+ m_ackQueue.pop();
+ nAcksInPkt++;
+ }
+}
+
+void
+LpReliability::startIdleAckTimer()
+{
+ BOOST_ASSERT(!m_isIdleAckTimerRunning);
+ m_isIdleAckTimerRunning = true;
+
+ m_idleAckTimer = scheduler::schedule(m_options.idleAckTimerPeriod, [this] {
+ while (!m_ackQueue.empty()) {
+ m_linkService->requestIdlePacket();
+ }
+
+ m_isIdleAckTimerRunning = false;
+ });
+}
+
+void
+LpReliability::stopIdleAckTimer()
+{
+ m_idleAckTimer.cancel();
+ m_isIdleAckTimerRunning = false;
+}
+
+std::vector<lp::Sequence>
+LpReliability::findLostLpPackets(lp::Sequence ackSeq)
+{
+ std::vector<lp::Sequence> lostLpPackets;
+
+ for (auto it = m_firstUnackedFrag; ; ++it) {
+ if (it == m_unackedFrags.end()) {
+ it = m_unackedFrags.begin();
+ }
+
+ if (it->first == ackSeq) {
+ break;
+ }
+
+ auto& unackedFrag = it->second;
+
+ unackedFrag.nGreaterSeqAcks++;
+
+ if (unackedFrag.nGreaterSeqAcks >= m_options.seqNumLossThreshold && !unackedFrag.wasTimedOutBySeq) {
+ unackedFrag.wasTimedOutBySeq = true;
+ lostLpPackets.push_back(it->first);
+ }
+ }
+
+ return lostLpPackets;
+}
+
+void
+LpReliability::onLpPacketLost(lp::Sequence seq)
+{
+ auto& txFrag = m_unackedFrags.at(seq);
+ auto netPktIt = getNetPktByFrag(seq);
+
+ // Check if maximum number of retransmissions exceeded
+ if (txFrag.retxCount >= m_options.maxRetx) {
+ // Delete all LpPackets of NetPkt from TransmitCache
+ lp::Sequence firstSeq = *(netPktIt->second.unackedFrags.begin());
+ lp::Sequence lastSeq = *(std::prev(netPktIt->second.unackedFrags.end()));
+ if (lastSeq >= firstSeq) { // Normal case: no wraparound
+ m_unackedFrags.erase(m_unackedFrags.find(firstSeq), std::next(m_unackedFrags.find(lastSeq)));
+ }
+ else { // sequence number wraparound
+ m_unackedFrags.erase(m_unackedFrags.find(firstSeq), m_unackedFrags.end());
+ m_unackedFrags.erase(m_unackedFrags.begin(), std::next(m_unackedFrags.find(lastSeq)));
+ }
+
+ m_netPkts.erase(netPktIt);
+
+ ++m_linkService->nRetxExhausted;
+ }
+ else {
+ txFrag.retxCount++;
+
+ // Start RTO timer for this sequence
+ txFrag.rtoTimer = scheduler::schedule(m_rto.computeRto(),
+ bind(&LpReliability::onLpPacketLost, this, seq));
+
+ // Retransmit fragment
+ m_linkService->sendLpPacket(lp::Packet(txFrag.pkt));
+ }
+}
+
+void
+LpReliability::onLpPacketAcknowledged(std::map<lp::Sequence, LpReliability::UnackedFrag>::iterator fragIt,
+ std::map<lp::Sequence, LpReliability::NetPkt>::iterator netPktIt)
+{
+ lp::Sequence seq = fragIt->first;
+ // We need to store the sequence of the window begin in case we are erasing it from m_unackedFrags
+ lp::Sequence firstUnackedSeq = m_firstUnackedFrag->first;
+ auto nextSeqIt = m_unackedFrags.erase(fragIt);
+ netPktIt->second.unackedFrags.erase(seq);
+
+ if (!m_unackedFrags.empty() && firstUnackedSeq == seq) {
+ // If "first" fragment in send window (allowing for wraparound), increment window begin
+ if (nextSeqIt == m_unackedFrags.end()) {
+ m_firstUnackedFrag = m_unackedFrags.begin();
+ }
+ else {
+ m_firstUnackedFrag = nextSeqIt;
+ }
+ }
+
+ // Check if network-layer packet completely received. If so, delete network packet mapping
+ // and increment counter
+ if (netPktIt->second.unackedFrags.empty()) {
+ if (netPktIt->second.didRetx) {
+ ++m_linkService->nRetransmitted;
+ }
+ else {
+ ++m_linkService->nAcknowledged;
+ }
+ m_netPkts.erase(netPktIt);
+ }
+}
+
+std::map<lp::Sequence, LpReliability::NetPkt>::iterator
+LpReliability::getNetPktByFrag(lp::Sequence seq)
+{
+ BOOST_ASSERT(!m_netPkts.empty());
+ auto it = m_netPkts.lower_bound(seq);
+ if (it == m_netPkts.end()) {
+ // This can happen because of sequence number wraparound in the middle of a network packet.
+ // In this case, the network packet will be at the end of m_netPkts and we will need to
+ // decrement the iterator to m_netPkts.end() to the one before it.
+ --it;
+ }
+ return it;
+}
+
+LpReliability::UnackedFrag::UnackedFrag(lp::Packet pkt)
+ : pkt(std::move(pkt))
+ , sendTime(time::steady_clock::now())
+ , retxCount(0)
+ , nGreaterSeqAcks(0)
+ , wasTimedOutBySeq(false)
+{
+}
+
+LpReliability::NetPkt::NetPkt()
+ : didRetx(false)
+{
+}
+
+std::ostream&
+operator<<(std::ostream& os, const FaceLogHelper<LpReliability>& flh)
+{
+ os << FaceLogHelper<LinkService>(*flh.obj.getLinkService());
+ return os;
+}
+
+} // namespace face
+} // namespace nfd
diff --git a/daemon/face/lp-reliability.hpp b/daemon/face/lp-reliability.hpp
new file mode 100644
index 0000000..47d5973
--- /dev/null
+++ b/daemon/face/lp-reliability.hpp
@@ -0,0 +1,195 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2017, 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/>.
+ */
+
+#ifndef NFD_DAEMON_FACE_LP_RELIABILITY_HPP
+#define NFD_DAEMON_FACE_LP_RELIABILITY_HPP
+
+#include "core/common.hpp"
+#include "core/rtt-estimator.hpp"
+#include "core/scheduler.hpp"
+
+#include "face-log.hpp"
+
+#include <ndn-cxx/lp/packet.hpp>
+#include <ndn-cxx/lp/sequence.hpp>
+
+#include <queue>
+
+namespace nfd {
+namespace face {
+
+class GenericLinkService;
+
+/** \brief provides for reliable sending and receiving of link-layer packets
+ * \sa https://redmine.named-data.net/projects/nfd/wiki/NDNLPv2
+ */
+class LpReliability : noncopyable
+{
+public:
+ struct Options
+ {
+ /** \brief enables link-layer reliability
+ */
+ bool isEnabled = false;
+
+ /** \brief maximum number of retransmissions for an LpPacket
+ */
+ size_t maxRetx = 3;
+
+ /** \brief period between sending pending Acks in an IDLE packet
+ */
+ time::nanoseconds idleAckTimerPeriod = time::milliseconds(5);
+
+ /** \brief a fragment is considered lost if this number of fragments with greater sequence
+ * numbers are acknowledged
+ */
+ size_t seqNumLossThreshold = 3;
+ };
+
+ LpReliability(const Options& options, GenericLinkService* linkService);
+
+ /** \brief set options for reliability
+ */
+ void
+ setOptions(const Options& options);
+
+ /** \return GenericLinkService that owns this instance
+ *
+ * This is only used for logging, and may be nullptr.
+ */
+ const GenericLinkService*
+ getLinkService() const;
+
+ /** \brief observe outgoing fragment(s) of a network packet
+ * \param frags fragments of network packet
+ */
+ void
+ observeOutgoing(const std::vector<lp::Packet>& frags);
+
+ /** \brief extract and parse all Acks and add Ack for contained Fragment (if any) to AckQueue
+ * \param pkt incoming LpPacket
+ */
+ void
+ processIncomingPacket(const lp::Packet& pkt);
+
+ /** \brief called by GenericLinkService to attach Acks onto an outgoing LpPacket
+ * \param pkt outgoing LpPacket to attach Acks to
+ * \param mtu MTU of the Transport
+ */
+ void
+ piggyback(lp::Packet& pkt, ssize_t mtu);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ /** \brief start the idle Ack timer
+ *
+ * This timer requests an IDLE packet to acknowledge pending fragments not already piggybacked.
+ * It is called regularly on a period configured in Options::idleAckTimerPeriod. This allows Acks
+ * to be returned to the sender, even if the link goes idle.
+ */
+ void
+ startIdleAckTimer();
+
+ /** \brief cancel the idle Ack timer
+ */
+ void
+ stopIdleAckTimer();
+
+private:
+ /** \brief find and mark as lost fragments where a configurable number of Acks have been received
+ * for greater sequence numbers
+ * \param ackSeq sequence number of received Ack
+ * \return vector containing sequence numbers marked lost by this mechanism
+ */
+ std::vector<lp::Sequence>
+ findLostLpPackets(lp::Sequence ackSeq);
+
+ /** \brief resend (or declare as lost) a lost fragment
+ */
+ void
+ onLpPacketLost(lp::Sequence seq);
+
+ class UnackedFrag;
+ class NetPkt;
+
+ /** \brief remove the fragment with the given sequence number from the map of unacknowledged
+ * fragments as well as its associated network packet
+ * \param fragIt iterator to fragment to be removed
+ *
+ * If the given sequence marks the beginning of the send window, the window will be incremented.
+ * If the associated network packet has been fully transmitted, it will be removed.
+ */
+ void
+ onLpPacketAcknowledged(std::map<lp::Sequence, UnackedFrag>::iterator fragIt,
+ std::map<lp::Sequence, NetPkt>::iterator netPktIt);
+
+ std::map<lp::Sequence, NetPkt>::iterator
+ getNetPktByFrag(lp::Sequence seq);
+
+private:
+ /** \brief contains a sent fragment that has not been acknowledged and associated data
+ */
+ class UnackedFrag
+ {
+ public:
+ // Allows implicit conversion from an lp::Packet
+ UnackedFrag(lp::Packet pkt);
+
+ public:
+ lp::Packet pkt;
+ scheduler::ScopedEventId rtoTimer;
+ time::steady_clock::TimePoint sendTime;
+ size_t retxCount;
+ size_t nGreaterSeqAcks; //!< number of Acks received for sequences greater than this fragment
+ bool wasTimedOutBySeq; //!< whether this fragment has been timed out by the sequence number mechanic
+ };
+
+ /** \brief contains a network-layer packet with unacknowledged fragments
+ */
+ class NetPkt
+ {
+ public:
+ NetPkt();
+
+ public:
+ std::set<lp::Sequence> unackedFrags;
+ bool didRetx;
+ };
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ Options m_options;
+ GenericLinkService* m_linkService;
+ std::map<lp::Sequence, UnackedFrag> m_unackedFrags;
+ std::map<lp::Sequence, UnackedFrag>::iterator m_firstUnackedFrag;
+ std::map<lp::Sequence, NetPkt> m_netPkts;
+ std::queue<lp::Sequence> m_ackQueue;
+ scheduler::ScopedEventId m_idleAckTimer;
+ bool m_isIdleAckTimerRunning;
+ RttEstimator m_rto;
+};
+
+} // namespace face
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_LP_RELIABILITY_HPP
diff --git a/daemon/fw/access-strategy.hpp b/daemon/fw/access-strategy.hpp
index 31f2180..4374fa2 100644
--- a/daemon/fw/access-strategy.hpp
+++ b/daemon/fw/access-strategy.hpp
@@ -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,
@@ -27,7 +27,7 @@
#define NFD_DAEMON_FW_ACCESS_STRATEGY_HPP
#include "strategy.hpp"
-#include "rtt-estimator.hpp"
+#include "core/rtt-estimator.hpp"
#include "retx-suppression-fixed.hpp"
#include <unordered_set>
#include <unordered_map>
@@ -56,11 +56,11 @@
getStrategyName();
public: // triggers
- virtual void
+ void
afterReceiveInterest(const Face& inFace, const Interest& interest,
const shared_ptr<pit::Entry>& pitEntry) override;
- virtual void
+ void
beforeSatisfyInterest(const shared_ptr<pit::Entry>& pitEntry,
const Face& inFace, const Data& data) override;
diff --git a/daemon/fw/asf-measurements.hpp b/daemon/fw/asf-measurements.hpp
index 0988837..c6ff096 100644
--- a/daemon/fw/asf-measurements.hpp
+++ b/daemon/fw/asf-measurements.hpp
@@ -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,
@@ -26,7 +26,7 @@
#ifndef NFD_DAEMON_FW_ASF_MEASUREMENTS_HPP
#define NFD_DAEMON_FW_ASF_MEASUREMENTS_HPP
-#include "fw/rtt-estimator.hpp"
+#include "core/rtt-estimator.hpp"
#include "fw/strategy-info.hpp"
#include "table/measurements-accessor.hpp"
diff --git a/tests/daemon/fw/rtt-estimator.t.cpp b/tests/core/rtt-estimator.t.cpp
similarity index 93%
rename from tests/daemon/fw/rtt-estimator.t.cpp
rename to tests/core/rtt-estimator.t.cpp
index b2c3fb6..e5952e9 100644
--- a/tests/daemon/fw/rtt-estimator.t.cpp
+++ b/tests/core/rtt-estimator.t.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,
@@ -23,14 +23,13 @@
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "fw/rtt-estimator.hpp"
+#include "core/rtt-estimator.hpp"
#include "tests/test-common.hpp"
namespace nfd {
namespace tests {
-BOOST_AUTO_TEST_SUITE(Fw)
BOOST_FIXTURE_TEST_SUITE(TestRttEstimator, BaseFixture)
static inline double
@@ -76,7 +75,6 @@
}
BOOST_AUTO_TEST_SUITE_END() // TestRttEstimator
-BOOST_AUTO_TEST_SUITE_END() // Fw
} // namespace tests
} // namespace nfd
diff --git a/tests/daemon/face/generic-link-service.t.cpp b/tests/daemon/face/generic-link-service.t.cpp
index 28a3288..d252078 100644
--- a/tests/daemon/face/generic-link-service.t.cpp
+++ b/tests/daemon/face/generic-link-service.t.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,
@@ -53,10 +53,15 @@
}
void
- initialize(const GenericLinkService::Options& options)
+ initialize(const GenericLinkService::Options& options, ssize_t mtu = MTU_UNLIMITED)
{
face.reset(new Face(make_unique<GenericLinkService>(options),
- make_unique<DummyTransport>()));
+ make_unique<DummyTransport>("dummy://",
+ "dummy://",
+ ndn::nfd::FACE_SCOPE_NON_LOCAL,
+ ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
+ ndn::nfd::LINK_TYPE_POINT_TO_POINT,
+ mtu)));
service = static_cast<GenericLinkService*>(face->getLinkService());
transport = static_cast<DummyTransport*>(face->getTransport());
@@ -254,7 +259,6 @@
BOOST_AUTO_TEST_SUITE_END() // SimpleSendReceive
-
BOOST_AUTO_TEST_SUITE(Fragmentation)
BOOST_AUTO_TEST_CASE(FragmentationDisabledExceedMtuDrop)
@@ -393,6 +397,70 @@
BOOST_AUTO_TEST_SUITE_END() // Fragmentation
+BOOST_AUTO_TEST_SUITE(Reliability)
+
+BOOST_AUTO_TEST_CASE(SendInterestWithSequence)
+{
+ // Initialize with Options that enables reliability
+ GenericLinkService::Options options;
+ options.allowLocalFields = false;
+ options.reliabilityOptions.isEnabled = true;
+ initialize(options);
+
+ shared_ptr<Interest> interest1 = makeInterest("/localhost/test");
+
+ face->sendInterest(*interest1);
+
+ BOOST_CHECK_EQUAL(service->getCounters().nOutInterests, 1);
+ BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 1);
+ lp::Packet interest1pkt;
+ BOOST_REQUIRE_NO_THROW(interest1pkt.wireDecode(transport->sentPackets.back().packet));
+ BOOST_CHECK(interest1pkt.has<lp::FragmentField>());
+ BOOST_CHECK(interest1pkt.has<lp::SequenceField>());
+}
+
+BOOST_AUTO_TEST_CASE(SendDataWithSequence)
+{
+ // Initialize with Options that enables reliability
+ GenericLinkService::Options options;
+ options.allowLocalFields = false;
+ options.reliabilityOptions.isEnabled = true;
+ initialize(options);
+
+ shared_ptr<Data> data1 = makeData("/localhost/test");
+
+ face->sendData(*data1);
+
+ BOOST_CHECK_EQUAL(service->getCounters().nOutData, 1);
+ BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 1);
+ lp::Packet data1pkt;
+ BOOST_REQUIRE_NO_THROW(data1pkt.wireDecode(transport->sentPackets.back().packet));
+ BOOST_CHECK(data1pkt.has<lp::FragmentField>());
+ BOOST_CHECK(data1pkt.has<lp::SequenceField>());
+}
+
+BOOST_AUTO_TEST_CASE(SendNackWithSequence)
+{
+ // Initialize with Options that enables reliability
+ GenericLinkService::Options options;
+ options.allowLocalFields = false;
+ options.reliabilityOptions.isEnabled = true;
+ initialize(options);
+
+ lp::Nack nack1 = makeNack("/localhost/test", 323, lp::NackReason::NO_ROUTE);
+
+ face->sendNack(nack1);
+
+ BOOST_CHECK_EQUAL(service->getCounters().nOutNacks, 1);
+ BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 1);
+ lp::Packet nack1pkt;
+ BOOST_REQUIRE_NO_THROW(nack1pkt.wireDecode(transport->sentPackets.back().packet));
+ BOOST_CHECK(nack1pkt.has<lp::NackField>());
+ BOOST_CHECK(nack1pkt.has<lp::FragmentField>());
+ BOOST_CHECK(nack1pkt.has<lp::SequenceField>());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Reliability
BOOST_AUTO_TEST_SUITE(LpFields)
diff --git a/tests/daemon/face/lp-reliability.t.cpp b/tests/daemon/face/lp-reliability.t.cpp
new file mode 100644
index 0000000..82e1f79
--- /dev/null
+++ b/tests/daemon/face/lp-reliability.t.cpp
@@ -0,0 +1,766 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2017, 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 "face/lp-reliability.hpp"
+#include "face/face.hpp"
+#include "face/lp-fragmenter.hpp"
+#include "face/generic-link-service.hpp"
+
+#include "tests/test-common.hpp"
+#include "dummy-face.hpp"
+#include "dummy-transport.hpp"
+
+namespace nfd {
+namespace face {
+namespace tests {
+
+using namespace nfd::tests;
+
+BOOST_AUTO_TEST_SUITE(Face)
+
+class DummyLpReliabilityLinkService : public GenericLinkService
+{
+public:
+ LpReliability*
+ getLpReliability()
+ {
+ return &m_reliability;
+ }
+
+ void
+ sendLpPackets(std::vector<lp::Packet> frags)
+ {
+ if (frags.front().has<lp::FragmentField>()) {
+ m_reliability.observeOutgoing(frags);
+ }
+
+ for (lp::Packet& frag : frags) {
+ this->sendLpPacket(std::move(frag));
+ }
+ }
+
+private:
+ void
+ doSendInterest(const Interest& interest) override
+ {
+ BOOST_ASSERT(false);
+ }
+
+ void
+ doSendData(const Data& data) override
+ {
+ BOOST_ASSERT(false);
+ }
+
+ void
+ doSendNack(const lp::Nack& nack) override
+ {
+ BOOST_ASSERT(false);
+ }
+
+ void
+ doReceivePacket(Transport::Packet&& packet) override
+ {
+ BOOST_ASSERT(false);
+ }
+};
+
+class LpReliabilityFixture : public UnitTestTimeFixture
+{
+public:
+ LpReliabilityFixture()
+ : linkService(make_unique<DummyLpReliabilityLinkService>())
+ , transport(make_unique<DummyTransport>())
+ , face(make_unique<DummyFace>())
+ {
+ linkService->setFaceAndTransport(*face, *transport);
+ transport->setFaceAndLinkService(*face, *linkService);
+
+ GenericLinkService::Options options;
+ options.reliabilityOptions.isEnabled = true;
+ linkService->setOptions(options);
+
+ reliability = linkService->getLpReliability();
+ }
+
+public:
+ unique_ptr<DummyLpReliabilityLinkService> linkService;
+ unique_ptr<DummyTransport> transport;
+ unique_ptr<DummyFace> face;
+ LpReliability* reliability;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestLpReliability, LpReliabilityFixture)
+
+BOOST_AUTO_TEST_CASE(SendNoFragmentField)
+{
+ lp::Packet pkt;
+ pkt.add<lp::AckField>(0);
+
+ linkService->sendLpPackets({pkt});
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.size(), 0);
+ BOOST_CHECK_EQUAL(reliability->m_netPkts.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(SendNotFragmented)
+{
+ shared_ptr<Interest> interest = makeInterest("/abc/def");
+
+ lp::Packet pkt;
+ pkt.add<lp::SequenceField>(123);
+ pkt.add<lp::FragmentField>(make_pair(interest->wireEncode().begin(), interest->wireEncode().end()));
+
+ linkService->sendLpPackets({pkt});
+
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.size(), 1);
+ lp::Packet cached;
+ BOOST_REQUIRE_NO_THROW(cached.wireDecode(transport->sentPackets.front().packet));
+ BOOST_REQUIRE(cached.has<lp::SequenceField>());
+ BOOST_CHECK_EQUAL(cached.get<lp::SequenceField>(), 123);
+ lp::Sequence seq = cached.get<lp::SequenceField>();
+ ndn::Buffer::const_iterator begin, end;
+ std::tie(begin, end) = cached.get<lp::FragmentField>();
+ Block block(&*begin, std::distance(begin, end));
+ Interest decodedInterest(block);
+ BOOST_CHECK_EQUAL(decodedInterest, *interest);
+
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(seq), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(seq).retxCount, 0);
+
+ BOOST_REQUIRE_EQUAL(reliability->m_netPkts.size(), 1);
+ BOOST_REQUIRE_EQUAL(reliability->m_netPkts.count(seq), 1);
+ BOOST_REQUIRE_EQUAL(reliability->m_netPkts.at(seq).unackedFrags.size(), 1);
+ BOOST_CHECK_EQUAL(reliability->m_netPkts.at(seq).unackedFrags.count(seq), 1);
+
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(SendFragmented)
+{
+ // Limit MTU
+ transport->setMtu(100);
+
+ Data data("/abc/def");
+
+ // Create a Data block containing 60 octets of content, which should fragment into 2 packets
+ uint8_t content[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
+
+ data.setContent(content, sizeof(content));
+ signData(data);
+
+ lp::Packet pkt;
+ pkt.add<lp::FragmentField>(make_pair(data.wireEncode().begin(), data.wireEncode().end()));
+
+ LpFragmenter fragmenter;
+ bool wasFragmentSuccessful;
+ std::vector<lp::Packet> frags;
+ std::tie(wasFragmentSuccessful, frags) = fragmenter.fragmentPacket(pkt, 100);
+ BOOST_REQUIRE(wasFragmentSuccessful);
+ BOOST_REQUIRE_EQUAL(frags.size(), 2);
+
+ frags.at(0).add<lp::SequenceField>(123);
+ frags.at(1).add<lp::SequenceField>(124);
+ linkService->sendLpPackets(std::move(frags));
+
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.size(), 2);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(123), 1);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(124), 1);
+ lp::Packet cached1;
+ BOOST_REQUIRE_NO_THROW(cached1.wireDecode(transport->sentPackets.front().packet));
+ BOOST_REQUIRE(cached1.has<lp::SequenceField>());
+ BOOST_CHECK_EQUAL(cached1.get<lp::SequenceField>(), 123);
+ lp::Packet cached2;
+ BOOST_REQUIRE_NO_THROW(cached2.wireDecode(transport->sentPackets.back().packet));
+ BOOST_REQUIRE(cached2.has<lp::SequenceField>());
+ BOOST_CHECK_EQUAL(cached2.get<lp::SequenceField>(), 124);
+ lp::Sequence firstSeq = cached1.get<lp::SequenceField>();
+
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(firstSeq).retxCount, 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(firstSeq + 1).retxCount, 0);
+
+ BOOST_REQUIRE_EQUAL(reliability->m_netPkts.size(), 1);
+ BOOST_REQUIRE_EQUAL(reliability->m_netPkts.count(firstSeq), 1);
+ BOOST_REQUIRE_EQUAL(reliability->m_netPkts.at(firstSeq).unackedFrags.size(), 2);
+ BOOST_CHECK_EQUAL(reliability->m_netPkts.at(firstSeq).unackedFrags.count(firstSeq), 1);
+ BOOST_CHECK_EQUAL(reliability->m_netPkts.at(firstSeq).unackedFrags.count(firstSeq + 1), 1);
+
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessIncomingPacket)
+{
+ BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
+
+ shared_ptr<Interest> interest = makeInterest("/abc/def");
+
+ lp::Packet pkt1;
+ pkt1.add<lp::SequenceField>(999888);
+ pkt1.add<lp::FragmentField>(make_pair(interest->wireEncode().begin(), interest->wireEncode().end()));
+
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 0);
+
+ reliability->processIncomingPacket(pkt1);
+
+ BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
+ BOOST_REQUIRE_EQUAL(reliability->m_ackQueue.size(), 1);
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.front(), 999888);
+
+ lp::Packet pkt2;
+ pkt2.add<lp::SequenceField>(111222);
+ pkt2.add<lp::FragmentField>(make_pair(interest->wireEncode().begin(), interest->wireEncode().end()));
+
+ reliability->processIncomingPacket(pkt2);
+
+ BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
+ BOOST_REQUIRE_EQUAL(reliability->m_ackQueue.size(), 2);
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.front(), 999888);
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.back(), 111222);
+
+ // T+5ms
+ advanceClocks(time::milliseconds(1), 5);
+ BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessReceivedAcks)
+{
+ shared_ptr<Interest> interest = makeInterest("/abc/def");
+
+ lp::Packet pkt1;
+ pkt1.add<lp::SequenceField>(1024);
+ pkt1.add<lp::FragmentField>(make_pair(interest->wireEncode().begin(), interest->wireEncode().end()));
+
+ lp::Packet pkt2;
+ pkt2.add<lp::SequenceField>(1025);
+ pkt2.add<lp::FragmentField>(make_pair(interest->wireEncode().begin(), interest->wireEncode().end()));
+
+ linkService->sendLpPackets({pkt1, pkt2});
+
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(1024), 1);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(1025), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(1024).retxCount, 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(1025).retxCount, 0);
+ BOOST_REQUIRE_EQUAL(reliability->m_netPkts.count(1024), 1);
+ BOOST_CHECK_EQUAL(reliability->m_netPkts[1024].unackedFrags.size(), 2);
+ BOOST_CHECK_EQUAL(reliability->m_netPkts[1024].unackedFrags.count(1024), 1);
+ BOOST_CHECK_EQUAL(reliability->m_netPkts[1024].unackedFrags.count(1025), 1);
+
+ advanceClocks(time::milliseconds(1), 500);
+
+ lp::Packet ackPkt1;
+ ackPkt1.add<lp::AckField>(101010); // Unknown sequence number - ignored
+ ackPkt1.add<lp::AckField>(1025);
+
+ reliability->processIncomingPacket(ackPkt1);
+
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(1024), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(1025), 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(1024).retxCount, 0);
+ BOOST_REQUIRE_EQUAL(reliability->m_netPkts.count(1024), 1);
+ BOOST_CHECK_EQUAL(reliability->m_netPkts[1024].unackedFrags.size(), 1);
+ BOOST_CHECK_EQUAL(reliability->m_netPkts[1024].unackedFrags.count(1024), 1);
+
+ lp::Packet ackPkt2;
+ ackPkt2.add<lp::AckField>(1024);
+
+ reliability->processIncomingPacket(ackPkt2);
+
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.size(), 0);
+ BOOST_CHECK_EQUAL(reliability->m_netPkts.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(RetxUnackedSequence)
+{
+ shared_ptr<Interest> interest = makeInterest("/abc/def");
+
+ lp::Packet pkt1;
+ pkt1.add<lp::SequenceField>(1024);
+ pkt1.add<lp::FragmentField>(make_pair(interest->wireEncode().begin(), interest->wireEncode().end()));
+
+ lp::Packet pkt2;
+ pkt2.add<lp::SequenceField>(1025);
+ pkt2.add<lp::FragmentField>(make_pair(interest->wireEncode().begin(), interest->wireEncode().end()));
+
+ linkService->sendLpPackets({pkt1});
+ // T+500ms
+ // 1024 rto: 1000ms, started T+0ms, retx 0
+ advanceClocks(time::milliseconds(1), 500);
+ linkService->sendLpPackets({pkt2});
+
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(1024), 1);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(1025), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(1024).retxCount, 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(1025).retxCount, 0);
+ BOOST_REQUIRE_EQUAL(reliability->m_netPkts.count(1024), 1);
+ BOOST_REQUIRE_EQUAL(reliability->m_netPkts.count(1025), 1);
+ BOOST_CHECK_EQUAL(reliability->m_netPkts[1024].unackedFrags.size(), 1);
+ BOOST_CHECK_EQUAL(reliability->m_netPkts[1024].unackedFrags.count(1024), 1);
+ BOOST_CHECK_EQUAL(reliability->m_netPkts[1025].unackedFrags.size(), 1);
+ BOOST_CHECK_EQUAL(reliability->m_netPkts[1025].unackedFrags.count(1025), 1);
+
+ // T+1250ms
+ // 1024 rto: 1000ms, started T+1000ms, retx 1
+ // 1025 rto: 1000ms, started T+500ms, retx 0
+ advanceClocks(time::milliseconds(1), 750);
+
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(1024), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(1024).retxCount, 1);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(1025), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(1025).retxCount, 0);
+
+ // T+2250ms
+ // 1024 rto: 1000ms, started T+2000ms, retx 2
+ // 1025 rto: 1000ms, started T+1500ms, retx 1
+ advanceClocks(time::milliseconds(1), 1000);
+
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(1024), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(1024).retxCount, 2);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(1025), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(1025).retxCount, 1);
+
+ // T+3250ms
+ // 1024 rto: 1000ms, started T+3000ms, retx 3
+ // 1025 rto: 1000ms, started T+2500ms, retx 2
+ advanceClocks(time::milliseconds(1), 1000);
+
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(1024), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(1024).retxCount, 3);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(1025), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(1025).retxCount, 2);
+
+ // T+4250ms
+ // 1024 rto: expired, removed
+ // 1025 rto: 1000ms, started T+3500ms, retx 3
+ advanceClocks(time::milliseconds(1), 1000);
+
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(1024), 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(1025), 1);
+
+ // T+4750ms
+ // 1024 rto: expired, removed
+ // 1025 rto: expired, removed
+ advanceClocks(time::milliseconds(1), 1000);
+
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.size(), 0);
+ BOOST_CHECK_EQUAL(reliability->m_netPkts.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(LostPacketsWraparound)
+{
+ shared_ptr<Interest> interest = makeInterest("/abc/def");
+
+ lp::Packet pkt1;
+ pkt1.add<lp::SequenceField>(0xFFFFFFFFFFFFFFFF);
+ pkt1.add<lp::FragmentField>(make_pair(interest->wireEncode().begin(), interest->wireEncode().end()));
+
+ lp::Packet pkt2;
+ pkt2.add<lp::SequenceField>(4);
+ pkt2.add<lp::FragmentField>(make_pair(interest->wireEncode().begin(), interest->wireEncode().end()));
+
+ lp::Packet pkt3;
+ pkt3.add<lp::SequenceField>(5);
+ pkt3.add<lp::FragmentField>(make_pair(interest->wireEncode().begin(), interest->wireEncode().end()));
+
+ lp::Packet pkt4;
+ pkt4.add<lp::SequenceField>(7);
+ pkt4.add<lp::FragmentField>(make_pair(interest->wireEncode().begin(), interest->wireEncode().end()));
+
+ lp::Packet pkt5;
+ pkt5.add<lp::SequenceField>(8);
+ pkt5.add<lp::FragmentField>(make_pair(interest->wireEncode().begin(), interest->wireEncode().end()));
+
+ // Passed to sendLpPackets individually since they are from separate (encoded) network packets
+ linkService->sendLpPackets({pkt1});
+ linkService->sendLpPackets({pkt2});
+ linkService->sendLpPackets({pkt3});
+ linkService->sendLpPackets({pkt4});
+ linkService->sendLpPackets({pkt5});
+
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.size(), 5);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(0xFFFFFFFFFFFFFFFF), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(4), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(5), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(7), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(8), 1);
+ BOOST_CHECK_EQUAL(reliability->m_firstUnackedFrag->first, 0xFFFFFFFFFFFFFFFF);
+
+ lp::Packet ackPkt1;
+ ackPkt1.add<lp::AckField>(4);
+
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 5);
+
+ reliability->processIncomingPacket(ackPkt1);
+
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.size(), 4);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(0xFFFFFFFFFFFFFFFF), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(0xFFFFFFFFFFFFFFFF).retxCount, 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(0xFFFFFFFFFFFFFFFF).nGreaterSeqAcks, 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(4), 0);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(5), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(5).retxCount, 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(5).nGreaterSeqAcks, 0);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(7), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(7).retxCount, 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(7).nGreaterSeqAcks, 0);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(8), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(8).retxCount, 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(8).nGreaterSeqAcks, 0);
+ BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 5);
+ BOOST_CHECK_EQUAL(reliability->m_firstUnackedFrag->first, 0xFFFFFFFFFFFFFFFF);
+
+ lp::Packet ackPkt2;
+ ackPkt2.add<lp::AckField>(7);
+
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 5);
+
+ reliability->processIncomingPacket(ackPkt2);
+
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.size(), 3);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(0xFFFFFFFFFFFFFFFF), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(0xFFFFFFFFFFFFFFFF).retxCount, 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(0xFFFFFFFFFFFFFFFF).nGreaterSeqAcks, 2);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(4), 0);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(5), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(5).retxCount, 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(5).nGreaterSeqAcks, 1);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(7), 0);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(8), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(8).retxCount, 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(8).nGreaterSeqAcks, 0);
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 5);
+ BOOST_CHECK_EQUAL(reliability->m_firstUnackedFrag->first, 0xFFFFFFFFFFFFFFFF);
+
+ lp::Packet ackPkt3;
+ ackPkt3.add<lp::AckField>(5);
+
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 5);
+
+ reliability->processIncomingPacket(ackPkt3);
+
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.size(), 2);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(0xFFFFFFFFFFFFFFFF), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(0xFFFFFFFFFFFFFFFF).retxCount, 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(0xFFFFFFFFFFFFFFFF).nGreaterSeqAcks, 3);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(4), 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(5), 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(7), 0);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(8), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(8).retxCount, 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(8).nGreaterSeqAcks, 0);
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 6);
+ BOOST_CHECK_EQUAL(reliability->m_firstUnackedFrag->first, 0xFFFFFFFFFFFFFFFF);
+ lp::Packet sentRetxPkt(transport->sentPackets.back().packet);
+ BOOST_REQUIRE(sentRetxPkt.has<lp::SequenceField>());
+ BOOST_CHECK_EQUAL(sentRetxPkt.get<lp::SequenceField>(), 0xFFFFFFFFFFFFFFFF);
+
+ lp::Packet ackPkt4;
+ ackPkt4.add<lp::AckField>(0xFFFFFFFFFFFFFFFF);
+
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 6);
+
+ reliability->processIncomingPacket(ackPkt4);
+
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.size(), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(0xFFFFFFFFFFFFFFFF), 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(4), 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(5), 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.count(7), 0);
+ BOOST_REQUIRE_EQUAL(reliability->m_unackedFrags.count(8), 1);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(8).retxCount, 0);
+ BOOST_CHECK_EQUAL(reliability->m_unackedFrags.at(8).nGreaterSeqAcks, 0);
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 6);
+ BOOST_CHECK_EQUAL(reliability->m_firstUnackedFrag->first, 8);
+}
+
+BOOST_AUTO_TEST_CASE(PiggybackAcks)
+{
+ reliability->m_ackQueue.push(256);
+ reliability->m_ackQueue.push(257);
+ reliability->m_ackQueue.push(10);
+
+ lp::Packet pkt;
+ pkt.add<lp::SequenceField>(123456);
+ linkService->sendLpPackets({pkt});
+
+ BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 1);
+ lp::Packet sentPkt(transport->sentPackets.front().packet);
+
+ BOOST_REQUIRE_EQUAL(sentPkt.count<lp::AckField>(), 3);
+ BOOST_CHECK_EQUAL(sentPkt.get<lp::AckField>(0), 256);
+ BOOST_CHECK_EQUAL(sentPkt.get<lp::AckField>(1), 257);
+ BOOST_CHECK_EQUAL(sentPkt.get<lp::AckField>(2), 10);
+
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(PiggybackAcksMtu)
+{
+ // This test case tests for piggybacking Acks when there is an MTU on the link.
+
+ reliability->m_ackQueue.push(1010);
+ reliability->m_ackQueue.push(1011);
+ reliability->m_ackQueue.push(1013);
+ reliability->m_ackQueue.push(1014);
+
+ Data data("/abc/def");
+
+ // Create a Data block containing 60 octets of content, which should fragment into 2 packets
+ uint8_t content[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
+
+ data.setContent(content, sizeof(content));
+ signData(data);
+
+ lp::Packet pkt1;
+ pkt1.add<lp::SequenceField>(123);
+ pkt1.add<lp::FragmentField>(make_pair(data.wireEncode().begin(), data.wireEncode().end()));
+
+ // Allow 2 Acks per packet, plus a little bit of extra space
+ // sizeof(lp::Sequence) + Ack Type (3 octets) + Ack Length (1 octet)
+ transport->setMtu(pkt1.wireEncode().size() + 2 * (sizeof(lp::Sequence) + 3 + 1) + 3);
+
+ linkService->sendLpPackets({pkt1});
+
+ BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 1);
+ lp::Packet sentPkt1(transport->sentPackets.front().packet);
+
+ BOOST_REQUIRE_EQUAL(sentPkt1.count<lp::AckField>(), 2);
+ BOOST_CHECK_EQUAL(sentPkt1.get<lp::AckField>(0), 1010);
+ BOOST_CHECK_EQUAL(sentPkt1.get<lp::AckField>(1), 1011);
+
+ BOOST_REQUIRE_EQUAL(reliability->m_ackQueue.size(), 2);
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.front(), 1013);
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.back(), 1014);
+
+ lp::Packet pkt2;
+ pkt2.add<lp::SequenceField>(105623);
+ pkt2.add<lp::FragmentField>(make_pair(data.wireEncode().begin(), data.wireEncode().end()));
+
+ // Allow 1 Acks per packet, plus a little bit of extra space (1 Ack - 1 octet)
+ // sizeof(lp::Sequence) + Ack Type (3 octets) + Ack Length (1 octet)
+ transport->setMtu(pkt2.wireEncode().size() + 2 * (sizeof(lp::Sequence) + 3 + 1) - 1);
+
+ linkService->sendLpPackets({pkt2});
+
+ BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 2);
+ lp::Packet sentPkt2(transport->sentPackets.back().packet);
+
+ BOOST_REQUIRE_EQUAL(sentPkt2.count<lp::AckField>(), 1);
+ BOOST_CHECK_EQUAL(sentPkt2.get<lp::AckField>(), 1013);
+
+ BOOST_REQUIRE_EQUAL(reliability->m_ackQueue.size(), 1);
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.front(), 1014);
+
+ lp::Packet pkt3;
+ pkt3.add<lp::SequenceField>(969456);
+ pkt3.add<lp::FragmentField>(make_pair(data.wireEncode().begin(), data.wireEncode().end()));
+
+ // Allow 3 Acks per packet
+ // sizeof(lp::Sequence) + Ack Type (3 octets) + Ack Length (1 octet)
+ transport->setMtu(pkt3.wireEncode().size() + 3 * (sizeof(lp::Sequence) + 3 + 1));
+
+ linkService->sendLpPackets({pkt3});
+
+ BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 3);
+ lp::Packet sentPkt3(transport->sentPackets.back().packet);
+
+ BOOST_REQUIRE_EQUAL(sentPkt3.count<lp::AckField>(), 1);
+ BOOST_CHECK_EQUAL(sentPkt3.get<lp::AckField>(), 1014);
+
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(IdleAckTimer)
+{
+ // T+1ms
+ advanceClocks(time::milliseconds(1), 1);
+
+ reliability->m_ackQueue.push(5000);
+ reliability->m_ackQueue.push(5001);
+ reliability->m_ackQueue.push(5002);
+ BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
+ reliability->startIdleAckTimer();
+ BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
+
+ // T+5ms
+ advanceClocks(time::milliseconds(1), 4);
+ BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 0);
+
+ // T+6ms
+ advanceClocks(time::milliseconds(1), 1);
+
+ BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
+ BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 1);
+ lp::Packet sentPkt1(transport->sentPackets.back().packet);
+
+ BOOST_REQUIRE_EQUAL(sentPkt1.count<lp::AckField>(), 3);
+ BOOST_CHECK_EQUAL(sentPkt1.get<lp::AckField>(0), 5000);
+ BOOST_CHECK_EQUAL(sentPkt1.get<lp::AckField>(1), 5001);
+ BOOST_CHECK_EQUAL(sentPkt1.get<lp::AckField>(2), 5002);
+
+ reliability->m_ackQueue.push(5003);
+ reliability->m_ackQueue.push(5004);
+ reliability->startIdleAckTimer();
+ BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
+
+ // T+10ms
+ advanceClocks(time::milliseconds(1), 4);
+ BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 1);
+
+ // T+11ms
+ advanceClocks(time::milliseconds(1), 1);
+
+ BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
+ BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 2);
+ lp::Packet sentPkt2(transport->sentPackets.back().packet);
+
+ BOOST_REQUIRE_EQUAL(sentPkt2.count<lp::AckField>(), 2);
+ BOOST_CHECK_EQUAL(sentPkt2.get<lp::AckField>(0), 5003);
+ BOOST_CHECK_EQUAL(sentPkt2.get<lp::AckField>(1), 5004);
+
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 0);
+
+ // T+16ms
+ advanceClocks(time::milliseconds(1), 5);
+
+ BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 2);
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(IdleAckTimerMtu)
+{
+ // 1 (LpPacket Type) + 1 (LpPacket Length) + 2 Acks
+ transport->setMtu(lp::Packet().wireEncode().size() + 2 * (sizeof(lp::Sequence) + 3 + 1));
+
+ // T+1ms
+ advanceClocks(time::milliseconds(1), 1);
+
+ reliability->m_ackQueue.push(3000);
+ reliability->m_ackQueue.push(3001);
+ reliability->m_ackQueue.push(3002);
+ reliability->m_ackQueue.push(3003);
+ reliability->m_ackQueue.push(3004);
+ BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
+ reliability->startIdleAckTimer();
+ BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
+
+ // T+5ms
+ advanceClocks(time::milliseconds(1), 4);
+ BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 0);
+
+ // T+6ms
+ advanceClocks(time::milliseconds(1), 1);
+
+ BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
+
+ reliability->m_ackQueue.push(3005);
+ reliability->m_ackQueue.push(3006);
+ reliability->startIdleAckTimer();
+ BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
+
+ BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 3);
+ lp::Packet sentPkt1(transport->sentPackets[0].packet);
+ BOOST_REQUIRE_EQUAL(sentPkt1.count<lp::AckField>(), 2);
+ BOOST_CHECK_EQUAL(sentPkt1.get<lp::AckField>(0), 3000);
+ BOOST_CHECK_EQUAL(sentPkt1.get<lp::AckField>(1), 3001);
+ lp::Packet sentPkt2(transport->sentPackets[1].packet);
+ BOOST_REQUIRE_EQUAL(sentPkt2.count<lp::AckField>(), 2);
+ BOOST_CHECK_EQUAL(sentPkt2.get<lp::AckField>(0), 3002);
+ BOOST_CHECK_EQUAL(sentPkt2.get<lp::AckField>(1), 3003);
+ lp::Packet sentPkt3(transport->sentPackets[2].packet);
+ BOOST_REQUIRE_EQUAL(sentPkt3.count<lp::AckField>(), 1);
+ BOOST_CHECK_EQUAL(sentPkt3.get<lp::AckField>(), 3004);
+
+ BOOST_REQUIRE_EQUAL(reliability->m_ackQueue.size(), 2);
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.front(), 3005);
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.back(), 3006);
+
+ // T+10ms
+ advanceClocks(time::milliseconds(1), 4);
+ BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 3);
+
+ // T+11ms
+ advanceClocks(time::milliseconds(1), 1);
+
+ BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
+
+ reliability->m_ackQueue.push(3007);
+ reliability->startIdleAckTimer();
+
+ BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
+ BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 4);
+ lp::Packet sentPkt4(transport->sentPackets[3].packet);
+ BOOST_REQUIRE_EQUAL(sentPkt4.count<lp::AckField>(), 2);
+ BOOST_CHECK_EQUAL(sentPkt4.get<lp::AckField>(0), 3005);
+ BOOST_CHECK_EQUAL(sentPkt4.get<lp::AckField>(1), 3006);
+
+ BOOST_REQUIRE_EQUAL(reliability->m_ackQueue.size(), 1);
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.front(), 3007);
+ BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
+
+ // T+15ms
+ advanceClocks(time::milliseconds(1), 4);
+ BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 4);
+
+ // T+16ms
+ advanceClocks(time::milliseconds(1), 1);
+
+ BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
+ BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 5);
+ lp::Packet sentPkt5(transport->sentPackets[4].packet);
+ BOOST_REQUIRE_EQUAL(sentPkt5.count<lp::AckField>(), 1);
+ BOOST_CHECK_EQUAL(sentPkt5.get<lp::AckField>(), 3007);
+
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 0);
+
+ // T+21ms
+ advanceClocks(time::milliseconds(1), 5);
+
+ BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 5);
+ BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestLpReliability
+
+BOOST_AUTO_TEST_SUITE_END() // Face
+
+} // namespace tests
+} // namespace face
+} // namespace nfd