face: LpFace counters

FaceCounters for LpFace consist of LinkService::Counters and
Transport::Counters. Old FaceCounters class from 2014 face architecture is
renamed as OldFaceCounters, and can be wrapped with new FaceCounters class.
OldFaceCounters is only used in old Face subclasses.

This commit also updates existing face test suites to use new FaceCounters,
updates ForwarderStatus so that it does not rely on FaceCounters,
and moves the logic of copying face properties and counters into management
structs from face system into FaceManager.

refs #3177

Change-Id: I587e8a32fa72bac3af9d750b2c88342740aa08d6
diff --git a/daemon/face/face-counters.cpp b/daemon/face/face-counters.cpp
new file mode 100644
index 0000000..611b151
--- /dev/null
+++ b/daemon/face/face-counters.cpp
@@ -0,0 +1,69 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  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"
+#include "old-face-counters.hpp"
+
+namespace nfd {
+namespace face {
+
+FaceCounters::FaceCounters(const LinkService::Counters& linkServiceCounters,
+                           const Transport::Counters& transportCounters)
+  : nInInterests(linkServiceCounters.nInInterests)
+  , nOutInterests(linkServiceCounters.nOutInterests)
+  , 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)
+{
+}
+
+static const LinkService::Counters g_dummyLinkServiceCounters{};
+static const Transport::Counters g_dummyTransportCounters{};
+
+FaceCounters::FaceCounters(const OldFaceCounters& oldCounters)
+  : nInInterests(oldCounters.getNInInterests())
+  , nOutInterests(oldCounters.getNOutInterests())
+  , nInData(oldCounters.getNInDatas())
+  , nOutData(oldCounters.getNOutDatas())
+  , nInNacks(g_dummyLinkServiceCounters.nInNacks)
+  , nOutNacks(g_dummyLinkServiceCounters.nOutNacks)
+  , nInPackets(g_dummyTransportCounters.nInPackets)
+  , nOutPackets(g_dummyTransportCounters.nOutPackets)
+  , nInBytes(oldCounters.getNInBytes())
+  , nOutBytes(oldCounters.getNOutBytes())
+  , m_linkServiceCounters(g_dummyLinkServiceCounters)
+  , m_transportCounters(g_dummyTransportCounters)
+{
+}
+
+} // namespace face
+} // namespace nfd
diff --git a/daemon/face/face-counters.hpp b/daemon/face/face-counters.hpp
index 90cf106..93aebf4 100644
--- a/daemon/face/face-counters.hpp
+++ b/daemon/face/face-counters.hpp
@@ -26,153 +26,77 @@
 #ifndef NFD_DAEMON_FACE_FACE_COUNTERS_HPP
 #define NFD_DAEMON_FACE_FACE_COUNTERS_HPP
 
-#include "core/counter.hpp"
+#include "link-service.hpp"
+#include "transport.hpp"
 
 namespace nfd {
 
-/** \brief contains network layer packet counters
+class OldFaceCounters;
+
+namespace 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 NetworkLayerCounters : noncopyable
+class FaceCounters
 {
 public:
-  /// incoming Interest
-  const PacketCounter&
-  getNInInterests() const
-  {
-    return m_nInInterests;
-  }
+  FaceCounters(const LinkService::Counters& linkServiceCounters,
+               const Transport::Counters& transportCounters);
 
-  PacketCounter&
-  getNInInterests()
-  {
-    return m_nInInterests;
-  }
-
-  /// incoming Data
-  const PacketCounter&
-  getNInDatas() const
-  {
-    return m_nInDatas;
-  }
-
-  PacketCounter&
-  getNInDatas()
-  {
-    return m_nInDatas;
-  }
-
-  /// outgoing Interest
-  const PacketCounter&
-  getNOutInterests() const
-  {
-    return m_nOutInterests;
-  }
-
-  PacketCounter&
-  getNOutInterests()
-  {
-    return m_nOutInterests;
-  }
-
-  /// outgoing Data
-  const PacketCounter&
-  getNOutDatas() const
-  {
-    return m_nOutDatas;
-  }
-
-  PacketCounter&
-  getNOutDatas()
-  {
-    return m_nOutDatas;
-  }
-
-protected:
-  /** \brief copy current obseverations to a struct
-   *  \param recipient an object with set methods for counters
+  /** \brief wraps old counters
+   *
+   *  FaceCounters instance created from this constructor only provides Interest/Data counters
+   *  and Nack counters. Other counters are always zero. get<T>() should not be used.
    */
-  template<typename R>
-  void
-  copyTo(R& recipient) const
+  explicit
+  FaceCounters(const OldFaceCounters& oldCounters);
+
+  /** \return counters provided by LinkService
+   *  \tparam T LinkService counters type
+   *  \throw std::bad_cast counters type mismatch
+   */
+  template<typename T>
+  typename std::enable_if<std::is_base_of<LinkService::Counters, T>::value, const T&>::type
+  get() const
   {
-    recipient.setNInInterests(this->getNInInterests());
-    recipient.setNInDatas(this->getNInDatas());
-    recipient.setNOutInterests(this->getNOutInterests());
-    recipient.setNOutDatas(this->getNOutDatas());
+    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>
+  typename std::enable_if<std::is_base_of<Transport::Counters, T>::value, const T&>::type
+  get() const
+  {
+    return dynamic_cast<const T&>(m_transportCounters);
+  }
+
+public:
+  const PacketCounter& nInInterests;
+  const PacketCounter& nOutInterests;
+  const PacketCounter& nInData;
+  const PacketCounter& nOutData;
+  const PacketCounter& nInNacks;
+  const PacketCounter& nOutNacks;
+
+  const PacketCounter& nInPackets;
+  const PacketCounter& nOutPackets;
+  const ByteCounter& nInBytes;
+  const ByteCounter& nOutBytes;
+
 private:
-  PacketCounter m_nInInterests;
-  PacketCounter m_nInDatas;
-  PacketCounter m_nOutInterests;
-  PacketCounter m_nOutDatas;
+  const LinkService::Counters& m_linkServiceCounters;
+  const Transport::Counters& m_transportCounters;
 };
 
-/** \brief contains link layer byte counters
- */
-class LinkLayerCounters : noncopyable
-{
-public:
-  /// received bytes
-  const ByteCounter&
-  getNInBytes() const
-  {
-    return m_nInBytes;
-  }
-
-  ByteCounter&
-  getNInBytes()
-  {
-    return m_nInBytes;
-  }
-
-  /// sent bytes
-  const ByteCounter&
-  getNOutBytes() const
-  {
-    return m_nOutBytes;
-  }
-
-  ByteCounter&
-  getNOutBytes()
-  {
-    return m_nOutBytes;
-  }
-
-protected:
-  /** \brief copy current obseverations to a struct
-   *  \param recipient an object with set methods for counters
-   */
-  template<typename R>
-  void
-  copyTo(R& recipient) const
-  {
-    recipient.setNInBytes(this->getNInBytes());
-    recipient.setNOutBytes(this->getNOutBytes());
-  }
-
-private:
-  ByteCounter m_nInBytes;
-  ByteCounter m_nOutBytes;
-};
-
-/** \brief contains counters on face
- */
-class FaceCounters : public NetworkLayerCounters, public LinkLayerCounters
-{
-public:
-  /** \brief copy current obseverations to a struct
-   *  \param recipient an object with set methods for counters
-   */
-  template<typename R>
-  void
-  copyTo(R& recipient) const
-  {
-    this->NetworkLayerCounters::copyTo(recipient);
-    this->LinkLayerCounters::copyTo(recipient);
-  }
-};
-
+} // namespace face
 } // namespace nfd
 
 #endif // NFD_DAEMON_FACE_FACE_COUNTERS_HPP
diff --git a/daemon/face/face.cpp b/daemon/face/face.cpp
index 0dd8169..5367d39 100644
--- a/daemon/face/face.cpp
+++ b/daemon/face/face.cpp
@@ -31,6 +31,7 @@
 
 Face::Face(const FaceUri& remoteUri, const FaceUri& localUri, bool isLocal, bool isMultiAccess)
   : m_id(INVALID_FACEID)
+  , m_countersWrapper(m_counters)
   , m_remoteUri(remoteUri)
   , m_localUri(localUri)
   , m_isLocal(isLocal)
