face: implement NDNLP fragmentation in EthernetFace
Change-Id: I5b9ff271087ca0b2d5ee38be2f77911cfe9283f4
Refs: #1209
diff --git a/daemon/face/ethernet-face.cpp b/daemon/face/ethernet-face.cpp
index 0e13844..21a12a4 100644
--- a/daemon/face/ethernet-face.cpp
+++ b/daemon/face/ethernet-face.cpp
@@ -60,6 +60,8 @@
NFD_LOG_INIT("EthernetFace");
+const time::nanoseconds EthernetFace::REASSEMBLER_LIFETIME = time::seconds(60);
+
EthernetFace::EthernetFace(const shared_ptr<boost::asio::posix::stream_descriptor>& socket,
const NetworkInterfaceInfo& interface,
const ethernet::Address& address)
@@ -90,6 +92,8 @@
NFD_LOG_DEBUG("[id:" << getId() << ",endpoint:" << m_interfaceName
<< "] Interface MTU is: " << m_interfaceMtu);
+ m_slicer.reset(new ndnlp::Slicer(m_interfaceMtu));
+
char filter[100];
std::snprintf(filter, sizeof(filter),
"(ether proto 0x%x) && (ether dst %s) && (not ether src %s)",
@@ -114,14 +118,20 @@
EthernetFace::sendInterest(const Interest& interest)
{
onSendInterest(interest);
- sendPacket(interest.wireEncode());
+ ndnlp::PacketArray pa = m_slicer->slice(interest.wireEncode());
+ for (const auto& packet : *pa) {
+ sendPacket(packet);
+ }
}
void
EthernetFace::sendData(const Data& data)
{
onSendData(data);
- sendPacket(data.wireEncode());
+ ndnlp::PacketArray pa = m_slicer->slice(data.wireEncode());
+ for (const auto& packet : *pa) {
+ sendPacket(packet);
+ }
}
void
@@ -257,13 +267,7 @@
return fail("Face closed");
}
- /// \todo Fragmentation
- if (block.size() > m_interfaceMtu)
- {
- NFD_LOG_ERROR("[id:" << getId() << ",endpoint:" << m_interfaceName
- << "] Fragmentation not implemented: dropping packet larger than MTU");
- return;
- }
+ BOOST_ASSERT(block.size() <= m_interfaceMtu);
/// \todo Right now there is no reserve when packet is received, but
/// we should reserve some space at the beginning and at the end
@@ -341,11 +345,12 @@
}
const ether_header* eh = reinterpret_cast<const ether_header*>(packet);
+ const ethernet::Address sourceAddress(eh->ether_shost);
// assert in case BPF fails to filter unwanted frames
BOOST_ASSERT_MSG(ethernet::Address(eh->ether_dhost) == m_destAddress,
"Received frame addressed to a different multicast group");
- BOOST_ASSERT_MSG(ethernet::Address(eh->ether_shost) != m_srcAddress,
+ BOOST_ASSERT_MSG(sourceAddress != m_srcAddress,
"Received frame sent by this host");
BOOST_ASSERT_MSG(ntohs(eh->ether_type) == ethernet::ETHERTYPE_NDN,
"Received frame with unrecognized ethertype");
@@ -354,25 +359,51 @@
length -= ethernet::HDR_LEN;
/// \todo Reserve space in front and at the back of the underlying buffer
- Block element;
- bool isOk = Block::fromBuffer(packet, length, element);
- if (isOk)
- {
- NFD_LOG_TRACE("[id:" << getId() << ",endpoint:" << m_interfaceName
- << "] Received: " << element.size() << " bytes");
- this->getMutableCounters().getNInBytes() += element.size();
-
- if (!decodeAndDispatchInput(element))
- {
- NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
- << "] Received unrecognized block of type " << element.type());
- }
- }
- else
+ Block fragment;
+ bool isOk = Block::fromBuffer(packet, length, fragment);
+ if (!isOk)
{
NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
- << "] Received block is invalid or too large to process");
+ << "] Block received from " << sourceAddress.toString()
+ << " is invalid or too large to process");
+ return;
}
+
+ NFD_LOG_TRACE("[id:" << getId() << ",endpoint:" << m_interfaceName
+ << "] Received: " << fragment.size() << " bytes from "
+ << sourceAddress.toString());
+ this->getMutableCounters().getNInBytes() += fragment.size();
+
+ Reassembler& reassembler = m_reassemblers[sourceAddress];
+ if (!reassembler.pms)
+ {
+ // new sender, setup a PartialMessageStore for it
+ reassembler.pms.reset(new ndnlp::PartialMessageStore);
+ reassembler.pms->onReceive +=
+ [this, sourceAddress] (const Block& block) {
+ NFD_LOG_TRACE("[id:" << getId() << ",endpoint:" << m_interfaceName
+ << "] All fragments received from " << sourceAddress.toString());
+ if (!decodeAndDispatchInput(block))
+ NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
+ << "] Received unrecognized TLV block of type " << block.type()
+ << " from " << sourceAddress.toString());
+ };
+ }
+
+ scheduler::cancel(reassembler.expireEvent);
+ reassembler.expireEvent = scheduler::schedule(REASSEMBLER_LIFETIME,
+ [this, sourceAddress] {
+ BOOST_VERIFY(m_reassemblers.erase(sourceAddress) == 1);
+ });
+
+ try {
+ reassembler.pms->receiveNdnlpData(fragment);
+ }
+ catch (const ndnlp::ParseError& e) {
+ NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
+ << "] Received invalid NDNLP fragment from "
+ << sourceAddress.toString() << " : " << e.what());
+ }
}
void
diff --git a/daemon/face/ethernet-face.hpp b/daemon/face/ethernet-face.hpp
index 1dd6593..87e2238 100644
--- a/daemon/face/ethernet-face.hpp
+++ b/daemon/face/ethernet-face.hpp
@@ -28,8 +28,12 @@
#include "common.hpp"
#include "face.hpp"
+#include "ndnlp-partial-message-store.hpp"
+#include "ndnlp-slicer.hpp"
#include "core/network-interface.hpp"
+#include <unordered_map>
+
#ifndef HAVE_LIBPCAP
#error "Cannot include this file when libpcap is not available"
#endif
@@ -136,6 +140,12 @@
getInterfaceMtu() const;
private:
+ struct Reassembler
+ {
+ unique_ptr<ndnlp::PartialMessageStore> pms;
+ EventId expireEvent;
+ };
+
unique_ptr<pcap_t, void(*)(pcap_t*)> m_pcap;
shared_ptr<boost::asio::posix::stream_descriptor> m_socket;
@@ -145,7 +155,11 @@
std::string m_interfaceName;
ethernet::Address m_srcAddress;
ethernet::Address m_destAddress;
+
size_t m_interfaceMtu;
+ unique_ptr<ndnlp::Slicer> m_slicer;
+ std::unordered_map<ethernet::Address, Reassembler> m_reassemblers;
+ static const time::nanoseconds REASSEMBLER_LIFETIME;
};
} // namespace nfd
diff --git a/tests/daemon/face/ethernet.cpp b/tests/daemon/face/ethernet.cpp
index 54cca5d..ab0181c 100644
--- a/tests/daemon/face/ethernet.cpp
+++ b/tests/daemon/face/ethernet.cpp
@@ -133,10 +133,12 @@
face->sendInterest(*interest2);
face->sendData (*data2 );
- BOOST_CHECK_EQUAL(face->getCounters().getNOutBytes(), interest1->wireEncode().size() +
- data1->wireEncode().size() +
- interest2->wireEncode().size() +
- data2->wireEncode().size());
+ BOOST_CHECK_EQUAL(face->getCounters().getNOutBytes(),
+ 14 * 4 + // 4 NDNLP headers
+ interest1->wireEncode().size() +
+ data1->wireEncode().size() +
+ interest2->wireEncode().size() +
+ data2->wireEncode().size());
// m_ioRemaining = 4;
// m_ioService.run();
@@ -191,11 +193,11 @@
// valid frame, but TLV block has invalid length
static const pcap_pkthdr header3{{}, ethernet::HDR_LEN + ethernet::MIN_DATA_LEN};
static const uint8_t packet3[ethernet::HDR_LEN + ethernet::MIN_DATA_LEN]{
- 0x01, 0x00, 0x5E, 0x00, 0x17, 0xAA, // destination address
+ 0x01, 0x00, 0x5e, 0x00, 0x17, 0xaa, // destination address
0x02, 0x00, 0x00, 0x00, 0x00, 0x02, // source address
0x86, 0x24, // NDN ethertype
- tlv::Data, // TLV type
- 0xFD, 0xFF, 0xFF // TLV length (invalid because greater than packet size)
+ tlv::NdnlpData, // TLV type
+ 0xfd, 0xff, 0xff // TLV length (invalid because greater than buffer size)
};
face->processIncomingPacket(&header3, packet3);
BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 0);
@@ -205,7 +207,7 @@
// valid frame, but TLV block has invalid type
static const pcap_pkthdr header4{{}, ethernet::HDR_LEN + ethernet::MIN_DATA_LEN};
static const uint8_t packet4[ethernet::HDR_LEN + ethernet::MIN_DATA_LEN]{
- 0x01, 0x00, 0x5E, 0x00, 0x17, 0xAA, // destination address
+ 0x01, 0x00, 0x5e, 0x00, 0x17, 0xaa, // destination address
0x02, 0x00, 0x00, 0x00, 0x00, 0x02, // source address
0x86, 0x24, // NDN ethertype
0x00, // TLV type (invalid)
@@ -216,21 +218,41 @@
BOOST_CHECK_EQUAL(recInterests.size(), 0);
BOOST_CHECK_EQUAL(recDatas.size(), 0);
- // valid frame and valid TLV block
+ // valid frame and valid NDNLP header, but invalid payload
static const pcap_pkthdr header5{{}, ethernet::HDR_LEN + ethernet::MIN_DATA_LEN};
static const uint8_t packet5[ethernet::HDR_LEN + ethernet::MIN_DATA_LEN]{
- 0x01, 0x00, 0x5E, 0x00, 0x17, 0xAA, // destination address
+ 0x01, 0x00, 0x5e, 0x00, 0x17, 0xaa, // destination address
0x02, 0x00, 0x00, 0x00, 0x00, 0x02, // source address
- 0x86, 0x24, // NDN ethertype
- tlv::Interest, // TLV type
- 0x16, // TLV length
+ 0x86, 0x24, // NDN ethertype
+ tlv::NdnlpData, 0x0e, // NDNLP header
+ tlv::NdnlpSequence, 0x08,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ tlv::NdnlpPayload, 0x02,
+ 0x00, // NDN TLV type (invalid)
+ 0x00 // NDN TLV length
+ };
+ face->processIncomingPacket(&header5, packet5);
+ BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 18);
+ BOOST_CHECK_EQUAL(recInterests.size(), 0);
+ BOOST_CHECK_EQUAL(recDatas.size(), 0);
+
+ // valid frame, valid NDNLP header, and valid NDN (interest) packet
+ static const pcap_pkthdr header6{{}, ethernet::HDR_LEN + ethernet::MIN_DATA_LEN};
+ static const uint8_t packet6[ethernet::HDR_LEN + ethernet::MIN_DATA_LEN]{
+ 0x01, 0x00, 0x5e, 0x00, 0x17, 0xaa, // destination address
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, // source address
+ 0x86, 0x24, // NDN ethertype
+ tlv::NdnlpData, 0x24, // NDNLP TLV type and length
+ 0x51, 0x08, 0x00, 0x00, 0x00, 0x00, // rest of NDNLP header
+ 0x00, 0x00, 0x00, 0x00, 0x54, 0x18,
+ tlv::Interest, 0x16, // NDN TLV type and length
0x07, 0x0e, 0x08, 0x07, 0x65, 0x78, // payload
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x08,
0x03, 0x66, 0x6f, 0x6f, 0x0a, 0x04,
0x03, 0xef, 0xe9, 0x7c
};
- face->processIncomingPacket(&header5, packet5);
- BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 26);
+ face->processIncomingPacket(&header6, packet6);
+ BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 56);
BOOST_CHECK_EQUAL(recInterests.size(), 1);
BOOST_CHECK_EQUAL(recDatas.size(), 0);
}