build: require boost >= 1.71.0

Refs: #5276
Change-Id: Ie428541639dcf1113acfb9e9d0a80f9be87d6cd3
diff --git a/daemon/common/global.cpp b/daemon/common/global.cpp
index c54f043..a9848cd 100644
--- a/daemon/common/global.cpp
+++ b/daemon/common/global.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2021,  Regents of the University of California,
+ * Copyright (c) 2014-2023,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -27,18 +27,18 @@
 
 namespace nfd {
 
-static thread_local unique_ptr<boost::asio::io_service> g_ioService;
+static thread_local unique_ptr<boost::asio::io_context> g_ioCtx;
 static thread_local unique_ptr<Scheduler> g_scheduler;
-static boost::asio::io_service* g_mainIoService = nullptr;
-static boost::asio::io_service* g_ribIoService = nullptr;
+static boost::asio::io_context* g_mainIoCtx = nullptr;
+static boost::asio::io_context* g_ribIoCtx = nullptr;
 
-boost::asio::io_service&
+boost::asio::io_context&
 getGlobalIoService()
 {
-  if (g_ioService == nullptr) {
-    g_ioService = make_unique<boost::asio::io_service>();
+  if (g_ioCtx == nullptr) {
+    g_ioCtx = make_unique<boost::asio::io_context>();
   }
-  return *g_ioService;
+  return *g_ioCtx;
 }
 
 Scheduler&
@@ -55,46 +55,34 @@
 resetGlobalIoService()
 {
   g_scheduler.reset();
-  g_ioService.reset();
+  g_ioCtx.reset();
 }
 #endif
 
-boost::asio::io_service&
+boost::asio::io_context&
 getMainIoService()
 {
-  BOOST_ASSERT(g_mainIoService != nullptr);
-  return *g_mainIoService;
+  BOOST_ASSERT(g_mainIoCtx != nullptr);
+  return *g_mainIoCtx;
 }
 
-boost::asio::io_service&
+boost::asio::io_context&
 getRibIoService()
 {
-  BOOST_ASSERT(g_ribIoService != nullptr);
-  return *g_ribIoService;
+  BOOST_ASSERT(g_ribIoCtx != nullptr);
+  return *g_ribIoCtx;
 }
 
 void
-setMainIoService(boost::asio::io_service* mainIo)
+setMainIoService(boost::asio::io_context* mainIo)
 {
-  g_mainIoService = mainIo;
+  g_mainIoCtx = mainIo;
 }
 
 void
-setRibIoService(boost::asio::io_service* ribIo)
+setRibIoService(boost::asio::io_context* ribIo)
 {
-  g_ribIoService = ribIo;
-}
-
-void
-runOnMainIoService(const std::function<void()>& f)
-{
-  getMainIoService().post(f);
-}
-
-void
-runOnRibIoService(const std::function<void()>& f)
-{
-  getRibIoService().post(f);
+  g_ribIoCtx = ribIo;
 }
 
 } // namespace nfd
diff --git a/daemon/common/global.hpp b/daemon/common/global.hpp
index 25102f9..3ee072d 100644
--- a/daemon/common/global.hpp
+++ b/daemon/common/global.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022  Regents of the University of California,
+ * Copyright (c) 2014-2023  Regents of the University of California,
  *                          Arizona Board of Regents,
  *                          Colorado State University,
  *                          University Pierre & Marie Curie, Sorbonne University,
@@ -27,46 +27,39 @@
 
 #include "core/common.hpp"
 
-#include <boost/asio/io_service.hpp>
+#include <boost/asio/io_context.hpp>
 
 namespace nfd {
 
-/** \brief Returns the global io_service instance for the calling thread.
+/**
+ * \brief Returns the global io_context instance for the calling thread.
  */
-boost::asio::io_service&
+boost::asio::io_context&
 getGlobalIoService();
 
-/** \brief Returns the global Scheduler instance for the calling thread.
+/**
+ * \brief Returns the global Scheduler instance for the calling thread.
  */
 Scheduler&
 getScheduler();
 
-boost::asio::io_service&
+boost::asio::io_context&
 getMainIoService();
 
-boost::asio::io_service&
+boost::asio::io_context&
 getRibIoService();
 
 void
-setMainIoService(boost::asio::io_service* mainIo);
+setMainIoService(boost::asio::io_context* mainIo);
 
 void
-setRibIoService(boost::asio::io_service* ribIo);
-
-/** \brief Run a function on the main io_service instance.
- */
-void
-runOnMainIoService(const std::function<void()>& f);
-
-/** \brief Run a function on the RIB io_service instance.
- */
-void
-runOnRibIoService(const std::function<void()>& f);
+setRibIoService(boost::asio::io_context* ribIo);
 
 #ifdef NFD_WITH_TESTS
-/** \brief Destroy the global io_service instance.
+/**
+ * \brief Destroy the global io_context instance.
  *
- *  It will be recreated at the next invocation of getGlobalIoService().
+ * It will be recreated at the next invocation of getGlobalIoService().
  */
 void
 resetGlobalIoService();
diff --git a/daemon/face/tcp-transport.cpp b/daemon/face/tcp-transport.cpp
index 0332c2f..45bc952 100644
--- a/daemon/face/tcp-transport.cpp
+++ b/daemon/face/tcp-transport.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022,  Regents of the University of California,
+ * Copyright (c) 2014-2023,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -128,13 +128,7 @@
   BOOST_ASSERT(getState() == TransportState::DOWN);
 
   // recreate the socket
-  m_socket = protocol::socket(
-#if BOOST_VERSION >= 107000
-                              m_socket.get_executor()
-#else
-                              m_socket.get_io_service()
-#endif // BOOST_VERSION >= 107000
-                              );
+  m_socket = protocol::socket(m_socket.get_executor());
   this->resetReceiveBuffer();
   this->resetSendQueue();
 
diff --git a/daemon/fw/self-learning-strategy.cpp b/daemon/fw/self-learning-strategy.cpp
index 19d9d75..acf1f22 100644
--- a/daemon/fw/self-learning-strategy.cpp
+++ b/daemon/fw/self-learning-strategy.cpp
@@ -34,6 +34,7 @@
 #include <ndn-cxx/lp/prefix-announcement-header.hpp>
 #include <ndn-cxx/lp/tags.hpp>
 
+#include <boost/asio/post.hpp>
 #include <boost/range/adaptor/reversed.hpp>
 
 namespace nfd::fw {
@@ -188,26 +189,28 @@
   // (the PIT entry's expiry timer was set to 0 before dispatching)
   this->setExpiryTimer(pitEntry, 1_s);
 
-  runOnRibIoService([this, pitEntryWeak = weak_ptr<pit::Entry>{pitEntry}, inFaceId = inFace.getId(), data] {
-    rib::Service::get().getRibManager().slFindAnn(data.getName(),
-      [this, pitEntryWeak, inFaceId, data] (std::optional<ndn::PrefixAnnouncement> paOpt) {
-        if (paOpt) {
-          runOnMainIoService([this, pitEntryWeak, inFaceId, data, pa = std::move(*paOpt)] {
-            auto pitEntry = pitEntryWeak.lock();
-            auto inFace = this->getFace(inFaceId);
-            if (pitEntry && inFace) {
-              NFD_LOG_DEBUG("Found PrefixAnnouncement=" << pa.getAnnouncedName());
-              data.setTag(make_shared<lp::PrefixAnnouncementTag>(lp::PrefixAnnouncementHeader(pa)));
-              this->sendDataToAll(data, pitEntry, *inFace);
-              this->setExpiryTimer(pitEntry, 0_ms);
-            }
-            else {
-              NFD_LOG_DEBUG("PIT entry or face no longer exists");
-            }
-          });
-        }
+  boost::asio::post(getRibIoService(),
+    [this, pitEntryWeak = weak_ptr<pit::Entry>{pitEntry}, inFaceId = inFace.getId(), data] {
+      rib::Service::get().getRibManager().slFindAnn(data.getName(),
+        [this, pitEntryWeak, inFaceId, data] (std::optional<ndn::PrefixAnnouncement> paOpt) {
+          if (paOpt) {
+            boost::asio::post(getMainIoService(),
+              [this, pitEntryWeak, inFaceId, data, pa = std::move(*paOpt)] {
+                auto pitEntry = pitEntryWeak.lock();
+                auto inFace = this->getFace(inFaceId);
+                if (pitEntry && inFace) {
+                  NFD_LOG_DEBUG("Found PrefixAnnouncement=" << pa.getAnnouncedName());
+                  data.setTag(make_shared<lp::PrefixAnnouncementTag>(lp::PrefixAnnouncementHeader(pa)));
+                  this->sendDataToAll(data, pitEntry, *inFace);
+                  this->setExpiryTimer(pitEntry, 0_ms);
+                }
+                else {
+                  NFD_LOG_DEBUG("PIT entry or face no longer exists");
+                }
+              });
+          }
+        });
     });
-  });
 }
 
 bool
@@ -235,24 +238,26 @@
 SelfLearningStrategy::addRoute(const shared_ptr<pit::Entry>& pitEntry, const Face& inFace,
                                const Data& data, const ndn::PrefixAnnouncement& pa)
 {
-  runOnRibIoService([pitEntryWeak = weak_ptr<pit::Entry>{pitEntry}, inFaceId = inFace.getId(), data, pa] {
-    rib::Service::get().getRibManager().slAnnounce(pa, inFaceId, ROUTE_RENEW_LIFETIME,
-      [] (RibManager::SlAnnounceResult res) {
-        NFD_LOG_DEBUG("Add route via PrefixAnnouncement with result=" << res);
-      });
-  });
+  boost::asio::post(getRibIoService(),
+    [pitEntryWeak = weak_ptr<pit::Entry>{pitEntry}, inFaceId = inFace.getId(), data, pa] {
+      rib::Service::get().getRibManager().slAnnounce(pa, inFaceId, ROUTE_RENEW_LIFETIME,
+        [] (RibManager::SlAnnounceResult res) {
+          NFD_LOG_DEBUG("Add route via PrefixAnnouncement with result=" << res);
+        });
+    });
 }
 
 void
 SelfLearningStrategy::renewRoute(const Name& name, FaceId inFaceId, time::milliseconds maxLifetime)
 {
   // renew route with PA or ignore PA (if route has no PA)
-  runOnRibIoService([name, inFaceId, maxLifetime] {
-    rib::Service::get().getRibManager().slRenew(name, inFaceId, maxLifetime,
-      [] (RibManager::SlAnnounceResult res) {
-        NFD_LOG_DEBUG("Renew route with result=" << res);
-      });
-  });
+  boost::asio::post(getRibIoService(),
+    [name, inFaceId, maxLifetime] {
+      rib::Service::get().getRibManager().slRenew(name, inFaceId, maxLifetime,
+        [] (RibManager::SlAnnounceResult res) {
+          NFD_LOG_DEBUG("Renew route with result=" << res);
+        });
+    });
 }
 
 } // namespace nfd::fw
diff --git a/daemon/main.cpp b/daemon/main.cpp
index 60a589a..bfaba73 100644
--- a/daemon/main.cpp
+++ b/daemon/main.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022,  Regents of the University of California,
+ * Copyright (c) 2014-2023,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -70,7 +70,7 @@
 /** \brief Executes NFD with RIB manager
  *
  *  NFD (main forwarding procedure) and RIB manager execute in two different threads.
- *  Each thread has its own instances of global io_service and global scheduler.
+ *  Each thread has its own instances of global io_context and global scheduler.
  *
  *  When either of the daemons fails, execution of non-failed daemon will be terminated as
  *  well.  In other words, when NFD fails, RIB manager will be terminated; when RIB manager
@@ -107,9 +107,9 @@
     // a separate thread) fails.
     std::atomic_int retval(0);
 
-    boost::asio::io_service* const mainIo = &getGlobalIoService();
+    boost::asio::io_context* const mainIo = &getGlobalIoService();
     setMainIoService(mainIo);
-    boost::asio::io_service* ribIo = nullptr;
+    boost::asio::io_context* ribIo = nullptr;
 
     // Mutex and conditional variable to implement synchronization between main and RIB manager
     // threads:
diff --git a/daemon/rib/service.hpp b/daemon/rib/service.hpp
index 3cedde4..0d0aa07 100644
--- a/daemon/rib/service.hpp
+++ b/daemon/rib/service.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022,  Regents of the University of California,
+ * Copyright (c) 2014-2023,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -46,7 +46,7 @@
  * \brief Initializes and executes the NFD-RIB service thread.
  *
  * Only one instance of this class can be created at any time.
- * After initialization, NFD-RIB instance can be started by running the global io_service.
+ * After initialization, NFD-RIB instance can be started by running the global io_context.
  */
 class Service : noncopyable
 {
diff --git a/tests/daemon/common/global.t.cpp b/tests/daemon/common/global.t.cpp
index b738d32..61556f7 100644
--- a/tests/daemon/common/global.t.cpp
+++ b/tests/daemon/common/global.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022,  Regents of the University of California,
+ * Copyright (c) 2014-2023,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -29,6 +29,8 @@
 #include "tests/daemon/global-io-fixture.hpp"
 #include "tests/daemon/rib-io-fixture.hpp"
 
+#include <boost/asio/post.hpp>
+
 #include <thread>
 
 namespace nfd::tests {
@@ -37,8 +39,8 @@
 
 BOOST_AUTO_TEST_CASE(ThreadLocalIoService)
 {
-  boost::asio::io_service* s1 = &getGlobalIoService();
-  boost::asio::io_service* s2 = nullptr;
+  boost::asio::io_context* s1 = &getGlobalIoService();
+  boost::asio::io_context* s2 = nullptr;
 
   std::thread t([&s2] { s2 = &getGlobalIoService(); });
   t.join();
@@ -63,8 +65,8 @@
 
 BOOST_FIXTURE_TEST_CASE(MainRibIoService, RibIoFixture)
 {
-  boost::asio::io_service* mainIo = &g_io;
-  boost::asio::io_service* ribIo = g_ribIo;
+  boost::asio::io_context* mainIo = &g_io;
+  boost::asio::io_context* ribIo = g_ribIo;
 
   BOOST_CHECK(mainIo != ribIo);
   BOOST_CHECK(&getGlobalIoService() == mainIo);
@@ -72,15 +74,15 @@
   BOOST_CHECK(&getRibIoService() == ribIo);
   auto mainThreadId = std::this_thread::get_id();
 
-  runOnRibIoService([&] {
+  boost::asio::post(getRibIoService(), [&] {
     BOOST_CHECK(mainThreadId != std::this_thread::get_id());
     BOOST_CHECK(&getGlobalIoService() == ribIo);
     BOOST_CHECK(&getMainIoService() == mainIo);
     BOOST_CHECK(&getRibIoService() == ribIo);
   });
 
-  runOnRibIoService([&] {
-    runOnMainIoService([&] {
+  boost::asio::post(getRibIoService(), [&] {
+    boost::asio::post(getMainIoService(), [&] {
       BOOST_CHECK(mainThreadId == std::this_thread::get_id());
       BOOST_CHECK(&getGlobalIoService() == mainIo);
       BOOST_CHECK(&getMainIoService() == mainIo);
@@ -92,7 +94,7 @@
 BOOST_FIXTURE_TEST_CASE(PollInAllThreads, RibIoFixture)
 {
   bool hasRibRun = false;
-  runOnRibIoService([&] { hasRibRun = true; });
+  boost::asio::post(getRibIoService(), [&] { hasRibRun = true; });
   std::this_thread::sleep_for(std::chrono::seconds(1));
   BOOST_CHECK_EQUAL(hasRibRun, false);
 
@@ -101,9 +103,9 @@
 
   hasRibRun = false;
   bool hasMainRun = false;
-  runOnMainIoService([&] {
+  boost::asio::post(getMainIoService(), [&] {
     hasMainRun = true;
-    runOnRibIoService([&] { hasRibRun = true; });
+    boost::asio::post(getRibIoService(), [&] { hasRibRun = true; });
   });
   BOOST_CHECK_EQUAL(hasMainRun, false);
   BOOST_CHECK_EQUAL(hasRibRun, false);
@@ -116,7 +118,7 @@
 BOOST_FIXTURE_TEST_CASE(AdvanceClocks, RibIoTimeFixture)
 {
   bool hasRibRun = false;
-  runOnRibIoService([&] { hasRibRun = true; });
+  boost::asio::post(getRibIoService(), [&] { hasRibRun = true; });
   std::this_thread::sleep_for(std::chrono::seconds(1));
   BOOST_CHECK_EQUAL(hasRibRun, false);
 
@@ -127,7 +129,7 @@
   bool hasMainRun = false;
   getScheduler().schedule(250_ms, [&] {
     hasMainRun = true;
-    runOnRibIoService([&] { hasRibRun = true; });
+    boost::asio::post(getRibIoService(), [&] { hasRibRun = true; });
   });
   BOOST_CHECK_EQUAL(hasMainRun, false);
   BOOST_CHECK_EQUAL(hasRibRun, false);
diff --git a/tests/daemon/face/test-netif.hpp b/tests/daemon/face/test-netif.hpp
index 9e32d60..cf4e3fa 100644
--- a/tests/daemon/face/test-netif.hpp
+++ b/tests/daemon/face/test-netif.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022,  Regents of the University of California,
+ * Copyright (c) 2014-2023,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -34,7 +34,7 @@
 namespace nfd::tests {
 
 /** \brief Enumerate network interfaces using the given NetworkMonitor
- *  \param netmon a NetworkMonitor constructed on the global io_service.
+ *  \param netmon a NetworkMonitor constructed on the global io_context.
  *  \note This function is blocking
  *  \note Signals are supported if caller keeps \p netmon running
  */
diff --git a/tests/daemon/face/unix-stream-transport-fixture.hpp b/tests/daemon/face/unix-stream-transport-fixture.hpp
index d95be2c..2590cda 100644
--- a/tests/daemon/face/unix-stream-transport-fixture.hpp
+++ b/tests/daemon/face/unix-stream-transport-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022,  Regents of the University of California,
+ * Copyright (c) 2014-2023,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -48,7 +48,7 @@
 {
 public:
   explicit
-  AcceptorWithCleanup(boost::asio::io_service& io, const std::string& path = "")
+  AcceptorWithCleanup(boost::asio::io_context& io, const std::string& path = "")
     : unix_stream::acceptor(io)
   {
     this->open();
diff --git a/tests/daemon/global-io-fixture.cpp b/tests/daemon/global-io-fixture.cpp
index 05aa8a1..2fdb6ed 100644
--- a/tests/daemon/global-io-fixture.cpp
+++ b/tests/daemon/global-io-fixture.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022,  Regents of the University of California,
+ * Copyright (c) 2014-2023,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -42,11 +42,7 @@
 GlobalIoFixture::pollIo()
 {
   if (g_io.stopped()) {
-#if BOOST_VERSION >= 106600
     g_io.restart();
-#else
-    g_io.reset();
-#endif
   }
   return g_io.poll();
 }
diff --git a/tests/daemon/global-io-fixture.hpp b/tests/daemon/global-io-fixture.hpp
index e374ec0..5422e72 100644
--- a/tests/daemon/global-io-fixture.hpp
+++ b/tests/daemon/global-io-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022,  Regents of the University of California,
+ * Copyright (c) 2014-2023,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -30,10 +30,11 @@
 
 namespace nfd::tests {
 
-/** \brief A fixture providing proper setup and teardown of the global io_service.
+/**
+ * \brief A fixture providing proper setup and teardown of the global io_context.
  *
- *  Every daemon fixture or test case should inherit from this fixture,
- *  to have per test case io_service initialization and cleanup.
+ * Every daemon fixture or test case should inherit from this fixture,
+ * to have per test case io_context initialization and cleanup.
  */
 class GlobalIoFixture
 {
@@ -42,18 +43,21 @@
 
   ~GlobalIoFixture();
 
-  /** \brief Poll the global io_service.
+  /**
+   * \brief Poll the global io_context.
    */
   size_t
   pollIo();
 
 protected:
-  /** \brief Reference to the global io_service instance.
+  /**
+   * \brief Reference to the global io_context instance.
    */
-  boost::asio::io_service& g_io;
+  boost::asio::io_context& g_io;
 };
 
-/** \brief GlobalIoFixture that also overrides steady clock and system clock.
+/**
+ * \brief GlobalIoFixture that also overrides steady clock and system clock.
  */
 class GlobalIoTimeFixture : public ClockFixture, public GlobalIoFixture
 {
diff --git a/tests/daemon/rib-io-fixture.hpp b/tests/daemon/rib-io-fixture.hpp
index e825f7d..b280535 100644
--- a/tests/daemon/rib-io-fixture.hpp
+++ b/tests/daemon/rib-io-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022,  Regents of the University of California,
+ * Copyright (c) 2014-2023,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -34,7 +34,8 @@
 
 namespace nfd::tests {
 
-/** \brief A base test fixture that provides both main and RIB io_service.
+/**
+ * \brief A base test fixture that provides both main and RIB io_context.
  */
 class RibIoFixture : public GlobalIoFixture
 {
@@ -44,27 +45,31 @@
   ~RibIoFixture();
 
 protected:
-  /** \brief Poll main and RIB thread io_service to process all pending I/O events.
+  /**
+   * \brief Poll main and RIB thread io_context to process all pending I/O events.
    *
    * This call will execute all pending I/O events, including events that are posted
-   * inside the processing event, i.e., main and RIB thread io_service will be polled
+   * inside the processing event, i.e., main and RIB thread io_context will be polled
    * repeatedly until all pending events are processed.
    *
-   * \warning Must be called from the main thread
+   * \warning Must be called from the main thread.
    */
   void
   poll();
 
 protected:
-  /** \brief Pointer to global main io_service.
+  /**
+   * \brief Pointer to global main io_context.
    */
-  boost::asio::io_service* g_mainIo = nullptr;
+  boost::asio::io_context* g_mainIo = nullptr;
 
-  /** \brief Pointer to global RIB io_service.
+  /**
+   * \brief Pointer to global RIB io_context.
    */
-  boost::asio::io_service* g_ribIo = nullptr;
+  boost::asio::io_context* g_ribIo = nullptr;
 
-  /** \brief Global RIB thread.
+  /**
+   * \brief Global RIB thread.
    */
   std::thread g_ribThread;
 
@@ -76,7 +81,8 @@
   std::condition_variable m_ribPollEndCv;
 };
 
-/** \brief RibIoFixture that also overrides steady clock and system clock.
+/**
+ * \brief RibIoFixture that also overrides steady clock and system clock.
  */
 class RibIoTimeFixture : public ClockFixture, public RibIoFixture
 {
diff --git a/tests/daemon/rib/service.t.cpp b/tests/daemon/rib/service.t.cpp
index 5b808cc..51189fc 100644
--- a/tests/daemon/rib/service.t.cpp
+++ b/tests/daemon/rib/service.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022,  Regents of the University of California,
+ * Copyright (c) 2014-2023,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -29,7 +29,9 @@
 #include "tests/test-common.hpp"
 #include "tests/daemon/rib-io-fixture.hpp"
 
+#include <boost/asio/post.hpp>
 #include <boost/property_tree/info_parser.hpp>
+
 #include <sstream>
 
 namespace nfd::tests {
@@ -63,7 +65,7 @@
   BOOST_CHECK_THROW(Service::get(), std::logic_error);
   BOOST_CHECK_THROW(Service(section, m_ribKeyChain), std::logic_error);
 
-  runOnRibIoService([&] {
+  boost::asio::post(getRibIoService(), [&] {
     {
       BOOST_CHECK_THROW(Service::get(), std::logic_error);
       Service ribService(section, m_ribKeyChain);
@@ -87,7 +89,7 @@
     }
   )CONFIG";
 
-  runOnRibIoService([&] {
+  boost::asio::post(getRibIoService(), [&] {
     BOOST_CHECK_NO_THROW(Service(makeSection(CONFIG), m_ribKeyChain));
   });
   poll();
@@ -102,7 +104,7 @@
     }
   )CONFIG";
 
-  runOnRibIoService([&] {
+  boost::asio::post(getRibIoService(), [&] {
     BOOST_CHECK_NO_THROW(Service(makeSection(CONFIG), m_ribKeyChain));
   });
   poll();
@@ -131,7 +133,7 @@
     }
   )CONFIG";
 
-  runOnRibIoService([&] {
+  boost::asio::post(getRibIoService(), [&] {
     BOOST_CHECK_EXCEPTION(Service(makeSection(CONFIG), m_ribKeyChain), ConfigFile::Error,
                           [] (const auto& e) {
                             return e.what() == "localhop_security and auto_prefix_propagate "
diff --git a/tests/io-fixture.hpp b/tests/io-fixture.hpp
index fec308e..ebc40da 100644
--- a/tests/io-fixture.hpp
+++ b/tests/io-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022,  Regents of the University of California,
+ * Copyright (c) 2014-2023,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -28,7 +28,7 @@
 
 #include "tests/clock-fixture.hpp"
 
-#include <boost/asio/io_service.hpp>
+#include <boost/asio/io_context.hpp>
 
 namespace nfd::tests {
 
@@ -39,17 +39,13 @@
   afterTick() final
   {
     if (m_io.stopped()) {
-#if BOOST_VERSION >= 106600
       m_io.restart();
-#else
-      m_io.reset();
-#endif
     }
     m_io.poll();
   }
 
 protected:
-  boost::asio::io_service m_io;
+  boost::asio::io_context m_io;
 };
 
 } // namespace nfd::tests
diff --git a/tests/other/wscript b/tests/other/wscript
index a2099aa..1514f61 100644
--- a/tests/other/wscript
+++ b/tests/other/wscript
@@ -31,7 +31,7 @@
         # main
         bld.objects(target=f'other-tests-{module}-main',
                     source='../main.cpp',
-                    use='BOOST',
+                    use='BOOST_TESTS',
                     defines=[f'BOOST_TEST_MODULE={name}'])
         # module
         bld.program(name=module,
diff --git a/tests/tools/ndn-autoconfig/procedure.t.cpp b/tests/tools/ndn-autoconfig/procedure.t.cpp
index ea5311b..d6a024d 100644
--- a/tests/tools/ndn-autoconfig/procedure.t.cpp
+++ b/tests/tools/ndn-autoconfig/procedure.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022,  Regents of the University of California,
+ * Copyright (c) 2014-2023,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -66,10 +66,10 @@
   /** \param stageName stage name
    *  \param nCalls pointer to a variable which is incremented each time doStart is invoked
    *  \param result expected result, nullopt to cause a failued
-   *  \param io io_service to asynchronously post the result
+   *  \param io io_context to asynchronously post the result
    */
   DummyStage(const std::string& stageName, int* nCalls,
-             const std::optional<FaceUri>& result, boost::asio::io_service& io)
+             const std::optional<FaceUri>& result, boost::asio::io_context& io)
     : m_stageName(stageName)
     , m_nCalls(nCalls)
     , m_result(result)
@@ -90,7 +90,8 @@
     if (m_nCalls != nullptr) {
       ++(*m_nCalls);
     }
-    m_io.post([this] {
+
+    boost::asio::post(m_io, [this] {
       if (m_result) {
         this->succeed(*m_result);
       }
@@ -104,7 +105,7 @@
   std::string m_stageName;
   int* m_nCalls;
   std::optional<FaceUri> m_result;
-  boost::asio::io_service& m_io;
+  boost::asio::io_context& m_io;
 };
 
 /** \brief Two-stage Procedure where the first stage succeeds and the second stage fails.
@@ -133,7 +134,7 @@
   int nCalls2 = 0;
 
 private:
-  boost::asio::io_service& m_io;
+  boost::asio::io_context& m_io;
 };
 
 /** \brief Two-stage Procedure where the first stage fails and the second stage succeeds.
@@ -160,7 +161,7 @@
   int nCalls2 = 0;
 
 private:
-  boost::asio::io_service& m_io;
+  boost::asio::io_context& m_io;
 };
 
 BOOST_AUTO_TEST_SUITE(NdnAutoconfig)
diff --git a/tests/tools/nfdc/status-report.t.cpp b/tests/tools/nfdc/status-report.t.cpp
index dcf797c..2e9626e 100644
--- a/tests/tools/nfdc/status-report.t.cpp
+++ b/tests/tools/nfdc/status-report.t.cpp
@@ -45,7 +45,7 @@
 class DummyModule : public Module
 {
 public:
-  DummyModule(const std::string& moduleName, boost::asio::io_service& io)
+  DummyModule(const std::string& moduleName, boost::asio::io_context& io)
     : m_moduleName(moduleName)
     , m_scheduler(io)
     , m_res(0)
diff --git a/tests/wscript b/tests/wscript
index ed4eb19..6e27661 100644
--- a/tests/wscript
+++ b/tests/wscript
@@ -35,7 +35,7 @@
             target='tests-common',
             features='pch',
             source=bld.path.ant_glob('*.cpp', excl='main.cpp'),
-            use='core-objects',
+            use='BOOST_TESTS core-objects',
             headers='../core/common.hpp boost-test.hpp',
             defines=[tmpdir])
 
@@ -43,7 +43,7 @@
             # main() for the module
             bld.objects(target=f'unit-tests-{module}-main',
                         source='main.cpp',
-                        use='BOOST',
+                        use='BOOST_TESTS',
                         defines=[f'BOOST_TEST_MODULE=NFD {module.capitalize()}'])
 
             subdir = 'daemon/rib' if module == 'rib' else module
diff --git a/tools/ndn-autoconfig/ndn-fch-discovery.cpp b/tools/ndn-autoconfig/ndn-fch-discovery.cpp
index 52521e7..0e5c6b0 100644
--- a/tools/ndn-autoconfig/ndn-fch-discovery.cpp
+++ b/tools/ndn-autoconfig/ndn-fch-discovery.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022,  Regents of the University of California,
+ * Copyright (c) 2014-2023,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -140,11 +140,7 @@
 {
   try {
     boost::asio::ip::tcp::iostream requestStream;
-#if BOOST_VERSION >= 106700
     requestStream.expires_after(std::chrono::seconds(3));
-#else
-    requestStream.expires_from_now(boost::posix_time::seconds(3));
-#endif // BOOST_VERSION >= 106700
 
     Url url(m_url);
     if (!url.isValid()) {
diff --git a/tools/ndn-autoconfig/procedure.hpp b/tools/ndn-autoconfig/procedure.hpp
index 3f1f630..d9513ed 100644
--- a/tools/ndn-autoconfig/procedure.hpp
+++ b/tools/ndn-autoconfig/procedure.hpp
@@ -57,7 +57,7 @@
   void
   runOnce();
 
-  boost::asio::io_service&
+  boost::asio::io_context&
   getIoService()
   {
     return m_face.getIoService();
diff --git a/tools/wscript b/tools/wscript
index 5a9c396..23cdf0a 100644
--- a/tools/wscript
+++ b/tools/wscript
@@ -63,7 +63,7 @@
             bld.objects(target=srcObjects,
                         source=srcFiles,
                         features='pch',
-                        headers=subdir.find_node(name + '-pch.hpp'),
+                        headers=subdir.find_node(f'{name}-pch.hpp'),
                         use='core-objects LIBRESOLV',
                         includes=name)
             testableObjects.append(srcObjects)
@@ -71,7 +71,7 @@
         bld.program(name=name,
                     target=f'{top}/bin/{name}',
                     source=[mainFile],
-                    use='core-objects ' + srcObjects,
+                    use=f'core-objects {srcObjects}',
                     includes=name)
 
     bld.objects(target='tools-objects',
diff --git a/wscript b/wscript
index a89edf3..11a1353 100644
--- a/wscript
+++ b/wscript
@@ -23,8 +23,9 @@
 NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
 """
 
+import os
+import subprocess
 from waflib import Context, Logs, Utils
-import os, subprocess
 
 VERSION = '22.12'
 APPNAME = 'nfd'
@@ -113,16 +114,15 @@
 
     conf.check_cxx(header_name='valgrind/valgrind.h', define_name='HAVE_VALGRIND', mandatory=False)
 
-    boost_libs = ['system', 'program_options', 'filesystem']
-    if conf.env.WITH_TESTS or conf.env.WITH_OTHER_TESTS:
-        boost_libs.append('unit_test_framework')
-
-    conf.check_boost(lib=boost_libs, mt=True)
-    if conf.env.BOOST_VERSION_NUMBER < 106501:
-        conf.fatal('The minimum supported version of Boost is 1.65.1.\n'
+    conf.check_boost(lib='filesystem program_options', mt=True)
+    if conf.env.BOOST_VERSION_NUMBER < 107100:
+        conf.fatal('The minimum supported version of Boost is 1.71.0.\n'
                    'Please upgrade your distribution or manually install a newer version of Boost.\n'
                    'For more information, see https://redmine.named-data.net/projects/nfd/wiki/Boost')
 
+    if conf.env.WITH_TESTS or conf.env.WITH_OTHER_TESTS:
+        conf.check_boost(lib='unit_test_framework', mt=True, uselib_store='BOOST_TESTS')
+
     conf.load('unix-socket')
 
     if not conf.options.without_libpcap:
@@ -160,8 +160,8 @@
 
     bld.objects(
         target='core-objects',
-        source=bld.path.find_node('core').ant_glob('*.cpp') + ['core/version.cpp'],
-        use='version.cpp version.hpp NDN_CXX BOOST LIBRT',
+        source=bld.path.find_dir('core').ant_glob('*.cpp') + ['core/version.cpp'],
+        use='version.cpp version.hpp BOOST NDN_CXX LIBRT',
         includes='.',
         export_includes='.')
 
@@ -199,9 +199,10 @@
                 source='daemon/main.cpp',
                 use='daemon-objects SYSTEMD')
 
-    bld.recurse('tools')
     bld.recurse('tests')
+    bld.recurse('tools')
 
+    # Install sample configs
     bld(features='subst',
         source='nfd.conf.sample.in',
         target='nfd.conf.sample',
@@ -209,7 +210,6 @@
         IF_HAVE_LIBPCAP='' if bld.env.HAVE_LIBPCAP else '; ',
         IF_HAVE_WEBSOCKET='' if bld.env.HAVE_WEBSOCKET else '; ',
         UNIX_SOCKET_PATH='/run/nfd.sock' if Utils.unversioned_sys_platform() == 'linux' else '/var/run/nfd.sock')
-
     bld.install_files('${SYSCONFDIR}/ndn', 'autoconfig.conf.sample')
 
     if bld.env.HAVE_SYSTEMD: