face: minor code reorganization and doxygen cleanups

Change-Id: I280bd2f4d282f246ef491e327a5b990ca7600dd3
diff --git a/daemon/face/face-counters.cpp b/daemon/face/face-counters.cpp
deleted file mode 100644
index ec8572e..0000000
--- a/daemon/face/face-counters.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/*
- * Copyright (c) 2014-2022,  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 "face-counters.hpp"
-
-namespace nfd::face {
-
-FaceCounters::FaceCounters(const LinkService::Counters& linkServiceCounters,
-                           const Transport::Counters& transportCounters)
-  : nInInterests(linkServiceCounters.nInInterests)
-  , nOutInterests(linkServiceCounters.nOutInterests)
-  , nInterestsExceededRetx(linkServiceCounters.nInterestsExceededRetx)
-  , nInData(linkServiceCounters.nInData)
-  , nOutData(linkServiceCounters.nOutData)
-  , nInNacks(linkServiceCounters.nInNacks)
-  , nOutNacks(linkServiceCounters.nOutNacks)
-  , nInPackets(transportCounters.nInPackets)
-  , nOutPackets(transportCounters.nOutPackets)
-  , nInBytes(transportCounters.nInBytes)
-  , nOutBytes(transportCounters.nOutBytes)
-  , m_linkServiceCounters(linkServiceCounters)
-  , m_transportCounters(transportCounters)
-{
-}
-
-} // namespace nfd::face
diff --git a/daemon/face/face-counters.hpp b/daemon/face/face-counters.hpp
deleted file mode 100644
index a3f8576..0000000
--- a/daemon/face/face-counters.hpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/*
- * Copyright (c) 2014-2022,  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/>.
- */
-
-#ifndef NFD_DAEMON_FACE_FACE_COUNTERS_HPP
-#define NFD_DAEMON_FACE_FACE_COUNTERS_HPP
-
-#include "link-service.hpp"
-#include "transport.hpp"
-
-namespace nfd::face {
-
-/** \brief Gives access to counters provided by Face.
- *
- *  This type is a facade that exposes common counters of a Face.
- *
- *  get<T>() can be used to access extended counters provided by
- *  LinkService or Transport of the Face.
- */
-class FaceCounters
-{
-public:
-  FaceCounters(const LinkService::Counters& linkServiceCounters,
-               const Transport::Counters& transportCounters);
-
-  /** \return counters provided by LinkService
-   *  \tparam T LinkService counters type
-   *  \throw std::bad_cast counters type mismatch
-   */
-  template<typename T>
-  std::enable_if_t<std::is_base_of_v<LinkService::Counters, T>, const T&>
-  get() const
-  {
-    return dynamic_cast<const T&>(m_linkServiceCounters);
-  }
-
-  /** \return counters provided by Transport
-   *  \tparam T Transport counters type
-   *  \throw std::bad_cast counters type mismatch
-   */
-  template<typename T>
-  std::enable_if_t<std::is_base_of_v<Transport::Counters, T>, const T&>
-  get() const
-  {
-    return dynamic_cast<const T&>(m_transportCounters);
-  }
-
-public:
-  const PacketCounter& nInInterests;
-  const PacketCounter& nOutInterests;
-  const PacketCounter& nInterestsExceededRetx;
-  const PacketCounter& nInData;
-  const PacketCounter& nOutData;
-  const PacketCounter& nInNacks;
-  const PacketCounter& nOutNacks;
-
-  const PacketCounter& nInPackets;
-  const PacketCounter& nOutPackets;
-  const ByteCounter& nInBytes;
-  const ByteCounter& nOutBytes;
-
-  /** \brief Count of incoming Interests dropped due to HopLimit == 0.
-   */
-  PacketCounter nInHopLimitZero;
-
-  /** \brief Count of outgoing Interests dropped due to HopLimit == 0 on non-local faces.
-   */
-  PacketCounter nOutHopLimitZero;
-
-private:
-  const LinkService::Counters& m_linkServiceCounters;
-  const Transport::Counters& m_transportCounters;
-};
-
-} // namespace nfd::face
-
-#endif // NFD_DAEMON_FACE_FACE_COUNTERS_HPP
diff --git a/daemon/face/face.cpp b/daemon/face/face.cpp
index 7b15dc5..3c65a98 100644
--- a/daemon/face/face.cpp
+++ b/daemon/face/face.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-2024,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -27,6 +27,24 @@
 
 namespace nfd::face {
 
+FaceCounters::FaceCounters(const LinkService::Counters& linkServiceCounters,
+                           const Transport::Counters& transportCounters)
+  : nInInterests(linkServiceCounters.nInInterests)
+  , nOutInterests(linkServiceCounters.nOutInterests)
+  , nInterestsExceededRetx(linkServiceCounters.nInterestsExceededRetx)
+  , nInData(linkServiceCounters.nInData)
+  , nOutData(linkServiceCounters.nOutData)
+  , nInNacks(linkServiceCounters.nInNacks)
+  , nOutNacks(linkServiceCounters.nOutNacks)
+  , nInPackets(transportCounters.nInPackets)
+  , nOutPackets(transportCounters.nOutPackets)
+  , nInBytes(transportCounters.nInBytes)
+  , nOutBytes(transportCounters.nOutBytes)
+  , m_linkServiceCounters(linkServiceCounters)
+  , m_transportCounters(transportCounters)
+{
+}
+
 Face::Face(unique_ptr<LinkService> service, unique_ptr<Transport> transport)
   : afterReceiveInterest(service->afterReceiveInterest)
   , afterReceiveData(service->afterReceiveData)
diff --git a/daemon/face/face.hpp b/daemon/face/face.hpp
index b18b9ca..f1a9765 100644
--- a/daemon/face/face.hpp
+++ b/daemon/face/face.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-2024,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -27,7 +27,6 @@
 #define NFD_DAEMON_FACE_FACE_HPP
 
 #include "face-common.hpp"
-#include "face-counters.hpp"
 #include "link-service.hpp"
 #include "transport.hpp"
 
@@ -41,16 +40,79 @@
  */
 using FaceState = TransportState;
 
-/** \brief Generalization of a network interface.
+/**
+ * \brief Gives access to the counters provided by Face.
  *
- *  A face generalizes a network interface.
- *  It provides best-effort network-layer packet delivery services
- *  on a physical interface, an overlay tunnel, or a link to a local application.
+ * This type is a facade that exposes common counters of a Face.
  *
- *  A face combines two parts: LinkService and Transport.
- *  Transport is the lower part, which provides best-effort TLV block deliveries.
- *  LinkService is the upper part, which translates between network-layer packets
- *  and TLV blocks, and may provide additional services such as fragmentation and reassembly.
+ * get<T>() can be used to access extended counters provided by
+ * LinkService or Transport of the Face.
+ */
+class FaceCounters
+{
+public:
+  FaceCounters(const LinkService::Counters& linkServiceCounters,
+               const Transport::Counters& transportCounters);
+
+  /**
+   * \brief Returns the counters provided by (a subclass of) LinkService.
+   * \tparam T The desired counters type
+   * \throw std::bad_cast counters type mismatch
+   */
+  template<typename T>
+  std::enable_if_t<std::is_base_of_v<LinkService::Counters, T>, const T&>
+  get() const
+  {
+    return dynamic_cast<const T&>(m_linkServiceCounters);
+  }
+
+  /**
+   * \brief Returns the counters provided by (a subclass of) Transport.
+   * \tparam T The desired counters type
+   * \throw std::bad_cast counters type mismatch
+   */
+  template<typename T>
+  std::enable_if_t<std::is_base_of_v<Transport::Counters, T>, const T&>
+  get() const
+  {
+    return dynamic_cast<const T&>(m_transportCounters);
+  }
+
+public:
+  const PacketCounter& nInInterests;           ///< \copydoc LinkService::Counters::nInInterests
+  const PacketCounter& nOutInterests;          ///< \copydoc LinkService::Counters::nOutInterests
+  const PacketCounter& nInterestsExceededRetx; ///< \copydoc LinkService::Counters::nInterestsExceededRetx
+  const PacketCounter& nInData;                ///< \copydoc LinkService::Counters::nInData
+  const PacketCounter& nOutData;               ///< \copydoc LinkService::Counters::nOutData
+  const PacketCounter& nInNacks;               ///< \copydoc LinkService::Counters::nInNacks
+  const PacketCounter& nOutNacks;              ///< \copydoc LinkService::Counters::nOutNacks
+
+  const PacketCounter& nInPackets;  ///< \copydoc Transport::Counters::nInPackets
+  const PacketCounter& nOutPackets; ///< \copydoc Transport::Counters::nOutPackets
+  const ByteCounter& nInBytes;      ///< \copydoc Transport::Counters::nInBytes
+  const ByteCounter& nOutBytes;     ///< \copydoc Transport::Counters::nOutBytes
+
+  /// Count of incoming Interests dropped due to HopLimit == 0.
+  PacketCounter nInHopLimitZero;
+  /// Count of outgoing Interests dropped due to HopLimit == 0 on non-local faces.
+  PacketCounter nOutHopLimitZero;
+
+private:
+  const LinkService::Counters& m_linkServiceCounters;
+  const Transport::Counters& m_transportCounters;
+};
+
+/**
+ * \brief Generalization of a network interface.
+ *
+ * A face generalizes a network interface.
+ * It provides best-effort network-layer packet delivery services
+ * on a physical interface, an overlay tunnel, or a link to a local application.
+ *
+ * A face combines two parts: LinkService and Transport.
+ * Transport is the lower part, which provides best-effort TLV block deliveries.
+ * LinkService is the upper part, which translates between network-layer packets
+ * and TLV blocks, and may provide additional services such as fragmentation and reassembly.
  */
 class Face NFD_FINAL_UNLESS_WITH_TESTS : public std::enable_shared_from_this<Face>, noncopyable
 {
@@ -69,48 +131,60 @@
     return m_transport.get();
   }
 
-  /** \brief Request that the face be closed.
+  /**
+   * \brief Request that the face be closed.
    *
-   *  This operation is effective only if face is in the UP or DOWN state; otherwise, it has no effect.
-   *  The face will change state to CLOSING, and then perform a cleanup procedure.
-   *  When the cleanup is complete, the state will be changed to CLOSED, which may happen
-   *  synchronously or asynchronously.
+   * This operation is effective only if face is in the UP or DOWN state; otherwise,
+   * it has no effect. The face will change state to CLOSING, and then perform a
+   * cleanup procedure. When the cleanup is complete, the state will be changed to
+   * CLOSED, which may happen synchronously or asynchronously.
    *
-   *  \warning The face must not be deallocated until its state changes to CLOSED.
+   * \warning The face must not be deallocated until its state changes to CLOSED.
    */
   void
-  close();
+  close()
+  {
+    m_transport->close();
+  }
 
 public: // upper interface connected to forwarding
-  /** \brief Send Interest.
+  /**
+   * \brief Send Interest.
    */
   void
-  sendInterest(const Interest& interest);
+  sendInterest(const Interest& interest)
+  {
+    m_service->sendInterest(interest);
+  }
 
-  /** \brief Send Data.
+  /**
+   * \brief Send Data.
    */
   void
-  sendData(const Data& data);
+  sendData(const Data& data)
+  {
+    m_service->sendData(data);
+  }
 
-  /** \brief Send Nack.
+  /**
+   * \brief Send Nack.
    */
   void
-  sendNack(const lp::Nack& nack);
+  sendNack(const lp::Nack& nack)
+  {
+    m_service->sendNack(nack);
+  }
 
-  /** \brief Signals on Interest received.
-   */
+  /// \copydoc LinkService::afterReceiveInterest
   signal::Signal<LinkService, Interest, EndpointId>& afterReceiveInterest;
 
-  /** \brief Signals on Data received.
-   */
+  /// \copydoc LinkService::afterReceiveData
   signal::Signal<LinkService, Data, EndpointId>& afterReceiveData;
 
-  /** \brief Signals on Nack received.
-   */
+  /// \copydoc LinkService::afterReceiveNack
   signal::Signal<LinkService, lp::Nack, EndpointId>& afterReceiveNack;
 
-  /** \brief Signals on Interest dropped by reliability system for exceeding allowed number of retx.
-   */
+  /// \copydoc LinkService::onDroppedInterest
   signal::Signal<LinkService, Interest>& onDroppedInterest;
 
 public: // properties
@@ -137,37 +211,55 @@
    * \brief Returns a FaceUri representing the local endpoint.
    */
   FaceUri
-  getLocalUri() const;
+  getLocalUri() const noexcept
+  {
+    return m_transport->getLocalUri();
+  }
 
   /**
    * \brief Returns a FaceUri representing the remote endpoint.
    */
   FaceUri
-  getRemoteUri() const;
+  getRemoteUri() const noexcept
+  {
+    return m_transport->getRemoteUri();
+  }
 
   /**
    * \brief Returns whether the face is local or non-local for scope control purposes.
    */
   ndn::nfd::FaceScope
-  getScope() const;
+  getScope() const noexcept
+  {
+    return m_transport->getScope();
+  }
 
   /**
    * \brief Returns the current persistency setting of the face.
    */
   ndn::nfd::FacePersistency
-  getPersistency() const;
+  getPersistency() const noexcept
+  {
+    return m_transport->getPersistency();
+  }
 
   /**
    * \brief Changes the face persistency setting.
    */
   void
-  setPersistency(ndn::nfd::FacePersistency persistency);
+  setPersistency(ndn::nfd::FacePersistency persistency)
+  {
+    return m_transport->setPersistency(persistency);
+  }
 
   /**
    * \brief Returns the link type of the face (point-to-point, multi-access, ...).
    */
   ndn::nfd::LinkType
-  getLinkType() const;
+  getLinkType() const noexcept
+  {
+    return m_transport->getLinkType();
+  }
 
   /**
    * \brief Returns the effective MTU of the face.
@@ -175,25 +267,32 @@
    * This function is a wrapper. The effective MTU of a face is determined by the link service.
    */
   ssize_t
-  getMtu() const;
+  getMtu() const
+  {
+    return m_service->getEffectiveMtu();
+  }
 
   /**
    * \brief Returns the face state.
    */
   FaceState
-  getState() const;
+  getState() const noexcept
+  {
+    return m_transport->getState();
+  }
 
-  /**
-   * \brief Signals after face state changed.
-   */
-  signal::Signal<Transport, FaceState/*old*/, FaceState/*new*/>& afterStateChange;
+  /// \copydoc Transport::afterStateChange
+  signal::Signal<Transport, FaceState /*old*/, FaceState /*new*/>& afterStateChange;
 
   /**
    * \brief Returns the expiration time of the face.
    * \retval time::steady_clock::time_point::max() The face has an indefinite lifetime.
    */
   time::steady_clock::time_point
-  getExpirationTime() const;
+  getExpirationTime() const noexcept
+  {
+    return m_transport->getExpirationTime();
+  }
 
   const FaceCounters&
   getCounters() const noexcept
@@ -211,7 +310,7 @@
    * \brief Get channel on which face was created (unicast) or the associated channel (multicast).
    */
   weak_ptr<Channel>
-  getChannel() const
+  getChannel() const noexcept
   {
     return m_channel;
   }
@@ -220,7 +319,7 @@
    * \brief Set channel on which face was created (unicast) or the associated channel (multicast).
    */
   void
-  setChannel(weak_ptr<Channel> channel)
+  setChannel(weak_ptr<Channel> channel) noexcept
   {
     m_channel = std::move(channel);
   }
@@ -233,84 +332,6 @@
   weak_ptr<Channel> m_channel;
 };
 
