face: NDNLP slicer
refs #1206
Change-Id: Ifbf0aeedff8fc9300799c97772d988dce2fce306
diff --git a/daemon/face/face.hpp b/daemon/face/face.hpp
index 58cccf6..68b8073 100644
--- a/daemon/face/face.hpp
+++ b/daemon/face/face.hpp
@@ -21,8 +21,7 @@
const std::size_t MAX_NDN_PACKET_SIZE = 8800;
-/** \class Face
- * \brief represents a face
+/** \brief represents a face
*/
class Face : noncopyable, public enable_shared_from_this<Face>
{
diff --git a/daemon/face/ndnlp-sequence-generator.cpp b/daemon/face/ndnlp-sequence-generator.cpp
new file mode 100644
index 0000000..01dfd14
--- /dev/null
+++ b/daemon/face/ndnlp-sequence-generator.cpp
@@ -0,0 +1,32 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "ndnlp-sequence-generator.hpp"
+
+namespace nfd {
+namespace ndnlp {
+
+SequenceBlock::SequenceBlock(uint64_t start, size_t count)
+ : m_start(start)
+ , m_count(count)
+{
+}
+
+SequenceGenerator::SequenceGenerator()
+ : m_next(0)
+{
+}
+
+SequenceBlock
+SequenceGenerator::nextBlock(size_t count)
+{
+ SequenceBlock sb(m_next, count);
+ m_next += count;
+ return sb;
+}
+
+} // namespace ndnlp
+} // namespace nfd
diff --git a/daemon/face/ndnlp-sequence-generator.hpp b/daemon/face/ndnlp-sequence-generator.hpp
new file mode 100644
index 0000000..f067552
--- /dev/null
+++ b/daemon/face/ndnlp-sequence-generator.hpp
@@ -0,0 +1,72 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_FACE_NDNLP_SEQUENCE_GENERATOR_HPP
+#define NFD_FACE_NDNLP_SEQUENCE_GENERATOR_HPP
+
+#include "common.hpp"
+
+namespace nfd {
+namespace ndnlp {
+
+/** \brief represents a block of sequence numbers
+ */
+class SequenceBlock
+{
+public:
+ SequenceBlock(uint64_t start, size_t count);
+
+ /** \return{ the pos-th sequence number }
+ */
+ uint64_t
+ operator[](size_t pos) const;
+
+ size_t
+ count() const;
+
+private:
+ uint64_t m_start;
+ size_t m_count;
+};
+
+inline uint64_t
+SequenceBlock::operator[](size_t pos) const
+{
+ if (pos >= m_count)
+ throw std::out_of_range("pos");
+ return m_start + static_cast<uint64_t>(pos);
+}
+
+inline size_t
+SequenceBlock::count() const
+{
+ return m_count;
+}
+
+/** \class SequenceGenerator
+ * \brief generates sequence numbers
+ */
+class SequenceGenerator : noncopyable
+{
+public:
+ SequenceGenerator();
+
+ /** \brief generates a block of consecutive sequence numbers
+ *
+ * This block must not overlap with a recent block.
+ */
+ SequenceBlock
+ nextBlock(size_t count);
+
+private:
+ /// next sequence number
+ uint64_t m_next;
+};
+
+} // namespace ndnlp
+} // namespace nfd
+
+#endif // NFD_FACE_NDNLP_SEQUENCE_GENERATOR_HPP
diff --git a/daemon/face/ndnlp-slicer.cpp b/daemon/face/ndnlp-slicer.cpp
new file mode 100644
index 0000000..074a4f7
--- /dev/null
+++ b/daemon/face/ndnlp-slicer.cpp
@@ -0,0 +1,115 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "ndnlp-slicer.hpp"
+
+#include <ndn-cpp-dev/encoding/encoding-buffer.hpp>
+
+namespace nfd {
+namespace ndnlp {
+
+Slicer::Slicer(size_t mtu)
+ : m_mtu(mtu)
+{
+ this->estimateOverhead();
+}
+
+Slicer::~Slicer()
+{
+}
+
+template<bool T>
+static inline size_t
+Slicer_encodeFragment(ndn::EncodingImpl<T>& blk,
+ uint64_t seq, uint16_t fragIndex, uint16_t fragCount,
+ const uint8_t* payload, size_t payloadSize)
+{
+ size_t totalLength = 0;
+
+ // NdnlpPayload
+ size_t payloadLength = blk.prependByteArray(payload, payloadSize);
+ totalLength += payloadLength;
+ totalLength += blk.prependVarNumber(payloadLength);
+ totalLength += blk.prependVarNumber(tlv::NdnlpPayload);
+
+ bool needFragIndexAndCount = fragCount > 1;
+ if (needFragIndexAndCount) {
+ // NdnlpFragCount
+ uint16_t fragCountBE = htobe16(fragCount);
+ size_t fragCountLength = blk.prependByteArray(
+ reinterpret_cast<uint8_t*>(&fragCountBE), sizeof(fragCountBE));
+ totalLength += fragCountLength;
+ totalLength += blk.prependVarNumber(fragCountLength);
+ totalLength += blk.prependVarNumber(tlv::NdnlpFragCount);
+
+ // NdnlpFragIndex
+ uint16_t fragIndexBE = htobe16(fragIndex);
+ size_t fragIndexLength = blk.prependByteArray(
+ reinterpret_cast<uint8_t*>(&fragIndexBE), sizeof(fragIndexBE));
+ totalLength += fragIndexLength;
+ totalLength += blk.prependVarNumber(fragIndexLength);
+ totalLength += blk.prependVarNumber(tlv::NdnlpFragIndex);
+ }
+
+ // NdnlpSequence
+ uint64_t sequenceBE = htobe64(seq);
+ size_t sequenceLength = blk.prependByteArray(
+ reinterpret_cast<uint8_t*>(&sequenceBE), sizeof(sequenceBE));
+ totalLength += sequenceLength;
+ totalLength += blk.prependVarNumber(sequenceLength);
+ totalLength += blk.prependVarNumber(tlv::NdnlpSequence);
+
+ totalLength += blk.prependVarNumber(totalLength);
+ totalLength += blk.prependVarNumber(tlv::NdnlpData);
+
+ return totalLength;
+}
+
+void
+Slicer::estimateOverhead()
+{
+ ndn::EncodingEstimator estimator;
+ size_t estimatedSize = Slicer_encodeFragment(estimator,
+ 0, 0, 2, 0, m_mtu);
+
+ size_t overhead = estimatedSize - m_mtu;
+ m_maxPayload = m_mtu - overhead;
+}
+
+PacketArray
+Slicer::slice(const Block& block)
+{
+ BOOST_ASSERT(block.hasWire());
+ const uint8_t* networkPacket = block.wire();
+ size_t networkPacketSize = block.size();
+
+ uint16_t fragCount = static_cast<uint16_t>(
+ (networkPacketSize / m_maxPayload) +
+ (networkPacketSize % m_maxPayload == 0 ? 0 : 1)
+ );
+ PacketArray pa = make_shared<std::vector<Block> >();
+ pa->reserve(fragCount);
+ SequenceBlock seqBlock = m_seqgen.nextBlock(fragCount);
+
+ for (uint16_t fragIndex = 0; fragIndex < fragCount; ++fragIndex) {
+ size_t payloadOffset = fragIndex * m_maxPayload;
+ const uint8_t* payload = networkPacket + payloadOffset;
+ size_t payloadSize = std::min(m_maxPayload, networkPacketSize - payloadOffset);
+
+ ndn::EncodingBuffer buffer(m_mtu, 0);
+ size_t pktSize = Slicer_encodeFragment(buffer,
+ seqBlock[fragIndex], fragIndex, fragCount, payload, payloadSize);
+
+ BOOST_ASSERT(pktSize <= m_mtu);
+
+ pa->push_back(buffer.block());
+ }
+
+ return pa;
+}
+
+} // namespace ndnlp
+} // namespace nfd
diff --git a/daemon/face/ndnlp-slicer.hpp b/daemon/face/ndnlp-slicer.hpp
new file mode 100644
index 0000000..ba04e3c
--- /dev/null
+++ b/daemon/face/ndnlp-slicer.hpp
@@ -0,0 +1,50 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_FACE_NDNLP_SLICER_HPP
+#define NFD_FACE_NDNLP_SLICER_HPP
+
+#include "ndnlp-tlv.hpp"
+#include "ndnlp-sequence-generator.hpp"
+
+namespace nfd {
+namespace ndnlp {
+
+typedef shared_ptr<std::vector<Block> > PacketArray;
+
+/** \brief provides fragmentation feature at sender
+ */
+class Slicer : noncopyable
+{
+public:
+ explicit
+ Slicer(size_t mtu);
+
+ virtual
+ ~Slicer();
+
+ PacketArray
+ slice(const Block& block);
+
+private:
+ /// estimate the size of NDNLP header and maximum payload size per packet
+ void
+ estimateOverhead();
+
+private:
+ SequenceGenerator m_seqgen;
+
+ /// maximum packet size
+ size_t m_mtu;
+
+ /// maximum payload size
+ size_t m_maxPayload;
+};
+
+} // namespace ndnlp
+} // namespace nfd
+
+#endif // NFD_FACE_NDNLP_SLICER_HPP
diff --git a/daemon/face/ndnlp-tlv.hpp b/daemon/face/ndnlp-tlv.hpp
new file mode 100644
index 0000000..fe15e6a
--- /dev/null
+++ b/daemon/face/ndnlp-tlv.hpp
@@ -0,0 +1,24 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_FACE_NDNLP_TLV_HPP
+#define NFD_FACE_NDNLP_TLV_HPP
+
+namespace nfd {
+namespace tlv {
+
+enum {
+ NdnlpData = 80,
+ NdnlpSequence = 81,
+ NdnlpFragIndex = 82,
+ NdnlpFragCount = 83,
+ NdnlpPayload = 84
+};
+
+} // namespace tlv
+} // namespace nfd
+
+#endif // NFD_FACE_NDNLP_TLV_HPP
diff --git a/tests/face/ndnlp.cpp b/tests/face/ndnlp.cpp
new file mode 100644
index 0000000..f3b8ab6
--- /dev/null
+++ b/tests/face/ndnlp.cpp
@@ -0,0 +1,137 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "face/ndnlp-sequence-generator.hpp"
+#include "face/ndnlp-slicer.hpp"
+
+#include <boost/test/unit_test.hpp>
+
+namespace nfd {
+
+BOOST_AUTO_TEST_SUITE(FaceNdnlp)
+
+BOOST_AUTO_TEST_CASE(SequenceBlock)
+{
+ ndnlp::SequenceBlock sb(0x8000, 2);
+ BOOST_CHECK_EQUAL(sb.count(), 2);
+ BOOST_CHECK_EQUAL(sb[0], 0x8000);
+ BOOST_CHECK_EQUAL(sb[1], 0x8001);
+ BOOST_CHECK_THROW(sb[2], std::out_of_range);
+}
+
+// sequence number can safely wrap around
+BOOST_AUTO_TEST_CASE(SequenceBlockWrap)
+{
+ ndnlp::SequenceBlock sb(std::numeric_limits<uint64_t>::max(), 2);
+ BOOST_CHECK_EQUAL(sb[0], std::numeric_limits<uint64_t>::max());
+ BOOST_CHECK_EQUAL(sb[1], std::numeric_limits<uint64_t>::min());
+ BOOST_CHECK_EQUAL(sb[1] - sb[0], 1);
+}
+
+BOOST_AUTO_TEST_CASE(SequenceGenerator)
+{
+ ndnlp::SequenceGenerator seqgen;
+
+ ndnlp::SequenceBlock sb1 = seqgen.nextBlock(2);
+ BOOST_CHECK_EQUAL(sb1.count(), 2);
+
+ ndnlp::SequenceBlock sb2 = seqgen.nextBlock(1);
+ BOOST_CHECK_NE(sb1[0], sb2[0]);
+ BOOST_CHECK_NE(sb1[1], sb2[0]);
+}
+
+// slice a Block to one NDNLP packet
+BOOST_AUTO_TEST_CASE(Slice1)
+{
+ uint8_t blockValue[60];
+ memset(blockValue, 0xcc, sizeof(blockValue));
+ Block block = ndn::dataBlock(0x01, blockValue, sizeof(blockValue));
+
+ ndnlp::Slicer slicer(9000);
+ ndnlp::PacketArray pa = slicer.slice(block);
+
+ BOOST_REQUIRE_EQUAL(pa->size(), 1);
+
+ const Block& pkt = pa->at(0);
+ BOOST_CHECK_EQUAL(pkt.type(), static_cast<uint32_t>(tlv::NdnlpData));
+ pkt.parse();
+
+ const Block::element_container& elements = pkt.elements();
+ BOOST_REQUIRE_EQUAL(elements.size(), 2);
+
+ const Block& sequenceElement = elements[0];
+ BOOST_CHECK_EQUAL(sequenceElement.type(), static_cast<uint32_t>(tlv::NdnlpSequence));
+ BOOST_REQUIRE_EQUAL(sequenceElement.value_size(), sizeof(uint64_t));
+
+ const Block& payloadElement = elements[1];
+ BOOST_CHECK_EQUAL(payloadElement.type(), static_cast<uint32_t>(tlv::NdnlpPayload));
+ size_t payloadSize = payloadElement.value_size();
+ BOOST_CHECK_EQUAL(payloadSize, block.size());
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(payloadElement.value_begin(), payloadElement.value_end(),
+ block.begin(), block.end());
+}
+
+// slice a Block to four NDNLP packets
+BOOST_AUTO_TEST_CASE(SliceData4)
+{
+ uint8_t blockValue[5050];
+ memset(blockValue, 0xcc, sizeof(blockValue));
+ Block block = ndn::dataBlock(0x01, blockValue, sizeof(blockValue));
+
+ ndnlp::Slicer slicer(1500);
+ ndnlp::PacketArray pa = slicer.slice(block);
+
+ BOOST_REQUIRE_EQUAL(pa->size(), 4);
+
+ uint64_t seq0 = 0xdddd;
+
+ size_t totalPayloadSize = 0;
+
+ for (size_t i = 0; i < 4; ++i) {
+ const Block& pkt = pa->at(i);
+ BOOST_CHECK_EQUAL(pkt.type(), static_cast<uint32_t>(tlv::NdnlpData));
+ pkt.parse();
+
+ const Block::element_container& elements = pkt.elements();
+ BOOST_REQUIRE_EQUAL(elements.size(), 4);
+
+ const Block& sequenceElement = elements[0];
+ BOOST_CHECK_EQUAL(sequenceElement.type(), static_cast<uint32_t>(tlv::NdnlpSequence));
+ BOOST_REQUIRE_EQUAL(sequenceElement.value_size(), sizeof(uint64_t));
+ uint64_t seq = be64toh(*reinterpret_cast<const uint64_t*>(
+ &*sequenceElement.value_begin()));
+ if (i == 0) {
+ seq0 = seq;
+ }
+ BOOST_CHECK_EQUAL(seq, seq0 + i);
+
+ const Block& fragIndexElement = elements[1];
+ BOOST_CHECK_EQUAL(fragIndexElement.type(), static_cast<uint32_t>(tlv::NdnlpFragIndex));
+ BOOST_REQUIRE_EQUAL(fragIndexElement.value_size(), sizeof(uint16_t));
+ uint16_t fragIndex = be16toh(*reinterpret_cast<const uint16_t*>(
+ &*fragIndexElement.value_begin()));
+ BOOST_CHECK_EQUAL(fragIndex, i);
+
+ const Block& fragCountElement = elements[2];
+ BOOST_CHECK_EQUAL(fragCountElement.type(), static_cast<uint32_t>(tlv::NdnlpFragCount));
+ BOOST_REQUIRE_EQUAL(fragCountElement.value_size(), sizeof(uint16_t));
+ uint16_t fragCount = be16toh(*reinterpret_cast<const uint16_t*>(
+ &*fragCountElement.value_begin()));
+ BOOST_CHECK_EQUAL(fragCount, 4);
+
+ const Block& payloadElement = elements[3];
+ BOOST_CHECK_EQUAL(payloadElement.type(), static_cast<uint32_t>(tlv::NdnlpPayload));
+ size_t payloadSize = payloadElement.value_size();
+ totalPayloadSize += payloadSize;
+ }
+
+ BOOST_CHECK_EQUAL(totalPayloadSize, block.size());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace nfd