@@ -93,35 +94,4 @@
   this->onFail(reason);
 }
 
-template<typename FaceTraits>
-void
-Face::copyStatusTo(FaceTraits& traits) const
-{
-  traits.setFaceId(getId())
-        .setRemoteUri(getRemoteUri().toString())
-        .setLocalUri(getLocalUri().toString())
-        .setFaceScope(isLocal() ? ndn::nfd::FACE_SCOPE_LOCAL
-                                : ndn::nfd::FACE_SCOPE_NON_LOCAL)
-        .setFacePersistency(getPersistency())
-        .setLinkType(isMultiAccess() ? ndn::nfd::LINK_TYPE_MULTI_ACCESS
-                                     : ndn::nfd::LINK_TYPE_POINT_TO_POINT);
-}
-
-template void
-Face::copyStatusTo<ndn::nfd::FaceStatus>(ndn::nfd::FaceStatus&) const;
-
-template void
-Face::copyStatusTo<ndn::nfd::FaceEventNotification>(ndn::nfd::FaceEventNotification&) const;
-
-ndn::nfd::FaceStatus
-Face::getFaceStatus() const
-{
-  ndn::nfd::FaceStatus status;
-
-  copyStatusTo(status);
-  this->getCounters().copyTo(status);
-
-  return status;
-}
-
 } // namespace nfd
diff --git a/daemon/face/face.hpp b/daemon/face/face.hpp
index 9c2f2e6..25b7fdf 100644
--- a/daemon/face/face.hpp
+++ b/daemon/face/face.hpp
@@ -29,6 +29,7 @@
 #include "common.hpp"
 #include "core/logger.hpp"
 #include "face-counters.hpp"
+#include "old-face-counters.hpp"
 #include "face-log.hpp"
 
 #include <ndn-cxx/management/nfd-face-status.hpp>
@@ -162,8 +163,7 @@
   virtual bool
   isUp() const;
 
-  // 'virtual' to allow override in LpFaceWrapper
-  virtual const FaceCounters&
+  virtual const face::FaceCounters&
   getCounters() const;
 
   /** \return a FaceUri that represents the remote endpoint
@@ -176,17 +176,6 @@
   const FaceUri&
   getLocalUri() const;
 
-  /** \return FaceTraits data structure filled with the current FaceTraits status
-   */
-  template<typename FaceTraits>
-  void
-  copyStatusTo(FaceTraits& traits) const;
-
-  /** \return FaceStatus data structure filled with the current Face status
-   */
-  virtual ndn::nfd::FaceStatus
-  getFaceStatus() const;
-
 protected:
   bool
   decodeAndDispatchInput(const Block& element);
@@ -196,7 +185,7 @@
   void
   fail(const std::string& reason);
 
-  FaceCounters&
+  OldFaceCounters&
   getMutableCounters();
 
   DECLARE_SIGNAL_EMIT(onReceiveInterest)
@@ -214,7 +203,8 @@
 private:
   FaceId m_id;
   std::string m_description;
-  FaceCounters m_counters;
+  OldFaceCounters m_counters;
+  face::FaceCounters m_countersWrapper;
   const FaceUri m_remoteUri;
   const FaceUri m_localUri;
   const bool m_isLocal;
@@ -274,13 +264,13 @@
   return m_isMultiAccess;
 }
 
-inline const FaceCounters&
+inline const face::FaceCounters&
 Face::getCounters() const
 {
-  return m_counters;
+  return m_countersWrapper;
 }
 
-inline FaceCounters&
+inline OldFaceCounters&
 Face::getMutableCounters()
 {
   return m_counters;
diff --git a/daemon/face/link-service.cpp b/daemon/face/link-service.cpp
index 45194dd..93507e0 100644
--- a/daemon/face/link-service.cpp
+++ b/daemon/face/link-service.cpp
@@ -35,7 +35,6 @@
 LinkService::LinkService()
   : m_face(nullptr)
   , m_transport(nullptr)
-  , m_oldCounters(nullptr)
 {
 }
 
@@ -51,7 +50,6 @@
 
   m_face = &face;
   m_transport = &transport;
-  m_oldCounters = &m_face->getMutableCounters();
 }
 
 void
@@ -61,7 +59,6 @@
   NFD_LOG_FACE_TRACE(__func__);
 
   ++this->nOutInterests;
-  ++m_oldCounters->getNOutInterests();
 
   doSendInterest(interest);
 }
@@ -73,7 +70,6 @@
   NFD_LOG_FACE_TRACE(__func__);
 
   ++this->nOutData;
-  ++m_oldCounters->getNOutDatas();
 
   doSendData(data);
 }
@@ -95,7 +91,6 @@
   NFD_LOG_FACE_TRACE(__func__);
 
   ++this->nInInterests;
-  ++m_oldCounters->getNInInterests();
 
   afterReceiveInterest(interest);
 }
@@ -106,7 +101,6 @@
   NFD_LOG_FACE_TRACE(__func__);
 
   ++this->nInData;
-  ++m_oldCounters->getNInDatas();
 
   afterReceiveData(data);
 }
diff --git a/daemon/face/link-service.hpp b/daemon/face/link-service.hpp
index ba7e72f..031735a 100644
--- a/daemon/face/link-service.hpp
+++ b/daemon/face/link-service.hpp
@@ -26,6 +26,7 @@
 #ifndef NFD_DAEMON_FACE_LINK_SERVICE_HPP
 #define NFD_DAEMON_FACE_LINK_SERVICE_HPP
 
+#include "core/counter.hpp"
 #include "transport.hpp"
 #include "face-log.hpp"
 
@@ -188,7 +189,6 @@
 private:
   LpFace* m_face;
   Transport* m_transport;
-  NetworkLayerCounters* m_oldCounters; // old counters from LpFaceWrapper
 };
 
 inline const LpFace*