-inline void
-Face::close()
-{
-  m_transport->close();
-}
-
-inline void
-Face::sendInterest(const Interest& interest)
-{
-  m_service->sendInterest(interest);
-}
-
-inline void
-Face::sendData(const Data& data)
-{
-  m_service->sendData(data);
-}
-
-inline void
-Face::sendNack(const lp::Nack& nack)
-{
-  m_service->sendNack(nack);
-}
-
-inline FaceUri
-Face::getLocalUri() const
-{
-  return m_transport->getLocalUri();
-}
-
-inline FaceUri
-Face::getRemoteUri() const
-{
-  return m_transport->getRemoteUri();
-}
-
-inline ndn::nfd::FaceScope
-Face::getScope() const
-{
-  return m_transport->getScope();
-}
-
-inline ndn::nfd::FacePersistency
-Face::getPersistency() const
-{
-  return m_transport->getPersistency();
-}
-
-inline void
-Face::setPersistency(ndn::nfd::FacePersistency persistency)
-{
-  return m_transport->setPersistency(persistency);
-}
-
-inline ndn::nfd::LinkType
-Face::getLinkType() const
-{
-  return m_transport->getLinkType();
-}
-
-inline ssize_t
-Face::getMtu() const
-{
-  return m_service->getEffectiveMtu();
-}
-
-inline FaceState
-Face::getState() const
-{
-  return m_transport->getState();
-}
-
-inline time::steady_clock::time_point
-Face::getExpirationTime() const
-{
-  return m_transport->getExpirationTime();
-}
-
 std::ostream&
 operator<<(std::ostream& os, const FaceLogHelper<Face>& flh);
 
