face: allow setting default UDP face MTU in config

refs #5138

Change-Id: Ibb3767b27aec2b046d1f41292f3d61001866f8c0
diff --git a/tests/daemon/common/config-file.t.cpp b/tests/daemon/common/config-file.t.cpp
index 2a2cff4..c24e335 100644
--- a/tests/daemon/common/config-file.t.cpp
+++ b/tests/daemon/common/config-file.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2020,  Regents of the University of California,
+ * Copyright (c) 2014-2021,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -375,6 +375,58 @@
                     ConfigFile::Error);
 }
 
+BOOST_AUTO_TEST_CASE(CheckRange)
+{
+  {
+    uint32_t value = 8000;
+    uint32_t min = 8000;
+    uint32_t max = 8999;
+    ConfigFile::checkRange(value, min, max, "key", "section");
+  }
+
+  {
+    size_t value = 8999;
+    size_t min = 8000;
+    size_t max = 8999;
+    ConfigFile::checkRange(value, min, max, "key", "section");
+  }
+
+  {
+    int64_t value = -7000;
+    int64_t min = -7999;
+    int64_t max = 1000;
+    ConfigFile::checkRange(value, min, max, "key", "section");
+  }
+
+  {
+    uint32_t value = 7999;
+    uint32_t min = 8000;
+    uint32_t max = 8999;
+    BOOST_CHECK_THROW(ConfigFile::checkRange(value, min, max, "key", "section"), ConfigFile::Error);
+  }
+
+  {
+    int16_t value = 9000;
+    int16_t min = 8000;
+    int16_t max = 8999;
+    BOOST_CHECK_THROW(ConfigFile::checkRange(value, min, max, "key", "section"), ConfigFile::Error);
+  }
+
+  {
+    int32_t value = -8000;
+    int32_t min = -7999;
+    int32_t max = 1000;
+    BOOST_CHECK_THROW(ConfigFile::checkRange(value, min, max, "key", "section"), ConfigFile::Error);
+  }
+
+  {
+    int64_t value = 0x1001;
+    int64_t min = -0x7fff;
+    int64_t max = 0x1000;
+    BOOST_CHECK_THROW(ConfigFile::checkRange(value, min, max, "key", "section"), ConfigFile::Error);
+  }
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestConfigFile
 
 } // namespace tests
diff --git a/tests/daemon/face/channel-fixture.hpp b/tests/daemon/face/channel-fixture.hpp
index 58b5ee4..31a9439 100644
--- a/tests/daemon/face/channel-fixture.hpp
+++ b/tests/daemon/face/channel-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2020,  Regents of the University of California,
+ * Copyright (c) 2014-2021,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -71,17 +71,17 @@
   }
 
   virtual shared_ptr<ChannelT>
-  makeChannel(const boost::asio::ip::address&, uint16_t port = 0)
+  makeChannel(const boost::asio::ip::address&, uint16_t port = 0, optional<size_t> mtu = nullopt)
   {
     BOOST_FAIL("Unimplemented");
     return nullptr;
   }
 
   void