diff --git a/daemon/face/lp-face-wrapper.hpp b/daemon/face/lp-face-wrapper.hpp
index 084094f..4cfb0f3 100644
--- a/daemon/face/lp-face-wrapper.hpp
+++ b/daemon/face/lp-face-wrapper.hpp
@@ -35,8 +35,6 @@
 
 /** \brief an adaptor to provide old Face APIs from an LpFace
  *  \sa LpFace
- *  \note When LpFace is adapted by LpFaceWrapper,
- *        FaceId and counters will come from old Face rather than LpFace.
  */
 class LpFaceWrapper : public Face
 {
diff --git a/daemon/face/lp-face.cpp b/daemon/face/lp-face.cpp
index 0b2e826..55cc4d0 100644
--- a/daemon/face/lp-face.cpp
+++ b/daemon/face/lp-face.cpp
@@ -36,6 +36,7 @@
   , m_id(INVALID_FACEID)
   , m_service(std::move(service))
   , m_transport(std::move(transport))
+  , m_counters(m_service->getCounters(), m_transport->getCounters())
 {
   m_service->setFaceAndTransport(*this, *m_transport);
   m_transport->setFaceAndLinkService(*this, *m_service);
diff --git a/daemon/face/lp-face.hpp b/daemon/face/lp-face.hpp
index d825928..17746f2 100644
--- a/daemon/face/lp-face.hpp
+++ b/daemon/face/lp-face.hpp
@@ -28,6 +28,7 @@
 
 #include "transport.hpp"
 #include "link-service.hpp"
+#include "face-counters.hpp"
 #include "face-log.hpp"
 
 namespace nfd {
@@ -179,9 +180,6 @@
   const FaceCounters&
   getCounters() const;
 
-  FaceCounters&
-  getMutableCounters();
-
 private:
   FaceId m_id;
   unique_ptr<LinkService> m_service;
@@ -285,12 +283,6 @@
   return m_counters;
 }
 
-inline FaceCounters&
-LpFace::getMutableCounters()
-{
-  return m_counters;
-}
-
 template<typename T>
 typename std::enable_if<std::is_base_of<LpFace, T>::value, std::ostream&>::type
 operator<<(std::ostream& os, const FaceLogHelper<T>& flh)
diff --git a/daemon/face/old-face-counters.hpp b/daemon/face/old-face-counters.hpp
new file mode 100644
index 0000000..1c1dd2e
--- /dev/null
+++ b/daemon/face/old-face-counters.hpp
@@ -0,0 +1,178 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  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_OLD_FACE_COUNTERS_HPP
+#define NFD_DAEMON_FACE_OLD_FACE_COUNTERS_HPP
+
+#include "core/counter.hpp"
+
+namespace nfd {
+
+/** \brief contains network layer packet counters
+ */
+class NetworkLayerCounters : noncopyable
+{
+public:
+  /// incoming Interest
+  const PacketCounter&
+  getNInInterests() const
+  {
+    return m_nInInterests;
+  }
+
+  PacketCounter&
+  getNInInterests()
+  {
+    return m_nInInterests;
+  }
+
+  /// incoming Data
+  const PacketCounter&
+  getNInDatas() const
+  {
+    return m_nInDatas;
+  }
+
+  PacketCounter&
+  getNInDatas()
+  {
+    return m_nInDatas;
+  }
+
+  /// outgoing Interest
+  const PacketCounter&
+  getNOutInterests() const
+  {
+    return m_nOutInterests;
+  }
+
+  PacketCounter&
+  getNOutInterests()
+  {
+    return m_nOutInterests;
+  }
+
+  /// outgoing Data
+  const PacketCounter&
+  getNOutDatas() const
+  {
+    return m_nOutDatas;
+  }
+
+  PacketCounter&
+  getNOutDatas()
+  {
+    return m_nOutDatas;
+  }
+
+protected:
+  /** \brief copy current obseverations to a struct
+   *  \param recipient an object with set methods for counters
+   */
+  template<typename R>
+  void
+  copyTo(R& recipient) const
+  {
+    recipient.setNInInterests(this->getNInInterests());
+    recipient.setNInDatas(this->getNInDatas());
+    recipient.setNOutInterests(this->getNOutInterests());
+    recipient.setNOutDatas(this->getNOutDatas());
+  }
+
+private:
+  PacketCounter m_nInInterests;
+  PacketCounter m_nInDatas;
+  PacketCounter m_nOutInterests;
+  PacketCounter m_nOutDatas;
+};
+
+/** \brief contains link layer byte counters
+ */
+class LinkLayerCounters : noncopyable
+{
+public:
+  /// received bytes
+  const ByteCounter&
+  getNInBytes() const
+  {
+    return m_nInBytes;
+  }
+
+  ByteCounter&
+  getNInBytes()
+  {
+    return m_nInBytes;
+  }
+
+  /// sent bytes
+  const ByteCounter&
+  getNOutBytes() const
+  {
+    return m_nOutBytes;
+  }
+
+  ByteCounter&
+  getNOutBytes()
+  {
+    return m_nOutBytes;
+  }
+
+protected:
+  /** \brief copy current obseverations to a struct
+   *  \param recipient an object with set methods for counters
+   */
+  template<typename R>
+  void
+  copyTo(R& recipient) const
+  {
+    recipient.setNInBytes(this->getNInBytes());
+    recipient.setNOutBytes(this->getNOutBytes());
+  }
+
+private:
+  ByteCounter m_nInBytes;
+  ByteCounter m_nOutBytes;
+};
+
+/** \brief contains counters on face (2014 face architecture)
+ */
+class OldFaceCounters : public NetworkLayerCounters, public LinkLayerCounters
+{
+public:
+  /** \brief copy current obseverations to a struct
+   *  \param recipient an object with set methods for counters
+   */
+  template<typename R>
+  void
+  copyTo(R& recipient) const
+  {
+    this->NetworkLayerCounters::copyTo(recipient);
+    this->LinkLayerCounters::copyTo(recipient);
+  }
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_OLD_FACE_COUNTERS_HPP
diff --git a/daemon/face/transport.cpp b/daemon/face/transport.cpp
index 0c4a4c6..e3e1e07 100644
--- a/daemon/face/transport.cpp
+++ b/daemon/face/transport.cpp
@@ -65,7 +65,6 @@
   , m_linkType(ndn::nfd::LINK_TYPE_NONE)
   , m_mtu(MTU_INVALID)
   , m_state(TransportState::UP)
-  , m_oldCounters(nullptr)
 {
 }
 
@@ -81,7 +80,6 @@
 
   m_face = &face;
   m_service = &service;
-  m_oldCounters = &m_face->getMutableCounters();
 }
 
 void
@@ -112,7 +110,6 @@
   if (state == TransportState::UP) {
     ++this->nOutPackets;
     this->nOutBytes += packet.packet.size();
-    m_oldCounters->getNOutBytes() += packet.packet.size();
   }
 
   this->doSend(std::move(packet));
@@ -126,7 +123,6 @@
 
   ++this->nInPackets;
   this->nInBytes += packet.packet.size();
-  m_oldCounters->getNInBytes() += packet.packet.size();
 
   m_service->receivePacket(std::move(packet));
 }
diff --git a/daemon/face/transport.hpp b/daemon/face/transport.hpp
index b0d10f4..8735de6 100644
--- a/daemon/face/transport.hpp
+++ b/daemon/face/transport.hpp
@@ -26,9 +26,7 @@
 #ifndef NFD_DAEMON_FACE_TRANSPORT_HPP
 #define NFD_DAEMON_FACE_TRANSPORT_HPP
 
-#include "common.hpp"
-
-#include "face-counters.hpp"
+#include "core/counter.hpp"
 #include "face-log.hpp"
 
 namespace nfd {
@@ -317,7 +315,6 @@
   ndn::nfd::LinkType m_linkType;
   ssize_t m_mtu;
   TransportState m_state;
-  LinkLayerCounters* m_oldCounters; // TODO#3177 change into LinkCounters
 };
 
 inline const LpFace*
diff --git a/daemon/fw/forwarder-counters.hpp b/daemon/fw/forwarder-counters.hpp
index ba3b57c..3f8c751 100644
--- a/daemon/fw/forwarder-counters.hpp
+++ b/daemon/fw/forwarder-counters.hpp
@@ -26,24 +26,21 @@
 #ifndef NFD_DAEMON_FW_FORWARDER_COUNTERS_HPP
 #define NFD_DAEMON_FW_FORWARDER_COUNTERS_HPP
 
-#include "face/face-counters.hpp"
+#include "core/counter.hpp"
 
 namespace nfd {
 
-/** \brief contains counters on forwarder
+/** \brief counters provided by Forwarder
  */
-class ForwarderCounters : public NetworkLayerCounters
+class ForwarderCounters
 {
 public:
-  /** \brief copy current obseverations to a struct
-   *  \param recipient an object with set methods for counters
-   */
-  template<typename R>
-  void
-  copyTo(R& recipient) const
-  {
-    this->NetworkLayerCounters::copyTo(recipient);
-  }
+  PacketCounter nInInterests;
+  PacketCounter nOutInterests;
+  PacketCounter nInData;
+  PacketCounter nOutData;
+  PacketCounter nInNacks;
+  PacketCounter nOutNacks;
 };
 
 } // namespace nfd
diff --git a/daemon/fw/forwarder.cpp b/daemon/fw/forwarder.cpp
index 20428c5..b2dc6bb 100644
--- a/daemon/fw/forwarder.cpp
+++ b/daemon/fw/forwarder.cpp
@@ -105,7 +105,7 @@
   NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId() <<
                 " interest=" << interest.getName());
   const_cast<Interest&>(interest).setIncomingFaceId(inFace.getId());
-  ++m_counters.getNInInterests();
+  ++m_counters.nInInterests;
 
   // /localhost scope control
   bool isViolatingLocalhost = !inFace.isLocal() &&
@@ -317,7 +317,7 @@
 
   // send Interest
   outFace.sendInterest(*interest);
-  ++m_counters.getNOutInterests();
+  ++m_counters.nOutInterests;
 }
 
 void
@@ -371,7 +371,7 @@
   // receive Data
   NFD_LOG_DEBUG("onIncomingData face=" << inFace.getId() << " data=" << data.getName());
   const_cast<Data&>(data).setIncomingFaceId(inFace.getId());
-  ++m_counters.getNInDatas();
+  ++m_counters.nInData;
 
   // /localhost scope control
   bool isViolatingLocalhost = !inFace.isLocal() &&
@@ -473,12 +473,14 @@
 
   // send Data
   outFace.sendData(data);
