mgmt+face: allow MTU of datagram faces to be overridden

refs #4005

Change-Id: I01d98b88cdee41b633f6fb9a5600088efe8de749
diff --git a/daemon/face/channel.hpp b/daemon/face/channel.hpp
index db97cc8..6d2c221 100644
--- a/daemon/face/channel.hpp
+++ b/daemon/face/channel.hpp
@@ -89,6 +89,7 @@
   ndn::nfd::FacePersistency persistency = ndn::nfd::FACE_PERSISTENCY_PERSISTENT;
   optional<time::nanoseconds> baseCongestionMarkingInterval;
   optional<uint64_t> defaultCongestionThreshold;
+  optional<ssize_t> mtu;
   bool wantLocalFields = false;
   bool wantLpReliability = false;
   boost::logic::tribool wantCongestionMarking = boost::logic::indeterminate;
diff --git a/daemon/face/ethernet-channel.cpp b/daemon/face/ethernet-channel.cpp
index 69c6f45..691b40b 100644
--- a/daemon/face/ethernet-channel.cpp
+++ b/daemon/face/ethernet-channel.cpp
@@ -203,7 +203,8 @@
 
   auto linkService = make_unique<GenericLinkService>(options);
   auto transport = make_unique<UnicastEthernetTransport>(*m_localEndpoint, remoteEndpoint,
-                                                         params.persistency, m_idleFaceTimeout);
+                                                         params.persistency, m_idleFaceTimeout,
+                                                         params.mtu);
   auto face = make_shared<Face>(std::move(linkService), std::move(transport));
 
   m_channelFaces[remoteEndpoint] = face;
diff --git a/daemon/face/ethernet-factory.cpp b/daemon/face/ethernet-factory.cpp
index f9767df..aab8aa4 100644
--- a/daemon/face/ethernet-factory.cpp
+++ b/daemon/face/ethernet-factory.cpp
@@ -200,6 +200,13 @@
     return;
   }
 