diff --git a/daemon/face/generic-link-service.hpp b/daemon/face/generic-link-service.hpp
index dba7e85..1d321e2 100644
--- a/daemon/face/generic-link-service.hpp
+++ b/daemon/face/generic-link-service.hpp
@@ -35,63 +35,125 @@
 
 namespace nfd::face {
 
-/** \brief Counters provided by GenericLinkService.
- *  \note The type name GenericLinkServiceCounters is an implementation detail.
- *        Use GenericLinkService::Counters in public API.
+/**
+ * \brief Counters provided by GenericLinkService.
+ * \note The type name GenericLinkServiceCounters is an implementation detail.
+ *       Use GenericLinkService::Counters in public API.
  */
 class GenericLinkServiceCounters : public virtual LinkService::Counters
 {
 public:
-  /** \brief Count of failed fragmentations.
-   */
+  /// Count of failed fragmentations.
   PacketCounter nFragmentationErrors;
 
-  /** \brief Count of outgoing LpPackets dropped due to exceeding MTU limit.
+  /**
+   * \brief Count of outgoing LpPackets dropped due to exceeding MTU limit.
    *
-   *  If this counter is non-zero, the operator should enable fragmentation.
+   * If this counter is non-zero, the operator should enable fragmentation.
    */
   PacketCounter nOutOverMtu;
 
-  /** \brief Count of invalid LpPackets dropped before reassembly.
-   */
+  /// Count of invalid LpPackets dropped before reassembly.
   PacketCounter nInLpInvalid;
 
-  /** \brief Count of network-layer packets currently being reassembled.
-   */
+  /// Count of network-layer packets currently being reassembled.
   SizeCounter<LpReassembler> nReassembling;
 
-  /** \brief Count of dropped partial network-layer packets due to reassembly timeout.
-   */
+  /// Count of dropped partial network-layer packets due to reassembly timeout.
   PacketCounter nReassemblyTimeouts;
 
-  /** \brief Count of invalid reassembled network-layer packets dropped.
-   */
+  /// Count of invalid reassembled network-layer packets dropped.
   PacketCounter nInNetInvalid;
 
-  /** \brief Count of network-layer packets that did not require retransmission of a fragment.
-   */
+  /// Count of network-layer packets that did not require retransmission of a fragment.
   PacketCounter nAcknowledged;
 
-  /** \brief Count of network-layer packets that had at least one fragment retransmitted, but were
-   *         eventually received in full.
+  /**
+   * \brief Count of network-layer packets that had at least one fragment retransmitted,
+   *        but were eventually received in full.
    */
   PacketCounter nRetransmitted;
 
-  /** \brief Count of network-layer packets dropped because a fragment reached the maximum number
-   *         of retransmissions.
+  /**
+   * \brief Count of network-layer packets dropped because a fragment reached the maximum
+   *        number of retransmissions.
    */
   PacketCounter nRetxExhausted;
 
-  /** \brief Count of LpPackets dropped due to duplicate Sequence numbers.
-   */
+  /// Count of LpPackets dropped due to duplicate Sequence numbers.
   PacketCounter nDuplicateSequence;
 
-  /** \brief Count of outgoing LpPackets that were marked with congestion marks.
-   */
+  /// Count of outgoing LpPackets that were marked with congestion marks.
   PacketCounter nCongestionMarked;
 };
 
 /**
+ * \brief Options that control the behavior of GenericLinkService.
+ * \note The type name GenericLinkServiceOptions is an implementation detail.
+ *       Use GenericLinkService::Options in public API.
+ */
+struct GenericLinkServiceOptions
+{
+  /** \brief Enables encoding of IncomingFaceId, and decoding of NextHopFaceId and CachePolicy.
+   */
+  bool allowLocalFields = false;
+
+  /** \brief Enables fragmentation.
+   */
+  bool allowFragmentation = false;
+
+  /** \brief Options for fragmentation.
+   */
+  LpFragmenter::Options fragmenterOptions;
+
+  /** \brief Enables reassembly.
+   */
+  bool allowReassembly = false;
+
+  /** \brief Options for reassembly.
+   */
+  LpReassembler::Options reassemblerOptions;
+
+  /** \brief Options for reliability.
+   */
+  LpReliability::Options reliabilityOptions;
+
+  /** \brief Enables send queue congestion detection and marking.
+   */
+  bool allowCongestionMarking = false;
+
+  /** \brief Starting value for congestion marking interval.
+   *
+   *  Packets are marked if the queue size stays above THRESHOLD for at least one INTERVAL.
+   *
+   *  The default value (100 ms) is taken from RFC 8289 (CoDel).
+   */
+  time::nanoseconds baseCongestionMarkingInterval = 100_ms;
+
+  /** \brief Default congestion threshold in bytes.
+   *
+   *  Packets are marked if the queue size stays above THRESHOLD for at least one INTERVAL.
+   *
+   *  The default value (64 KiB) works well for a queue capacity of 200 KiB.
+   */
+  size_t defaultCongestionThreshold = 65536;
+
+  /** \brief Enables self-learning forwarding support.
+   */
+  bool allowSelfLearning = true;
+
+  /** \brief Overrides the MTU provided by Transport.
+   *
+   *  This MTU value will be used instead of the MTU provided by the transport if it is less than
+   *  the transport MTU. However, it will not be utilized when the transport MTU is unlimited.
+   *
+   *  Acceptable values for this option are values >= #MIN_MTU, which can be validated before
+   *  being set with canOverrideMtuTo().
+   */
+  ssize_t overrideMtu = std::numeric_limits<ssize_t>::max();
+};
+
+/**
  * \brief GenericLinkService is a LinkService that implements the NDNLPv2 protocol.
  * \sa https://redmine.named-data.net/projects/nfd/wiki/NDNLPv2
  */
@@ -99,83 +161,21 @@
                                                      , protected virtual GenericLinkServiceCounters
 {
 public:
-  /** \brief %Options that control the behavior of GenericLinkService.
-   */
-  class Options
-  {
-  public:
-    Options() noexcept
-    {
-    }
-
-  public:
-    /** \brief Enables encoding of IncomingFaceId, and decoding of NextHopFaceId and CachePolicy.
-     */
-    bool allowLocalFields = false;
-
-    /** \brief Enables fragmentation.
-     */
-    bool allowFragmentation = false;
-
-    /** \brief Options for fragmentation.
-     */
-    LpFragmenter::Options fragmenterOptions;
-
-    /** \brief Enables reassembly.
-     */
-    bool allowReassembly = false;
-
-    /** \brief Options for reassembly.
-     */
-    LpReassembler::Options reassemblerOptions;
-
-    /** \brief Options for reliability.
-     */
-    LpReliability::Options reliabilityOptions;
-
-    /** \brief Enables send queue congestion detection and marking.
-     */
-    bool allowCongestionMarking = false;
-
-    /** \brief Starting value for congestion marking interval.
-     *
-     *  Packets are marked if the queue size stays above THRESHOLD for at least one INTERVAL.
-     *
-     *  The default value (100 ms) is taken from RFC 8289 (CoDel).
-     */
-    time::nanoseconds baseCongestionMarkingInterval = 100_ms;
-
-    /** \brief Default congestion threshold in bytes.
-     *
-     *  Packets are marked if the queue size stays above THRESHOLD for at least one INTERVAL.
-     *
-     *  The default value (64 KiB) works well for a queue capacity of 200 KiB.
-     */
-    size_t defaultCongestionThreshold = 65536;
-
-    /** \brief Enables self-learning forwarding support.
-     */
-    bool allowSelfLearning = true;
-
-    /** \brief Overrides the MTU provided by Transport.
-     *
-     *  This MTU value will be used instead of the MTU provided by the transport if it is less than
-     *  the transport MTU. However, it will not be utilized when the transport MTU is unlimited.
-     *
-     *  Acceptable values for this option are values >= #MIN_MTU, which can be validated before
-     *  being set with canOverrideMtuTo().
-     */
-    ssize_t overrideMtu = std::numeric_limits<ssize_t>::max();
-  };
-
-  /** \brief %Counters provided by GenericLinkService.
+  /**
+   * \brief %Counters provided by GenericLinkService.
    */
   using Counters = GenericLinkServiceCounters;
 
+  /**
+   * \brief %Options for GenericLinkService.
+   */
+  using Options = GenericLinkServiceOptions;
+
   explicit
   GenericLinkService(const Options& options = {});
 
-  /** \brief Get the options used by GenericLinkService.
+  /**
+   * \brief Get the options used by GenericLinkService.
    */
   const Options&
   getOptions() const
@@ -183,7 +183,8 @@
     return m_options;
   }
 
-  /** \brief Sets the options used by GenericLinkService.
+  /**
+   * \brief Sets the options used by GenericLinkService.
    */
   void
   setOptions(const Options& options);
diff --git a/daemon/face/link-service.hpp b/daemon/face/link-service.hpp
index da752e6..66dab05 100644
--- a/daemon/face/link-service.hpp
+++ b/daemon/face/link-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-2024,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -32,39 +32,27 @@
 
 namespace nfd::face {
 
-/** \brief Counters provided by LinkService.
- *  \note The type name LinkServiceCounters is an implementation detail.
- *        Use LinkService::Counters in public API.
+/**
+ * \brief Counters provided by LinkService.
+ * \note The type name LinkServiceCounters is an implementation detail.
+ *       Use LinkService::Counters in public API.
  */
 class LinkServiceCounters
 {
 public:
-  /** \brief Count of incoming Interests.
-   */
+  /// Count of incoming Interest packets.
   PacketCounter nInInterests;
-
-  /** \brief Count of outgoing Interests.
-   */
+  /// Count of outgoing Interest packets.
   PacketCounter nOutInterests;
-
-  /** \brief Count of Interests dropped by reliability system for exceeding allowed number of retx.
-   */
+  /// Count of Interests dropped by reliability system for exceeding allowed number of retx.
   PacketCounter nInterestsExceededRetx;
-
-  /** \brief Count of incoming Data packets.
-   */
+  /// Count of incoming Data packets.
   PacketCounter nInData;
-
-  /** \brief Count of outgoing Data packets.
-   */
+  /// Count of outgoing Data packets.
   PacketCounter nOutData;
-
-  /** \brief Count of incoming Nacks.
-   */
+  /// Count of incoming Nack packets.
   PacketCounter nInNacks;
-
-  /** \brief Count of outgoing Nacks.
-   */
+  /// Count of outgoing Nack packets.
   PacketCounter nOutNacks;
 };
 
@@ -125,87 +113,112 @@
   }
 
   virtual ssize_t
-  getEffectiveMtu() const;
+  getEffectiveMtu() const
+  {
+    return m_transport->getMtu();
+  }
 
 public: // upper interface to be used by forwarding
-  /** \brief Send Interest.
-   *  \pre setFaceAndTransport() has been called.
+  /**
+   * \brief Send Interest.
+   * \pre setFaceAndTransport() has been called.
    */
   void
   sendInterest(const Interest& interest);
 
-  /** \brief Send Data.
-   *  \pre setFaceAndTransport() has been called.
+  /**
+   * \brief Send Data.
+   * \pre setFaceAndTransport() has been called.
    */
   void
   sendData(const Data& data);
 
-  /** \brief Send Nack.
-   *  \pre setFaceAndTransport() has been called.
+  /**
+   * \brief Send Nack.
+   * \pre setFaceAndTransport() has been called.
    */
   void
   sendNack(const ndn::lp::Nack& nack);
 
-  /** \brief Signals on Interest received.
+  /**
+   * \brief Called when an Interest packet is received.
    */
   signal::Signal<LinkService, Interest, EndpointId> afterReceiveInterest;
 
-  /** \brief Signals on Data received.
+  /**
+   * \brief Called when a Data packet is received.
    */
   signal::Signal<LinkService, Data, EndpointId> afterReceiveData;
 
-  /** \brief Signals on Nack received.
+  /**
+   * \brief Called when a Nack packet is received.
    */
   signal::Signal<LinkService, lp::Nack, EndpointId> afterReceiveNack;
 
-  /** \brief Signals on Interest dropped by reliability system for exceeding allowed number of retx.
+  /**
+   * \brief Called when an Interest is dropped by the reliability system
+   *        for exceeding the allowed number of retransmissions.
    */
   signal::Signal<LinkService, Interest> onDroppedInterest;
 
 public: // lower interface to be invoked by Transport
-  /** \brief Performs LinkService specific operations to receive a lower-layer packet.
+  /**
+   * \brief Performs LinkService-specific operations to receive a lower-layer packet.
    */
   void
-  receivePacket(const Block& packet, const EndpointId& endpoint);
+  receivePacket(const Block& packet, const EndpointId& endpoint)
+  {
+    doReceivePacket(packet, endpoint);
+  }
 
 protected: // upper interface to be invoked in subclass (receive path termination)
-  /** \brief Delivers received Interest to forwarding.
+  /**
+   * \brief Delivers received Interest to forwarding.
    */
   void
   receiveInterest(const Interest& interest, const EndpointId& endpoint);
 
-  /** \brief Delivers received Data to forwarding.
+  /**
+   * \brief Delivers received Data to forwarding.
    */
   void
   receiveData(const Data& data, const EndpointId& endpoint);
 
-  /** \brief Delivers received Nack to forwarding.
+  /**
+   * \brief Delivers received Nack to forwarding.
    */
   void
   receiveNack(const lp::Nack& nack, const EndpointId& endpoint);
 
 protected: // lower interface to be invoked in subclass (send path termination)
-  /** \brief Send a lower-layer packet via Transport.
+  /**
+   * \brief Send a lower-layer packet via Transport.
    */
   void
-  sendPacket(const Block& packet);
+  sendPacket(const Block& packet)
+  {
+    m_transport->send(packet);
+  }
 
 protected:
   void
   notifyDroppedInterest(const Interest& packet);
 
 private: // upper interface to be overridden in subclass (send path entrypoint)
-  /** \brief Performs LinkService specific operations to send an Interest.
+  /**
+   * \brief Performs LinkService-specific operations to send an Interest.
    */
   virtual void
   doSendInterest(const Interest& interest) = 0;
 
-  /** \brief Performs LinkService specific operations to send a Data.
+  /**
+   * \brief Performs LinkService-specific operations to send a Data.
    */
   virtual void
   doSendData(const Data& data) = 0;
 
-  /** \brief Performs LinkService specific operations to send a Nack.
+  /**
+   * \brief Performs LinkService-specific operations to send a Nack.
    */
   virtual void
   doSendNack(const lp::Nack& nack) = 0;
@@ -219,24 +232,6 @@
   Transport* m_transport = nullptr;
 };
 