-  ++m_counters.getNOutDatas();
+  ++m_counters.nOutData;
 }
 
 void
 Forwarder::onIncomingNack(Face& inFace, const lp::Nack& nack)
 {
+  ++m_counters.nInNacks;
+
   // if multi-access face, drop
   if (inFace.isMultiAccess()) {
     NFD_LOG_DEBUG("onIncomingNack face=" << inFace.getId() <<
@@ -572,6 +574,7 @@
 
   // send Nack on face
   const_cast<Face&>(outFace).sendNack(nackPkt);
+  ++m_counters.nOutNacks;
 }
 
 static inline bool
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index 6413334..1b68a3d 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -269,7 +269,10 @@
                        ndn::mgmt::StatusDatasetContext& context)
 {
   for (const auto& face : m_faceTable) {
-    context.append(face->getFaceStatus().wireEncode());
+    ndn::nfd::FaceStatus status;
+    collectFaceProperties(*face, status);
+    collectFaceCounters(*face, status);
+    context.append(status.wireEncode());
   }
   context.end();
 }
@@ -312,9 +315,13 @@
   }
 
   for (const auto& face : m_faceTable) {
-    if (doesMatchFilter(faceFilter, face)) {
-      context.append(face->getFaceStatus().wireEncode());
+    if (!doesMatchFilter(faceFilter, face)) {
+      continue;
     }
+    ndn::nfd::FaceStatus status;
+    collectFaceProperties(*face, status);
+    collectFaceCounters(*face, status);
+    context.append(status.wireEncode());
   }
 
   context.end();
@@ -362,13 +369,60 @@
   return true;
 }
 
+template<typename FaceTraits>
+inline void
+collectLpFaceProperties(const face::LpFace& face, FaceTraits& traits)
+{
+  traits.setFaceId(face.getId())
+        .setRemoteUri(face.getRemoteUri().toString())
+        .setLocalUri(face.getLocalUri().toString())
+        .setFaceScope(face.getScope())
+        .setFacePersistency(face.getPersistency())
+        .setLinkType(face.getLinkType());
+  // TODO#3172 replace this into FaceManager::collectFaceProperties
+}
+
+template<typename FaceTraits>
+void
+FaceManager::collectFaceProperties(const Face& face, FaceTraits& traits)
+{
+  auto lpFace = dynamic_cast<const face::LpFace*>(&face);
+  if (lpFace != nullptr) {
+    collectLpFaceProperties(*lpFace, traits);
+    return;
+  }
+
+  traits.setFaceId(face.getId())
+        .setRemoteUri(face.getRemoteUri().toString())
+        .setLocalUri(face.getLocalUri().toString())
+        .setFaceScope(face.isLocal() ? ndn::nfd::FACE_SCOPE_LOCAL
+                                     : ndn::nfd::FACE_SCOPE_NON_LOCAL)
+        .setFacePersistency(face.getPersistency())
+        .setLinkType(face.isMultiAccess() ? ndn::nfd::LINK_TYPE_MULTI_ACCESS
+                                          : ndn::nfd::LINK_TYPE_POINT_TO_POINT);
+}
+
+void
+FaceManager::collectFaceCounters(const Face& face, ndn::nfd::FaceStatus& status)
+{
+  const face::FaceCounters& counters = face.getCounters();
+  status.setNInInterests(counters.nInInterests)
+        .setNOutInterests(counters.nOutInterests)
+        .setNInDatas(counters.nInData)
+        .setNOutDatas(counters.nOutData)
+        .setNInNacks(counters.nInNacks)
+        .setNOutNacks(counters.nOutNacks)
+        .setNInBytes(counters.nInBytes)
+        .setNOutBytes(counters.nOutBytes);
+}
+
 void
 FaceManager::afterFaceAdded(shared_ptr<Face> face,
                             const ndn::mgmt::PostNotification& post)
 {
   ndn::nfd::FaceEventNotification notification;
   notification.setKind(ndn::nfd::FACE_EVENT_CREATED);
-  face->copyStatusTo(notification);
+  collectFaceProperties(*face, notification);
 
   post(notification.wireEncode());
 }
@@ -379,7 +433,7 @@
 {
   ndn::nfd::FaceEventNotification notification;
   notification.setKind(ndn::nfd::FACE_EVENT_DESTROYED);
-  face->copyStatusTo(notification);
+  collectFaceProperties(*face, notification);
 
   post(notification.wireEncode());
 }
diff --git a/daemon/mgmt/face-manager.hpp b/daemon/mgmt/face-manager.hpp
index 57788b7..da217b2 100644
--- a/daemon/mgmt/face-manager.hpp
+++ b/daemon/mgmt/face-manager.hpp
@@ -118,6 +118,18 @@
   bool
   doesMatchFilter(const ndn::nfd::FaceQueryFilter& filter, shared_ptr<Face> face);
 
+  /** \brief copy face properties into traits
+   *  \tparam FaceTraits either FaceStatus or FaceEventNotification
+   */
+  template<typename FaceTraits>
+  static void
+  collectFaceProperties(const Face& face, FaceTraits& traits);
+
+  /** \brief copy face counters into FaceStatus
+   */
+  static void
+  collectFaceCounters(const Face& face, ndn::nfd::FaceStatus& status);
+
 private: // NotificationStream
   void
   afterFaceAdded(shared_ptr<Face> face,
diff --git a/daemon/mgmt/forwarder-status-manager.cpp b/daemon/mgmt/forwarder-status-manager.cpp
index bf2372b..b614a58 100644
--- a/daemon/mgmt/forwarder-status-manager.cpp
+++ b/daemon/mgmt/forwarder-status-manager.cpp
@@ -56,7 +56,13 @@
   status.setNMeasurementsEntries(m_forwarder.getMeasurements().size());
   status.setNCsEntries(m_forwarder.getCs().size());
 
-  m_forwarder.getCounters().copyTo(status);
+  const ForwarderCounters& counters = m_forwarder.getCounters();
+  status.setNInInterests(counters.nInInterests)
+        .setNOutInterests(counters.nOutInterests)
+        .setNInDatas(counters.nInData)
+        .setNOutDatas(counters.nOutData)
+        .setNInNacks(counters.nInNacks)
+        .setNOutNacks(counters.nOutNacks);
 
   context.setExpiry(STATUS_SERVER_DEFAULT_FRESHNESS);
 
diff --git a/tests/daemon/face/ethernet.t.cpp b/tests/daemon/face/ethernet.t.cpp
index 4d6f053..7d97016 100644
--- a/tests/daemon/face/ethernet.t.cpp
+++ b/tests/daemon/face/ethernet.t.cpp
@@ -34,6 +34,10 @@
 namespace nfd {
 namespace tests {
 
+BOOST_AUTO_TEST_SUITE(Face)
+
+using nfd::Face;
+
 class InterfacesFixture : protected BaseFixture
 {
 protected:
@@ -59,7 +63,7 @@
   std::vector<NetworkInterfaceInfo> m_interfaces;
 };
 
-BOOST_FIXTURE_TEST_SUITE(FaceEthernet, InterfacesFixture)
+BOOST_FIXTURE_TEST_SUITE(TestEthernet, InterfacesFixture)
 
 BOOST_AUTO_TEST_CASE(GetChannels)
 {
@@ -142,8 +146,8 @@
                     "ether://[" + ethernet::getDefaultMulticastAddress().toString() + "]");
   BOOST_CHECK_EQUAL(face->getLocalUri().toString(),
                     "dev://" + m_interfaces.front().name);
-  BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 0);
-  BOOST_CHECK_EQUAL(face->getCounters().getNOutBytes(), 0);
+  BOOST_CHECK_EQUAL(face->getCounters().nInBytes, 0);
+  BOOST_CHECK_EQUAL(face->getCounters().nOutBytes, 0);
 
   face->onFail.connect([] (const std::string& reason) { BOOST_FAIL(reason); });
 
@@ -157,7 +161,7 @@
   face->sendInterest(*interest2);
   face->sendData    (*data2    );
 
-  BOOST_CHECK_EQUAL(face->getCounters().getNOutBytes(),
+  BOOST_CHECK_EQUAL(face->getCounters().nOutBytes,
                     14 * 4 + // 4 NDNLP headers
                     interest1->wireEncode().size() +
                     data1->wireEncode().size() +
@@ -203,7 +207,7 @@
   // check that packet data is not accessed if pcap didn't capture anything (caplen == 0)
   static const pcap_pkthdr zeroHeader{};
   face->processIncomingPacket(&zeroHeader, nullptr);
-  BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 0);
+  BOOST_CHECK_EQUAL(face->getCounters().nInBytes, 0);
   BOOST_CHECK_EQUAL(recInterests.size(), 0);
   BOOST_CHECK_EQUAL(recDatas.size(), 0);
 
@@ -212,7 +216,7 @@
   runtHeader.caplen = ethernet::HDR_LEN + 6;
   static const uint8_t packet2[ethernet::HDR_LEN + 6]{};
   face->processIncomingPacket(&runtHeader, packet2);
-  BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 0);
+  BOOST_CHECK_EQUAL(face->getCounters().nInBytes, 0);
   BOOST_CHECK_EQUAL(recInterests.size(), 0);
   BOOST_CHECK_EQUAL(recDatas.size(), 0);
 
@@ -227,7 +231,7 @@
     0xfd, 0xff, 0xff  // TLV length (invalid because greater than buffer size)
   };
   face->processIncomingPacket(&validHeader, packet3);
-  BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 0);
+  BOOST_CHECK_EQUAL(face->getCounters().nInBytes, 0);
   BOOST_CHECK_EQUAL(recInterests.size(), 0);
   BOOST_CHECK_EQUAL(recDatas.size(), 0);
 