+  if (req.params.mtu && *req.params.mtu < Transport::MIN_MTU) {
+    // The specified MTU must be greater than the minimum possible
+    NFD_LOG_TRACE("createFace cannot create a face with an MTU less than " << Transport::MIN_MTU);
+    onFailure(406, "MTU cannot be less than " + to_string(Transport::MIN_MTU));
+    return;
+  }
+
   for (const auto& i : m_channels) {
     if (i.first == localEndpoint) {
       i.second->connect(remoteEndpoint, req.params, onCreated, onFailure);
diff --git a/daemon/face/tcp-factory.cpp b/daemon/face/tcp-factory.cpp
index 369af43..5b56839 100644
--- a/daemon/face/tcp-factory.cpp
+++ b/daemon/face/tcp-factory.cpp
@@ -179,6 +179,12 @@
     return;
   }
 
+  if (req.params.mtu) {
+    NFD_LOG_TRACE("createFace cannot create a TCP face with an overridden MTU");
+    onFailure(406, "TCP faces do not support MTU overrides");
+    return;
+  }
+
   // very simple logic for now
   for (const auto& i : m_channels) {
     if ((i.first.address().is_v4() && endpoint.address().is_v4()) ||
diff --git a/daemon/face/transport.cpp b/daemon/face/transport.cpp
index f615a10..457e162 100644
--- a/daemon/face/transport.cpp
+++ b/daemon/face/transport.cpp
@@ -126,12 +126,6 @@
   m_service->receivePacket(std::move(packet));
 }
 
-ssize_t
-Transport::getSendQueueLength()
-{
-  return QUEUE_UNSUPPORTED;
-}
-
 bool
 Transport::canChangePersistencyTo(ndn::nfd::FacePersistency newPersistency) const
 {
diff --git a/daemon/face/transport.hpp b/daemon/face/transport.hpp
index a9768e5..a0a97a0 100644
--- a/daemon/face/transport.hpp
+++ b/daemon/face/transport.hpp
@@ -202,13 +202,6 @@
   void
   send(Packet&& packet);
 
-protected: // upper interface to be invoked by subclass
-  /** \brief receive a link-layer packet
-   *  \warning undefined behavior if packet size exceeds MTU limit
-   */
-  void
-  receive(Packet&& packet);
-
 public: // static properties
   /** \return a FaceUri representing local endpoint
    */
@@ -291,7 +284,17 @@
    *  \retval QUEUE_ERROR transport was unable to retrieve the queue length
    */
   virtual ssize_t
-  getSendQueueLength();
+  getSendQueueLength()
+  {
+    return QUEUE_UNSUPPORTED;
+  }
+
+protected: // upper interface to be invoked by subclass
+  /** \brief receive a link-layer packet
+   *  \warning undefined behavior if packet size exceeds MTU limit
+   */
+  void
+  receive(Packet&& packet);
 
 protected: // properties to be set by subclass
   void
@@ -363,6 +366,13 @@
   virtual void
   doSend(Packet&& packet) = 0;
 
+public:
+  /** \brief minimum MTU that may be set on a transport
+   *
+   *  This is done to ensure the NDNLPv2 fragmentation feature functions properly.
+   */
+  static constexpr ssize_t MIN_MTU = 64;
+
 private:
   Face* m_face;
   LinkService* m_service;
diff --git a/daemon/face/udp-channel.cpp b/daemon/face/udp-channel.cpp
index b87e697..eb4337f 100644
--- a/daemon/face/udp-channel.cpp
+++ b/daemon/face/udp-channel.cpp
@@ -160,6 +160,8 @@
   socket.connect(remoteEndpoint);
 
   GenericLinkService::Options options;
+  options.allowFragmentation = true;
+  options.allowReassembly = true;
   options.reliabilityOptions.isEnabled = params.wantLpReliability;
 
   if (boost::logic::indeterminate(params.wantCongestionMarking)) {
@@ -178,7 +180,8 @@
   }
 
   auto linkService = make_unique<GenericLinkService>(options);
-  auto transport = make_unique<UnicastUdpTransport>(std::move(socket), params.persistency, m_idleFaceTimeout);
+  auto transport = make_unique<UnicastUdpTransport>(std::move(socket), params.persistency,
+                                                    m_idleFaceTimeout, params.mtu);
   auto face = make_shared<Face>(std::move(linkService), std::move(transport));
 
   m_channelFaces[remoteEndpoint] = face;
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
index 3660db5..e649871 100644
--- a/daemon/face/udp-factory.cpp
+++ b/daemon/face/udp-factory.cpp
@@ -270,6 +270,13 @@
     return;
   }
 
+  if (req.params.mtu && *req.params.mtu < Transport::MIN_MTU) {
+    // The specified MTU must be greater than the minimum possible
+    NFD_LOG_TRACE("createFace cannot create a face with MTU less than " << Transport::MIN_MTU);
+    onFailure(406, "MTU cannot be less than " + to_string(Transport::MIN_MTU));
+    return;
+  }
+
   // very simple logic for now
   for (const auto& i : m_channels) {
     if ((i.first.address().is_v4() && endpoint.address().is_v4()) ||
diff --git a/daemon/face/unicast-ethernet-transport.cpp b/daemon/face/unicast-ethernet-transport.cpp
index e28fa93..60059bb 100644
--- a/daemon/face/unicast-ethernet-transport.cpp
+++ b/daemon/face/unicast-ethernet-transport.cpp
@@ -35,7 +35,8 @@
 UnicastEthernetTransport::UnicastEthernetTransport(const ndn::net::NetworkInterface& localEndpoint,
                                                    const ethernet::Address& remoteEndpoint,
                                                    ndn::nfd::FacePersistency persistency,
-                                                   time::nanoseconds idleTimeout)
+                                                   time::nanoseconds idleTimeout,
+                                                   optional<ssize_t> overrideMtu)
   : EthernetTransport(localEndpoint, remoteEndpoint)
   , m_idleTimeout(idleTimeout)
 {
@@ -44,7 +45,13 @@
   this->setScope(ndn::nfd::FACE_SCOPE_NON_LOCAL);
   this->setPersistency(persistency);
   this->setLinkType(ndn::nfd::LINK_TYPE_POINT_TO_POINT);
-  this->setMtu(localEndpoint.getMtu());
+
+  if (overrideMtu) {
+    this->setMtu(std::min<ssize_t>(localEndpoint.getMtu(), *overrideMtu));
+  }
+  else {
+    this->setMtu(localEndpoint.getMtu());
+  }
 
   NFD_LOG_FACE_INFO("Creating transport");
 
diff --git a/daemon/face/unicast-ethernet-transport.hpp b/daemon/face/unicast-ethernet-transport.hpp
index 19617e3..5a148c9 100644
--- a/daemon/face/unicast-ethernet-transport.hpp
+++ b/daemon/face/unicast-ethernet-transport.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2017,  Regents of the University of California,
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -44,7 +44,8 @@
   UnicastEthernetTransport(const ndn::net::NetworkInterface& localEndpoint,
                            const ethernet::Address& remoteEndpoint,
                            ndn::nfd::FacePersistency persistency,
-                           time::nanoseconds idleTimeout);
+                           time::nanoseconds idleTimeout,
+                           optional<ssize_t> overrideMtu = {});
 
 protected:
   bool
diff --git a/daemon/face/unicast-udp-transport.cpp b/daemon/face/unicast-udp-transport.cpp
index 8e3d7d8..062c924 100644
--- a/daemon/face/unicast-udp-transport.cpp
+++ b/daemon/face/unicast-udp-transport.cpp
@@ -40,7 +40,8 @@
 
 UnicastUdpTransport::UnicastUdpTransport(protocol::socket&& socket,
                                          ndn::nfd::FacePersistency persistency,
-                                         time::nanoseconds idleTimeout)
+                                         time::nanoseconds idleTimeout,
+                                         optional<ssize_t> overrideMtu)
   : DatagramTransport(std::move(socket))
   , m_idleTimeout(idleTimeout)
 {
@@ -49,7 +50,14 @@
   this->setScope(ndn::nfd::FACE_SCOPE_NON_LOCAL);
   this->setPersistency(persistency);
   this->setLinkType(ndn::nfd::LINK_TYPE_POINT_TO_POINT);
-  this->setMtu(udp::computeMtu(m_socket.local_endpoint()));
+
+  if (overrideMtu) {
+    this->setMtu(std::min(udp::computeMtu(m_socket.local_endpoint()), *overrideMtu));
+  }
+  else {
+    this->setMtu(udp::computeMtu(m_socket.local_endpoint()));
+  }
+  BOOST_ASSERT(this->getMtu() >= MIN_MTU);
 
   NFD_LOG_FACE_INFO("Creating transport");
 
diff --git a/daemon/face/unicast-udp-transport.hpp b/daemon/face/unicast-udp-transport.hpp
index b1eb672..03f1c86 100644
--- a/daemon/face/unicast-udp-transport.hpp
+++ b/daemon/face/unicast-udp-transport.hpp
@@ -42,7 +42,8 @@
 public:
   UnicastUdpTransport(protocol::socket&& socket,
                       ndn::nfd::FacePersistency persistency,
-                      time::nanoseconds idleTimeout);
+                      time::nanoseconds idleTimeout,
+                      optional<ssize_t> overrideMtu = {});
 
 protected:
   bool
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index 1d53eab..b1e934f 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -122,6 +122,9 @@
   if (parameters.hasDefaultCongestionThreshold()) {
     faceParams.defaultCongestionThreshold = parameters.getDefaultCongestionThreshold();
   }
+  if (parameters.hasMtu()) {
+    faceParams.mtu = parameters.getMtu();
+  }
   faceParams.wantLocalFields = parameters.hasFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED) &&
                                parameters.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED);
   faceParams.wantLpReliability = parameters.hasFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED) &&