-inline ssize_t
-LinkService::getEffectiveMtu() const
-{
-  return m_transport->getMtu();
-}
-
-inline void
-LinkService::receivePacket(const Block& packet, const EndpointId& endpoint)
-{
-  doReceivePacket(packet, endpoint);
-}
-
-inline void
-LinkService::sendPacket(const Block& packet)
-{
-  m_transport->send(packet);
-}
-
 std::ostream&
 operator<<(std::ostream& os, const FaceLogHelper<LinkService>& flh);
 
diff --git a/daemon/face/lp-fragmenter.cpp b/daemon/face/lp-fragmenter.cpp
index f3875c8..8e717c5 100644
--- a/daemon/face/lp-fragmenter.cpp
+++ b/daemon/face/lp-fragmenter.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2023,  Regents of the University of California,
+ * Copyright (c) 2014-2024,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -62,18 +62,6 @@
 {
 }
 
-void
-LpFragmenter::setOptions(const Options& options)
-{
-  m_options = options;
-}
-
-const LinkService*
-LpFragmenter::getLinkService() const
-{
-  return m_linkService;
-}
-
 std::tuple<bool, std::vector<lp::Packet>>
 LpFragmenter::fragmentPacket(const lp::Packet& packet, size_t mtu)
 {
diff --git a/daemon/face/lp-fragmenter.hpp b/daemon/face/lp-fragmenter.hpp
index 40e429b..fa364f8 100644
--- a/daemon/face/lp-fragmenter.hpp
+++ b/daemon/face/lp-fragmenter.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-2024,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -32,17 +32,20 @@
 
 namespace nfd::face {
 
-/** \brief Fragments network-layer packets into NDNLPv2 link-layer packets.
- *  \sa https://redmine.named-data.net/projects/nfd/wiki/NDNLPv2
+/**
+ * \brief Fragments network-layer packets into NDNLPv2 link-layer packets.
+ * \sa https://redmine.named-data.net/projects/nfd/wiki/NDNLPv2
  */
 class LpFragmenter
 {
 public:
-  /** \brief %Options that control the behavior of LpFragmenter.
+  /**
+   * \brief %Options that control the behavior of LpFragmenter.
    */
   struct Options
   {
-    /** \brief Maximum number of fragments in a packet.
+    /**
+     * \brief Maximum number of fragments in a packet.
      */
     size_t nMaxFragments = 400;
   };
@@ -50,23 +53,32 @@
   explicit
   LpFragmenter(const Options& options, const LinkService* linkService = nullptr);
 
-  /** \brief Set options for fragmenter.
+  /**
+   * \brief Set options for fragmenter.
    */
   void
-  setOptions(const Options& options);
+  setOptions(const Options& options)
+  {
+    m_options = options;
+  }
 
-  /** \return LinkService that owns this instance
+  /**
+   * \brief Returns the LinkService that owns this instance.
    *
-   *  This is only used for logging, and may be nullptr.
+   * This is only used for logging, and may be nullptr.
    */
   const LinkService*
-  getLinkService() const;
+  getLinkService() const noexcept
+  {
+    return m_linkService;
+  }
 
-  /** \brief Fragments a network-layer packet into link-layer packets.
-   *  \param packet an LpPacket that contains a network-layer packet;
-   *                must have Fragment field, must not have FragIndex and FragCount fields
-   *  \param mtu maximum allowable LpPacket size after fragmentation and sequence number assignment
-   *  \return whether fragmentation succeeded, fragmented packets without sequence number
+  /**
+   * \brief Fragments a network-layer packet into link-layer packets.
+   * \param packet an LpPacket that contains a network-layer packet;
+   *               must have Fragment field, must not have FragIndex and FragCount fields
+   * \param mtu maximum allowable LpPacket size after fragmentation and sequence number assignment
+   * \return whether fragmentation succeeded, fragmented packets without sequence number
    */
   std::tuple<bool, std::vector<lp::Packet>>
   fragmentPacket(const lp::Packet& packet, size_t mtu);
diff --git a/daemon/face/lp-reassembler.hpp b/daemon/face/lp-reassembler.hpp
index dbb9b9f..9f3a0ea 100644
--- a/daemon/face/lp-reassembler.hpp
+++ b/daemon/face/lp-reassembler.hpp
@@ -43,17 +43,20 @@
 class LpReassembler : noncopyable
 {
 public:
-  /** \brief %Options that control the behavior of LpReassembler.
+  /**
+   * \brief %Options that control the behavior of LpReassembler.
    */
   struct Options
   {
-    /** \brief Maximum number of fragments in a packet.
+    /**
+     * \brief Maximum number of fragments in a packet.
      *
-     *  LpPackets with FragCount over this limit are dropped.
+     * LpPackets with FragCount over this limit are dropped.
      */
     size_t nMaxFragments = 400;
 
-    /** \brief Timeout before a partially reassembled packet is dropped.
+    /**
+     * \brief Timeout before a partially reassembled packet is dropped.
      */
     time::nanoseconds reassemblyTimeout = 500_ms;
   };
@@ -61,34 +64,47 @@
   explicit
   LpReassembler(const Options& options, const LinkService* linkService = nullptr);
 
-  /** \brief Set options for reassembler.
+  /**
+   * \brief Set options for reassembler.
    */
   void
-  setOptions(const Options& options);
+  setOptions(const Options& options)
+  {
+    m_options = options;
+  }
 
-  /** \return LinkService that owns this instance
+  /**
+   * \brief Returns the LinkService that owns this instance.
    *
-   *  This is only used for logging, and may be nullptr.
+   * This is only used for logging, and may be nullptr.
    */
   const LinkService*
-  getLinkService() const;
+  getLinkService() const noexcept
+  {
+    return m_linkService;
+  }
 
-  /** \brief Adds received fragment to the buffer.
-   *  \param remoteEndpoint endpoint that sent the packet
-   *  \param packet received fragment; must have Fragment field
-   *  \return a tuple containing:
-   *          whether a network-layer packet has been completely received,
-   *          the reassembled network-layer packet,
-   *          the first fragment for inspecting other NDNLPv2 headers
-   *  \throw tlv::Error packet is malformed
+  /**
+   * \brief Adds received fragment to the buffer.
+   * \param remoteEndpoint endpoint that sent the packet
+   * \param packet received fragment; must have Fragment field
+   * \return a tuple containing:
+   *         whether a network-layer packet has been completely received,
+   *         the reassembled network-layer packet,
+   *         the first fragment for inspecting other NDNLPv2 headers
+   * \throw tlv::Error packet is malformed
    */
   std::tuple<bool, Block, lp::Packet>
   receiveFragment(const EndpointId& remoteEndpoint, const lp::Packet& packet);
 
-  /** \brief Count of partial packets.
+  /**
+   * \brief Count of partial packets.
    */
   size_t
-  size() const;
+  size() const noexcept
+  {
+    return m_partialPackets.size();
+  }
 
   /**
    * \brief Notifies before a partial packet is dropped due to timeout.
@@ -135,24 +151,6 @@
 std::ostream&
 operator<<(std::ostream& os, const FaceLogHelper<LpReassembler>& flh);
 
-inline void
-LpReassembler::setOptions(const Options& options)
-{
-  m_options = options;
-}
-
-inline const LinkService*
-LpReassembler::getLinkService() const
-{
-  return m_linkService;
-}
-
-inline size_t
-LpReassembler::size() const
-{
-  return m_partialPackets.size();
-}
-
 } // namespace nfd::face
 
 #endif // NFD_DAEMON_FACE_LP_REASSEMBLER_HPP
diff --git a/daemon/face/lp-reliability.cpp b/daemon/face/lp-reliability.cpp
index 4f26e5d..74782c2 100644
--- a/daemon/face/lp-reliability.cpp
+++ b/daemon/face/lp-reliability.cpp
@@ -58,12 +58,6 @@
   m_options = options;
 }
 
-const GenericLinkService*
-LpReliability::getLinkService() const
-{
-  return m_linkService;
-}
-
 void
 LpReliability::handleOutgoing(std::vector<lp::Packet>& frags, lp::Packet&& pkt, bool isInterest)
 {
diff --git a/daemon/face/lp-reliability.hpp b/daemon/face/lp-reliability.hpp
index 0324304..d4f9582 100644
--- a/daemon/face/lp-reliability.hpp
+++ b/daemon/face/lp-reliability.hpp
@@ -74,21 +74,27 @@
 
   LpReliability(const Options& options, GenericLinkService* linkService);
 
-  /** \brief Signals on Interest dropped by reliability system for exceeding allowed number of retx.
+  /**
+   * \brief Called when an Interest is dropped for exceeding the allowed number of retransmissions.
    */
   signal::Signal<LpReliability, Interest> onDroppedInterest;
 
-  /** \brief Set options for reliability.
+  /**
+   * \brief Set options for reliability.
    */
   void
   setOptions(const Options& options);
 
-  /** \return GenericLinkService that owns this instance
+  /**
+   * \brief Returns the GenericLinkService that owns this instance.
    *
-   *  This is only used for logging, and may be nullptr.
+   * This is only used for logging, and may be nullptr.
    */
   const GenericLinkService*
-  getLinkService() const;
+  getLinkService() const noexcept
+  {
+    return m_linkService;
+  }
 
   /** \brief Observe outgoing fragment(s) of a network packet and store for potential retransmission.
    *  \param frags fragments of network packet
diff --git a/daemon/face/transport.hpp b/daemon/face/transport.hpp
index cebc927..33cef80 100644
--- a/daemon/face/transport.hpp
+++ b/daemon/face/transport.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-2024,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -46,42 +46,51 @@
 std::ostream&
 operator<<(std::ostream& os, TransportState state);
 
-/** \brief Counters provided by a transport.
- *  \note The type name TransportCounters is an implementation detail.
- *        Use Transport::Counters in public API.
+/**
+ * \brief Counters provided by a transport.
+ * \note The type name TransportCounters is an implementation detail.
+ *       Use Transport::Counters in public API.
  */
 class TransportCounters
 {
 public:
-  /** \brief Count of incoming packets.
+  /**
+   * \brief Count of incoming packets.
    *
-   *  A 'packet' typically means a top-level TLV block.
-   *  For a datagram-based transport, an incoming packet that cannot be parsed as TLV
-   *  would not be counted.
+   * A 'packet' typically means a top-level TLV element.
+   *
+   * For a datagram-based transport, an incoming packet that cannot be parsed as TLV
+   * will not be counted.
    */
   PacketCounter nInPackets;
 
-  /** \brief Count of outgoing packets.
+  /**
+   * \brief Count of outgoing packets.
    *
-   *  A 'packet' typically means a top-level TLV block.
-   *  This counter is incremented only if transport is UP.
+   * A 'packet' typically means a top-level TLV element.
+   *
+   * This counter is incremented only when the transport is UP.
    */
   PacketCounter nOutPackets;
 
-  /** \brief Total incoming bytes.
+  /**
+   * \brief Total bytes received.
    *
-   *  This counter includes headers imposed by NFD (such as NDNLP),
-   *  but excludes overhead of underlying protocol (such as IP header).
-   *  For a datagram-based transport, an incoming packet that cannot be parsed as TLV
-   *  would not be counted.
+   * This counter includes headers imposed by NFD (such as NDNLP), but excludes the
+   * overhead of the underlying protocol (such as IP header).
+   *
+   * For a datagram-based transport, an incoming packet that cannot be parsed as TLV
+   * will not be counted.
    */
   ByteCounter nInBytes;
 
-  /** \brief Total outgoing bytes.
+  /**
+   * \brief Total bytes sent.
    *
-   *  This counter includes headers imposed by NFD (such as NDNLP),
-   *  but excludes overhead of underlying protocol (such as IP header).
-   *  This counter is increased only if transport is UP.
+   * This counter includes headers imposed by NFD (such as NDNLP), but excludes the
+   * overhead of the underlying protocol (such as IP header).
+   *
+   * This counter is increased only when the transport is UP.
    */
   ByteCounter nOutBytes;
 };
@@ -294,9 +303,9 @@
   }
 
   /**
-   * \brief Signals when the transport state changes.
+   * \brief Called when the transport state changes.
    */
-  signal::Signal<Transport, TransportState/*old*/, TransportState/*new*/> afterStateChange;
+  signal::Signal<Transport, TransportState /*old*/, TransportState /*new*/> afterStateChange;
 
   /**
    * \brief Returns the expiration time of the transport.
diff --git a/daemon/face/websocket-transport.hpp b/daemon/face/websocket-transport.hpp
index 3f08d79..34bb454 100644
--- a/daemon/face/websocket-transport.hpp
+++ b/daemon/face/websocket-transport.hpp
@@ -33,29 +33,29 @@
 
 namespace nfd::face {
 
-/** \brief Counters provided by WebSocketTransport.
- *  \note The type name WebSocketTransportCounters is an implementation detail.
- *        Use WebSocketTransport::Counters in public API.
+/**
+ * \brief Counters provided by WebSocketTransport.
+ * \note The type name WebSocketTransportCounters is an implementation detail.
+ *       Use WebSocketTransport::Counters in public API.
  */
 class WebSocketTransportCounters : public virtual Transport::Counters
 {
 public:
-  /** \brief Count of outgoing pings.
-   */
+  /// Count of outgoing pings.
   PacketCounter nOutPings;
-
-  /** \brief Count of incoming pongs.
-   */
+  /// Count of incoming pongs.
   PacketCounter nInPongs;
 };
 
-/** \brief A Transport that communicates on a WebSocket connection.
+/**
+ * \brief A Transport that communicates on a WebSocket connection.
  */
 class WebSocketTransport final : public Transport
                                , protected virtual WebSocketTransportCounters
 {
 public:
-  /** \brief %Counters provided by WebSocketTransport.
+  /**
+   * \brief %Counters provided by WebSocketTransport.
    */
   using Counters = WebSocketTransportCounters;
 
@@ -64,9 +64,13 @@
                      time::milliseconds pingInterval);
 
   const Counters&
-  getCounters() const final;
+  getCounters() const final
+  {
+    return *this;
+  }
 
-  /** \brief Translates a message into a Block and delivers it to the link service.
+  /**
+   * \brief Translates a message into a Block and delivers it to the link service.
    */
   void
   receiveMessage(const std::string& msg);
@@ -101,12 +105,6 @@
   ndn::scheduler::ScopedEventId m_pingEventId;
 };
 
-inline const WebSocketTransport::Counters&
-WebSocketTransport::getCounters() const
-{
-  return *this;
-}
-
 } // namespace nfd::face
 
 #endif // NFD_DAEMON_FACE_WEBSOCKET_TRANSPORT_HPP