@@ -240,7 +244,7 @@
     0x00              // TLV length
   };
   face->processIncomingPacket(&validHeader, packet4);
-  BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 2);
+  BOOST_CHECK_EQUAL(face->getCounters().nInBytes, 2);
   BOOST_CHECK_EQUAL(recInterests.size(), 0);
   BOOST_CHECK_EQUAL(recDatas.size(), 0);
 
@@ -257,7 +261,7 @@
     0x00              // NDN TLV length
   };
   face->processIncomingPacket(&validHeader, packet5);
-  BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 18);
+  BOOST_CHECK_EQUAL(face->getCounters().nInBytes, 18);
   BOOST_CHECK_EQUAL(recInterests.size(), 0);
   BOOST_CHECK_EQUAL(recDatas.size(), 0);
 
@@ -276,12 +280,13 @@
     0x03, 0xef, 0xe9, 0x7c
   };
   face->processIncomingPacket(&validHeader, packet6);
-  BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 56);
+  BOOST_CHECK_EQUAL(face->getCounters().nInBytes, 56);
   BOOST_CHECK_EQUAL(recInterests.size(), 1);
   BOOST_CHECK_EQUAL(recDatas.size(), 0);
 }
 
-BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END() // TestEthernet
+BOOST_AUTO_TEST_SUITE_END() // Face
 
 } // namespace tests
 } // namespace nfd
diff --git a/tests/daemon/face/face-counters.t.cpp b/tests/daemon/face/face-counters.t.cpp
deleted file mode 100644
index 949ac5b..0000000
--- a/tests/daemon/face/face-counters.t.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2015,  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/face-counters.hpp"
-#include "dummy-face.hpp"
-
-#include "tests/test-common.hpp"
-
-namespace nfd {
-namespace tests {
-
-BOOST_FIXTURE_TEST_SUITE(FaceFaceCounters, BaseFixture)
-
-BOOST_AUTO_TEST_CASE(Counters)
-{
-  DummyFace face;
-  const FaceCounters& counters = face.getCounters();
-  BOOST_CHECK_EQUAL(counters.getNInInterests() , 0);
-  BOOST_CHECK_EQUAL(counters.getNInDatas()     , 0);
-  BOOST_CHECK_EQUAL(counters.getNOutInterests(), 0);
-  BOOST_CHECK_EQUAL(counters.getNOutDatas()    , 0);
-  BOOST_CHECK_EQUAL(counters.getNInBytes()     , 0);
-  BOOST_CHECK_EQUAL(counters.getNOutBytes()    , 0);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-} // namespace tests
-} // namespace nfd
diff --git a/tests/daemon/face/lp-face-wrapper.t.cpp b/tests/daemon/face/lp-face-wrapper.t.cpp
index 182d2fc..8217d77 100644
--- a/tests/daemon/face/lp-face-wrapper.t.cpp
+++ b/tests/daemon/face/lp-face-wrapper.t.cpp
@@ -71,6 +71,14 @@
   BOOST_CHECK_EQUAL(face1w->getPersistency(), ndn::nfd::FACE_PERSISTENCY_PERMANENT);
 }
 