@@ -243,6 +246,7 @@
 
   // Set ControlResponse fields
   response = collectFaceProperties(*face, false);
+  response.unsetMtu(); // This parameter is only included with the response to faces/create and FaceStatus
 
   done(ControlResponse(200, "OK").setBody(response.wireEncode()));
 }
@@ -294,6 +298,9 @@
   BOOST_ASSERT(linkService != nullptr);
   auto options = linkService->getOptions();
 
+  auto transport = face.getTransport();
+  BOOST_ASSERT(transport != nullptr);
+
   ControlParameters params;
   params.setFaceId(face.getId())
         .setFacePersistency(face.getPersistency())
@@ -302,6 +309,14 @@
         .setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED, options.allowLocalFields, false)
         .setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, options.reliabilityOptions.isEnabled, false)
         .setFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED, options.allowCongestionMarking, false);
+
+  if (transport->getMtu() > 0) {
+    params.setMtu(std::min<uint64_t>(transport->getMtu(), ndn::MAX_NDN_PACKET_SIZE));
+  }
+  else if (transport->getMtu() == face::MTU_UNLIMITED) {
+    params.setMtu(ndn::MAX_NDN_PACKET_SIZE);
+  }
+
   if (wantUris) {
     params.setUri(face.getRemoteUri().toString())
           .setLocalUri(face.getLocalUri().toString());
diff --git a/tests/daemon/face/ethernet-factory.t.cpp b/tests/daemon/face/ethernet-factory.t.cpp
index 9fe867f..b0351b7 100644
--- a/tests/daemon/face/ethernet-factory.t.cpp
+++ b/tests/daemon/face/ethernet-factory.t.cpp
@@ -460,7 +460,7 @@
   createFace(factory,
              FaceUri("ether://[00:00:5e:00:53:5e]"),
              FaceUri("dev://eth0"),
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 504, "No channels available to connect"});
 
   SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
