face: NDNLPv2 fragmentation and reassembly
refs #3171
Change-Id: If29035b697b904ee49cb86d9248be488657c6f9e
diff --git a/daemon/face/generic-link-service.cpp b/daemon/face/generic-link-service.cpp
index 23e2ce2..d6d6c02 100644
--- a/daemon/face/generic-link-service.cpp
+++ b/daemon/face/generic-link-service.cpp
@@ -30,14 +30,26 @@
NFD_LOG_INIT("GenericLinkService");
+GenericLinkServiceCounters::GenericLinkServiceCounters(const LpReassembler& reassembler)
+ : nReassembling(reassembler)
+{
+}
+
GenericLinkService::Options::Options()
: allowLocalFields(false)
+ , allowFragmentation(false)
+ , allowReassembly(false)
{
}
GenericLinkService::GenericLinkService(const GenericLinkService::Options& options)
- : m_options(options)
+ : GenericLinkServiceCounters(m_reassembler)
+ , m_options(options)
+ , m_fragmenter(m_options.fragmenterOptions, this)
+ , m_reassembler(m_options.reassemblerOptions, this)
+ , m_lastSeqNo(-2)
{
+ m_reassembler.beforeTimeout.connect(bind([this] { ++nReassemblyTimeouts; }));
}
void
@@ -47,9 +59,8 @@
if (m_options.allowLocalFields) {
encodeLocalFields(interest, lpPacket);
}
- Transport::Packet packet;
- packet.packet = lpPacket.wireEncode();
- sendPacket(std::move(packet));
+
+ sendNetPacket(std::move(lpPacket));
}
void
@@ -59,9 +70,8 @@
if (m_options.allowLocalFields) {
encodeLocalFields(data, lpPacket);
}
- Transport::Packet packet;
- packet.packet = lpPacket.wireEncode();
- sendPacket(std::move(packet));
+
+ sendNetPacket(std::move(lpPacket));
}
void
@@ -72,9 +82,8 @@
if (m_options.allowLocalFields) {
encodeLocalFields(nack.getInterest(), lpPacket);
}
- Transport::Packet packet;
- packet.packet = lpPacket.wireEncode();
- sendPacket(std::move(packet));
+
+ sendNetPacket(std::move(lpPacket));
}
bool
@@ -117,6 +126,59 @@
}
void
+GenericLinkService::sendNetPacket(lp::Packet&& pkt)
+{
+ std::vector<lp::Packet> frags;
+ const ssize_t mtu = this->getTransport()->getMtu();
+ if (m_options.allowFragmentation && mtu != MTU_UNLIMITED) {
+ bool isOk = false;
+ std::tie(isOk, frags) = m_fragmenter.fragmentPacket(pkt, mtu);
+ if (!isOk) {
+ // fragmentation failed (warning is logged by LpFragmenter)
+ ++nFragmentationErrors;
+ return;
+ }
+ }
+ else {
+ frags.push_back(pkt);
+ }
+
+ if (frags.size() > 1) {
+ // sequence is needed only if packet is fragmented
+ assignSequences(frags);
+ }
+ else {
+ // 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)) {
+ ++nOutOverMtu;
+ NFD_LOG_FACE_WARN("attempt to send packet over MTU limit");
+ continue;
+ }
+ sendPacket(std::move(tp));
+ }
+}
+
+void
+GenericLinkService::assignSequence(lp::Packet& pkt)
+{
+ pkt.set<lp::SequenceField>(++m_lastSeqNo);
+}
+
+void
+GenericLinkService::assignSequences(std::vector<lp::Packet>& pkts)
+{
+ std::for_each(pkts.begin(), pkts.end(), bind(&GenericLinkService::assignSequence, this, _1));
+}
+
+void
GenericLinkService::doReceivePacket(Transport::Packet&& packet)
{
try {
@@ -127,38 +189,55 @@
return;
}
- if (pkt.has<lp::FragIndexField>() || pkt.has<lp::FragCountField>()) {
- NFD_LOG_FACE_WARN("received fragment, but reassembly not implemented: DROP");
+ if ((pkt.has<lp::FragIndexField>() || pkt.has<lp::FragCountField>()) &&
+ !m_options.allowReassembly) {
+ NFD_LOG_FACE_WARN("received fragment, but reassembly disabled: DROP");
return;
}
- ndn::Buffer::const_iterator fragBegin, fragEnd;
- std::tie(fragBegin, fragEnd) = pkt.get<lp::FragmentField>();
- Block netPkt(&*fragBegin, std::distance(fragBegin, fragEnd));
+ bool isReassembled = false;
+ Block netPkt;
+ lp::Packet firstPkt;
+ std::tie(isReassembled, netPkt, firstPkt) = m_reassembler.receiveFragment(packet.remoteEndpoint,
+ pkt);
+ if (isReassembled) {
+ this->decodeNetPacket(netPkt, firstPkt);
+ }
+ }
+ catch (const tlv::Error& e) {
+ ++this->nInLpInvalid;
+ NFD_LOG_FACE_WARN("packet parse error (" << e.what() << "): DROP");
+ }
+}
+void
+GenericLinkService::decodeNetPacket(const Block& netPkt, const lp::Packet& firstPkt)
+{
+ try {
switch (netPkt.type()) {
case tlv::Interest:
- if (pkt.has<lp::NackField>()) {
- this->decodeNack(netPkt, pkt);
+ if (firstPkt.has<lp::NackField>()) {
+ this->decodeNack(netPkt, firstPkt);
}
else {
- this->decodeInterest(netPkt, pkt);
+ this->decodeInterest(netPkt, firstPkt);
}
break;
case tlv::Data:
- this->decodeData(netPkt, pkt);
+ this->decodeData(netPkt, firstPkt);
break;
default:
+ ++this->nInNetInvalid;
NFD_LOG_FACE_WARN("unrecognized network-layer packet TLV-TYPE " << netPkt.type() << ": DROP");
return;
}
}
catch (const tlv::Error& e) {
+ ++this->nInNetInvalid;
NFD_LOG_FACE_WARN("packet parse error (" << e.what() << "): DROP");
}
}
-
void
GenericLinkService::decodeInterest(const Block& netPkt, const lp::Packet& firstPkt)
{
@@ -179,6 +258,7 @@
}
if (firstPkt.has<lp::CachePolicyField>()) {
+ ++this->nInNetInvalid;
NFD_LOG_FACE_WARN("received CachePolicy with Interest: DROP");
return;
}
@@ -199,11 +279,13 @@
auto data = make_shared<Data>(netPkt);
if (firstPkt.has<lp::NackField>()) {
+ ++this->nInNetInvalid;
NFD_LOG_FACE_WARN("received Nack with Data: DROP");
return;
}
if (firstPkt.has<lp::NextHopFaceIdField>()) {
+ ++this->nInNetInvalid;
NFD_LOG_FACE_WARN("received NextHopFaceId with Data: DROP");
return;
}
@@ -216,6 +298,7 @@
data->setCachingPolicy(ndn::nfd::LocalControlHeader::CachingPolicy::NO_CACHE);
break;
default:
+ ++this->nInNetInvalid;
NFD_LOG_FACE_WARN("unrecognized CachePolicyType " << policy << ": DROP");
return;
}
@@ -242,11 +325,13 @@
nack.setHeader(firstPkt.get<lp::NackField>());
if (firstPkt.has<lp::NextHopFaceIdField>()) {
+ ++this->nInNetInvalid;
NFD_LOG_FACE_WARN("received NextHopFaceId with Nack: DROP");
return;
}
if (firstPkt.has<lp::CachePolicyField>()) {
+ ++this->nInNetInvalid;
NFD_LOG_FACE_WARN("received CachePolicy with Nack: DROP");
return;
}
diff --git a/daemon/face/generic-link-service.hpp b/daemon/face/generic-link-service.hpp
index ae3328f..d7cc22f 100644
--- a/daemon/face/generic-link-service.hpp
+++ b/daemon/face/generic-link-service.hpp
@@ -30,16 +30,54 @@
#include "core/logger.hpp"
#include "link-service.hpp"
-
-#include <ndn-cxx/lp/packet.hpp>
+#include "lp-fragmenter.hpp"
+#include "lp-reassembler.hpp"
namespace nfd {
namespace face {
+/** \brief counters provided by GenericLinkService
+ * \note The type name 'GenericLinkServiceCounters' is implementation detail.
+ * Use 'GenericLinkService::Counters' in public API.
+ */
+class GenericLinkServiceCounters : public virtual LinkService::Counters
+{
+public:
+ explicit
+ GenericLinkServiceCounters(const LpReassembler& reassembler);
+
+ /** \brief count of failed fragmentations
+ */
+ PacketCounter nFragmentationErrors;
+
+ /** \brief count of outgoing LpPackets dropped due to exceeding MTU limit
+ *
+ * If this counter is non-zero, the operator should enable fragmentation.
+ */
+ PacketCounter nOutOverMtu;
+
+ /** \brief count of invalid LpPackets dropped before reassembly
+ */
+ PacketCounter nInLpInvalid;
+
+ /** \brief count of network-layer packets currently being reassembled
+ */
+ SizeCounter<LpReassembler> nReassembling;
+
+ /** \brief count of dropped partial network-layer packets due to reassembly timeout
+ */
+ PacketCounter nReassemblyTimeouts;
+
+ /** \brief count of invalid reassembled network-layer packets dropped
+ */
+ PacketCounter nInNetInvalid;
+};
+
/** \brief GenericLinkService is a LinkService that implements the NDNLPv2 protocol
* \sa http://redmine.named-data.net/projects/nfd/wiki/NDNLPv2
*/
class GenericLinkService : public LinkService
+ , protected virtual GenericLinkServiceCounters
{
public:
/** \brief Options that control the behavior of GenericLinkService
@@ -50,13 +88,31 @@
Options();
public:
- // TODO #3171: fragmentation and reassembly options
-
/** \brief enables encoding of IncomingFaceId, and decoding of NextHopFaceId and CachePolicy
*/
bool allowLocalFields;
+
+ /** \brief enables fragmentation
+ */
+ bool allowFragmentation;
+
+ /** \brief options for fragmentation
+ */
+ LpFragmenter::Options fragmenterOptions;
+
+ /** \brief enables reassembly
+ */
+ bool allowReassembly;
+
+ /** \brief options for reassembly
+ */
+ LpReassembler::Options reassemblerOptions;
};
+ /** \brief counters provided by GenericLinkService
+ */
+ typedef GenericLinkServiceCounters Counters;
+
explicit
GenericLinkService(const Options& options = Options());
@@ -70,7 +126,10 @@
void
setOptions(const Options& options);
-private: // send path entrypoint
+ virtual const Counters&
+ getCounters() const DECL_OVERRIDE;
+
+private: // send path
/** \brief sends Interest
*/
void
@@ -87,13 +146,6 @@
void
doSendNack(const ndn::lp::Nack& nack) DECL_OVERRIDE;
-private: // receive path entrypoint
- /** \brief receives Packet
- */
- void
- doReceivePacket(Transport::Packet&& packet) DECL_OVERRIDE;
-
-private: // encoding and decoding
/** \brief encode IncomingFaceId into LpPacket and verify local fields
*/
static bool
@@ -104,6 +156,38 @@
static bool
encodeLocalFields(const Data& data, lp::Packet& lpPacket);
+ /** \brief encode and send a complete network layer packet
+ * \param pkt LpPacket containing a complete network layer packet
+ */
+ void
+ sendNetPacket(lp::Packet&& pkt);
+
+ /** \brief assign a sequence number to an LpPacket
+ */
+ void
+ assignSequence(lp::Packet& pkt);
+
+ /** \brief assign consecutive sequence numbers to LpPackets
+ */
+ void
+ assignSequences(std::vector<lp::Packet>& pkts);
+
+private: // receive path
+ /** \brief receive Packet from Transport
+ */
+ void
+ doReceivePacket(Transport::Packet&& packet) DECL_OVERRIDE;
+
+ /** \brief decode incoming network-layer packet
+ * \param netPkt reassembled network-layer packet
+ * \param firstPkt LpPacket of first fragment
+ *
+ * If decoding is successful, a receive signal is emitted;
+ * otherwise, a warning is logged.
+ */
+ void
+ decodeNetPacket(const Block& netPkt, const lp::Packet& firstPkt);
+
/** \brief decode incoming Interest
* \param netPkt reassembled network-layer packet; TLV-TYPE must be Interest
* \param firstPkt LpPacket of first fragment; must not have Nack field
@@ -142,6 +226,9 @@
private:
Options m_options;
+ LpFragmenter m_fragmenter;
+ LpReassembler m_reassembler;
+ lp::Sequence m_lastSeqNo;
};
inline const GenericLinkService::Options&
@@ -156,6 +243,12 @@
m_options = options;
}
+inline const GenericLinkService::Counters&
+GenericLinkService::getCounters() const
+{
+ return *this;
+}
+
} // namespace face
} // namespace nfd
diff --git a/daemon/face/lp-fragmenter.cpp b/daemon/face/lp-fragmenter.cpp
new file mode 100644
index 0000000..b54f7d5
--- /dev/null
+++ b/daemon/face/lp-fragmenter.cpp
@@ -0,0 +1,162 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015, 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-fragmenter.hpp"
+#include "link-service.hpp"
+#include <ndn-cxx/encoding/tlv.hpp>
+
+namespace nfd {
+namespace face {
+
+NFD_LOG_INIT("LpFragmenter");
+
+static_assert(lp::tlv::LpPacket < 253, "LpPacket TLV-TYPE must fit in 1 octet");
+static_assert(lp::tlv::Sequence < 253, "Sequence TLV-TYPE must fit in 1 octet");
+static_assert(lp::tlv::FragIndex < 253, "FragIndex TLV-TYPE must fit in 1 octet");
+static_assert(lp::tlv::FragCount < 253, "FragCount TLV-TYPE must fit in 1 octet");
+static_assert(lp::tlv::Fragment < 253, "Fragment TLV-TYPE must fit in 1 octet");
+
+/** \brief maximum overhead on a single fragment,
+ * not counting other NDNLPv2 headers
+ */
+static const size_t MAX_SINGLE_FRAG_OVERHEAD =
+ 1 + 9 + // LpPacket TLV-TYPE and TLV-LENGTH
+ 1 + 1 + 8 + // Sequence TLV
+ 1 + 9; // Fragment TLV-TYPE and TLV-LENGTH
+
+/** \brief maximum overhead of adding fragmentation to payload,
+ * not counting other NDNLPv2 headers
+ */
+static const size_t MAX_FRAG_OVERHEAD =
+ 1 + 9 + // LpPacket TLV-TYPE and TLV-LENGTH
+ 1 + 1 + 8 + // Sequence TLV
+ 1 + 1 + 8 + // FragIndex TLV
+ 1 + 1 + 8 + // FragCount TLV
+ 1 + 9; // Fragment TLV-TYPE and TLV-LENGTH
+
+LpFragmenter::Options::Options()
+ : nMaxFragments(400)
+{
+}
+
+LpFragmenter::LpFragmenter(const LpFragmenter::Options& options, const LinkService* linkService)
+ : m_options(options)
+ , m_linkService(linkService)
+{
+}
+
+void
+LpFragmenter::setOptions(const Options& options)
+{
+ m_options = options;
+}
+
+const LinkService*
+LpFragmenter::getLinkService() const
+{
+ return m_linkService;
+}
+
+std::tuple<bool, std::vector<lp::Packet>>
+LpFragmenter::fragmentPacket(const lp::Packet& packet, size_t mtu)
+{
+ BOOST_ASSERT(packet.has<lp::FragmentField>());
+ BOOST_ASSERT(!packet.has<lp::FragIndexField>());
+ BOOST_ASSERT(!packet.has<lp::FragCountField>());
+
+ if (MAX_SINGLE_FRAG_OVERHEAD + packet.wireEncode().size() <= mtu) {
+ // fast path: fragmentation not needed
+ // To qualify for fast path, the packet must have space for adding a sequence number,
+ // because another NDNLPv2 feature may require the sequence number.
+ return std::make_tuple(true, std::vector<lp::Packet>{packet});
+ }
+
+ ndn::Buffer::const_iterator netPktBegin, netPktEnd;
+ std::tie(netPktBegin, netPktEnd) = packet.get<lp::FragmentField>();
+ size_t netPktSize = std::distance(netPktBegin, netPktEnd);
+
+ // compute size of other NDNLPv2 headers to be placed on the first fragment
+ size_t firstHeaderSize = 0;
+ const Block& packetWire = packet.wireEncode();
+ if (packetWire.type() == lp::tlv::LpPacket) {
+ for (const Block& element : packetWire.elements()) {
+ if (element.type() != lp::tlv::Fragment) {
+ firstHeaderSize += element.size();
+ }
+ }
+ }
+
+ // compute payload size
+ if (MAX_FRAG_OVERHEAD + firstHeaderSize + 1 > mtu) { // 1-octet fragment
+ NFD_LOG_FACE_WARN("fragmentation error, MTU too small for first fragment: DROP");
+ return std::make_tuple(false, std::vector<lp::Packet>{});
+ }
+ size_t firstPayloadSize = std::min(netPktSize, mtu - firstHeaderSize - MAX_FRAG_OVERHEAD);
+ size_t payloadSize = mtu - MAX_FRAG_OVERHEAD;
+ size_t fragCount = 1 + ((netPktSize - firstPayloadSize) / payloadSize) +
+ ((netPktSize - firstPayloadSize) % payloadSize != 0);
+
+ // compute FragCount
+ if (fragCount > m_options.nMaxFragments) {
+ NFD_LOG_FACE_WARN("fragmentation error, FragCount over limit: DROP");
+ return std::make_pair(false, std::vector<lp::Packet>{});
+ }
+
+ // populate fragments
+ std::vector<lp::Packet> frags(fragCount);
+ frags.front() = packet; // copy input packet to preserve other NDNLPv2 fields
+ size_t fragIndex = 0;
+ auto fragBegin = netPktBegin,
+ fragEnd = fragBegin + firstPayloadSize;
+ while (fragBegin < netPktEnd) {
+ lp::Packet& frag = frags[fragIndex];
+ frag.add<lp::FragIndexField>(fragIndex);
+ frag.add<lp::FragCountField>(fragCount);
+ frag.set<lp::FragmentField>(std::make_pair(fragBegin, fragEnd));
+ BOOST_ASSERT(frag.wireEncode().size() <= mtu);
+
+ ++fragIndex;
+ fragBegin = fragEnd;
+ fragEnd = std::min(netPktEnd, fragBegin + payloadSize);
+ }
+ BOOST_ASSERT(fragIndex == fragCount);
+
+ return std::make_pair(true, frags);
+}
+
+std::ostream&
+operator<<(std::ostream& os, const FaceLogHelper<LpFragmenter>& flh)
+{
+ if (flh.obj.getLinkService() == nullptr) {
+ os << "[id=0,local=unknown,remote=unknown] ";
+ }
+ else {
+ os << FaceLogHelper<LinkService>(*flh.obj.getLinkService());
+ }
+ return os;
+}
+
+} // namespace face
+} // namespace nfd
diff --git a/daemon/face/lp-fragmenter.hpp b/daemon/face/lp-fragmenter.hpp
new file mode 100644
index 0000000..8275d0f
--- /dev/null
+++ b/daemon/face/lp-fragmenter.hpp
@@ -0,0 +1,92 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015, 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_FRAGMENTER_HPP
+#define NFD_DAEMON_FACE_LP_FRAGMENTER_HPP
+
+#include "face-log.hpp"
+
+#include <ndn-cxx/lp/packet.hpp>
+
+namespace nfd {
+namespace face {
+
+class LinkService;
+
+/** \brief fragments network-layer packets into NDNLPv2 link-layer packets
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/NDNLPv2
+ */
+class LpFragmenter
+{
+public:
+ /** \brief Options that control the behavior of LpFragmenter
+ */
+ class Options
+ {
+ public:
+ Options();
+
+ public:
+ /** \brief maximum number of fragments in a packet
+ */
+ size_t nMaxFragments;
+ };
+
+ explicit
+ LpFragmenter(const Options& options = Options(), const LinkService* linkService = nullptr);
+
+ /** \brief set options for fragmenter
+ */
+ void
+ setOptions(const Options& options);
+
+ /** \return LinkService that owns this instance
+ *
+ * This is only used for logging, and may be nullptr.
+ */
+ const LinkService*
+ getLinkService() const;
+
+ /** \brief fragments a network-layer packet into link-layer packets
+ * \param packet an LpPacket that contains a network-layer packet;
+ * must have Fragment field, must not have FragIndex and FragCount fields
+ * \param mtu maximum allowable LpPacket size after fragmentation and sequence number assignment
+ * \return whether fragmentation succeeded, fragmented packets without sequence number
+ */
+ std::tuple<bool, std::vector<lp::Packet>>
+ fragmentPacket(const lp::Packet& packet, size_t mtu);
+
+private:
+ Options m_options;
+ const LinkService* m_linkService;
+};
+
+std::ostream&
+operator<<(std::ostream& os, const FaceLogHelper<LpFragmenter>& flh);
+
+} // namespace face
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_LP_FRAGMENTER_HPP
diff --git a/daemon/face/lp-reassembler.cpp b/daemon/face/lp-reassembler.cpp
new file mode 100644
index 0000000..88ef1c1
--- /dev/null
+++ b/daemon/face/lp-reassembler.cpp
@@ -0,0 +1,176 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015, 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-reassembler.hpp"
+#include "link-service.hpp"
+#include <numeric>
+
+namespace nfd {
+namespace face {
+
+NFD_LOG_INIT("LpReassembler");
+
+LpReassembler::Options::Options()
+ : nMaxFragments(400)
+ , reassemblyTimeout(time::milliseconds(500))
+{
+}
+
+LpReassembler::LpReassembler(const LpReassembler::Options& options, const LinkService* linkService)
+ : m_options(options)
+ , m_linkService(linkService)
+{
+}
+
+std::tuple<bool, Block, lp::Packet>
+LpReassembler::receiveFragment(Transport::EndpointId remoteEndpoint, const lp::Packet& packet)
+{
+ BOOST_ASSERT(packet.has<lp::FragmentField>());
+
+ static auto FALSE_RETURN = std::make_tuple(false, Block(), lp::Packet());
+
+ // read and check FragIndex and FragCount
+ uint64_t fragIndex = 0;
+ uint64_t fragCount = 1;
+ if (packet.has<lp::FragIndexField>()) {
+ fragIndex = packet.get<lp::FragIndexField>();
+ }
+ if (packet.has<lp::FragCountField>()) {
+ fragCount = packet.get<lp::FragCountField>();
+ }
+
+ if (fragIndex >= fragCount) {
+ NFD_LOG_FACE_WARN("reassembly error, FragIndex>=FragCount: DROP");
+ return FALSE_RETURN;
+ }
+
+ if (fragCount > m_options.nMaxFragments) {
+ NFD_LOG_FACE_WARN("reassembly error, FragCount over limit: DROP");
+ return FALSE_RETURN;
+ }
+
+ // check for fast path
+ if (fragIndex == 0 && fragCount == 1) {
+ ndn::Buffer::const_iterator fragBegin, fragEnd;
+ std::tie(fragBegin, fragEnd) = packet.get<lp::FragmentField>();
+ Block netPkt(&*fragBegin, std::distance(fragBegin, fragEnd));
+ return std::make_tuple(true, netPkt, packet);
+ }
+
+ // check Sequence and compute message identifier
+ if (!packet.has<lp::SequenceField>()) {
+ NFD_LOG_FACE_WARN("reassembly error, Sequence missing: DROP");
+ return FALSE_RETURN;
+ }
+ lp::Sequence messageIdentifier = packet.get<lp::SequenceField>() - fragIndex;
+ Key key = std::make_tuple(remoteEndpoint, messageIdentifier);
+
+ // add to PartialPacket
+ PartialPacket& pp = m_partialPackets[key];
+ if (pp.fragCount == 0) { // new PartialPacket
+ pp.fragCount = fragCount;
+ pp.nReceivedFragments = 0;
+ pp.fragments.resize(fragCount);
+ }
+ else {
+ if (fragCount != pp.fragCount) {
+ NFD_LOG_FACE_WARN("reassembly error, FragCount changed: DROP");
+ return FALSE_RETURN;
+ }
+ }
+
+ if (pp.fragments[fragIndex].has<lp::SequenceField>()) {
+ NFD_LOG_FACE_TRACE("fragment already received: DROP");
+ return FALSE_RETURN;
+ }
+
+ pp.fragments[fragIndex] = packet;
+ ++pp.nReceivedFragments;
+
+ // check complete condition
+ if (pp.nReceivedFragments == pp.fragCount) {
+ Block reassembled = doReassembly(key);
+ lp::Packet firstFrag(std::move(pp.fragments[0]));
+ m_partialPackets.erase(key);
+ return std::make_tuple(true, reassembled, firstFrag);
+ }
+
+ // set drop timer
+ pp.dropTimer = scheduler::schedule(m_options.reassemblyTimeout,
+ bind(&LpReassembler::timeoutPartialPacket, this, key));
+
+ return FALSE_RETURN;
+}
+
+Block
+LpReassembler::doReassembly(const Key& key)
+{
+ PartialPacket& pp = m_partialPackets[key];
+
+ size_t payloadSize = std::accumulate(pp.fragments.begin(), pp.fragments.end(), 0,
+ [&] (size_t sum, const lp::Packet& pkt) -> size_t {
+ ndn::Buffer::const_iterator fragBegin, fragEnd;
+ std::tie(fragBegin, fragEnd) = pkt.get<lp::FragmentField>();
+ return sum + std::distance(fragBegin, fragEnd);
+ });
+
+ ndn::Buffer fragBuffer(payloadSize);
+ ndn::Buffer::iterator it = fragBuffer.begin();
+
+ for (const lp::Packet& frag : pp.fragments) {
+ ndn::Buffer::const_iterator fragBegin, fragEnd;
+ std::tie(fragBegin, fragEnd) = frag.get<lp::FragmentField>();
+ it = std::copy(fragBegin, fragEnd, it);
+ }
+
+ return Block(&*(fragBuffer.cbegin()), std::distance(fragBuffer.cbegin(), fragBuffer.cend()));
+}
+
+void
+LpReassembler::timeoutPartialPacket(const Key& key)
+{
+ auto it = m_partialPackets.find(key);
+ if (it == m_partialPackets.end()) {
+ return;
+ }
+
+ this->beforeTimeout(std::get<0>(key), it->second.nReceivedFragments);
+ m_partialPackets.erase(it);
+}
+
+std::ostream&
+operator<<(std::ostream& os, const FaceLogHelper<LpReassembler>& flh)
+{
+ if (flh.obj.getLinkService() == nullptr) {
+ os << "[id=0,local=unknown,remote=unknown] ";
+ }
+ else {
+ os << FaceLogHelper<LinkService>(*flh.obj.getLinkService());
+ }
+ return os;
+}
+
+} // namespace face
+} // namespace nfd
diff --git a/daemon/face/lp-reassembler.hpp b/daemon/face/lp-reassembler.hpp
new file mode 100644
index 0000000..fd0d874
--- /dev/null
+++ b/daemon/face/lp-reassembler.hpp
@@ -0,0 +1,160 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015, 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_REASSEMBLER_HPP
+#define NFD_DAEMON_FACE_LP_REASSEMBLER_HPP
+
+#include "core/scheduler.hpp"
+#include "face-log.hpp"
+#include "transport.hpp"
+
+#include <ndn-cxx/lp/packet.hpp>
+
+namespace nfd {
+namespace face {
+
+class LinkService;
+
+/** \brief reassembles fragmented network-layer packets
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/NDNLPv2
+ */
+class LpReassembler : noncopyable
+{
+public:
+ /** \brief Options that control the behavior of LpReassembler
+ */
+ class Options
+ {
+ public:
+ Options();
+
+ public:
+ /** \brief maximum number of fragments in a packet
+ *
+ * LpPackets with FragCount over this limit are dropped.
+ */
+ size_t nMaxFragments;
+
+ /** \brief timeout before a partially reassembled packet is dropped
+ */
+ time::nanoseconds reassemblyTimeout;
+ };
+
+ explicit
+ LpReassembler(const Options& options = Options(), const LinkService* linkService = nullptr);
+
+ /** \brief set options for reassembler
+ */
+ void
+ setOptions(const Options& options);
+
+ /** \return LinkService that owns this instance
+ *
+ * This is only used for logging, and may be nullptr.
+ */
+ const LinkService*
+ getLinkService() const;
+
+ /** \brief adds received fragment to buffer
+ * \param remoteEndpoint endpoint whose sends the packet
+ * \param packet received fragment;
+ * must have Fragment field
+ * \return whether network-layer packet has been completely received,
+ * the reassembled network-layer packet,
+ * and the first fragment for inspecting other NDNLPv2 headers
+ * \throw tlv::Error packet is malformed
+ */
+ std::tuple<bool, Block, lp::Packet>
+ receiveFragment(Transport::EndpointId remoteEndpoint, const lp::Packet& packet);
+
+ /** \brief count of partial packets
+ */
+ size_t
+ size() const;
+
+ /** \brief signals before a partial packet is dropped due to timeout
+ *
+ * If a partial packet is incomplete and no new fragment is received
+ * within Options::reassemblyTimeout, it would be dropped due to timeout.
+ * Before it's erased, this signal is emitted with the remote endpoint,
+ * and the number of fragments being dropped.
+ */
+ signal::Signal<LpReassembler, Transport::EndpointId, size_t> beforeTimeout;
+
+private:
+ /** \brief holds all fragments of packet until reassembled
+ */
+ struct PartialPacket
+ {
+ std::vector<lp::Packet> fragments;
+ size_t fragCount; ///< total fragments
+ size_t nReceivedFragments; ///< number of received fragments
+ scheduler::ScopedEventId dropTimer;
+ };
+
+ /** \brief index key for PartialPackets
+ */
+ typedef std::tuple<
+ Transport::EndpointId, // remoteEndpoint
+ lp::Sequence // message identifier (sequence of the first fragment)
+ > Key;
+
+ Block
+ doReassembly(const Key& key);
+
+ void
+ timeoutPartialPacket(const Key& key);
+
+private:
+ Options m_options;
+ std::map<Key, PartialPacket> m_partialPackets;
+ const LinkService* m_linkService;
+};
+
+std::ostream&
+operator<<(std::ostream& os, const FaceLogHelper<LpReassembler>& flh);
+
+inline void
+LpReassembler::setOptions(const Options& options)
+{
+ m_options = options;
+}
+
+inline const LinkService*
+LpReassembler::getLinkService() const
+{
+ return m_linkService;
+}
+
+inline size_t
+LpReassembler::size() const
+{
+ return m_partialPackets.size();
+}
+
+} // namespace face
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_LP_REASSEMBLER_HPP
diff --git a/tests/daemon/face/dummy-transport.hpp b/tests/daemon/face/dummy-transport.hpp
index 6d1451c..ce2833b 100644
--- a/tests/daemon/face/dummy-transport.hpp
+++ b/tests/daemon/face/dummy-transport.hpp
@@ -56,6 +56,12 @@
}
void
+ setMtu(ssize_t mtu)
+ {
+ this->Transport::setMtu(mtu);
+ }
+
+ void
setState(FaceState state)
{
this->Transport::setState(state);
diff --git a/tests/daemon/face/generic-link-service.t.cpp b/tests/daemon/face/generic-link-service.t.cpp
index ac01c7d..f3c3c7b 100644
--- a/tests/daemon/face/generic-link-service.t.cpp
+++ b/tests/daemon/face/generic-link-service.t.cpp
@@ -90,8 +90,12 @@
face->sendInterest(*interest1);
+ BOOST_CHECK_EQUAL(service->getCounters().nOutInterests, 1);
BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 1);
- BOOST_CHECK(transport->sentPackets.back().packet == interest1->wireEncode());
+ 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(SendData)
@@ -105,8 +109,12 @@
face->sendData(*data1);
+ BOOST_CHECK_EQUAL(service->getCounters().nOutData, 1);
BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 1);
- BOOST_CHECK(transport->sentPackets.back().packet == data1->wireEncode());
+ 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(SendNack)
@@ -120,11 +128,13 @@
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_EQUAL(nack1pkt.has<lp::NackField>(), true);
- BOOST_CHECK_EQUAL(nack1pkt.has<lp::FragmentField>(), true);
+ BOOST_CHECK(nack1pkt.has<lp::NackField>());
+ BOOST_CHECK(nack1pkt.has<lp::FragmentField>());
+ BOOST_CHECK(!nack1pkt.has<lp::SequenceField>());
}
BOOST_AUTO_TEST_CASE(ReceiveBareInterest)
@@ -138,6 +148,7 @@
transport->receivePacket(interest1->wireEncode());
+ BOOST_CHECK_EQUAL(service->getCounters().nInInterests, 1);
BOOST_REQUIRE_EQUAL(receivedInterests.size(), 1);
BOOST_CHECK_EQUAL(receivedInterests.back(), *interest1);
}
@@ -157,6 +168,7 @@
transport->receivePacket(lpPacket.wireEncode());
+ BOOST_CHECK_EQUAL(service->getCounters().nInInterests, 1);
BOOST_REQUIRE_EQUAL(receivedInterests.size(), 1);
BOOST_CHECK_EQUAL(receivedInterests.back(), *interest1);
}
@@ -172,6 +184,7 @@
transport->receivePacket(data1->wireEncode());
+ BOOST_CHECK_EQUAL(service->getCounters().nInData, 1);
BOOST_REQUIRE_EQUAL(receivedData.size(), 1);
BOOST_CHECK_EQUAL(receivedData.back(), *data1);
}
@@ -191,6 +204,7 @@
transport->receivePacket(lpPacket.wireEncode());
+ BOOST_CHECK_EQUAL(service->getCounters().nInData, 1);
BOOST_REQUIRE_EQUAL(receivedData.size(), 1);
BOOST_CHECK_EQUAL(receivedData.back(), *data1);
}
@@ -210,7 +224,10 @@
transport->receivePacket(lpPacket.wireEncode());
+ BOOST_CHECK_EQUAL(service->getCounters().nInNacks, 1);
BOOST_REQUIRE_EQUAL(receivedNacks.size(), 1);
+ BOOST_CHECK(receivedNacks.back().getReason() == nack1.getReason());
+ BOOST_CHECK(receivedNacks.back().getInterest() == nack1.getInterest());
}
BOOST_AUTO_TEST_CASE(ReceiveIdlePacket)
@@ -225,7 +242,8 @@
BOOST_CHECK_NO_THROW(transport->receivePacket(lpPacket.wireEncode()));
- // IDLE packet should be ignored
+ // IDLE packet should be ignored, but is not an error
+ BOOST_CHECK_EQUAL(service->getCounters().nInLpInvalid, 0);
BOOST_CHECK_EQUAL(receivedInterests.size(), 0);
BOOST_CHECK_EQUAL(receivedData.size(), 0);
BOOST_CHECK_EQUAL(receivedNacks.size(), 0);
@@ -236,9 +254,112 @@
BOOST_AUTO_TEST_SUITE(Fragmentation)
+BOOST_AUTO_TEST_CASE(FragmentationDisabledExceedMtuDrop)
+{
+ // Initialize with Options that disable fragmentation
+ GenericLinkService::Options options;
+ options.allowFragmentation = false;
+ initialize(options);
+
+ transport->setMtu(55);
+
+ shared_ptr<Data> data = makeData("/test/data/123456789/987654321/123456789");
+ face->sendData(*data);
+
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 0);
+ BOOST_CHECK_EQUAL(service->getCounters().nOutOverMtu, 1);
+}
+
+BOOST_AUTO_TEST_CASE(FragmentationUnlimitedMtu)
+{
+ // Initialize with Options that enable fragmentation
+ GenericLinkService::Options options;
+ options.allowFragmentation = true;
+ initialize(options);
+
+ transport->setMtu(MTU_UNLIMITED);
+
+ shared_ptr<Data> data = makeData("/test/data/123456789/987654321/123456789");
+ face->sendData(*data);
+
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(FragmentationUnderMtu)
+{
+ // Initialize with Options that enable fragmentation
+ GenericLinkService::Options options;
+ options.allowFragmentation = true;
+ initialize(options);
+
+ transport->setMtu(105);
+
+ shared_ptr<Data> data = makeData("/test/data/123456789/987654321/123456789");
+ face->sendData(*data);
+
+ BOOST_CHECK_EQUAL(transport->sentPackets.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(FragmentationOverMtu)
+{
+ // Initialize with Options that enable fragmentation
+ GenericLinkService::Options options;
+ options.allowFragmentation = true;
+ initialize(options);
+
+ transport->setMtu(60);
+
+ shared_ptr<Data> data = makeData("/test/data/123456789/987654321/123456789");
+ face->sendData(*data);
+
+ BOOST_CHECK_GT(transport->sentPackets.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ReassembleFragments)
+{
+ // Initialize with Options that enables reassembly
+ GenericLinkService::Options options;
+ options.allowReassembly = true;
+ initialize(options);
+
+ shared_ptr<Interest> interest = makeInterest(
+ "/mt7P130BHXmtLm5dwaY5dpUM6SWYNN2B05g7y3UhsQuLvDdnTWdNnTeEiLuW3FAbJRSG3tzQ0UfaSEgG9rvYHmsKtgPMag1Hj4Tr");
+ lp::Packet packet(interest->wireEncode());
+
+ // fragment the packet
+ LpFragmenter fragmenter;
+ size_t mtu = 100;
+ bool isOk = false;
+ std::vector<lp::Packet> frags;
+ std::tie(isOk, frags) = fragmenter.fragmentPacket(packet, mtu);
+ BOOST_REQUIRE(isOk);
+ BOOST_CHECK_GT(frags.size(), 1);
+
+ // receive the fragments
+ for (ssize_t fragIndex = frags.size() - 1; fragIndex >= 0; --fragIndex) {
+ size_t sequence = 1000 + fragIndex;
+ frags[fragIndex].add<lp::SequenceField>(sequence);
+
+ transport->receivePacket(frags[fragIndex].wireEncode());
+
+ if (fragIndex > 0) {
+ BOOST_CHECK(receivedInterests.empty());
+ BOOST_CHECK_EQUAL(service->getCounters().nReassembling, 1);
+ }
+ else {
+ BOOST_CHECK_EQUAL(receivedInterests.size(), 1);
+ BOOST_CHECK_EQUAL(receivedInterests.back(), *interest);
+ BOOST_CHECK_EQUAL(service->getCounters().nReassembling, 0);
+ }
+ }
+}
+
BOOST_AUTO_TEST_CASE(ReassemblyDisabledDropFragIndex)
{
- // TODO#3171 Initialize with Options that disables reassembly
+ // Initialize with Options that disables reassembly
+ GenericLinkService::Options options;
+ options.allowReassembly = false;
+ initialize(options);
shared_ptr<Interest> interest = makeInterest("/IgFe6NvH");
lp::Packet packet(interest->wireEncode());
@@ -246,12 +367,16 @@
transport->receivePacket(packet.wireEncode());
+ BOOST_CHECK_EQUAL(service->getCounters().nInLpInvalid, 0); // not an error
BOOST_CHECK(receivedInterests.empty());
}
BOOST_AUTO_TEST_CASE(ReassemblyDisabledDropFragCount)
{
- // TODO#3171 Initialize with Options that disables reassembly
+ // Initialize with Options that disables reassembly
+ GenericLinkService::Options options;
+ options.allowReassembly = false;
+ initialize(options);
shared_ptr<Interest> interest = makeInterest("/SeGmEjvIVX");
lp::Packet packet(interest->wireEncode());
@@ -259,6 +384,7 @@
transport->receivePacket(packet.wireEncode());
+ BOOST_CHECK_EQUAL(service->getCounters().nInLpInvalid, 0); // not an error
BOOST_CHECK(receivedInterests.empty());
}
@@ -298,6 +424,7 @@
transport->receivePacket(packet.wireEncode());
+ BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 0); // not an error
BOOST_CHECK(receivedInterests.empty());
}
@@ -314,6 +441,7 @@
transport->receivePacket(packet.wireEncode());
+ BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 1);
BOOST_CHECK(receivedData.empty());
}
@@ -333,6 +461,7 @@
transport->receivePacket(packet.wireEncode());
+ BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 1);
BOOST_CHECK(receivedNacks.empty());
}
@@ -372,6 +501,7 @@
transport->receivePacket(packet.wireEncode());
+ BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 0); // not an error
BOOST_REQUIRE_EQUAL(receivedData.size(), 1);
BOOST_CHECK(!receivedData.back().getLocalControlHeader().hasCachingPolicy());
}
@@ -391,6 +521,7 @@
transport->receivePacket(packet.wireEncode());
+ BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 1);
BOOST_CHECK(receivedInterests.empty());
}
@@ -410,6 +541,7 @@
transport->receivePacket(packet.wireEncode());
+ BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 1);
BOOST_CHECK(receivedNacks.empty());
}
@@ -461,6 +593,7 @@
transport->receivePacket(packet.wireEncode());
+ BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 0); // not an error
BOOST_REQUIRE_EQUAL(receivedInterests.size(), 1);
BOOST_CHECK(!receivedInterests.back().getLocalControlHeader().hasIncomingFaceId());
}
@@ -478,6 +611,7 @@
transport->receivePacket(packet.wireEncode());
+ BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 0); // not an error
BOOST_REQUIRE_EQUAL(receivedData.size(), 1);
BOOST_CHECK(!receivedData.back().getLocalControlHeader().hasIncomingFaceId());
}
@@ -496,6 +630,7 @@
transport->receivePacket(packet.wireEncode());
+ BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 0); // not an error
BOOST_REQUIRE_EQUAL(receivedNacks.size(), 1);
BOOST_CHECK(!receivedNacks.back().getLocalControlHeader().hasIncomingFaceId());
}
@@ -516,6 +651,7 @@
BOOST_CHECK_NO_THROW(transport->receivePacket(packet));
+ BOOST_CHECK_EQUAL(service->getCounters().nInLpInvalid, 1);
BOOST_CHECK_EQUAL(receivedInterests.size(), 0);
BOOST_CHECK_EQUAL(receivedData.size(), 0);
BOOST_CHECK_EQUAL(receivedNacks.size(), 0);
@@ -533,6 +669,7 @@
BOOST_CHECK_NO_THROW(transport->receivePacket(packet));
+ BOOST_CHECK_EQUAL(service->getCounters().nInLpInvalid, 1);
BOOST_CHECK_EQUAL(receivedInterests.size(), 0);
BOOST_CHECK_EQUAL(receivedData.size(), 0);
BOOST_CHECK_EQUAL(receivedNacks.size(), 0);
diff --git a/tests/daemon/face/lp-fragmenter.t.cpp b/tests/daemon/face/lp-fragmenter.t.cpp
new file mode 100644
index 0000000..4bc7022
--- /dev/null
+++ b/tests/daemon/face/lp-fragmenter.t.cpp
@@ -0,0 +1,163 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015, 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-fragmenter.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace face {
+namespace tests {
+
+using namespace nfd::tests;
+
+BOOST_AUTO_TEST_SUITE(Face)
+
+class LpFragmenterFixture
+{
+public:
+ LpFragmenter fragmenter;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestLpFragmenter, LpFragmenterFixture)
+
+BOOST_AUTO_TEST_CASE(FragmentSingleFragment)
+{
+ size_t mtu = 256;
+
+ lp::Packet packet;
+ packet.add<lp::IncomingFaceIdField>(123);
+
+ shared_ptr<Data> data = makeData("/test/data1");
+ BOOST_REQUIRE_EQUAL(data->wireEncode().size(), 30);
+ packet.add<lp::FragmentField>(std::make_pair(data->wireEncode().begin(),
+ data->wireEncode().end()));
+
+ bool isOk = false;
+ std::vector<lp::Packet> frags;
+ std::tie(isOk, frags) = fragmenter.fragmentPacket(packet, mtu);
+
+ BOOST_REQUIRE(isOk);
+ BOOST_REQUIRE_EQUAL(frags.size(), 1);
+ BOOST_CHECK(frags[0].has<lp::FragmentField>());
+ BOOST_CHECK_EQUAL(frags[0].get<lp::IncomingFaceIdField>(), 123);
+ BOOST_CHECK(!frags[0].has<lp::FragIndexField>());
+ BOOST_CHECK(!frags[0].has<lp::FragCountField>());
+ BOOST_CHECK_LE(frags[0].wireEncode().size(), mtu);
+
+ ndn::Buffer::const_iterator fragBegin, fragEnd;
+ std::tie(fragBegin, fragEnd) = frags[0].get<lp::FragmentField>();
+ BOOST_CHECK_EQUAL_COLLECTIONS(data->wireEncode().begin(), data->wireEncode().end(),
+ fragBegin, fragEnd);
+}
+
+BOOST_AUTO_TEST_CASE(FragmentMultipleFragments)
+{
+ size_t mtu = 90;
+
+ lp::Packet packet;
+ packet.add<lp::IncomingFaceIdField>(123);
+
+ shared_ptr<Data> data = makeData("/test/data1/123456789/987654321/123456789");
+ BOOST_REQUIRE_EQUAL(data->wireEncode().size(), 63);
+ packet.add<lp::FragmentField>(std::make_pair(data->wireEncode().begin(),
+ data->wireEncode().end()));
+
+ bool isOk = false;
+ std::vector<lp::Packet> frags;
+ std::tie(isOk, frags) = fragmenter.fragmentPacket(packet, mtu);
+
+ BOOST_REQUIRE(isOk);
+ BOOST_REQUIRE_EQUAL(frags.size(), 2);
+
+ ndn::Buffer reassembledPayload(63);
+
+ BOOST_CHECK(frags[0].has<lp::FragmentField>());
+ BOOST_CHECK_EQUAL(frags[0].get<lp::IncomingFaceIdField>(), 123);
+ BOOST_CHECK_EQUAL(frags[0].get<lp::FragIndexField>(), 0);
+ BOOST_CHECK_EQUAL(frags[0].get<lp::FragCountField>(), 2);
+ BOOST_CHECK_LE(frags[0].wireEncode().size(), mtu);
+ ndn::Buffer::const_iterator frag0Begin, frag0End;
+ std::tie(frag0Begin, frag0End) = frags[0].get<lp::FragmentField>();
+ BOOST_REQUIRE_LE(std::distance(frag0Begin, frag0End), reassembledPayload.size());
+ auto reassembledPos = std::copy(frag0Begin, frag0End, reassembledPayload.begin());
+
+ BOOST_CHECK(frags[1].has<lp::FragmentField>());
+ BOOST_CHECK(!frags[1].has<lp::IncomingFaceIdField>());
+ BOOST_CHECK_EQUAL(frags[1].get<lp::FragIndexField>(), 1);
+ BOOST_CHECK_EQUAL(frags[1].get<lp::FragCountField>(), 2);
+ BOOST_CHECK_LE(frags[1].wireEncode().size(), mtu);
+ ndn::Buffer::const_iterator frag1Begin, frag1End;
+ std::tie(frag1Begin, frag1End) = frags[1].get<lp::FragmentField>();
+ BOOST_REQUIRE_LE(std::distance(frag1Begin, frag1End),
+ std::distance(reassembledPos, reassembledPayload.end()));
+ std::copy(frag1Begin, frag1End, reassembledPos);
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(data->wireEncode().begin(), data->wireEncode().end(),
+ reassembledPayload.begin(), reassembledPayload.end());
+}
+
+BOOST_AUTO_TEST_CASE(FragmentMtuTooSmall)
+{
+ size_t mtu = 20;
+
+ lp::Packet packet;
+ packet.add<lp::IncomingFaceIdField>(123);
+
+ shared_ptr<Data> data = makeData("/test/data1/123456789/987654321/123456789");
+ packet.add<lp::FragmentField>(std::make_pair(data->wireEncode().begin(),
+ data->wireEncode().end()));
+
+ bool isOk = false;
+ std::tie(isOk, std::ignore) = fragmenter.fragmentPacket(packet, mtu);
+ BOOST_REQUIRE(!isOk);
+}
+
+BOOST_AUTO_TEST_CASE(FragmentOverFragCount)
+{
+ LpFragmenter::Options options;
+ options.nMaxFragments = 2;
+ fragmenter.setOptions(options);
+
+ size_t mtu = 70;
+
+ lp::Packet packet;
+ packet.add<lp::IncomingFaceIdField>(123);
+
+ shared_ptr<Data> data = makeData("/test/data1/123456789/987654321/123456789");
+ packet.add<lp::FragmentField>(std::make_pair(data->wireEncode().begin(),
+ data->wireEncode().end()));
+
+ bool isOk = false;
+ std::tie(isOk, std::ignore) = fragmenter.fragmentPacket(packet, mtu);
+ BOOST_REQUIRE(!isOk);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestLpFragmentation
+BOOST_AUTO_TEST_SUITE_END() // Face
+
+} // namespace tests
+} // namespace face
+} // namespace nfd
diff --git a/tests/daemon/face/lp-reassembler.t.cpp b/tests/daemon/face/lp-reassembler.t.cpp
new file mode 100644
index 0000000..5b62d4e
--- /dev/null
+++ b/tests/daemon/face/lp-reassembler.t.cpp
@@ -0,0 +1,518 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015, 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-reassembler.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace face {
+namespace tests {
+
+using namespace nfd::tests;
+
+BOOST_AUTO_TEST_SUITE(Face)
+
+class LpReassemblerFixture : public UnitTestTimeFixture
+{
+public:
+ LpReassemblerFixture()
+ {
+ reassembler.beforeTimeout.connect(
+ [this] (Transport::EndpointId remoteEp, size_t nDroppedFragments) {
+ timeoutHistory.push_back(std::make_tuple(remoteEp, nDroppedFragments));
+ });
+ }
+
+public:
+ LpReassembler reassembler;
+ std::vector<std::tuple<Transport::EndpointId, size_t>> timeoutHistory;
+
+ static const uint8_t data[10];
+};
+
+const uint8_t LpReassemblerFixture::data[10] = {
+ 0x06, 0x08, // Data
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestLpReassembler, LpReassemblerFixture)
+
+BOOST_AUTO_TEST_SUITE(SingleFragment)
+
+BOOST_AUTO_TEST_CASE(Normal)
+{
+ ndn::Buffer dataBuffer(data, sizeof(data));
+
+ lp::Packet received;
+ received.add<lp::FragmentField>(std::make_pair(dataBuffer.begin(), dataBuffer.end()));
+ received.add<lp::FragIndexField>(0);
+ received.add<lp::FragCountField>(1);
+ received.add<lp::SequenceField>(1000);
+ received.add<lp::NextHopFaceIdField>(200);
+
+ bool isComplete = false;
+ Block netPacket;
+ lp::Packet packet;
+ std::tie(isComplete, netPacket, packet) = reassembler.receiveFragment(0, received);
+
+ BOOST_REQUIRE(isComplete);
+ BOOST_CHECK(packet.has<lp::NextHopFaceIdField>());
+ BOOST_CHECK_EQUAL_COLLECTIONS(data, data + sizeof(data), netPacket.begin(), netPacket.end());
+ BOOST_CHECK_EQUAL(reassembler.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(OmitFragIndex)
+{
+ ndn::Buffer dataBuffer(data, sizeof(data));
+
+ lp::Packet received;
+ received.add<lp::FragmentField>(std::make_pair(dataBuffer.begin(), dataBuffer.end()));
+ received.add<lp::FragCountField>(1);
+ received.add<lp::SequenceField>(1000);
+ received.add<lp::NextHopFaceIdField>(200);
+
+ bool isComplete = false;
+ Block netPacket;
+ lp::Packet packet;
+ std::tie(isComplete, netPacket, packet) = reassembler.receiveFragment(0, received);
+
+ BOOST_REQUIRE(isComplete);
+ BOOST_CHECK(packet.has<lp::NextHopFaceIdField>());
+ BOOST_CHECK_EQUAL_COLLECTIONS(data, data + sizeof(data), netPacket.begin(), netPacket.end());
+}
+
+BOOST_AUTO_TEST_CASE(OmitFragCount)
+{
+ ndn::Buffer dataBuffer(data, sizeof(data));
+
+ lp::Packet received;
+ received.add<lp::FragmentField>(std::make_pair(dataBuffer.begin(), dataBuffer.end()));
+ received.add<lp::FragIndexField>(0);
+ received.add<lp::SequenceField>(1000);
+ received.add<lp::NextHopFaceIdField>(200);
+
+ bool isComplete = false;
+ Block netPacket;
+ lp::Packet packet;
+ std::tie(isComplete, netPacket, packet) = reassembler.receiveFragment(0, received);
+
+ BOOST_REQUIRE(isComplete);
+ BOOST_CHECK(packet.has<lp::NextHopFaceIdField>());
+ BOOST_CHECK_EQUAL_COLLECTIONS(data, data + sizeof(data), netPacket.begin(), netPacket.end());
+}
+
+BOOST_AUTO_TEST_CASE(OmitFragIndexAndFragCount)
+{
+ ndn::Buffer dataBuffer(data, sizeof(data));
+
+ lp::Packet received;
+ received.add<lp::FragmentField>(std::make_pair(dataBuffer.begin(), dataBuffer.end()));
+ received.add<lp::SequenceField>(1000);
+ received.add<lp::NextHopFaceIdField>(200);
+
+ bool isComplete = false;
+ Block netPacket;
+ lp::Packet packet;
+ std::tie(isComplete, netPacket, packet) = reassembler.receiveFragment(0, received);
+
+ BOOST_REQUIRE(isComplete);
+ BOOST_CHECK(packet.has<lp::NextHopFaceIdField>());
+ BOOST_CHECK_EQUAL_COLLECTIONS(data, data + sizeof(data), netPacket.begin(), netPacket.end());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // SingleFragment
+
+BOOST_AUTO_TEST_SUITE(MultiFragment)
+
+BOOST_AUTO_TEST_CASE(Normal)
+{
+ ndn::Buffer data1Buffer(data, 4);
+ ndn::Buffer data2Buffer(data + 4, 4);
+ ndn::Buffer data3Buffer(data + 8, 2);
+
+ lp::Packet received1;
+ received1.add<lp::FragmentField>(std::make_pair(data1Buffer.begin(), data1Buffer.end()));
+ received1.add<lp::FragIndexField>(0);
+ received1.add<lp::FragCountField>(3);
+ received1.add<lp::SequenceField>(1000);
+ received1.add<lp::NextHopFaceIdField>(200);
+
+ lp::Packet received2;
+ received2.add<lp::FragmentField>(std::make_pair(data2Buffer.begin(), data2Buffer.end()));
+ received2.add<lp::FragIndexField>(1);
+ received2.add<lp::FragCountField>(3);
+ received2.add<lp::SequenceField>(1001);
+
+ lp::Packet received3;
+ received3.add<lp::FragmentField>(std::make_pair(data3Buffer.begin(), data3Buffer.end()));
+ received3.add<lp::FragIndexField>(2);
+ received3.add<lp::FragCountField>(3);
+ received3.add<lp::SequenceField>(1002);
+
+ bool isComplete = false;
+ Block netPacket;
+ lp::Packet packet;
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, received1);
+ BOOST_REQUIRE(!isComplete);
+ BOOST_CHECK_EQUAL(reassembler.size(), 1);
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, received2);
+ BOOST_REQUIRE(!isComplete);
+ BOOST_CHECK_EQUAL(reassembler.size(), 1);
+
+ std::tie(isComplete, netPacket, packet) = reassembler.receiveFragment(0, received3);
+ BOOST_REQUIRE(isComplete);
+ BOOST_CHECK(packet.has<lp::NextHopFaceIdField>());
+ BOOST_CHECK_EQUAL_COLLECTIONS(data, data + sizeof(data), netPacket.begin(), netPacket.end());
+ BOOST_CHECK_EQUAL(reassembler.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(OmitFragIndex0)
+{
+ ndn::Buffer data1Buffer(data, 4);
+ ndn::Buffer data2Buffer(data + 4, 4);
+ ndn::Buffer data3Buffer(data + 8, 2);
+
+ lp::Packet received1;
+ received1.add<lp::FragmentField>(std::make_pair(data1Buffer.begin(), data1Buffer.end()));
+ received1.add<lp::FragCountField>(3);
+ received1.add<lp::SequenceField>(1000);
+ received1.add<lp::NextHopFaceIdField>(200);
+
+ lp::Packet received2;
+ received2.add<lp::FragmentField>(std::make_pair(data2Buffer.begin(), data2Buffer.end()));
+ received2.add<lp::FragIndexField>(1);
+ received2.add<lp::FragCountField>(3);
+ received2.add<lp::SequenceField>(1001);
+
+ lp::Packet received3;
+ received3.add<lp::FragmentField>(std::make_pair(data3Buffer.begin(), data3Buffer.end()));
+ received3.add<lp::FragIndexField>(2);
+ received3.add<lp::FragCountField>(3);
+ received3.add<lp::SequenceField>(1002);
+
+ bool isComplete = false;
+ Block netPacket;
+ lp::Packet packet;
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, received1);
+ BOOST_REQUIRE(!isComplete);
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, received2);
+ BOOST_REQUIRE(!isComplete);
+
+ std::tie(isComplete, netPacket, packet) = reassembler.receiveFragment(0, received3);
+ BOOST_REQUIRE(isComplete);
+ BOOST_CHECK(packet.has<lp::NextHopFaceIdField>());
+ BOOST_CHECK_EQUAL_COLLECTIONS(data, data + sizeof(data), netPacket.begin(), netPacket.end());
+}
+
+BOOST_AUTO_TEST_CASE(OutOfOrder)
+{
+ ndn::Buffer data0Buffer(data, 4);
+ ndn::Buffer data1Buffer(data + 4, 4);
+ ndn::Buffer data2Buffer(data + 8, 2);
+
+ lp::Packet frag0;
+ frag0.add<lp::FragmentField>(std::make_pair(data0Buffer.begin(), data0Buffer.end()));
+ frag0.add<lp::FragIndexField>(0);
+ frag0.add<lp::FragCountField>(3);
+ frag0.add<lp::SequenceField>(1000);
+ frag0.add<lp::NextHopFaceIdField>(200);
+
+ lp::Packet frag1;
+ frag1.add<lp::FragmentField>(std::make_pair(data1Buffer.begin(), data1Buffer.end()));
+ frag1.add<lp::FragIndexField>(1);
+ frag1.add<lp::FragCountField>(3);
+ frag1.add<lp::SequenceField>(1001);
+
+ lp::Packet frag2;
+ frag2.add<lp::FragmentField>(std::make_pair(data2Buffer.begin(), data2Buffer.end()));
+ frag2.add<lp::FragIndexField>(2);
+ frag2.add<lp::FragCountField>(3);
+ frag2.add<lp::SequenceField>(1002);
+
+ bool isComplete = false;
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, frag2);
+ BOOST_REQUIRE(!isComplete);
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, frag0);
+ BOOST_REQUIRE(!isComplete);
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, frag1);
+ BOOST_REQUIRE(isComplete);
+}
+
+BOOST_AUTO_TEST_CASE(Duplicate)
+{
+ ndn::Buffer data0Buffer(data, 5);
+
+ lp::Packet frag0;
+ frag0.add<lp::FragmentField>(std::make_pair(data0Buffer.begin(), data0Buffer.end()));
+ frag0.add<lp::FragIndexField>(0);
+ frag0.add<lp::FragCountField>(2);
+ frag0.add<lp::SequenceField>(1000);
+
+ bool isComplete = false;
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, frag0);
+ BOOST_REQUIRE(!isComplete);
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(1, frag0);
+ BOOST_REQUIRE(!isComplete);
+}
+
+BOOST_AUTO_TEST_CASE(Timeout)
+{
+ ndn::Buffer data1Buffer(data, 5);
+ ndn::Buffer data2Buffer(data + 5, 5);
+
+ lp::Packet received1;
+ received1.add<lp::FragmentField>(std::make_pair(data1Buffer.begin(), data1Buffer.end()));
+ received1.add<lp::FragIndexField>(0);
+ received1.add<lp::FragCountField>(2);
+ received1.add<lp::SequenceField>(1000);
+ received1.add<lp::NextHopFaceIdField>(200);
+
+ lp::Packet received2;
+ received2.add<lp::FragmentField>(std::make_pair(data2Buffer.begin(), data2Buffer.end()));
+ received2.add<lp::FragIndexField>(1);
+ received2.add<lp::FragCountField>(2);
+ received2.add<lp::SequenceField>(1001);
+
+ const Transport::EndpointId REMOTE_EP = 11028;
+ bool isComplete = false;
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(REMOTE_EP, received1);
+ BOOST_REQUIRE(!isComplete);
+ BOOST_CHECK_EQUAL(reassembler.size(), 1);
+ BOOST_CHECK(timeoutHistory.empty());
+
+ advanceClocks(time::milliseconds(1), 600);
+ BOOST_CHECK_EQUAL(reassembler.size(), 0);
+ BOOST_REQUIRE_EQUAL(timeoutHistory.size(), 1);
+ BOOST_CHECK_EQUAL(std::get<0>(timeoutHistory.back()), REMOTE_EP);
+ BOOST_CHECK_EQUAL(std::get<1>(timeoutHistory.back()), 1);
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(REMOTE_EP, received2);
+ BOOST_REQUIRE(!isComplete);
+}
+
+BOOST_AUTO_TEST_CASE(MissingSequence)
+{
+ ndn::Buffer data1Buffer(data, 4);
+ ndn::Buffer data2Buffer(data + 4, 4);
+ ndn::Buffer data3Buffer(data + 8, 2);
+
+ lp::Packet received1;
+ received1.add<lp::FragmentField>(std::make_pair(data1Buffer.begin(), data1Buffer.end()));
+ received1.add<lp::FragIndexField>(0);
+ received1.add<lp::FragCountField>(3);
+ received1.add<lp::SequenceField>(1000);
+ received1.add<lp::NextHopFaceIdField>(200);
+
+ lp::Packet received2;
+ received2.add<lp::FragmentField>(std::make_pair(data2Buffer.begin(), data2Buffer.end()));
+ received2.add<lp::FragIndexField>(1);
+ received2.add<lp::FragCountField>(3);
+
+ lp::Packet received3;
+ received3.add<lp::FragmentField>(std::make_pair(data3Buffer.begin(), data3Buffer.end()));
+ received3.add<lp::FragIndexField>(2);
+ received3.add<lp::FragCountField>(3);
+ received3.add<lp::SequenceField>(1002);
+
+ bool isComplete = false;
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, received1);
+ BOOST_REQUIRE(!isComplete);
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, received2);
+ BOOST_REQUIRE(!isComplete);
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, received3);
+ BOOST_REQUIRE(!isComplete);
+
+ advanceClocks(time::milliseconds(1), 600);
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, received2);
+ BOOST_REQUIRE(!isComplete);
+}
+
+BOOST_AUTO_TEST_CASE(FragCountOverLimit)
+{
+ ndn::Buffer data1Buffer(data, sizeof(data));
+
+ lp::Packet received1;
+ received1.add<lp::FragmentField>(std::make_pair(data1Buffer.begin(), data1Buffer.end()));
+ received1.add<lp::FragIndexField>(0);
+ received1.add<lp::FragCountField>(256);
+ received1.add<lp::SequenceField>(1000);
+ received1.add<lp::NextHopFaceIdField>(200);
+
+ bool isComplete = false;
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, received1);
+ BOOST_REQUIRE(!isComplete);
+}
+
+BOOST_AUTO_TEST_CASE(MissingFragCount)
+{
+ ndn::Buffer data1Buffer(data, 4);
+ ndn::Buffer data2Buffer(data + 4, 4);
+ ndn::Buffer data3Buffer(data + 8, 2);
+
+ lp::Packet received1;
+ received1.add<lp::FragmentField>(std::make_pair(data1Buffer.begin(), data1Buffer.end()));
+ received1.add<lp::FragIndexField>(0);
+ received1.add<lp::FragCountField>(3);
+ received1.add<lp::SequenceField>(1000);
+ received1.add<lp::NextHopFaceIdField>(200);
+
+ lp::Packet received2;
+ received2.add<lp::FragmentField>(std::make_pair(data2Buffer.begin(), data2Buffer.end()));
+ received2.add<lp::FragIndexField>(1);
+ received2.add<lp::FragCountField>(50);
+ received2.add<lp::SequenceField>(1001);
+
+ lp::Packet received3;
+ received3.add<lp::FragmentField>(std::make_pair(data3Buffer.begin(), data3Buffer.end()));
+ received3.add<lp::FragIndexField>(2);
+ received3.add<lp::FragCountField>(3);
+ received3.add<lp::SequenceField>(1002);
+
+ bool isComplete = false;
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, received1);
+ BOOST_REQUIRE(!isComplete);
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, received2);
+ BOOST_REQUIRE(!isComplete);
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, received3);
+ BOOST_REQUIRE(!isComplete);
+}
+
+BOOST_AUTO_TEST_CASE(OverFragCount)
+{
+ LpReassembler::Options options;
+ options.nMaxFragments = 2;
+ reassembler.setOptions(options);
+
+ ndn::Buffer data1Buffer(data, 4);
+ ndn::Buffer data2Buffer(data + 4, 4);
+ ndn::Buffer data3Buffer(data + 8, 2);
+
+ lp::Packet received1;
+ received1.add<lp::FragmentField>(std::make_pair(data1Buffer.begin(), data1Buffer.end()));
+ received1.add<lp::FragIndexField>(0);
+ received1.add<lp::FragCountField>(3);
+ received1.add<lp::SequenceField>(1000);
+ received1.add<lp::NextHopFaceIdField>(200);
+
+ lp::Packet received2;
+ received2.add<lp::FragmentField>(std::make_pair(data2Buffer.begin(), data2Buffer.end()));
+ received2.add<lp::FragIndexField>(1);
+ received2.add<lp::FragCountField>(3);
+ received2.add<lp::SequenceField>(1001);
+
+ lp::Packet received3;
+ received3.add<lp::FragmentField>(std::make_pair(data3Buffer.begin(), data3Buffer.end()));
+ received3.add<lp::FragIndexField>(2);
+ received3.add<lp::FragCountField>(3);
+ received3.add<lp::SequenceField>(1002);
+
+ bool isComplete = false;
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, received1);
+ BOOST_REQUIRE(!isComplete);
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, received2);
+ BOOST_REQUIRE(!isComplete);
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(0, received3);
+ BOOST_REQUIRE(!isComplete);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // MultiFragment
+
+BOOST_AUTO_TEST_CASE(MultiRemoteEndpoints)
+{
+ ndn::Buffer data1Buffer(data, 5);
+ ndn::Buffer data2Buffer(data + 5, 5);
+
+ lp::Packet frag1_1;
+ frag1_1.add<lp::FragmentField>(std::make_pair(data1Buffer.begin(), data1Buffer.end()));
+ frag1_1.add<lp::FragIndexField>(0);
+ frag1_1.add<lp::FragCountField>(2);
+ frag1_1.add<lp::SequenceField>(2000);
+ frag1_1.add<lp::NextHopFaceIdField>(200);
+
+ lp::Packet frag1_2;
+ frag1_2.add<lp::FragmentField>(std::make_pair(data2Buffer.begin(), data2Buffer.end()));
+ frag1_2.add<lp::FragIndexField>(1);
+ frag1_2.add<lp::FragCountField>(2);
+ frag1_2.add<lp::SequenceField>(2001);
+
+ lp::Packet frag2_1;
+ frag2_1.add<lp::FragmentField>(std::make_pair(data1Buffer.begin(), data1Buffer.end()));
+ frag2_1.add<lp::FragIndexField>(0);
+ frag2_1.add<lp::FragCountField>(2);
+ frag2_1.add<lp::SequenceField>(2000);
+ frag2_1.add<lp::NextHopFaceIdField>(200);
+
+ lp::Packet frag2_2;
+ frag2_2.add<lp::FragmentField>(std::make_pair(data2Buffer.begin(), data2Buffer.end()));
+ frag2_2.add<lp::FragIndexField>(1);
+ frag2_2.add<lp::FragCountField>(2);
+ frag2_2.add<lp::SequenceField>(2001);
+
+ bool isComplete = false;
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(1, frag1_1);
+ BOOST_REQUIRE(!isComplete);
+ BOOST_CHECK_EQUAL(reassembler.size(), 1);
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(2, frag2_2);
+ BOOST_REQUIRE(!isComplete);
+ BOOST_CHECK_EQUAL(reassembler.size(), 2);
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(1, frag1_2);
+ BOOST_REQUIRE(isComplete);
+ BOOST_CHECK_EQUAL(reassembler.size(), 1);
+
+ std::tie(isComplete, std::ignore, std::ignore) = reassembler.receiveFragment(2, frag2_1);
+ BOOST_REQUIRE(isComplete);
+ BOOST_CHECK_EQUAL(reassembler.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestLpReassembler
+BOOST_AUTO_TEST_SUITE_END() // Face
+
+} // namespace tests
+} // namespace face
+} // namespace nfd