+BOOST_AUTO_TEST_CASE(Counters)
+{
+  auto face1w = make_shared<face::LpFaceWrapper>(make_unique<DummyLpFace>());
+  auto face1 = static_cast<DummyLpFace*>(face1w->getLpFace());
+
+  BOOST_CHECK(&face1->getCounters() == &face1w->getCounters());
+}
+
 BOOST_AUTO_TEST_CASE(FailSignal)
 {
   auto face1w = make_shared<face::LpFaceWrapper>(make_unique<DummyLpFace>());
@@ -108,16 +116,6 @@
   face1w->onReceiveData.connect(bind([&nReceivedData] { ++nReceivedData; }));
   face1w->onReceiveNack.connect(bind([&nReceivedNacks] { ++nReceivedNacks; }));
 
-  BOOST_CHECK_EQUAL(face1->getCounters().getNInInterests(), 0);
-  BOOST_CHECK_EQUAL(face1->getCounters().getNInDatas(), 0);
-  BOOST_CHECK_EQUAL(face1->getCounters().getNOutInterests(), 0);
-  BOOST_CHECK_EQUAL(face1->getCounters().getNOutDatas(), 0);
-  BOOST_CHECK_EQUAL(face1w->getCounters().getNInInterests(), 0);
-  BOOST_CHECK_EQUAL(face1w->getCounters().getNInDatas(), 0);
-  BOOST_CHECK_EQUAL(face1w->getCounters().getNOutInterests(), 0);
-  BOOST_CHECK_EQUAL(face1w->getCounters().getNOutDatas(), 0);
-  // There's no counters for NACK for now.
-
   for (size_t i = 0; i < nInInterests; ++i) {
     shared_ptr<Interest> interest = makeInterest("/JSQdqward4");
     face1->receiveInterest(*interest);
@@ -148,15 +146,6 @@
     face1w->sendNack(nack);
   }
 
-  BOOST_CHECK_EQUAL(face1->getCounters().getNInInterests(), nInInterests);
-  BOOST_CHECK_EQUAL(face1->getCounters().getNInDatas(), nInData);
-  BOOST_CHECK_EQUAL(face1->getCounters().getNOutInterests(), nOutInterests);
-  BOOST_CHECK_EQUAL(face1->getCounters().getNOutDatas(), nOutData);
-  BOOST_CHECK_EQUAL(face1w->getCounters().getNInInterests(), nInInterests);
-  BOOST_CHECK_EQUAL(face1w->getCounters().getNInDatas(), nInData);
-  BOOST_CHECK_EQUAL(face1w->getCounters().getNOutInterests(), nOutInterests);
-  BOOST_CHECK_EQUAL(face1w->getCounters().getNOutDatas(), nOutData);
-
   BOOST_CHECK_EQUAL(nReceivedInterests, nInInterests);
   BOOST_CHECK_EQUAL(nReceivedData, nInData);
   BOOST_CHECK_EQUAL(nReceivedNacks, nInNacks);
diff --git a/tests/daemon/face/lp-face.t.cpp b/tests/daemon/face/lp-face.t.cpp
new file mode 100644
index 0000000..92a3755
--- /dev/null
+++ b/tests/daemon/face/lp-face.t.cpp
@@ -0,0 +1,108 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  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/lp-face.hpp"
+
+#include "tests/test-common.hpp"
+#include "dummy-lp-face.hpp"
+
+namespace nfd {
+namespace face {
+namespace tests {
+
+using namespace nfd::tests;
+
+BOOST_AUTO_TEST_SUITE(Face)
+BOOST_FIXTURE_TEST_SUITE(TestLpFace, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(LinkServiceSendReceive)
+{
+  auto face1 = make_shared<DummyLpFace>();
+
+  const size_t nInInterests = 192;
+  const size_t nInData = 91;
+  const size_t nInNacks = 29;
+  const size_t nOutInterests = 202;
+  const size_t nOutData = 128;
+  const size_t nOutNacks = 84;
+
+  size_t nReceivedInterests = 0;
+  size_t nReceivedData = 0;
+  size_t nReceivedNacks = 0;
+  face1->afterReceiveInterest.connect(bind([&nReceivedInterests] { ++nReceivedInterests; }));
+  face1->afterReceiveData.connect(bind([&nReceivedData] { ++nReceivedData; }));
+  face1->afterReceiveNack.connect(bind([&nReceivedNacks] { ++nReceivedNacks; }));
+
+  for (size_t i = 0; i < nInInterests; ++i) {
+    shared_ptr<Interest> interest = makeInterest("/JSQdqward4");
+    face1->receiveInterest(*interest);
+  }
+
+  for (size_t i = 0; i < nInData; ++i) {
+    shared_ptr<Data> data = makeData("/hT8FDigWn1");
+    face1->receiveData(*data);
+  }
+
+  for (size_t i = 0; i < nInNacks; ++i) {
+    lp::Nack nack = makeNack("/StnEVTj4Ex", 561, lp::NackReason::CONGESTION);
+    face1->receiveNack(nack);
+  }
+
+  for (size_t i = 0; i < nOutInterests; ++i) {
+    shared_ptr<Interest> interest = makeInterest("/XyUAFYQDmd");
+    face1->sendInterest(*interest);
+  }
+
+  for (size_t i = 0; i < nOutData; ++i) {
+    shared_ptr<Data> data = makeData("/GigPEtPH6");
+    face1->sendData(*data);
+  }
+
+  for (size_t i = 0; i < nOutNacks; ++i) {
+    lp::Nack nack = makeNack("/9xK6FbwIBM", 365, lp::NackReason::CONGESTION);
+    face1->sendNack(nack);
+  }
+
+  BOOST_CHECK_EQUAL(face1->getCounters().nInInterests, nInInterests);
+  BOOST_CHECK_EQUAL(face1->getCounters().nInData, nInData);
+  BOOST_CHECK_EQUAL(face1->getCounters().nInNacks, nInNacks);
+  BOOST_CHECK_EQUAL(face1->getCounters().nOutInterests, nOutInterests);
+  BOOST_CHECK_EQUAL(face1->getCounters().nOutData, nOutData);
+  BOOST_CHECK_EQUAL(face1->getCounters().nOutNacks, nOutNacks);
+
+  BOOST_CHECK_EQUAL(nReceivedInterests, nInInterests);
+  BOOST_CHECK_EQUAL(nReceivedData, nInData);
+  BOOST_CHECK_EQUAL(nReceivedNacks, nInNacks);
+  BOOST_CHECK_EQUAL(face1->sentInterests.size(), nOutInterests);
+  BOOST_CHECK_EQUAL(face1->sentData.size(), nOutData);
+  BOOST_CHECK_EQUAL(face1->sentNacks.size(), nOutNacks);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace face
+} // namespace nfd
diff --git a/tests/daemon/face/tcp.t.cpp b/tests/daemon/face/tcp.t.cpp
index 2548dcd..6d5fe19 100644
--- a/tests/daemon/face/tcp.t.cpp
+++ b/tests/daemon/face/tcp.t.cpp
@@ -340,19 +340,19 @@
   // needed to ensure NOutBytes counters are accurate
   limitedIo.run(LimitedIo::UNLIMITED_OPS, time::seconds(1));
 
-  const FaceCounters& counters1 = face1->getCounters();
-  BOOST_CHECK_EQUAL(counters1.getNInInterests() , 1);
-  BOOST_CHECK_EQUAL(counters1.getNInDatas()     , 3);
-  BOOST_CHECK_EQUAL(counters1.getNOutInterests(), 3);
-  BOOST_CHECK_EQUAL(counters1.getNOutDatas()    , 1);
-  BOOST_CHECK_EQUAL(counters1.getNOutBytes(), nBytesSent1);
+  const face::FaceCounters& counters1 = face1->getCounters();
+  BOOST_CHECK_EQUAL(counters1.nInInterests, 1);
+  BOOST_CHECK_EQUAL(counters1.nInData, 3);
+  BOOST_CHECK_EQUAL(counters1.nOutInterests, 3);
+  BOOST_CHECK_EQUAL(counters1.nOutData, 1);
+  BOOST_CHECK_EQUAL(counters1.nOutBytes, nBytesSent1);
 
-  const FaceCounters& counters2 = face2->getCounters();
-  BOOST_CHECK_EQUAL(counters2.getNInInterests() , 3);
-  BOOST_CHECK_EQUAL(counters2.getNInDatas()     , 1);
-  BOOST_CHECK_EQUAL(counters2.getNOutInterests(), 1);
-  BOOST_CHECK_EQUAL(counters2.getNOutDatas()    , 3);
-  BOOST_CHECK_EQUAL(counters2.getNInBytes(), nBytesSent1);
+  const face::FaceCounters& counters2 = face2->getCounters();
+  BOOST_CHECK_EQUAL(counters2.nInInterests, 3);
+  BOOST_CHECK_EQUAL(counters2.nInData, 1);
+  BOOST_CHECK_EQUAL(counters2.nOutInterests, 1);
+  BOOST_CHECK_EQUAL(counters2.nOutData, 3);
+  BOOST_CHECK_EQUAL(counters2.nInBytes, nBytesSent1);
 }
 
 BOOST_FIXTURE_TEST_CASE(EndToEnd6, EndToEndFixture)
diff --git a/tests/daemon/face/udp.t.cpp b/tests/daemon/face/udp.t.cpp
index 86c9bfe..d38c908 100644
--- a/tests/daemon/face/udp.t.cpp
+++ b/tests/daemon/face/udp.t.cpp
@@ -380,8 +380,8 @@
   BOOST_CHECK_EQUAL(face1->getRemoteUri(), A::getFaceUri2());
   BOOST_CHECK_EQUAL(face1->getLocalUri(), A::getFaceUri1());
   BOOST_CHECK_EQUAL(face1->isLocal(), false); // UdpFace is never local
-  BOOST_CHECK_EQUAL(face1->getCounters().getNInBytes(), 0);
-  BOOST_CHECK_EQUAL(face1->getCounters().getNOutBytes(), 0);
+  BOOST_CHECK_EQUAL(face1->getCounters().nInBytes, 0);
+  BOOST_CHECK_EQUAL(face1->getCounters().nOutBytes, 0);
 
   // channel2 creation must be after face1 creation,
   // otherwise channel2's endpoint would be prohibited
@@ -417,8 +417,8 @@
   BOOST_CHECK_EQUAL(face2->getRemoteUri(), A::getFaceUri1());
   BOOST_CHECK_EQUAL(face2->getLocalUri(), A::getFaceUri2());
   BOOST_CHECK_EQUAL(face2->isLocal(), false); // UdpFace is never local
-  BOOST_CHECK_EQUAL(face2->getCounters().getNInBytes(), nBytesSent1);
-  BOOST_CHECK_EQUAL(face2->getCounters().getNOutBytes(), 0);
+  BOOST_CHECK_EQUAL(face2->getCounters().nInBytes, nBytesSent1);
+  BOOST_CHECK_EQUAL(face2->getCounters().nOutBytes, 0);
 
   BOOST_REQUIRE_EQUAL(history2->receivedInterests.size(), 1);
   BOOST_CHECK_EQUAL(history2->receivedInterests.front().getName(), interest1->getName());
@@ -440,21 +440,21 @@
   BOOST_CHECK_EQUAL(history1->receivedData.front().getName(), data2->getName());
 
   // counters
-  const FaceCounters& counters1 = face1->getCounters();
-  BOOST_CHECK_EQUAL(counters1.getNInInterests(), 3);
-  BOOST_CHECK_EQUAL(counters1.getNInDatas(), 1);
-  BOOST_CHECK_EQUAL(counters1.getNOutInterests(), 1);
-  BOOST_CHECK_EQUAL(counters1.getNOutDatas(), 3);
-  BOOST_CHECK_EQUAL(counters1.getNInBytes(), nBytesSent2);
-  BOOST_CHECK_EQUAL(counters1.getNOutBytes(), nBytesSent1);
+  const face::FaceCounters& counters1 = face1->getCounters();
+  BOOST_CHECK_EQUAL(counters1.nInInterests, 3);
+  BOOST_CHECK_EQUAL(counters1.nInData, 1);
+  BOOST_CHECK_EQUAL(counters1.nOutInterests, 1);
+  BOOST_CHECK_EQUAL(counters1.nOutData, 3);
+  BOOST_CHECK_EQUAL(counters1.nInBytes, nBytesSent2);
+  BOOST_CHECK_EQUAL(counters1.nOutBytes, nBytesSent1);
 
-  const FaceCounters& counters2 = face2->getCounters();
-  BOOST_CHECK_EQUAL(counters2.getNInInterests(), 1);
-  BOOST_CHECK_EQUAL(counters2.getNInDatas(), 3);
-  BOOST_CHECK_EQUAL(counters2.getNOutInterests(), 3);
-  BOOST_CHECK_EQUAL(counters2.getNOutDatas(), 1);
-  BOOST_CHECK_EQUAL(counters2.getNInBytes(), nBytesSent1);
-  BOOST_CHECK_EQUAL(counters2.getNOutBytes(), nBytesSent2);
+  const face::FaceCounters& counters2 = face2->getCounters();
+  BOOST_CHECK_EQUAL(counters2.nInInterests, 1);
+  BOOST_CHECK_EQUAL(counters2.nInData, 3);
+  BOOST_CHECK_EQUAL(counters2.nOutInterests, 3);
+  BOOST_CHECK_EQUAL(counters2.nOutData, 1);
+  BOOST_CHECK_EQUAL(counters2.nInBytes, nBytesSent1);
+  BOOST_CHECK_EQUAL(counters2.nOutBytes, nBytesSent2);
 }
 
 // channel accepting multiple incoming connections
diff --git a/tests/daemon/face/unix-stream.t.cpp b/tests/daemon/face/unix-stream.t.cpp
index 0af9db2..47610af 100644
--- a/tests/daemon/face/unix-stream.t.cpp
+++ b/tests/daemon/face/unix-stream.t.cpp
@@ -266,19 +266,19 @@
   // needed to ensure NOutBytes counters are accurate
   limitedIo.run(LimitedIo::UNLIMITED_OPS, time::seconds(1));
 
-  const FaceCounters& counters1 = face1->getCounters();
-  BOOST_CHECK_EQUAL(counters1.getNInInterests() , 1);
-  BOOST_CHECK_EQUAL(counters1.getNInDatas()     , 3);
-  BOOST_CHECK_EQUAL(counters1.getNOutInterests(), 3);
-  BOOST_CHECK_EQUAL(counters1.getNOutDatas()    , 1);
-  BOOST_CHECK_EQUAL(counters1.getNInBytes(), nBytesSent2);
-  BOOST_CHECK_EQUAL(counters1.getNOutBytes(), nBytesSent1);
+  const face::FaceCounters& counters1 = face1->getCounters();
+  BOOST_CHECK_EQUAL(counters1.nInInterests, 1);
+  BOOST_CHECK_EQUAL(counters1.nInData, 3);
+  BOOST_CHECK_EQUAL(counters1.nOutInterests, 3);
+  BOOST_CHECK_EQUAL(counters1.nOutData, 1);
+  BOOST_CHECK_EQUAL(counters1.nInBytes, nBytesSent2);
+  BOOST_CHECK_EQUAL(counters1.nOutBytes, nBytesSent1);
 
-  const FaceCounters& counters2 = face2->getCounters();
-  BOOST_CHECK_EQUAL(counters2.getNInInterests() , 3);
-  BOOST_CHECK_EQUAL(counters2.getNInDatas()     , 1);
-  BOOST_CHECK_EQUAL(counters2.getNOutInterests(), 1);
-  BOOST_CHECK_EQUAL(counters2.getNOutDatas()    , 3);
+  const face::FaceCounters& counters2 = face2->getCounters();
+  BOOST_CHECK_EQUAL(counters2.nInInterests, 3);
+  BOOST_CHECK_EQUAL(counters2.nInData, 1);
+  BOOST_CHECK_EQUAL(counters2.nOutInterests, 1);
+  BOOST_CHECK_EQUAL(counters2.nOutData, 3);
 }
 
 BOOST_FIXTURE_TEST_CASE(MultipleAccepts, EndToEndFixture)
diff --git a/tests/daemon/face/websocket.t.cpp b/tests/daemon/face/websocket.t.cpp
index f790cf4..3811635 100644
--- a/tests/daemon/face/websocket.t.cpp
+++ b/tests/daemon/face/websocket.t.cpp
@@ -287,13 +287,13 @@
   BOOST_CHECK_EQUAL(client1_receivedInterests[0].getName(), interest2->getName());
   BOOST_CHECK_EQUAL(client1_receivedDatas    [0].getName(), data1->getName());
 
-  const FaceCounters& counters1 = face1->getCounters();
-  BOOST_CHECK_EQUAL(counters1.getNInInterests() , 3);
-  BOOST_CHECK_EQUAL(counters1.getNInDatas()     , 3);
-  BOOST_CHECK_EQUAL(counters1.getNOutInterests(), 1);
-  BOOST_CHECK_EQUAL(counters1.getNOutDatas()    , 1);
-  BOOST_CHECK_EQUAL(counters1.getNInBytes(), nBytesReceived);
-  BOOST_CHECK_EQUAL(counters1.getNOutBytes(), nBytesSent);
+  const face::FaceCounters& counters1 = face1->getCounters();
+  BOOST_CHECK_EQUAL(counters1.nInInterests, 3);
+  BOOST_CHECK_EQUAL(counters1.nInData, 3);
+  BOOST_CHECK_EQUAL(counters1.nOutInterests, 1);
+  BOOST_CHECK_EQUAL(counters1.nOutData, 1);
+  BOOST_CHECK_EQUAL(counters1.nInBytes, nBytesReceived);
+  BOOST_CHECK_EQUAL(counters1.nOutBytes, nBytesSent);
 
   limitedIo.run(LimitedIo::UNLIMITED_OPS, time::seconds(8));
   BOOST_CHECK_EQUAL(channel1->size(), 0);
diff --git a/tests/daemon/fw/access-strategy.t.cpp b/tests/daemon/fw/access-strategy.t.cpp
index fa211a6..78ed88f 100644
--- a/tests/daemon/fw/access-strategy.t.cpp
+++ b/tests/daemon/fw/access-strategy.t.cpp
@@ -126,9 +126,9 @@
   this->advanceClocks(time::milliseconds(5), time::seconds(12));
 
   // most Interests should be satisfied, and few Interests can go to wrong laptop
-  BOOST_CHECK_GE(consumer->getForwarderFace().getCounters().getNOutDatas(), 97);
-  BOOST_CHECK_GE(linkA->getFace(router).getCounters().getNOutInterests(), 97);
-  BOOST_CHECK_LE(linkB->getFace(router).getCounters().getNOutInterests(), 5);
+  BOOST_CHECK_GE(consumer->getForwarderFace().getCounters().nOutData, 97);
+  BOOST_CHECK_GE(linkA->getFace(router).getCounters().nOutInterests, 97);
+  BOOST_CHECK_LE(linkB->getFace(router).getCounters().nOutInterests, 5);
 }
 
 BOOST_FIXTURE_TEST_CASE(FastSlowProducer, TwoLaptopsFixture)
@@ -173,9 +173,9 @@
   this->advanceClocks(time::milliseconds(5), time::seconds(12));
 
   // most Interests should be satisfied, and few Interests can go to slower laptopB
-  BOOST_CHECK_GE(consumer->getForwarderFace().getCounters().getNOutDatas(), 97);
-  BOOST_CHECK_GE(linkA->getFace(router).getCounters().getNOutInterests(), 90);
-  BOOST_CHECK_LE(linkB->getFace(router).getCounters().getNOutInterests(), 15);
+  BOOST_CHECK_GE(consumer->getForwarderFace().getCounters().nOutData, 97);
+  BOOST_CHECK_GE(linkA->getFace(router).getCounters().nOutInterests, 90);
+  BOOST_CHECK_LE(linkB->getFace(router).getCounters().nOutInterests, 15);
 }
 
 BOOST_FIXTURE_TEST_CASE(ProducerMobility, TwoLaptopsFixture)