@@ -469,31 +469,37 @@
   createFace(factory,
              FaceUri("ether://[00:00:5e:00:53:5e]"),
              localUri,
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 
   createFace(factory,
              FaceUri("ether://[00:00:5e:00:53:5e]"),
              localUri,
-             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 
   createFace(factory,
              FaceUri("ether://[00:00:5e:00:53:53]"),
              localUri,
-             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 
   createFace(factory,
              FaceUri("ether://[00:00:5e:00:53:57]"),
              localUri,
-             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, false, true, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, {}, false, true, false},
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 
   createFace(factory,
              FaceUri("ether://[00:00:5e:00:53:5b]"),
              localUri,
-             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, false, false, true},
+             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, {}, false, false, true},
+             {CreateFaceExpectedResult::SUCCESS, 0, ""});
+
+  createFace(factory,
+             FaceUri("ether://[00:00:5e:00:53:5c]"),
+             localUri,
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, 1000, false, false, false},
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 }
 
@@ -502,35 +508,35 @@
   createFace(factory,
              FaceUri("ether://[00:00:5e:00:53:5e]"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 406,
               "Creation of unicast Ethernet faces requires a LocalUri with dev:// scheme"});
 
   createFace(factory,
              FaceUri("ether://[00:00:5e:00:53:5e]"),
              FaceUri("udp4://127.0.0.1:20071"),
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 406,
               "Creation of unicast Ethernet faces requires a LocalUri with dev:// scheme"});
 
   createFace(factory,
              FaceUri("ether://[00:00:5e:00:53:5e]"),
              FaceUri("dev://eth0"),
-             {ndn::nfd::FACE_PERSISTENCY_ON_DEMAND, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_ON_DEMAND, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 406,
               "Outgoing Ethernet faces do not support on-demand persistency"});
 
   createFace(factory,
              FaceUri("ether://[01:00:5e:90:10:5e]"),
              FaceUri("dev://eth0"),
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 406,
               "Cannot create multicast Ethernet faces"});
 
   createFace(factory,
              FaceUri("ether://[00:00:5e:00:53:5e]"),
              FaceUri("dev://eth0"),
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, true, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, true, false, false},
              {CreateFaceExpectedResult::FAILURE, 406,
               "Local fields can only be enabled on faces with local scope"});
 }