-  listen(const boost::asio::ip::address& addr)
+  listen(const boost::asio::ip::address& addr, optional<size_t> mtu = nullopt)
   {
     listenerEp = EndpointT{addr, 7030};
-    listenerChannel = makeChannel(addr, 7030);
+    listenerChannel = makeChannel(addr, 7030, mtu);
     listenerChannel->listen(
       [this] (const shared_ptr<Face>& newFace) {
         BOOST_REQUIRE(newFace != nullptr);
diff --git a/tests/daemon/face/tcp-channel-fixture.hpp b/tests/daemon/face/tcp-channel-fixture.hpp
index 41f17d5..ca0f28e 100644
--- a/tests/daemon/face/tcp-channel-fixture.hpp
+++ b/tests/daemon/face/tcp-channel-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2020,  Regents of the University of California,
+ * Copyright (c) 2014-2021,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -44,7 +44,7 @@
   }
 
   shared_ptr<TcpChannel>
-  makeChannel(const boost::asio::ip::address& addr, uint16_t port = 0) final
+  makeChannel(const boost::asio::ip::address& addr, uint16_t port = 0, optional<size_t> mtu = nullopt) final
   {
     if (port == 0)
       port = getNextPort();
diff --git a/tests/daemon/face/udp-channel-fixture.hpp b/tests/daemon/face/udp-channel-fixture.hpp
index 93c5a86..6f57b90 100644
--- a/tests/daemon/face/udp-channel-fixture.hpp
+++ b/tests/daemon/face/udp-channel-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2020,  Regents of the University of California,
+ * Copyright (c) 2014-2021,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -39,12 +39,12 @@
 {
 protected:
   shared_ptr<UdpChannel>
-  makeChannel(const boost::asio::ip::address& addr, uint16_t port = 0) final
+  makeChannel(const boost::asio::ip::address& addr, uint16_t port = 0, optional<size_t> mtu = nullopt) final
   {
     if (port == 0)
       port = getNextPort();
 
-    return std::make_shared<UdpChannel>(udp::Endpoint(addr, port), 2_s, false);
+    return std::make_shared<UdpChannel>(udp::Endpoint(addr, port), 2_s, false, mtu.value_or(ndn::MAX_NDN_PACKET_SIZE));
   }
 
   void
diff --git a/tests/daemon/face/udp-channel.t.cpp b/tests/daemon/face/udp-channel.t.cpp
new file mode 100644
index 0000000..b61e5af
--- /dev/null
+++ b/tests/daemon/face/udp-channel.t.cpp
@@ -0,0 +1,69 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2021,  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 "udp-channel-fixture.hpp"
+
+#include "test-ip.hpp"
+#include <boost/mpl/vector.hpp>
+
+namespace nfd {
+namespace face {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Face)
+BOOST_FIXTURE_TEST_SUITE(TestUdpChannel, UdpChannelFixture)
+
+using AddressFamilies = boost::mpl::vector<
+  std::integral_constant<AddressFamily, AddressFamily::V4>,
+  std::integral_constant<AddressFamily, AddressFamily::V6>>;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(DefaultMtu, F, AddressFamilies)
+{
+  auto address = getTestIp(F::value, AddressScope::Loopback);
+  SKIP_IF_IP_UNAVAILABLE(address);
+  this->listen(address,1452);
+
+  auto ch1 = this->makeChannel(typename IpAddressFromFamily<F::value>::type(), 0, 1232);
+  connect(*ch1);
+
+  BOOST_CHECK_EQUAL(this->limitedIo.run(2, 1_s), LimitedIo::EXCEED_OPS);
+  BOOST_CHECK_EQUAL(this->listenerChannel->size(), 1);
+  BOOST_CHECK_EQUAL(ch1->size(), 1);
+  BOOST_CHECK_EQUAL(ch1->isListening(), false);
+
+  for (const auto& face : this->listenerFaces) {
+    BOOST_CHECK_EQUAL(face->getMtu(), 1452);
+  }
+  for (const auto& face : this->clientFaces) {
+    BOOST_CHECK_EQUAL(face->getMtu(), 1232);
+  }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestUdpChannel
+BOOST_AUTO_TEST_SUITE_END() // Face
+
+} // namespace tests
+} // namespace face
+} // namespace nfd
diff --git a/tests/daemon/face/udp-factory.t.cpp b/tests/daemon/face/udp-factory.t.cpp
index 0bf7e03..e2788f5 100644
--- a/tests/daemon/face/udp-factory.t.cpp
+++ b/tests/daemon/face/udp-factory.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2020,  Regents of the University of California,
+ * Copyright (c) 2014-2021,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -195,9 +195,10 @@
   parseConfig(CONFIG, false);
 
   checkChannelListEqual(factory, {"udp4://0.0.0.0:6363", "udp6://[::]:6363"});
-  auto channels = factory.getChannels();
-  BOOST_CHECK(std::all_of(channels.begin(), channels.end(),
-                          [] (const auto& ch) { return ch->isListening(); }));
+  for (const auto& ch : factory.getChannels()) {
+    BOOST_CHECK(ch->isListening());
+    BOOST_CHECK_EQUAL(ch->getDefaultMtu(), ndn::MAX_NDN_PACKET_SIZE);
+  }
 }
 
 BOOST_AUTO_TEST_CASE(DisableListen)
@@ -218,9 +219,9 @@
   parseConfig(CONFIG, false);
 
   checkChannelListEqual(factory, {"udp4://0.0.0.0:7001", "udp6://[::]:7001"});
-  auto channels = factory.getChannels();
-  BOOST_CHECK(std::none_of(channels.begin(), channels.end(),
-                           [] (const auto& ch) { return ch->isListening(); }));
+  for (const auto& ch : factory.getChannels()) {
+    BOOST_CHECK(!ch->isListening());
+  }
 }
 
 BOOST_AUTO_TEST_CASE(DisableV4)
@@ -233,6 +234,7 @@
         port 7001
         enable_v4 no
         enable_v6 yes
+        unicast_mtu 1452
         mcast no
       }
     }
