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);