diff --git a/tests/daemon/face/lp-fragmenter.t.cpp b/tests/daemon/face/lp-fragmenter.t.cpp
index b3ee8d6..4bc8607 100644
--- a/tests/daemon/face/lp-fragmenter.t.cpp
+++ b/tests/daemon/face/lp-fragmenter.t.cpp
@@ -24,6 +24,7 @@
  */
 
 #include "face/lp-fragmenter.hpp"
+#include "face/transport.hpp"
 
 #include "tests/test-common.hpp"
 
@@ -74,7 +75,7 @@
 
 BOOST_AUTO_TEST_CASE(FragmentMultipleFragments)
 {
-  size_t mtu = 90;
+  size_t mtu = Transport::MIN_MTU;
 
   lp::Packet packet;
   packet.add<lp::IncomingFaceIdField>(123);
@@ -89,14 +90,14 @@
   std::tie(isOk, frags) = fragmenter.fragmentPacket(packet, mtu);
 
   BOOST_REQUIRE(isOk);
-  BOOST_REQUIRE_EQUAL(frags.size(), 2);
+  BOOST_REQUIRE_EQUAL(frags.size(), 5);
 
   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_EQUAL(frags[0].get<lp::FragCountField>(), 5);
   BOOST_CHECK_LE(frags[0].wireEncode().size(), mtu);
   ndn::Buffer::const_iterator frag0Begin, frag0End;
   std::tie(frag0Begin, frag0End) = frags[0].get<lp::FragmentField>();
@@ -106,13 +107,46 @@
   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_EQUAL(frags[1].get<lp::FragCountField>(), 5);
   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);
+  reassembledPos = std::copy(frag1Begin, frag1End, reassembledPos);
+
+  BOOST_CHECK(frags[2].has<lp::FragmentField>());
+  BOOST_CHECK(!frags[2].has<lp::IncomingFaceIdField>());
+  BOOST_CHECK_EQUAL(frags[2].get<lp::FragIndexField>(), 2);
+  BOOST_CHECK_EQUAL(frags[2].get<lp::FragCountField>(), 5);
+  BOOST_CHECK_LE(frags[2].wireEncode().size(), mtu);
+  ndn::Buffer::const_iterator frag2Begin, frag2End;
+  std::tie(frag2Begin, frag2End) = frags[2].get<lp::FragmentField>();
+  BOOST_REQUIRE_LE(std::distance(frag2Begin, frag2End),
+                   std::distance(reassembledPos, reassembledPayload.end()));
+  reassembledPos = std::copy(frag2Begin, frag2End, reassembledPos);
+
+  BOOST_CHECK(frags[3].has<lp::FragmentField>());
+  BOOST_CHECK(!frags[3].has<lp::IncomingFaceIdField>());
+  BOOST_CHECK_EQUAL(frags[3].get<lp::FragIndexField>(), 3);
+  BOOST_CHECK_EQUAL(frags[3].get<lp::FragCountField>(), 5);
+  BOOST_CHECK_LE(frags[3].wireEncode().size(), mtu);
+  ndn::Buffer::const_iterator frag3Begin, frag3End;
+  std::tie(frag3Begin, frag3End) = frags[3].get<lp::FragmentField>();
+  BOOST_REQUIRE_LE(std::distance(frag3Begin, frag3End),
+                   std::distance(reassembledPos, reassembledPayload.end()));
+  reassembledPos = std::copy(frag3Begin, frag3End, reassembledPos);
+
+  BOOST_CHECK(frags[4].has<lp::FragmentField>());
+  BOOST_CHECK(!frags[4].has<lp::IncomingFaceIdField>());
+  BOOST_CHECK_EQUAL(frags[4].get<lp::FragIndexField>(), 4);
+  BOOST_CHECK_EQUAL(frags[4].get<lp::FragCountField>(), 5);
+  BOOST_CHECK_LE(frags[4].wireEncode().size(), mtu);
+  ndn::Buffer::const_iterator frag4Begin, frag4End;
+  std::tie(frag4Begin, frag4End) = frags[4].get<lp::FragmentField>();
+  BOOST_REQUIRE_LE(std::distance(frag4Begin, frag4End),
+                   std::distance(reassembledPos, reassembledPayload.end()));
+  std::copy(frag4Begin, frag4End, reassembledPos);
 
   BOOST_CHECK_EQUAL_COLLECTIONS(data->wireEncode().begin(), data->wireEncode().end(),
                                 reassembledPayload.begin(), reassembledPayload.end());