@@ -242,6 +244,9 @@
   parseConfig(CONFIG, false);
 
   checkChannelListEqual(factory, {"udp6://[::]:7001"});
+  for (const auto& ch : factory.getChannels()) {
+    BOOST_CHECK_EQUAL(ch->getDefaultMtu(), 1452);
+  }
 }
 
 BOOST_AUTO_TEST_CASE(DisableV6)
@@ -254,6 +259,7 @@
         port 7001
         enable_v4 yes
         enable_v6 no
+        unicast_mtu 1452
         mcast no
       }
     }
@@ -263,6 +269,9 @@
   parseConfig(CONFIG, false);
 
   checkChannelListEqual(factory, {"udp4://0.0.0.0:7001"});
+  for (const auto& ch : factory.getChannels()) {
+    BOOST_CHECK_EQUAL(ch->getDefaultMtu(), 1452);
+  }
 }
 
 BOOST_FIXTURE_TEST_CASE(EnableDisableMcast, UdpFactoryMcastFixture)
@@ -666,6 +675,51 @@
   BOOST_CHECK_THROW(parseConfig(CONFIG2, false), ConfigFile::Error);
 }
 
+BOOST_AUTO_TEST_CASE(BadMtu)
+{
+  // not a number
+  const std::string CONFIG1 = R"CONFIG(
+    face_system
+    {
+      udp
+      {
+        unicast_mtu hello
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG1, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG1, false), ConfigFile::Error);
+
+  // underflow
+  const std::string CONFIG2 = R"CONFIG(
+    face_system
+    {
+      udp
+      {
+        unicast_mtu 63
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG2, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG2, false), ConfigFile::Error);
+
+  // underflow
+  const std::string CONFIG3 = R"CONFIG(
+    face_system
+    {
+      udp
+      {
+        unicast_mtu 8801
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG3, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG3, false), ConfigFile::Error);
+}
+
 BOOST_AUTO_TEST_CASE(BadMcast)
 {
   const std::string CONFIG = R"CONFIG(
diff --git a/tests/daemon/face/websocket-channel-fixture.hpp b/tests/daemon/face/websocket-channel-fixture.hpp
index f28459f..69b2e9b 100644
--- a/tests/daemon/face/websocket-channel-fixture.hpp
+++ b/tests/daemon/face/websocket-channel-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2020,  Regents of the University of California,
+ * Copyright (c) 2014-2021,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -38,7 +38,7 @@
 {
 protected:
   shared_ptr<WebSocketChannel>
-  makeChannel(const boost::asio::ip::address& addr, uint16_t port = 0) final
+  makeChannel(const boost::asio::ip::address& addr, uint16_t port = 0, optional<size_t> mtu = nullopt) final
   {
     if (port == 0)
       port = getNextPort();
diff --git a/tests/other/face-benchmark.cpp b/tests/other/face-benchmark.cpp
index 9ee179f..28af8dc 100644
--- a/tests/other/face-benchmark.cpp
+++ b/tests/other/face-benchmark.cpp
@@ -47,7 +47,7 @@
     : m_terminationSignalSet{getGlobalIoService()}
     , m_tcpChannel{tcp::Endpoint{boost::asio::ip::tcp::v4(), 6363}, false,
                    bind([] { return ndn::nfd::FACE_SCOPE_NON_LOCAL; })}
-    , m_udpChannel{udp::Endpoint{boost::asio::ip::udp::v4(), 6363}, 10_min, false}
+    , m_udpChannel{udp::Endpoint{boost::asio::ip::udp::v4(), 6363}, 10_min, false, ndn::MAX_NDN_PACKET_SIZE}
   {
     m_terminationSignalSet.add(SIGINT);
     m_terminationSignalSet.add(SIGTERM);