@@ -222,19 +222,19 @@
   this->advanceClocks(time::milliseconds(5), time::seconds(6));
 
   // few Interests can go to laptopB
-  BOOST_CHECK_LE(linkB->getFace(router).getCounters().getNOutInterests(), 5);
+  BOOST_CHECK_LE(linkB->getFace(router).getCounters().nOutInterests, 5);
 
   // producer moves to laptopB
   producerA->fail();
   producerB->recover();
-  const_cast<FaceCounters&>(linkA->getFace(router).getCounters()).getNOutInterests().set(0);
+  PacketCounter::rep nInterestsToA_beforeMove = linkA->getFace(router).getCounters().nOutInterests;
   this->advanceClocks(time::milliseconds(5), time::seconds(6));
 
   // few additional Interests can go to laptopA
-  BOOST_CHECK_LE(linkA->getFace(router).getCounters().getNOutInterests(), 5);
+  BOOST_CHECK_LE(linkA->getFace(router).getCounters().nOutInterests - nInterestsToA_beforeMove, 5);
 
   // most Interests should be satisfied
-  BOOST_CHECK_GE(consumer->getForwarderFace().getCounters().getNOutDatas(), 97);
+  BOOST_CHECK_GE(consumer->getForwarderFace().getCounters().nOutData, 97);
 }
 
 BOOST_FIXTURE_TEST_CASE(Bidirectional, TwoLaptopsFixture)