@@ -121,6 +155,7 @@
 BOOST_AUTO_TEST_CASE(FragmentMtuTooSmall)
 {
   size_t mtu = 20;
+  BOOST_ASSERT(mtu < Transport::MIN_MTU);
 
   lp::Packet packet;
   packet.add<lp::IncomingFaceIdField>(123);
diff --git a/tests/daemon/face/lp-reliability.t.cpp b/tests/daemon/face/lp-reliability.t.cpp
index ddd1e33..d2b9f22 100644
--- a/tests/daemon/face/lp-reliability.t.cpp
+++ b/tests/daemon/face/lp-reliability.t.cpp
@@ -795,17 +795,17 @@
 
 BOOST_AUTO_TEST_CASE(PiggybackAcksMtuNoSpace)
 {
-  // MTU is 250, payload has 230 octets plus 4 octets for LpPacket and Fragment TL and 10 octets for
+  // MTU is 64, payload has 44 octets plus 4 octets for LpPacket and Fragment TL and 10 octets for
   // TxSequence, leaving 6 octets for piggybacking. Each Ack header is 12 octets, so there's no room
   // to piggyback any Ack in LpPacket.
 
-  transport->setMtu(250);
+  transport->setMtu(Transport::MIN_MTU);
 
   for (lp::Sequence i = 1000; i < 1100; i++) {
     reliability->m_ackQueue.push(i);
   }
 
-  lp::Packet pkt = makeFrag(1, 230);
+  lp::Packet pkt = makeFrag(1, 44);
   linkService->sendLpPackets({pkt});
 
   BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 1);
diff --git a/tests/daemon/face/tcp-factory.t.cpp b/tests/daemon/face/tcp-factory.t.cpp
index 5bb4b9f..21c49cc 100644
--- a/tests/daemon/face/tcp-factory.t.cpp
+++ b/tests/daemon/face/tcp-factory.t.cpp
@@ -169,7 +169,7 @@
   createFace(factory,
              FaceUri("tcp4://127.0.0.1:6363"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::SUCCESS, 0, ""},
              [] (const nfd::Face& face) {
                BOOST_CHECK_EQUAL(face.getScope(), ndn::nfd::FACE_SCOPE_LOCAL);
@@ -211,7 +211,7 @@
   createFace(factory,
              FaceUri("tcp4://127.0.0.1:6363"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::SUCCESS, 0, ""},
              [] (const nfd::Face& face) {
                BOOST_CHECK_EQUAL(face.getScope(), ndn::nfd::FACE_SCOPE_NON_LOCAL);
@@ -362,7 +362,7 @@
   createFace(factory,
              FaceUri("tcp4://127.0.0.1:6363"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 504, "No channels available to connect"});
 
   createChannel("127.0.0.1", "20071");
@@ -370,31 +370,31 @@
   createFace(factory,
              FaceUri("tcp4://127.0.0.1:6363"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 
   createFace(factory,
              FaceUri("tcp4://127.0.0.1:6363"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 
   createFace(factory,
              FaceUri("tcp4://127.0.0.1:20072"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 
   createFace(factory,
              FaceUri("tcp4://127.0.0.1:20073"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, true, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, true, false},
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 
   createFace(factory,
              FaceUri("tcp4://127.0.0.1:20073"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, true},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, true},
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 }
 
@@ -405,23 +405,30 @@
   createFace(factory,
              FaceUri("tcp4://127.0.0.1:20072"),
              FaceUri("tcp4://127.0.0.1:20071"),
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 406,
               "Unicast TCP faces cannot be created with a LocalUri"});
 
   createFace(factory,
              FaceUri("tcp4://127.0.0.1:20072"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_ON_DEMAND, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_ON_DEMAND, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 406,
               "Outgoing TCP faces do not support on-demand persistency"});
 
   createFace(factory,
              FaceUri("tcp4://198.51.100.100:6363"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, true, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, true, false, false},
              {CreateFaceExpectedResult::FAILURE, 406,
               "Local fields can only be enabled on faces with local scope"});
+
+  createFace(factory,
+             FaceUri("tcp4://127.0.0.1:20072"),
+             {},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, 1000, false, false, false},
+             {CreateFaceExpectedResult::FAILURE, 406,
+              "TCP faces do not support MTU overrides"});
 }
 
 class CreateFaceTimeoutFixture : public TcpFactoryFixture
diff --git a/tests/daemon/face/udp-factory.t.cpp b/tests/daemon/face/udp-factory.t.cpp
index 42bfe1d..360cc34 100644
--- a/tests/daemon/face/udp-factory.t.cpp
+++ b/tests/daemon/face/udp-factory.t.cpp
@@ -973,7 +973,7 @@
   createFace(factory,
              FaceUri("udp4://127.0.0.1:6363"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 504, "No channels available to connect"});
 
   createChannel("127.0.0.1", 20071);
@@ -981,31 +981,37 @@
   createFace(factory,
              FaceUri("udp4://127.0.0.1:6363"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 
   createFace(factory,
              FaceUri("udp4://127.0.0.1:6363"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 
   createFace(factory,
              FaceUri("udp4://127.0.0.1:20072"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 
   createFace(factory,
              FaceUri("udp4://127.0.0.1:20073"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, false, true, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, {}, false, true, false},
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 
   createFace(factory,
              FaceUri("udp4://127.0.0.1:20073"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, false, false, true},
+             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, {}, false, false, true},
+             {CreateFaceExpectedResult::SUCCESS, 0, ""});
+
+  createFace(factory,
+             FaceUri("udp4://127.0.0.1:20074"),
+             {},
+             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, 1000, false, false, false},
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 }
 
@@ -1016,28 +1022,28 @@
   createFace(factory,
              FaceUri("udp4://127.0.0.1:20072"),
              FaceUri("udp4://127.0.0.1:20071"),
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 406,
               "Unicast UDP faces cannot be created with a LocalUri"});
 
   createFace(factory,
              FaceUri("udp4://127.0.0.1:20072"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_ON_DEMAND, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_ON_DEMAND, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 406,
               "Outgoing UDP faces do not support on-demand persistency"});
 
   createFace(factory,
              FaceUri("udp4://233.252.0.1:23252"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 406,
               "Cannot create multicast UDP faces"});
 
   createFace(factory,
              FaceUri("udp4://127.0.0.1:20072"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, true, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, true, false, false},
              {CreateFaceExpectedResult::FAILURE, 406,
               "Local fields can only be enabled on faces with local scope"});
 }
diff --git a/tests/daemon/face/unix-stream-factory.t.cpp b/tests/daemon/face/unix-stream-factory.t.cpp
index 886a649..dd6f5cf 100644
--- a/tests/daemon/face/unix-stream-factory.t.cpp
+++ b/tests/daemon/face/unix-stream-factory.t.cpp
@@ -126,19 +126,19 @@
   createFace(factory,
              FaceUri("unix:///var/run/nfd.sock"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_ON_DEMAND, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_ON_DEMAND, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 
   createFace(factory,
              FaceUri("unix:///var/run/nfd.sock"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 
   createFace(factory,
              FaceUri("unix:///var/run/nfd.sock"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 }
 
diff --git a/tests/daemon/face/websocket-factory.t.cpp b/tests/daemon/face/websocket-factory.t.cpp
index bedef87..5c85e2e 100644
--- a/tests/daemon/face/websocket-factory.t.cpp
+++ b/tests/daemon/face/websocket-factory.t.cpp
@@ -280,19 +280,19 @@
   createFace(factory,
              FaceUri("ws://127.0.0.1:20070"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_ON_DEMAND, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_ON_DEMAND, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 
   createFace(factory,
              FaceUri("ws://127.0.0.1:20070"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 
   createFace(factory,
              FaceUri("ws://127.0.0.1:20070"),
              {},
-             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, false, false, false},
+             {ndn::nfd::FACE_PERSISTENCY_PERMANENT, {}, {}, {}, false, false, false},
              {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 }
 
diff --git a/tests/daemon/mgmt/face-manager-create-face.t.cpp b/tests/daemon/mgmt/face-manager-create-face.t.cpp
index 87edbe2..41c68c2 100644
--- a/tests/daemon/mgmt/face-manager-create-face.t.cpp
+++ b/tests/daemon/mgmt/face-manager-create-face.t.cpp
@@ -23,6 +23,9 @@
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
+#define BOOST_MPL_LIMIT_VECTOR_SIZE 40
+
 #include "mgmt/face-manager.hpp"
 #include "face/generic-link-service.hpp"
 #include "face-manager-command-fixture.hpp"
@@ -242,6 +245,32 @@
   }
 };
 
+class TcpFaceMtuOverride
+{
+public:
+  static ControlParameters
+  getParameters()
+  {
+    return ControlParameters()
+      .setUri("tcp4://127.0.0.1:26363")
+      .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT)
+      .setMtu(1000);
+  }
+};
+
+class UdpFaceMtuOverride
+{
+public:
+  static ControlParameters
+  getParameters()
+  {
+    return ControlParameters()
+      .setUri("udp4://127.0.0.1:26363")
+      .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT)
+      .setMtu(1000);
+  }
+};
+
 class FaceUriMalformed
 {
 public:
@@ -295,6 +324,8 @@
                     mpl::pair<UdpFaceLpReliabilityDisabled, CommandSuccess>,
                     mpl::pair<TcpFaceCongestionMarkingEnabled, CommandSuccess>,
                     mpl::pair<TcpFaceCongestionMarkingDisabled, CommandSuccess>,
+                    mpl::pair<TcpFaceMtuOverride, CommandFailure<406>>,
+                    mpl::pair<UdpFaceMtuOverride, CommandSuccess>,
                     mpl::pair<FaceUriMalformed, CommandFailure<400>>,
                     mpl::pair<FaceUriNonCanonical, CommandFailure<400>>,
                     mpl::pair<FaceUriUnsupportedScheme, CommandFailure<406>>>;
@@ -361,6 +392,10 @@
         else {
           BOOST_CHECK_EQUAL(actualParams.getDefaultCongestionThreshold(), 65536);
         }
+
+        if (expectedParams.hasMtu()) {
+          BOOST_CHECK_EQUAL(expectedParams.getMtu(), actualParams.getMtu());
+        }
       }
       else {
         BOOST_CHECK_EQUAL(expectedParams.getUri(), actualParams.getUri());