face: NDNLPv2 fragmentation and reassembly
refs #3171
Change-Id: If29035b697b904ee49cb86d9248be488657c6f9e
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