@@ -279,8 +279,8 @@
   this->advanceClocks(time::milliseconds(5), time::seconds(12));
 
   // most Interests should be satisfied
-  BOOST_CHECK_GE(consumerAB->getForwarderFace().getCounters().getNOutDatas(), 97);
-  BOOST_CHECK_GE(consumerBA->getForwarderFace().getCounters().getNOutDatas(), 97);
+  BOOST_CHECK_GE(consumerAB->getForwarderFace().getCounters().nOutData, 97);
+  BOOST_CHECK_GE(consumerBA->getForwarderFace().getCounters().nOutData, 97);
 }
 
 BOOST_FIXTURE_TEST_CASE(PacketLoss, TwoLaptopsFixture)
@@ -365,7 +365,7 @@
   this->advanceClocks(time::milliseconds(5), time::seconds(2));
 
   // Interest shouldn't loop back from router
-  BOOST_CHECK_EQUAL(linkA->getFace(router).getCounters().getNOutInterests(), 0);
+  BOOST_CHECK_EQUAL(linkA->getFace(router).getCounters().nOutInterests, 0);
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestAccessStrategy
diff --git a/tests/daemon/fw/forwarder.t.cpp b/tests/daemon/fw/forwarder.t.cpp
index 6a3509f..e40b5ad 100644
--- a/tests/daemon/fw/forwarder.t.cpp
+++ b/tests/daemon/fw/forwarder.t.cpp
@@ -62,25 +62,25 @@
   shared_ptr<fib::Entry> fibEntry = fib.insert(Name("ndn:/A")).first;
   fibEntry->addNextHop(face2, 0);
 
-  BOOST_CHECK_EQUAL(forwarder.getCounters().getNInInterests (), 0);
-  BOOST_CHECK_EQUAL(forwarder.getCounters().getNOutInterests(), 0);
+  BOOST_CHECK_EQUAL(forwarder.getCounters().nInInterests, 0);
+  BOOST_CHECK_EQUAL(forwarder.getCounters().nOutInterests, 0);
   g_io.post([&] { face1->receiveInterest(*interestAB); });
   BOOST_CHECK_EQUAL(limitedIo.run(1, time::seconds(1)), LimitedIo::EXCEED_OPS);
   BOOST_REQUIRE_EQUAL(face2->m_sentInterests.size(), 1);
   BOOST_CHECK_EQUAL(face2->m_sentInterests[0].getName(), nameAB);
   BOOST_CHECK_EQUAL(face2->m_sentInterests[0].getIncomingFaceId(), face1->getId());
-  BOOST_CHECK_EQUAL(forwarder.getCounters().getNInInterests (), 1);
-  BOOST_CHECK_EQUAL(forwarder.getCounters().getNOutInterests(), 1);
+  BOOST_CHECK_EQUAL(forwarder.getCounters().nInInterests, 1);
+  BOOST_CHECK_EQUAL(forwarder.getCounters().nOutInterests, 1);
 
-  BOOST_CHECK_EQUAL(forwarder.getCounters().getNInDatas (), 0);
-  BOOST_CHECK_EQUAL(forwarder.getCounters().getNOutDatas(), 0);
+  BOOST_CHECK_EQUAL(forwarder.getCounters().nInData, 0);
+  BOOST_CHECK_EQUAL(forwarder.getCounters().nOutData, 0);
   g_io.post([&] { face2->receiveData(*dataABC); });
   BOOST_CHECK_EQUAL(limitedIo.run(1, time::seconds(1)), LimitedIo::EXCEED_OPS);
   BOOST_REQUIRE_EQUAL(face1->m_sentDatas.size(), 1);
   BOOST_CHECK_EQUAL(face1->m_sentDatas[0].getName(), nameABC);
   BOOST_CHECK_EQUAL(face1->m_sentDatas[0].getIncomingFaceId(), face2->getId());
-  BOOST_CHECK_EQUAL(forwarder.getCounters().getNInDatas (), 1);
-  BOOST_CHECK_EQUAL(forwarder.getCounters().getNOutDatas(), 1);
+  BOOST_CHECK_EQUAL(forwarder.getCounters().nInData, 1);
+  BOOST_CHECK_EQUAL(forwarder.getCounters().nOutData, 1);
 }
 
 BOOST_AUTO_TEST_CASE(CsMatched)
diff --git a/tests/daemon/mgmt/face-manager.t.cpp b/tests/daemon/mgmt/face-manager.t.cpp
index b8944b4..8e4da1f 100644
--- a/tests/daemon/mgmt/face-manager.t.cpp
+++ b/tests/daemon/mgmt/face-manager.t.cpp
@@ -301,22 +301,18 @@
   BOOST_CHECK_NO_THROW(content.parse());
   BOOST_REQUIRE_EQUAL(content.elements().size(), nEntries);
 
-  std::vector<FaceStatus> expectedStatuses, receivedStatuses;
   std::set<FaceId> faceIds;
   for (size_t idx = 0; idx < nEntries; ++idx) {
     BOOST_TEST_MESSAGE("processing element: " << idx);
 
     ndn::nfd::FaceStatus decodedStatus;
     BOOST_REQUIRE_NO_THROW(decodedStatus.wireDecode(content.elements()[idx]));
-    BOOST_REQUIRE(m_faceTable.get(decodedStatus.getFaceId()) != nullptr);
+    BOOST_CHECK(m_faceTable.get(decodedStatus.getFaceId()) != nullptr);
     faceIds.insert(decodedStatus.getFaceId());
-    receivedStatuses.push_back(decodedStatus);
-    expectedStatuses.push_back(m_faceTable.get(decodedStatus.getFaceId())->getFaceStatus());
   }
 
   BOOST_CHECK_EQUAL(faceIds.size(), nEntries);
-  BOOST_CHECK_EQUAL_COLLECTIONS(receivedStatuses.begin(), receivedStatuses.end(),
-                                expectedStatuses.begin(), expectedStatuses.end());
+  // TODO#3325 check dataset contents including counter values
 }
 
 BOOST_AUTO_TEST_CASE(FaceQuery)
diff --git a/tests/daemon/mgmt/forwarder-status-manager.t.cpp b/tests/daemon/mgmt/forwarder-status-manager.t.cpp
index 104adaa..cd7fce2 100644
--- a/tests/daemon/mgmt/forwarder-status-manager.t.cpp
+++ b/tests/daemon/mgmt/forwarder-status-manager.t.cpp
@@ -92,6 +92,7 @@
   BOOST_CHECK_EQUAL(status.getNPitEntries(), forwarder.getPit().size());
   BOOST_CHECK_EQUAL(status.getNMeasurementsEntries(), forwarder.getMeasurements().size());
   BOOST_CHECK_EQUAL(status.getNCsEntries(), forwarder.getCs().size());
+  // TODO#3325 check packet counter values
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestForwarderStatusManager