Merge feature branch 'mgmt-refactoring'

Change-Id: I3f122ede5bcfbad40800d8dae440dfea887bb10e
Refs: #2107
diff --git a/common.hpp b/common.hpp
index bd8f10b..9e44569 100644
--- a/common.hpp
+++ b/common.hpp
@@ -97,6 +97,7 @@
 using ndn::Interest;
 using ndn::Data;
 using ndn::Name;
+using ndn::PartialName;
 using ndn::Exclude;
 using ndn::Link;
 using ndn::Block;
diff --git a/core/config-file.cpp b/core/config-file.cpp
index 9724168..d989455 100644
--- a/core/config-file.cpp
+++ b/core/config-file.cpp
@@ -52,6 +52,25 @@
   // do nothing
 }
 
+bool
+ConfigFile::parseYesNo(const ConfigSection::const_iterator& i,
+                        const std::string& optionName,
+                        const std::string& sectionName)
+{
+  const std::string value = i->second.get_value<std::string>();
+  if (value == "yes") {
+    return true;
+  }
+
+  if (value == "no") {
+    return false;
+  }
+
+  BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
+                                          optionName + "\" in \"" +
+                                          sectionName + "\" section"));
+}
+
 ConfigFile::ConfigFile(UnknownConfigSectionHandler unknownSectionCallback)
   : m_unknownSectionCallback(unknownSectionCallback)
 {
diff --git a/core/config-file.hpp b/core/config-file.hpp
index 9991484..ae9102e 100644
--- a/core/config-file.hpp
+++ b/core/config-file.hpp
@@ -73,6 +73,16 @@
                        const ConfigSection& section,
                        bool isDryRun);
 
+  /** @brief parse a config option that can be either "yes" or "no"
+   *
+   *  @throw ConfigFile::Error value is neither "yes" nor "no"
+   *  @return true if "yes", false if "no"
+   */
+  static bool
+  parseYesNo(const ConfigSection::const_iterator& i,
+             const std::string& optionName,
+             const std::string& sectionName);
+
   /// \brief setup notification of configuration file sections
   void
   addSectionHandler(const std::string& sectionName,
diff --git a/daemon/face/internal-client-face.cpp b/daemon/face/internal-client-face.cpp
new file mode 100644
index 0000000..510b087
--- /dev/null
+++ b/daemon/face/internal-client-face.cpp
@@ -0,0 +1,99 @@
+/* -*- 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 "internal-client-face.hpp"
+#include "internal-face.hpp"
+#include "core/global-io.hpp"
+
+#include <ndn-cxx/management/nfd-local-control-header.hpp>
+
+namespace nfd {
+
+void
+InternalClientFace::Transport::receive(const Block& block)
+{
+  if (m_receiveCallback) {
+    m_receiveCallback(block);
+  }
+}
+
+void
+InternalClientFace::Transport::close()
+{
+}
+
+void
+InternalClientFace::Transport::send(const Block& wire)
+{
+  onSendBlock(wire);
+}
+
+void
+InternalClientFace::Transport::send(const Block& header, const Block& payload)
+{
+  ndn::EncodingBuffer encoder(header.size() + payload.size(), header.size() + payload.size());
+  encoder.appendByteArray(header.wire(), header.size());
+  encoder.appendByteArray(payload.wire(), payload.size());
+
+  this->send(encoder.block());
+}
+
+void
+InternalClientFace::Transport::pause()
+{
+}
+
+void
+InternalClientFace::Transport::resume()
+{
+}
+
+InternalClientFace::InternalClientFace(const shared_ptr<InternalFace>& face,
+                                       const shared_ptr<InternalClientFace::Transport>& transport,
+                                       ndn::KeyChain& keyChain)
+  : ndn::Face(transport, getGlobalIoService(), keyChain)
+  , m_face(face)
+  , m_transport(transport)
+{
+  construct();
+}
+
+void
+InternalClientFace::construct()
+{
+  m_face->onSendInterest.connect(bind(&InternalClientFace::receive<Interest>, this, _1));
+  m_face->onSendData.connect(bind(&InternalClientFace::receive<Data>, this, _1));
+  m_transport->onSendBlock.connect(bind(&InternalFace::receive, m_face, _1));
+}
+
+shared_ptr<InternalClientFace>
+makeInternalClientFace(const shared_ptr<InternalFace>& face,
+                       ndn::KeyChain& keyChain)
+{
+  auto transport = make_shared<InternalClientFace::Transport>();
+  return shared_ptr<InternalClientFace>(new InternalClientFace(face, transport, keyChain));
+}
+
+} // namespace nfd
diff --git a/daemon/face/internal-client-face.hpp b/daemon/face/internal-client-face.hpp
new file mode 100644
index 0000000..8eb3c46
--- /dev/null
+++ b/daemon/face/internal-client-face.hpp
@@ -0,0 +1,118 @@
+/* -*- 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_INTERNAL_CLIENT_FACE_HPP
+#define NFD_DAEMON_FACE_INTERNAL_CLIENT_FACE_HPP
+
+#include "common.hpp"
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/transport/transport.hpp>
+
+namespace nfd {
+
+class InternalFace;
+class InternalClientFace;
+
+shared_ptr<InternalClientFace>
+makeInternalClientFace(const shared_ptr<InternalFace>& face, ndn::KeyChain& keyChain);
+
+/** \brief a client face that is connected to NFD InternalFace
+ */
+class InternalClientFace : public ndn::Face
+{
+public:
+  template<typename Packet>
+  void
+  receive(const Packet& packet);
+
+private:
+  class Transport : public ndn::Transport
+  {
+  public:
+    signal::Signal<Transport, Block> onSendBlock;
+
+    void
+    receive(const Block& block);
+
+    virtual void
+    close() DECL_OVERRIDE;
+
+    virtual void
+    send(const Block& wire) DECL_OVERRIDE;
+
+    virtual void
+    send(const Block& header, const Block& payload) DECL_OVERRIDE;
+
+    virtual void
+    pause() DECL_OVERRIDE;
+
+    virtual void
+    resume() DECL_OVERRIDE;
+  };
+
+private:
+  InternalClientFace(const shared_ptr<InternalFace>& face,
+                     const shared_ptr<InternalClientFace::Transport>& transport,
+                     ndn::KeyChain& keyChain);
+
+  void
+  construct();
+
+  friend shared_ptr<InternalClientFace>
+  makeInternalClientFace(const shared_ptr<InternalFace>& face,
+                         ndn::KeyChain& keyChain);
+
+private:
+  shared_ptr<InternalFace> m_face;
+  shared_ptr<InternalClientFace::Transport> m_transport;
+};
+
+template<typename Packet>
+void
+InternalClientFace::receive(const Packet& packet)
+{
+  if (!packet.getLocalControlHeader().empty(ndn::nfd::LocalControlHeader::ENCODE_ALL)) {
+
+    Block header = packet.getLocalControlHeader()
+                         .wireEncode(packet, ndn::nfd::LocalControlHeader::ENCODE_ALL);
+    Block payload = packet.wireEncode();
+
+    ndn::EncodingBuffer encoder(header.size() + payload.size(),
+                                header.size() + payload.size());
+    encoder.appendByteArray(header.wire(), header.size());
+    encoder.appendByteArray(payload.wire(), payload.size());
+
+    m_transport->receive(encoder.block());
+  }
+  else {
+    m_transport->receive(packet.wireEncode());
+  }
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_INTERNAL_CLIENT_FACE_HPP
diff --git a/daemon/face/internal-face.cpp b/daemon/face/internal-face.cpp
new file mode 100644
index 0000000..c5ab2ca
--- /dev/null
+++ b/daemon/face/internal-face.cpp
@@ -0,0 +1,93 @@
+/* -*- 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 "internal-face.hpp"
+
+namespace nfd {
+
+InternalFace::InternalFace()
+  : Face(FaceUri("internal://"), FaceUri("internal://"), true)
+{
+}
+
+void
+InternalFace::sendInterest(const Interest& interest)
+{
+  this->emitSignal(onSendInterest, interest);
+}
+
+void
+InternalFace::sendData(const Data& data)
+{
+  this->emitSignal(onSendData, data);
+}
+
+void
+InternalFace::close()
+{
+}
+
+void
+InternalFace::receive(const Block& blockFromClient)
+{
+  const Block& payLoad = ndn::nfd::LocalControlHeader::getPayload(blockFromClient);
+  if (payLoad.type() == ndn::tlv::Interest) {
+    receiveInterest(*extractPacketFromBlock<Interest>(blockFromClient, payLoad));
+  }
+  else if (payLoad.type() == ndn::tlv::Data) {
+    receiveData(*extractPacketFromBlock<Data>(blockFromClient, payLoad));
+  }
+}
+
+void
+InternalFace::receiveInterest(const Interest& interest)
+{
+  this->emitSignal(onReceiveInterest, interest);
+}
+
+void
+InternalFace::receiveData(const Data& data)
+{
+  this->emitSignal(onReceiveData, data);
+}
+
+template<typename Packet>
+shared_ptr<Packet>
+InternalFace::extractPacketFromBlock(const Block& blockFromClient, const Block& payLoad)
+{
+  auto packet = make_shared<Packet>(payLoad);
+  if (&payLoad != &blockFromClient) {
+    packet->getLocalControlHeader().wireDecode(blockFromClient);
+  }
+  return packet;
+}
+
+template shared_ptr<Interest>
+InternalFace::extractPacketFromBlock<Interest>(const Block& blockFromClient, const Block& payLoad);
+
+template shared_ptr<Data>
+InternalFace::extractPacketFromBlock<Data>(const Block& blockFromClient, const Block& payLoad);
+
+} // namespace nfd
diff --git a/daemon/face/internal-face.hpp b/daemon/face/internal-face.hpp
new file mode 100644
index 0000000..1014fca
--- /dev/null
+++ b/daemon/face/internal-face.hpp
@@ -0,0 +1,102 @@
+/* -*- 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_INTERNAL_FACE_HPP
+#define NFD_DAEMON_FACE_INTERNAL_FACE_HPP
+
+#include "face.hpp"
+
+namespace nfd {
+
+/**
+ * @brief represents a face for internal use in NFD.
+ */
+class InternalFace : public Face
+{
+public:
+
+  InternalFace();
+
+  virtual void
+  sendInterest(const Interest& interest) DECL_OVERRIDE;
+
+  virtual void
+  sendData(const Data& data) DECL_OVERRIDE;
+
+  virtual void
+  close() DECL_OVERRIDE;
+
+  /**
+   * @brief receive a block from a client face
+   *
+   * step1. extracte the packet payload from the received block.
+   * step2. check the type (either Interest or Data) through the payload.
+   * step3. call receiveInterest / receiveData respectively according to the type.
+   *
+   * @param blockFromClient the block from a client face
+   */
+  void
+  receive(const Block& blockFromClient);
+
+  /**
+   * @brief receive an Interest from a client face
+   *
+   * emit the onReceiveInterest signal.
+   *
+   * @param interest the received Interest packet
+   */
+  void
+  receiveInterest(const Interest& interest);
+
+  /**
+   * @brief receive a Data from a client face
+   *
+   * emit the onReceiveData signal.
+   *
+   * @param data the received Data packet
+   */
+  void
+  receiveData(const Data& data);
+
+  /**
+   * @brief compose the whole packet from the received block after payload is extracted
+   *
+   * construct a packet from the extracted payload, and then decode the localControlHeader if the
+   * received block holds more information than the payload.
+   *
+   * @tparam Packet the type of packet, Interest or Data
+   * @param blockFromClient the received block
+   * @param payLoad the extracted payload
+   *
+   * @return a complete packet
+   */
+  template<typename Packet>
+  static shared_ptr<Packet>
+  extractPacketFromBlock(const Block& blockFromClient, const Block& payLoad);
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_INTERNAL_FACE_HPP
diff --git a/daemon/mgmt/channel-status-publisher.cpp b/daemon/mgmt/channel-status-publisher.cpp
deleted file mode 100644
index bb14879..0000000
--- a/daemon/mgmt/channel-status-publisher.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014,  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 "channel-status-publisher.hpp"
-#include "face/protocol-factory.hpp"
-#include "face/channel.hpp"
-
-#include <ndn-cxx/management/nfd-channel-status.hpp>
-
-namespace nfd {
-
-ChannelStatusPublisher::ChannelStatusPublisher(const FactoryMap& factories,
-                                               AppFace& face,
-                                               const Name& prefix,
-                                               ndn::KeyChain& keyChain)
-  : SegmentPublisher(face, prefix, keyChain)
-  , m_factories(factories)
-{
-}
-
-ChannelStatusPublisher::~ChannelStatusPublisher()
-{
-}
-
-size_t
-ChannelStatusPublisher::generate(ndn::EncodingBuffer& outBuffer)
-{
-  size_t totalLength = 0;
-  std::set<shared_ptr<ProtocolFactory> > seenFactories;
-
-  for (FactoryMap::const_iterator i = m_factories.begin();
-       i != m_factories.end(); ++i)
-    {
-      const shared_ptr<ProtocolFactory>& factory = i->second;
-
-      if (seenFactories.find(factory) != seenFactories.end())
-        {
-          continue;
-        }
-      seenFactories.insert(factory);
-
-      std::list<shared_ptr<const Channel> > channels = factory->getChannels();
-
-      for (std::list<shared_ptr<const Channel> >::const_iterator j = channels.begin();
-           j != channels.end(); ++j)
-        {
-          ndn::nfd::ChannelStatus entry;
-          entry.setLocalUri((*j)->getUri().toString());
-
-          totalLength += entry.wireEncode(outBuffer);
-        }
-    }
-
-  return totalLength;
-}
-
-} // namespace nfd
diff --git a/daemon/mgmt/channel-status-publisher.hpp b/daemon/mgmt/channel-status-publisher.hpp
deleted file mode 100644
index ef660a3..0000000
--- a/daemon/mgmt/channel-status-publisher.hpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014,  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_MGMT_CHANNEL_STATUS_PUBLISHER_HPP
-#define NFD_DAEMON_MGMT_CHANNEL_STATUS_PUBLISHER_HPP
-
-#include "core/segment-publisher.hpp"
-#include "mgmt/app-face.hpp"
-
-namespace nfd {
-
-class ProtocolFactory;
-
-class ChannelStatusPublisher : public SegmentPublisher<AppFace>
-{
-public:
-  typedef std::map< std::string/*protocol*/, shared_ptr<ProtocolFactory> > FactoryMap;
-
-  ChannelStatusPublisher(const FactoryMap& factories,
-                         AppFace& face,
-                         const Name& prefix,
-                         ndn::KeyChain& keyChain);
-
-  virtual
-  ~ChannelStatusPublisher();
-
-protected:
-
-  virtual size_t
-  generate(ndn::EncodingBuffer& outBuffer);
-
-private:
-  const FactoryMap& m_factories;
-};
-
-} // namespace nfd
-
-#endif // NFD_DAEMON_MGMT_CHANNEL_STATUS_PUBLISHER_HPP
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index db3b92f..2991ee6 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -25,12 +25,14 @@
 
 #include "face-manager.hpp"
 
-#include "core/logger.hpp"
 #include "core/network-interface.hpp"
 #include "fw/face-table.hpp"
 #include "face/tcp-factory.hpp"
 #include "face/udp-factory.hpp"
-#include "core/config-file.hpp"
+
+#include <ndn-cxx/management/nfd-face-status.hpp>
+#include <ndn-cxx/management/nfd-channel-status.hpp>
+#include <ndn-cxx/management/nfd-face-event-notification.hpp>
 
 #ifdef HAVE_UNIX_SOCKETS
 #include "face/unix-stream-factory.hpp"
@@ -45,120 +47,304 @@
 #include "face/websocket-factory.hpp"
 #endif // HAVE_WEBSOCKET
 
-#include <ndn-cxx/management/nfd-face-event-notification.hpp>
-#include <ndn-cxx/management/nfd-face-query-filter.hpp>
-
 namespace nfd {
 
 NFD_LOG_INIT("FaceManager");
 
-const Name FaceManager::COMMAND_PREFIX("/localhost/nfd/faces");
-
-const size_t FaceManager::COMMAND_UNSIGNED_NCOMPS =
-  FaceManager::COMMAND_PREFIX.size() +
-  1 + // verb
-  1;  // verb parameters
-
-const size_t FaceManager::COMMAND_SIGNED_NCOMPS =
-  FaceManager::COMMAND_UNSIGNED_NCOMPS +
-  4; // (timestamp, nonce, signed info tlv, signature tlv)
-
-const FaceManager::SignedVerbAndProcessor FaceManager::SIGNED_COMMAND_VERBS[] =
-  {
-    SignedVerbAndProcessor(
-                           Name::Component("create"),
-                           &FaceManager::createFace
-                           ),
-
-    SignedVerbAndProcessor(
-                           Name::Component("destroy"),
-                           &FaceManager::destroyFace
-                           ),
-
-    SignedVerbAndProcessor(
-                           Name::Component("enable-local-control"),
-                           &FaceManager::enableLocalControl
-                           ),
-
-    SignedVerbAndProcessor(
-                           Name::Component("disable-local-control"),
-                           &FaceManager::disableLocalControl
-                           ),
-  };
-
-const FaceManager::UnsignedVerbAndProcessor FaceManager::UNSIGNED_COMMAND_VERBS[] =
-  {
-    UnsignedVerbAndProcessor(
-                             Name::Component("list"),
-                             &FaceManager::listFaces
-                             ),
-
-    UnsignedVerbAndProcessor(
-                             Name::Component("events"),
-                             &FaceManager::ignoreUnsignedVerb
-                             ),
-
-    UnsignedVerbAndProcessor(
-                             Name::Component("channels"),
-                             &FaceManager::listChannels
-                             ),
-
-    UnsignedVerbAndProcessor(
-                             Name::Component("query"),
-                             &FaceManager::listQueriedFaces
-                             ),
-  };
-
-const Name FaceManager::FACES_LIST_DATASET_PREFIX("/localhost/nfd/faces/list");
-const size_t FaceManager::FACES_LIST_DATASET_NCOMPS = FACES_LIST_DATASET_PREFIX.size();
-
-const Name FaceManager::FACE_EVENTS_PREFIX("/localhost/nfd/faces/events");
-
-const Name FaceManager::CHANNELS_LIST_DATASET_PREFIX("/localhost/nfd/faces/channels");
-const size_t FaceManager::CHANNELS_LIST_DATASET_NCOMPS = CHANNELS_LIST_DATASET_PREFIX.size();
-
-const Name FaceManager::FACES_QUERY_DATASET_PREFIX("/localhost/nfd/faces/query");
-const size_t FaceManager::FACES_QUERY_DATASET_NCOMPS = FACES_QUERY_DATASET_PREFIX.size() + 1;
-
 FaceManager::FaceManager(FaceTable& faceTable,
-                         shared_ptr<InternalFace> face,
-                         ndn::KeyChain& keyChain)
-  : ManagerBase(face, FACE_MANAGER_PRIVILEGE, keyChain)
+                         Dispatcher& dispatcher,
+                         CommandValidator& validator)
+  : ManagerBase(dispatcher, validator, "faces")
   , m_faceTable(faceTable)
-  , m_faceAddConn(m_faceTable.onAdd.connect(bind(&FaceManager::onAddFace, this, _1)))
-  , m_faceRemoveConn(m_faceTable.onRemove.connect(bind(&FaceManager::onRemoveFace, this, _1)))
-  , m_faceStatusPublisher(m_faceTable, *m_face, FACES_LIST_DATASET_PREFIX, keyChain)
-  , m_channelStatusPublisher(m_factories, *m_face, CHANNELS_LIST_DATASET_PREFIX, keyChain)
-  , m_notificationStream(*m_face, FACE_EVENTS_PREFIX, keyChain)
-  , m_signedVerbDispatch(SIGNED_COMMAND_VERBS,
-                         SIGNED_COMMAND_VERBS +
-                         (sizeof(SIGNED_COMMAND_VERBS) / sizeof(SignedVerbAndProcessor)))
-  , m_unsignedVerbDispatch(UNSIGNED_COMMAND_VERBS,
-                           UNSIGNED_COMMAND_VERBS +
-                           (sizeof(UNSIGNED_COMMAND_VERBS) / sizeof(UnsignedVerbAndProcessor)))
-
 {
-  face->setInterestFilter("/localhost/nfd/faces",
-                          bind(&FaceManager::onFaceRequest, this, _2));
-}
+  registerCommandHandler<ndn::nfd::FaceCreateCommand>("create",
+    bind(&FaceManager::createFace, this, _2, _3, _4, _5));
 
-FaceManager::~FaceManager()
-{
+  registerCommandHandler<ndn::nfd::FaceDestroyCommand>("destroy",
+    bind(&FaceManager::destroyFace, this, _2, _3, _4, _5));
 
+  registerCommandHandler<ndn::nfd::FaceEnableLocalControlCommand>("enable-local-control",
+    bind(&FaceManager::enableLocalControl, this, _2, _3, _4, _5));
+
+  registerCommandHandler<ndn::nfd::FaceDisableLocalControlCommand>("disable-local-control",
+    bind(&FaceManager::disableLocalControl, this, _2, _3, _4, _5));
+
+  registerStatusDatasetHandler("list", bind(&FaceManager::listFaces, this, _1, _2, _3));
+  registerStatusDatasetHandler("channels", bind(&FaceManager::listChannels, this, _1, _2, _3));
+  registerStatusDatasetHandler("query", bind(&FaceManager::queryFaces, this, _1, _2, _3));
+
+  auto postNotification = registerNotificationStream("events");
+  m_faceAddConn =
+    m_faceTable.onAdd.connect(bind(&FaceManager::afterFaceAdded, this, _1, postNotification));
+  m_faceRemoveConn =
+    m_faceTable.onRemove.connect(bind(&FaceManager::afterFaceRemoved, this, _1, postNotification));
 }
 
 void
 FaceManager::setConfigFile(ConfigFile& configFile)
 {
-  configFile.addSectionHandler("face_system",
-                               bind(&FaceManager::onConfig, this, _1, _2, _3));
+  configFile.addSectionHandler("face_system", bind(&FaceManager::processConfig, this, _1, _2, _3));
 }
 
+void
+FaceManager::createFace(const Name& topPrefix, const Interest& interest,
+                        const ControlParameters& parameters,
+                        const ndn::mgmt::CommandContinuation& done)
+{
+  FaceUri uri;
+  if (!uri.parse(parameters.getUri())) {
+    NFD_LOG_TRACE("failed to parse URI");
+    return done(ControlResponse(400, "Malformed command"));
+  }
+
+  if (!uri.isCanonical()) {
+    NFD_LOG_TRACE("received non-canonical URI");
+    return done(ControlResponse(400, "Non-canonical URI"));
+  }
+
+  FactoryMap::iterator factory = m_factories.find(uri.getScheme());
+  if (factory == m_factories.end()) {
+    return done(ControlResponse(501, "Unsupported protocol"));
+  }
+
+  try {
+    factory->second->createFace(uri,
+                                parameters.getFacePersistency(),
+                                bind(&FaceManager::afterCreateFaceSuccess,
+                                     this, parameters, _1, done),
+                                bind(&FaceManager::afterCreateFaceFailure,
+                                     this, _1, done));
+  }
+  catch (const std::runtime_error& error) {
+    std::string errorMessage = "Face creation failed: ";
+    errorMessage += error.what();
+
+    NFD_LOG_ERROR(errorMessage);
+    return done(ControlResponse(500, errorMessage));
+  }
+  catch (const std::logic_error& error) {
+    std::string errorMessage = "Face creation failed: ";
+    errorMessage += error.what();
+
+    NFD_LOG_ERROR(errorMessage);
+    return done(ControlResponse(500, errorMessage));
+  }
+}
 
 void
-FaceManager::onConfig(const ConfigSection& configSection,
-                      bool isDryRun,
-                      const std::string& filename)
+FaceManager::destroyFace(const Name& topPrefix, const Interest& interest,
+                         const ControlParameters& parameters,
+                         const ndn::mgmt::CommandContinuation& done)
+{
+  shared_ptr<Face> target = m_faceTable.get(parameters.getFaceId());
+  if (target) {
+    target->close();
+  }
+
+  done(ControlResponse(200, "OK").setBody(parameters.wireEncode()));
+}
+
+void
+FaceManager::enableLocalControl(const Name& topPrefix, const Interest& interest,
+                                const ControlParameters& parameters,
+                                const ndn::mgmt::CommandContinuation& done)
+{
+  auto result = extractLocalControlParameters(interest, parameters, done);
+  if (result.isValid) {
+    result.face->setLocalControlHeaderFeature(result.feature, true);
+    return done(ControlResponse(200, "OK").setBody(parameters.wireEncode()));
+  }
+}
+
+void
+FaceManager::disableLocalControl(const Name& topPrefix, const Interest& interest,
+                                 const ControlParameters& parameters,
+                                 const ndn::mgmt::CommandContinuation& done)
+{
+  auto result = extractLocalControlParameters(interest, parameters, done);
+  if (result.isValid) {
+    result.face->setLocalControlHeaderFeature(result.feature, false);
+    return done(ControlResponse(200, "OK").setBody(parameters.wireEncode()));
+  }
+}
+
+void
+FaceManager::afterCreateFaceSuccess(ControlParameters& parameters,
+                                    const shared_ptr<Face>& newFace,
+                                    const ndn::mgmt::CommandContinuation& done)
+{
+  addCreatedFaceToForwarder(newFace);
+  parameters.setFaceId(newFace->getId());
+  parameters.setUri(newFace->getRemoteUri().toString());
+  parameters.setFacePersistency(newFace->getPersistency());
+
+  done(ControlResponse(200, "OK").setBody(parameters.wireEncode()));
+}
+
+void
+FaceManager::afterCreateFaceFailure(const std::string& reason,
+                                    const ndn::mgmt::CommandContinuation& done)
+{
+  NFD_LOG_DEBUG("Failed to create face: " << reason);
+
+  done(ControlResponse(408, "Failed to create face: " + reason));
+}
+
+FaceManager::ExtractLocalControlParametersResult
+FaceManager::extractLocalControlParameters(const Interest& request,
+                                           const ControlParameters& parameters,
+                                           const ndn::mgmt::CommandContinuation& done)
+{
+  ExtractLocalControlParametersResult result;
+  result.isValid = false;
+
+  auto face = m_faceTable.get(request.getIncomingFaceId());
+  if (!static_cast<bool>(face)) {
+    NFD_LOG_DEBUG("command result: faceid " << request.getIncomingFaceId() << " not found");
+    done(ControlResponse(410, "Face not found"));
+    return result;
+  }
+
+  if (!face->isLocal()) {
+    NFD_LOG_DEBUG("command result: cannot enable local control on non-local faceid " <<
+                  face->getId());
+    done(ControlResponse(412, "Face is non-local"));
+    return result;
+  }
+
+  result.isValid = true;
+  result.face = dynamic_pointer_cast<LocalFace>(face);
+  result.feature = static_cast<LocalControlFeature>(parameters.getLocalControlFeature());
+
+  return result;
+}
+
+void
+FaceManager::listFaces(const Name& topPrefix, const Interest& interest,
+                       ndn::mgmt::StatusDatasetContext& context)
+{
+  for (const auto& face : m_faceTable) {
+    context.append(face->getFaceStatus().wireEncode());
+  }
+  context.end();
+}
+
+void
+FaceManager::listChannels(const Name& topPrefix, const Interest& interest,
+                          ndn::mgmt::StatusDatasetContext& context)
+{
+  std::set<shared_ptr<ProtocolFactory>> seenFactories;
+
+  for (auto i = m_factories.begin(); i != m_factories.end(); ++i) {
+    const shared_ptr<ProtocolFactory>& factory = i->second;
+
+    if (seenFactories.find(factory) != seenFactories.end()) {
+      continue;
+    }
+    seenFactories.insert(factory);
+
+    std::list<shared_ptr<const Channel>> channels = factory->getChannels();
+
+    for (auto j = channels.begin(); j != channels.end(); ++j) {
+      ndn::nfd::ChannelStatus entry;
+      entry.setLocalUri((*j)->getUri().toString());
+      context.append(entry.wireEncode());
+    }
+  }
+
+  context.end();
+}
+
+void
+FaceManager::queryFaces(const Name& topPrefix, const Interest& interest,
+                        ndn::mgmt::StatusDatasetContext& context)
+{
+  ndn::nfd::FaceQueryFilter faceFilter;
+  const Name& query = interest.getName();
+  try {
+    faceFilter.wireDecode(query[-1].blockFromValue());
+  }
+  catch (const tlv::Error&) {
+    NFD_LOG_DEBUG("query result: malformed filter");
+    return context.reject(ControlResponse(400, "malformed filter"));
+  }
+
+  for (const auto& face : m_faceTable) {
+    if (doesMatchFilter(faceFilter, face)) {
+      context.append(face->getFaceStatus().wireEncode());
+    }
+  }
+  context.end();
+}
+
+bool
+FaceManager::doesMatchFilter(const ndn::nfd::FaceQueryFilter& filter, shared_ptr<Face> face)
+{
+  if (filter.hasFaceId() &&
+      filter.getFaceId() != static_cast<uint64_t>(face->getId())) {
+    return false;
+  }
+
+  if (filter.hasUriScheme() &&
+      filter.getUriScheme() != face->getRemoteUri().getScheme() &&
+      filter.getUriScheme() != face->getLocalUri().getScheme()) {
+    return false;
+  }
+
+  if (filter.hasRemoteUri() &&
+      filter.getRemoteUri() != face->getRemoteUri().toString()) {
+    return false;
+  }
+
+  if (filter.hasLocalUri() &&
+      filter.getLocalUri() != face->getLocalUri().toString()) {
+    return false;
+  }
+
+  if (filter.hasFaceScope() &&
+      (filter.getFaceScope() == ndn::nfd::FACE_SCOPE_LOCAL) != face->isLocal()) {
+    return false;
+  }
+
+  if (filter.hasFacePersistency() &&
+      filter.getFacePersistency() != face->getPersistency()) {
+    return false;
+  }
+
+  if (filter.hasLinkType() &&
+      (filter.getLinkType() == ndn::nfd::LINK_TYPE_MULTI_ACCESS) != face->isMultiAccess()) {
+    return false;
+  }
+
+  return true;
+}
+
+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);
+
+  post(notification.wireEncode());
+}
+
+void
+FaceManager::afterFaceRemoved(shared_ptr<Face> face,
+                              const ndn::mgmt::PostNotification& post)
+{
+  ndn::nfd::FaceEventNotification notification;
+  notification.setKind(ndn::nfd::FACE_EVENT_DESTROYED);
+  face->copyStatusTo(notification);
+
+  post(notification.wireEncode());
+}
+
+void
+FaceManager::processConfig(const ConfigSection& configSection,
+                           bool isDryRun,
+                           const std::string& filename)
 {
   bool hasSeenUnix = false;
   bool hasSeenTcp = false;
@@ -168,53 +354,51 @@
 
   const std::vector<NetworkInterfaceInfo> nicList(listNetworkInterfaces());
 
-  for (const auto& item : configSection)
-    {
-      if (item.first == "unix")
-        {
-          if (hasSeenUnix)
-            BOOST_THROW_EXCEPTION(Error("Duplicate \"unix\" section"));
-          hasSeenUnix = true;
+  for (const auto& item : configSection) {
+    if (item.first == "unix") {
+      if (hasSeenUnix) {
+        BOOST_THROW_EXCEPTION(Error("Duplicate \"unix\" section"));
+      }
+      hasSeenUnix = true;
 
-          processSectionUnix(item.second, isDryRun);
-        }
-      else if (item.first == "tcp")
-        {
-          if (hasSeenTcp)
-            BOOST_THROW_EXCEPTION(Error("Duplicate \"tcp\" section"));
-          hasSeenTcp = true;
-
-          processSectionTcp(item.second, isDryRun);
-        }
-      else if (item.first == "udp")
-        {
-          if (hasSeenUdp)
-            BOOST_THROW_EXCEPTION(Error("Duplicate \"udp\" section"));
-          hasSeenUdp = true;
-
-          processSectionUdp(item.second, isDryRun, nicList);
-        }
-      else if (item.first == "ether")
-        {
-          if (hasSeenEther)
-            BOOST_THROW_EXCEPTION(Error("Duplicate \"ether\" section"));
-          hasSeenEther = true;
-
-          processSectionEther(item.second, isDryRun, nicList);
-        }
-      else if (item.first == "websocket")
-        {
-          if (hasSeenWebSocket)
-            BOOST_THROW_EXCEPTION(Error("Duplicate \"websocket\" section"));
-          hasSeenWebSocket = true;
-
-          processSectionWebSocket(item.second, isDryRun);
-        }
-      else
-        {
-          BOOST_THROW_EXCEPTION(Error("Unrecognized option \"" + item.first + "\""));
-        }
+      processSectionUnix(item.second, isDryRun);
     }
+    else if (item.first == "tcp") {
+      if (hasSeenTcp) {
+        BOOST_THROW_EXCEPTION(Error("Duplicate \"tcp\" section"));
+      }
+      hasSeenTcp = true;
+
+      processSectionTcp(item.second, isDryRun);
+    }
+    else if (item.first == "udp") {
+      if (hasSeenUdp) {
+        BOOST_THROW_EXCEPTION(Error("Duplicate \"udp\" section"));
+      }
+      hasSeenUdp = true;
+
+      processSectionUdp(item.second, isDryRun, nicList);
+    }
+    else if (item.first == "ether") {
+      if (hasSeenEther) {
+        BOOST_THROW_EXCEPTION(Error("Duplicate \"ether\" section"));
+      }
+      hasSeenEther = true;
+
+      processSectionEther(item.second, isDryRun, nicList);
+    }
+    else if (item.first == "websocket") {
+      if (hasSeenWebSocket) {
+        BOOST_THROW_EXCEPTION(Error("Duplicate \"websocket\" section"));
+      }
+      hasSeenWebSocket = true;
+
+      processSectionWebSocket(item.second, isDryRun);
+    }
+    else {
+      BOOST_THROW_EXCEPTION(Error("Unrecognized option \"" + item.first + "\""));
+    }
+  }
 }
 
 void
@@ -230,50 +414,34 @@
 
   std::string path = "/var/run/nfd.sock";
 
-  for (ConfigSection::const_iterator i = configSection.begin();
-       i != configSection.end();
-       ++i)
-    {
-      if (i->first == "path")
-        {
-          path = i->second.get_value<std::string>();
-        }
-      else
-        {
-          BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" + i->first + "\" in "
-                                                  "\"unix\" section"));
-        }
+  for (auto i = configSection.begin(); i != configSection.end(); ++i) {
+    if (i->first == "path") {
+      path = i->second.get_value<std::string>();
+    }
+    else {
+      BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
+                                              i->first + "\" in \"unix\" section"));
+    }
+  }
+
+  if (!isDryRun) {
+    if (m_factories.count("unix") > 0) {
+      return;
     }
 
-  if (!isDryRun)
-    {
-      if (m_factories.count("unix") > 0)
-        {
-          return;
-          // shared_ptr<UnixStreamFactory> factory
-          //   = static_pointer_cast<UnixStreamFactory>(m_factories["unix"]);
-          // shared_ptr<UnixStreamChannel> unixChannel = factory->findChannel(path);
+    shared_ptr<UnixStreamFactory> factory = make_shared<UnixStreamFactory>();
+    shared_ptr<UnixStreamChannel> unixChannel = factory->createChannel(path);
 
-          // if (static_cast<bool>(unixChannel))
-          //   {
-          //     return;
-          //   }
-        }
+    // Should acceptFailed callback be used somehow?
+    unixChannel->listen(bind(&FaceTable::add, &m_faceTable, _1),
+                        UnixStreamChannel::ConnectFailedCallback());
 
-      shared_ptr<UnixStreamFactory> factory = make_shared<UnixStreamFactory>();
-      shared_ptr<UnixStreamChannel> unixChannel = factory->createChannel(path);
-
-      // Should acceptFailed callback be used somehow?
-      unixChannel->listen(bind(&FaceTable::add, &m_faceTable, _1),
-                          UnixStreamChannel::ConnectFailedCallback());
-
-      m_factories.insert(std::make_pair("unix", factory));
-    }
+    m_factories.insert(std::make_pair("unix", factory));
+  }
 #else
   BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without Unix sockets support, "
                                           "cannot process \"unix\" section"));
 #endif // HAVE_UNIX_SOCKETS
-
 }
 
 void
@@ -291,86 +459,69 @@
   bool enableV4 = true;
   bool enableV6 = true;
 
-  for (ConfigSection::const_iterator i = configSection.begin();
-       i != configSection.end();
-       ++i)
-    {
-      if (i->first == "port")
-        {
-          port = i->second.get_value<std::string>();
-          try
-            {
-              uint16_t portNo = boost::lexical_cast<uint16_t>(port);
-              NFD_LOG_TRACE("TCP port set to " << portNo);
-            }
-          catch (const std::bad_cast& error)
-            {
-              BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option " +
-                                                      i->first + "\" in \"tcp\" section"));
-            }
-        }
-      else if (i->first == "listen")
-        {
-          needToListen = parseYesNo(i, i->first, "tcp");
-        }
-      else if (i->first == "enable_v4")
-        {
-          enableV4 = parseYesNo(i, i->first, "tcp");
-        }
-      else if (i->first == "enable_v6")
-        {
-          enableV6 = parseYesNo(i, i->first, "tcp");
-        }
-      else
-        {
-          BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" + i->first + "\" in "
-                                                  "\"tcp\" section"));
-        }
+  for (auto i = configSection.begin(); i != configSection.end(); ++i) {
+    if (i->first == "port") {
+      port = i->second.get_value<std::string>();
+      try {
+        uint16_t portNo = boost::lexical_cast<uint16_t>(port);
+        NFD_LOG_TRACE("TCP port set to " << portNo);
+      }
+      catch (const std::bad_cast& error) {
+        BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option " +
+                                                i->first + "\" in \"tcp\" section"));
+      }
+    }
+    else if (i->first == "listen") {
+      needToListen = ConfigFile::parseYesNo(i, i->first, "tcp");
+    }
+    else if (i->first == "enable_v4") {
+      enableV4 = ConfigFile::parseYesNo(i, i->first, "tcp");
+    }
+    else if (i->first == "enable_v6") {
+      enableV6 = ConfigFile::parseYesNo(i, i->first, "tcp");
+    }
+    else {
+      BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
+                                              i->first + "\" in \"tcp\" section"));
+    }
+  }
+
+  if (!enableV4 && !enableV6) {
+    BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 channels have been disabled."
+                                            " Remove \"tcp\" section to disable TCP channels or"
+                                            " re-enable at least one channel type."));
+  }
+
+  if (!isDryRun) {
+    if (m_factories.count("tcp") > 0) {
+      return;
     }
 
-  if (!enableV4 && !enableV6)
-    {
-      BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 channels have been disabled."
-                                              " Remove \"tcp\" section to disable TCP channels or"
-                                              " re-enable at least one channel type."));
+    shared_ptr<TcpFactory> factory = make_shared<TcpFactory>(port);
+    m_factories.insert(std::make_pair("tcp", factory));
+
+    if (enableV4) {
+      shared_ptr<TcpChannel> ipv4Channel = factory->createChannel("0.0.0.0", port);
+      if (needToListen) {
+        // Should acceptFailed callback be used somehow?
+        ipv4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1),
+                            TcpChannel::ConnectFailedCallback());
+      }
+
+      m_factories.insert(std::make_pair("tcp4", factory));
     }
 
-  if (!isDryRun)
-    {
-      if (m_factories.count("tcp") > 0)
-        {
-          return;
-        }
+    if (enableV6) {
+      shared_ptr<TcpChannel> ipv6Channel = factory->createChannel("::", port);
+      if (needToListen) {
+        // Should acceptFailed callback be used somehow?
+        ipv6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1),
+                            TcpChannel::ConnectFailedCallback());
+      }
 
-      shared_ptr<TcpFactory> factory = make_shared<TcpFactory>(port);
-      m_factories.insert(std::make_pair("tcp", factory));
-
-      if (enableV4)
-        {
-          shared_ptr<TcpChannel> ipv4Channel = factory->createChannel("0.0.0.0", port);
-          if (needToListen)
-            {
-              // Should acceptFailed callback be used somehow?
-              ipv4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1),
-                                  TcpChannel::ConnectFailedCallback());
-            }
-
-          m_factories.insert(std::make_pair("tcp4", factory));
-        }
-
-      if (enableV6)
-        {
-          shared_ptr<TcpChannel> ipv6Channel = factory->createChannel("::", port);
-          if (needToListen)
-            {
-              // Should acceptFailed callback be used somehow?
-              ipv6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1),
-                                  TcpChannel::ConnectFailedCallback());
-            }
-
-          m_factories.insert(std::make_pair("tcp6", factory));
-        }
+      m_factories.insert(std::make_pair("tcp6", factory));
     }
+  }
 }
 
 void
@@ -401,221 +552,177 @@
   std::string mcastPort = "56363";
 
 
-  for (ConfigSection::const_iterator i = configSection.begin();
-       i != configSection.end();
-       ++i)
-    {
-      if (i->first == "port")
-        {
-          port = i->second.get_value<std::string>();
-          try
-            {
-              uint16_t portNo = boost::lexical_cast<uint16_t>(port);
-              NFD_LOG_TRACE("UDP port set to " << portNo);
-            }
-          catch (const std::bad_cast& error)
-            {
-              BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option " +
-                                                      i->first + "\" in \"udp\" section"));
-            }
-        }
-      else if (i->first == "enable_v4")
-        {
-          enableV4 = parseYesNo(i, i->first, "udp");
-        }
-      else if (i->first == "enable_v6")
-        {
-          enableV6 = parseYesNo(i, i->first, "udp");
-        }
-      else if (i->first == "idle_timeout")
-        {
-          try
-            {
-              timeout = i->second.get_value<size_t>();
-            }
-          catch (const std::exception& e)
-            {
-              BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
-                                                      i->first + "\" in \"udp\" section"));
-            }
-        }
-      else if (i->first == "keep_alive_interval")
-        {
-          try
-            {
-              keepAliveInterval = i->second.get_value<size_t>();
-
-              /// \todo Make use of keepAliveInterval
-              (void)(keepAliveInterval);
-            }
-          catch (const std::exception& e)
-            {
-              BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
-                                                      i->first + "\" in \"udp\" section"));
-            }
-        }
-      else if (i->first == "mcast")
-        {
-          useMcast = parseYesNo(i, i->first, "udp");
-        }
-      else if (i->first == "mcast_port")
-        {
-          mcastPort = i->second.get_value<std::string>();
-          try
-            {
-              uint16_t portNo = boost::lexical_cast<uint16_t>(mcastPort);
-              NFD_LOG_TRACE("UDP multicast port set to " << portNo);
-            }
-          catch (const std::bad_cast& error)
-            {
-              BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option " +
-                                                      i->first + "\" in \"udp\" section"));
-            }
-        }
-      else if (i->first == "mcast_group")
-        {
-          using namespace boost::asio::ip;
-          mcastGroup = i->second.get_value<std::string>();
-          try
-            {
-              address mcastGroupTest = address::from_string(mcastGroup);
-              if (!mcastGroupTest.is_v4())
-                {
-                  BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
-                                                          i->first + "\" in \"udp\" section"));
-                }
-            }
-          catch(const std::runtime_error& e)
-            {
-              BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
-                                                      i->first + "\" in \"udp\" section"));
-            }
-        }
-      else
-        {
-          BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" + i->first + "\" in "
-                                                  "\"udp\" section"));
-        }
-    }
-
-  if (!enableV4 && !enableV6)
-    {
-      BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 channels have been disabled."
-                                              " Remove \"udp\" section to disable UDP channels or"
-                                              " re-enable at least one channel type."));
-    }
-  else if (useMcast && !enableV4)
-    {
-      BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 multicast requested, but IPv4 channels"
-                                              " have been disabled (conflicting configuration"
-                                              " options set)"));
-    }
-
-  /// \todo what is keep alive interval used for?
-
-  if (!isDryRun)
-    {
-      shared_ptr<UdpFactory> factory;
-      bool isReload = false;
-      if (m_factories.count("udp") > 0) {
-        isReload = true;
-        factory = static_pointer_cast<UdpFactory>(m_factories["udp"]);
+  for (auto i = configSection.begin(); i != configSection.end(); ++i) {
+    if (i->first == "port") {
+      port = i->second.get_value<std::string>();
+      try {
+        uint16_t portNo = boost::lexical_cast<uint16_t>(port);
+        NFD_LOG_TRACE("UDP port set to " << portNo);
       }
-      else {
-        factory = make_shared<UdpFactory>(port);
-        m_factories.insert(std::make_pair("udp", factory));
+      catch (const std::bad_cast& error) {
+        BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option " +
+                                                i->first + "\" in \"udp\" section"));
+      }
+    }
+    else if (i->first == "enable_v4") {
+      enableV4 = ConfigFile::parseYesNo(i, i->first, "udp");
+    }
+    else if (i->first == "enable_v6") {
+      enableV6 = ConfigFile::parseYesNo(i, i->first, "udp");
+    }
+    else if (i->first == "idle_timeout") {
+      try {
+        timeout = i->second.get_value<size_t>();
+      }
+      catch (const std::exception& e) {
+        BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
+                                                i->first + "\" in \"udp\" section"));
+      }
+    }
+    else if (i->first == "keep_alive_interval") {
+      try {
+        keepAliveInterval = i->second.get_value<size_t>();
+
+        /// \todo Make use of keepAliveInterval
+        /// \todo what is keep alive interval used for?
+        (void)(keepAliveInterval);
+      }
+      catch (const std::exception& e) {
+        BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
+                                                i->first + "\" in \"udp\" section"));
+      }
+    }
+    else if (i->first == "mcast") {
+      useMcast = ConfigFile::parseYesNo(i, i->first, "udp");
+    }
+    else if (i->first == "mcast_port") {
+      mcastPort = i->second.get_value<std::string>();
+      try {
+        uint16_t portNo = boost::lexical_cast<uint16_t>(mcastPort);
+        NFD_LOG_TRACE("UDP multicast port set to " << portNo);
+      }
+      catch (const std::bad_cast& error) {
+        BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option " +
+                                                i->first + "\" in \"udp\" section"));
+      }
+    }
+    else if (i->first == "mcast_group") {
+      using namespace boost::asio::ip;
+      mcastGroup = i->second.get_value<std::string>();
+      try {
+        address mcastGroupTest = address::from_string(mcastGroup);
+        if (!mcastGroupTest.is_v4()) {
+          BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
+                                                  i->first + "\" in \"udp\" section"));
+        }
+      }
+      catch(const std::runtime_error& e) {
+        BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
+                                                i->first + "\" in \"udp\" section"));
+      }
+    }
+    else {
+      BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
+                                              i->first + "\" in \"udp\" section"));
+    }
+  }
+
+  if (!enableV4 && !enableV6) {
+    BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 channels have been disabled."
+                                            " Remove \"udp\" section to disable UDP channels or"
+                                            " re-enable at least one channel type."));
+  }
+  else if (useMcast && !enableV4) {
+    BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 multicast requested, but IPv4 channels"
+                                            " have been disabled (conflicting configuration options set)"));
+  }
+
+  if (!isDryRun) {
+    shared_ptr<UdpFactory> factory;
+    bool isReload = false;
+    if (m_factories.count("udp") > 0) {
+      isReload = true;
+      factory = static_pointer_cast<UdpFactory>(m_factories["udp"]);
+    }
+    else {
+      factory = make_shared<UdpFactory>(port);
+      m_factories.insert(std::make_pair("udp", factory));
+    }
+
+    if (!isReload && enableV4) {
+      shared_ptr<UdpChannel> v4Channel =
+        factory->createChannel("0.0.0.0", port, time::seconds(timeout));
+
+      v4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1),
+                        UdpChannel::ConnectFailedCallback());
+
+      m_factories.insert(std::make_pair("udp4", factory));
+    }
+
+    if (!isReload && enableV6) {
+      shared_ptr<UdpChannel> v6Channel =
+        factory->createChannel("::", port, time::seconds(timeout));
+
+      v6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1),
+                        UdpChannel::ConnectFailedCallback());
+      m_factories.insert(std::make_pair("udp6", factory));
+    }
+
+    if (useMcast && enableV4) {
+      std::vector<NetworkInterfaceInfo> ipv4MulticastInterfaces;
+      for (const auto& nic : nicList) {
+        if (nic.isUp() && nic.isMulticastCapable() && !nic.ipv4Addresses.empty()) {
+          ipv4MulticastInterfaces.push_back(nic);
+        }
       }
 
-      if (!isReload && enableV4)
-        {
-          shared_ptr<UdpChannel> v4Channel =
-            factory->createChannel("0.0.0.0", port, time::seconds(timeout));
-
-          v4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1),
-                            UdpChannel::ConnectFailedCallback());
-
-          m_factories.insert(std::make_pair("udp4", factory));
-        }
-
-      if (!isReload && enableV6)
-        {
-          shared_ptr<UdpChannel> v6Channel =
-            factory->createChannel("::", port, time::seconds(timeout));
-
-          v6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1),
-                            UdpChannel::ConnectFailedCallback());
-          m_factories.insert(std::make_pair("udp6", factory));
-        }
-
-      if (useMcast && enableV4)
-        {
-          std::vector<NetworkInterfaceInfo> ipv4MulticastInterfaces;
-          for (const auto& nic : nicList)
-            {
-              if (nic.isUp() && nic.isMulticastCapable() && !nic.ipv4Addresses.empty())
-                {
-                  ipv4MulticastInterfaces.push_back(nic);
-                }
-            }
-
-          bool isNicNameNecessary = false;
+      bool isNicNameNecessary = false;
 #if defined(__linux__)
-          if (ipv4MulticastInterfaces.size() > 1)
-            {
-              // On Linux if we have more than one MulticastUdpFace
-              // we need to specify the name of the interface
-              isNicNameNecessary = true;
-            }
+      if (ipv4MulticastInterfaces.size() > 1) {
+        // On Linux if we have more than one MulticastUdpFace
+        // we need to specify the name of the interface
+        isNicNameNecessary = true;
+      }
 #endif
 
-          std::list<shared_ptr<MulticastUdpFace> > multicastFacesToRemove;
-          for (UdpFactory::MulticastFaceMap::const_iterator i =
-                 factory->getMulticastFaces().begin();
-               i != factory->getMulticastFaces().end();
-               ++i)
-            {
-              multicastFacesToRemove.push_back(i->second);
-            }
+      std::list<shared_ptr<MulticastUdpFace> > multicastFacesToRemove;
+      for (auto i = factory->getMulticastFaces().begin();
+           i != factory->getMulticastFaces().end();
+           ++i) {
+        multicastFacesToRemove.push_back(i->second);
+      }
 
-          for (const auto& nic : ipv4MulticastInterfaces)
-            {
-              shared_ptr<MulticastUdpFace> newFace;
-              newFace = factory->createMulticastFace(nic.ipv4Addresses[0].to_string(),
-                                                     mcastGroup,
-                                                     mcastPort,
-                                                     isNicNameNecessary ? nic.name : "");
-              addCreatedFaceToForwarder(newFace);
-              multicastFacesToRemove.remove(newFace);
-            }
+      for (const auto& nic : ipv4MulticastInterfaces) {
+        shared_ptr<MulticastUdpFace> newFace;
+        newFace = factory->createMulticastFace(nic.ipv4Addresses[0].to_string(),
+                                               mcastGroup,
+                                               mcastPort,
+                                               isNicNameNecessary ? nic.name : "");
+        addCreatedFaceToForwarder(newFace);
+        multicastFacesToRemove.remove(newFace);
+      }
 
-          for (std::list<shared_ptr<MulticastUdpFace> >::iterator i =
-                 multicastFacesToRemove.begin();
-               i != multicastFacesToRemove.end();
-               ++i)
-            {
-              (*i)->close();
-            }
-        }
-      else
-        {
-          std::list<shared_ptr<MulticastUdpFace> > multicastFacesToRemove;
-          for (UdpFactory::MulticastFaceMap::const_iterator i =
-                 factory->getMulticastFaces().begin();
-               i != factory->getMulticastFaces().end();
-               ++i)
-            {
-              multicastFacesToRemove.push_back(i->second);
-            }
-
-          for (std::list<shared_ptr<MulticastUdpFace> >::iterator i =
-                 multicastFacesToRemove.begin();
-               i != multicastFacesToRemove.end();
-               ++i)
-            {
-              (*i)->close();
-            }
-        }
+      for (auto i = multicastFacesToRemove.begin();
+           i != multicastFacesToRemove.end();
+           ++i) {
+        (*i)->close();
+      }
     }
+    else {
+      std::list<shared_ptr<MulticastUdpFace>> multicastFacesToRemove;
+      for (auto i = factory->getMulticastFaces().begin();
+           i != factory->getMulticastFaces().end();
+           ++i) {
+        multicastFacesToRemove.push_back(i->second);
+      }
+
+      for (auto i = multicastFacesToRemove.begin();
+           i != multicastFacesToRemove.end();
+           ++i) {
+        (*i)->close();
+      }
+    }
+  }
 }
 
 void
@@ -635,107 +742,82 @@
   bool useMcast = true;
   ethernet::Address mcastGroup(ethernet::getDefaultMulticastAddress());
 
-  for (ConfigSection::const_iterator i = configSection.begin();
-       i != configSection.end();
-       ++i)
-    {
-      if (i->first == "mcast")
-        {
-          useMcast = parseYesNo(i, i->first, "ether");
-        }
+  for (auto i = configSection.begin(); i != configSection.end(); ++i) {
+    if (i->first == "mcast") {
+      useMcast = ConfigFile::parseYesNo(i, i->first, "ether");
+    }
+    else if (i->first == "mcast_group") {
+      mcastGroup = ethernet::Address::fromString(i->second.get_value<std::string>());
+      if (mcastGroup.isNull()) {
+        BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
+                                                i->first + "\" in \"ether\" section"));
+      }
+    }
+    else {
+      BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
+                                              i->first + "\" in \"ether\" section"));
+    }
+  }
 
-      else if (i->first == "mcast_group")
-        {
-          mcastGroup = ethernet::Address::fromString(i->second.get_value<std::string>());
-          if (mcastGroup.isNull())
-            {
-              BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
-                                                      i->first + "\" in \"ether\" section"));
-            }
-        }
-      else
-        {
-          BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" + i->first +
-                                                  "\" in \"ether\" section"));
-        }
+  if (!isDryRun) {
+    shared_ptr<EthernetFactory> factory;
+    if (m_factories.count("ether") > 0) {
+      factory = static_pointer_cast<EthernetFactory>(m_factories["ether"]);
+    }
+    else {
+      factory = make_shared<EthernetFactory>();
+      m_factories.insert(std::make_pair("ether", factory));
     }
 
-  if (!isDryRun)
-    {
-      shared_ptr<EthernetFactory> factory;
-      if (m_factories.count("ether") > 0) {
-        factory = static_pointer_cast<EthernetFactory>(m_factories["ether"]);
-      }
-      else {
-        factory = make_shared<EthernetFactory>();
-        m_factories.insert(std::make_pair("ether", factory));
+    if (useMcast) {
+      std::list<shared_ptr<EthernetFace> > multicastFacesToRemove;
+      for (auto i = factory->getMulticastFaces().begin();
+           i != factory->getMulticastFaces().end();
+           ++i) {
+        multicastFacesToRemove.push_back(i->second);
       }
 
-      if (useMcast)
-        {
-          std::list<shared_ptr<EthernetFace> > multicastFacesToRemove;
-          for (EthernetFactory::MulticastFaceMap::const_iterator i =
-                 factory->getMulticastFaces().begin();
-               i != factory->getMulticastFaces().end();
-               ++i)
-            {
-              multicastFacesToRemove.push_back(i->second);
-            }
+      for (const auto& nic : nicList) {
+        if (nic.isUp() && nic.isMulticastCapable()) {
+          try {
+            shared_ptr<EthernetFace> newFace =
+            factory->createMulticastFace(nic, mcastGroup);
 
-          for (const auto& nic : nicList)
-            {
-              if (nic.isUp() && nic.isMulticastCapable())
-                {
-                  try
-                    {
-                      shared_ptr<EthernetFace> newFace =
-                        factory->createMulticastFace(nic, mcastGroup);
-
-                      addCreatedFaceToForwarder(newFace);
-                      multicastFacesToRemove.remove(newFace);
-                    }
-                  catch (const EthernetFactory::Error& factoryError)
-                    {
-                      NFD_LOG_ERROR(factoryError.what() << ", continuing");
-                    }
-                  catch (const EthernetFace::Error& faceError)
-                    {
-                      NFD_LOG_ERROR(faceError.what() << ", continuing");
-                    }
-                }
-            }
-
-          for (std::list<shared_ptr<EthernetFace> >::iterator i =
-                 multicastFacesToRemove.begin();
-               i != multicastFacesToRemove.end();
-               ++i)
-            {
-              (*i)->close();
-            }
+            addCreatedFaceToForwarder(newFace);
+            multicastFacesToRemove.remove(newFace);
+          }
+          catch (const EthernetFactory::Error& factoryError) {
+            NFD_LOG_ERROR(factoryError.what() << ", continuing");
+          }
+          catch (const EthernetFace::Error& faceError) {
+            NFD_LOG_ERROR(faceError.what() << ", continuing");
+          }
         }
-      else
-        {
-          std::list<shared_ptr<EthernetFace> > multicastFacesToRemove;
-          for (EthernetFactory::MulticastFaceMap::const_iterator i =
-                 factory->getMulticastFaces().begin();
-               i != factory->getMulticastFaces().end();
-               ++i)
-            {
-              multicastFacesToRemove.push_back(i->second);
-            }
+      }
 
-          for (std::list<shared_ptr<EthernetFace> >::iterator i =
-                 multicastFacesToRemove.begin();
-               i != multicastFacesToRemove.end();
-               ++i)
-            {
-              (*i)->close();
-            }
-        }
+      for (auto i = multicastFacesToRemove.begin();
+           i != multicastFacesToRemove.end();
+           ++i) {
+        (*i)->close();
+      }
     }
+    else {
+      std::list<shared_ptr<EthernetFace> > multicastFacesToRemove;
+      for (auto i = factory->getMulticastFaces().begin();
+           i != factory->getMulticastFaces().end();
+           ++i) {
+        multicastFacesToRemove.push_back(i->second);
+      }
+
+      for (auto i = multicastFacesToRemove.begin();
+           i != multicastFacesToRemove.end();
+           ++i) {
+        (*i)->close();
+      }
+    }
+  }
 #else
-  BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without libpcap, cannot "
-                                          "process \"ether\" section"));
+  BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without libpcap, cannot process \"ether\" section"));
 #endif // HAVE_LIBPCAP
 }
 
@@ -758,442 +840,78 @@
   bool enableV4 = true;
   bool enableV6 = true;
 
-  for (ConfigSection::const_iterator i = configSection.begin();
-       i != configSection.end();
-       ++i)
-    {
-      if (i->first == "port")
-        {
-          port = i->second.get_value<std::string>();
-          try
-            {
-              uint16_t portNo = boost::lexical_cast<uint16_t>(port);
-              NFD_LOG_TRACE("WebSocket port set to " << portNo);
-            }
-          catch (const std::bad_cast& error)
-            {
-              BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option " +
-                                                      i->first + "\" in \"websocket\" section"));
-            }
-        }
-      else if (i->first == "listen")
-        {
-          needToListen = parseYesNo(i, i->first, "websocket");
-        }
-      else if (i->first == "enable_v4")
-        {
-          enableV4 = parseYesNo(i, i->first, "websocket");
-        }
-      else if (i->first == "enable_v6")
-        {
-          enableV6 = parseYesNo(i, i->first, "websocket");
-        }
-      else
-        {
-          BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
-                                                  i->first + "\" in \"websocket\" section"));
-        }
+  for (auto i = configSection.begin(); i != configSection.end(); ++i) {
+    if (i->first == "port") {
+      port = i->second.get_value<std::string>();
+      try {
+        uint16_t portNo = boost::lexical_cast<uint16_t>(port);
+        NFD_LOG_TRACE("WebSocket port set to " << portNo);
+      }
+      catch (const std::bad_cast& error) {
+        BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option " +
+                                                i->first + "\" in \"websocket\" section"));
+      }
+    }
+    else if (i->first == "listen") {
+      needToListen = ConfigFile::parseYesNo(i, i->first, "websocket");
+    }
+    else if (i->first == "enable_v4") {
+      enableV4 = ConfigFile::parseYesNo(i, i->first, "websocket");
+    }
+    else if (i->first == "enable_v6") {
+      enableV6 = ConfigFile::parseYesNo(i, i->first, "websocket");
+    }
+    else {
+      BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
+                                              i->first + "\" in \"websocket\" section"));
+    }
+  }
+
+  if (!enableV4 && !enableV6) {
+    BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 channels have been disabled."
+                                            " Remove \"websocket\" section to disable WebSocket channels or"
+                                            " re-enable at least one channel type."));
+  }
+
+  if (!enableV4 && enableV6) {
+    BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD does not allow pure IPv6 WebSocket channel."));
+  }
+
+  if (!isDryRun) {
+    if (m_factories.count("websocket") > 0) {
+      return;
     }
 
-  if (!enableV4 && !enableV6)
-    {
-      BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 channels have been disabled."
-                                              " Remove \"websocket\" section to disable WebSocket"
-                                              " channels or re-enable at least one channel type."));
+    shared_ptr<WebSocketFactory> factory = make_shared<WebSocketFactory>(port);
+    m_factories.insert(std::make_pair("websocket", factory));
+
+    if (enableV6 && enableV4) {
+      shared_ptr<WebSocketChannel> ip46Channel = factory->createChannel("::", port);
+      if (needToListen) {
+        ip46Channel->listen(bind(&FaceTable::add, &m_faceTable, _1));
+      }
+
+      m_factories.insert(std::make_pair("websocket46", factory));
     }
+    else if (enableV4) {
+      shared_ptr<WebSocketChannel> ipv4Channel = factory->createChannel("0.0.0.0", port);
+      if (needToListen) {
+        ipv4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1));
+      }
 
-  if (!enableV4 && enableV6)
-    {
-      BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD does not allow pure IPv6 WebSocket channel."));
+      m_factories.insert(std::make_pair("websocket4", factory));
     }
-
-  if (!isDryRun)
-    {
-      if (m_factories.count("websocket") > 0)
-        {
-          return;
-        }
-
-      shared_ptr<WebSocketFactory> factory = make_shared<WebSocketFactory>(port);
-      m_factories.insert(std::make_pair("websocket", factory));
-
-      if (enableV6 && enableV4)
-        {
-          shared_ptr<WebSocketChannel> ip46Channel = factory->createChannel("::", port);
-          if (needToListen)
-            {
-              ip46Channel->listen(bind(&FaceTable::add, &m_faceTable, _1));
-            }
-
-          m_factories.insert(std::make_pair("websocket46", factory));
-        }
-      else if (enableV4)
-        {
-          shared_ptr<WebSocketChannel> ipv4Channel = factory->createChannel("0.0.0.0", port);
-          if (needToListen)
-            {
-              ipv4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1));
-            }
-
-          m_factories.insert(std::make_pair("websocket4", factory));
-        }
-    }
+  }
 #else
   BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without WebSocket, "
                                           "cannot process \"websocket\" section"));
 #endif // HAVE_WEBSOCKET
 }
 
-
 void
-FaceManager::onFaceRequest(const Interest& request)
-{
-  const Name& command = request.getName();
-  const size_t commandNComps = command.size();
-
-  if (commandNComps <= COMMAND_PREFIX.size())
-    {
-      // command is too short to have a verb
-      NFD_LOG_DEBUG("command result: malformed");
-      sendResponse(command, 400, "Malformed command");
-      return;
-    }
-
-  const Name::Component& verb = command.at(COMMAND_PREFIX.size());
-
-  const auto unsignedVerbProcessor = m_unsignedVerbDispatch.find(verb);
-  if (unsignedVerbProcessor != m_unsignedVerbDispatch.end())
-    {
-      NFD_LOG_DEBUG("command result: processing verb: " << verb);
-      (unsignedVerbProcessor->second)(this, request);
-    }
-  else if (COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
-           commandNComps < COMMAND_SIGNED_NCOMPS)
-    {
-      NFD_LOG_DEBUG("command result: unsigned verb: " << command);
-      sendResponse(command, 401, "Signature required");
-    }
-  else if (commandNComps < COMMAND_SIGNED_NCOMPS ||
-           !COMMAND_PREFIX.isPrefixOf(command))
-    {
-      NFD_LOG_DEBUG("command result: malformed");
-      sendResponse(command, 400, "Malformed command");
-    }
-  else
-    {
-      validate(request,
-               bind(&FaceManager::onValidatedFaceRequest, this, _1),
-               bind(&ManagerBase::onCommandValidationFailed, this, _1, _2));
-    }
-}
-
-void
-FaceManager::onValidatedFaceRequest(const shared_ptr<const Interest>& request)
-{
-  const Name& command = request->getName();
-  const Name::Component& verb = command[COMMAND_PREFIX.size()];
-  const Name::Component& parameterComponent = command[COMMAND_PREFIX.size() + 1];
-
-  SignedVerbDispatchTable::const_iterator signedVerbProcessor = m_signedVerbDispatch.find(verb);
-  if (signedVerbProcessor != m_signedVerbDispatch.end())
-    {
-      ControlParameters parameters;
-      if (!extractParameters(parameterComponent, parameters))
-        {
-          sendResponse(command, 400, "Malformed command");
-          return;
-        }
-
-      NFD_LOG_DEBUG("command result: processing verb: " << verb);
-      (signedVerbProcessor->second)(this, *request, parameters);
-    }
-  else
-    {
-      NFD_LOG_DEBUG("command result: unsupported verb: " << verb);
-      sendResponse(command, 501, "Unsupported command");
-    }
-
-}
-
-void
-FaceManager::addCreatedFaceToForwarder(const shared_ptr<Face>& newFace)
+FaceManager::addCreatedFaceToForwarder(shared_ptr<Face> newFace)
 {
   m_faceTable.add(newFace);
-
-  //NFD_LOG_DEBUG("Created face " << newFace->getRemoteUri() << " ID " << newFace->getId());
 }
 
-void
-FaceManager::onCreated(const Name& requestName,
-                       ControlParameters& parameters,
-                       const shared_ptr<Face>& newFace)
-{
-  addCreatedFaceToForwarder(newFace);
-  parameters.setFaceId(newFace->getId());
-  parameters.setUri(newFace->getRemoteUri().toString());
-  parameters.setFacePersistency(newFace->getPersistency());
-
-  sendResponse(requestName, 200, "Success", parameters.wireEncode());
-}
-
-void
-FaceManager::onConnectFailed(const Name& requestName, const std::string& reason)
-{
-  NFD_LOG_DEBUG("Failed to create face: " << reason);
-  sendResponse(requestName, 408, reason);
-}
-
-void
-FaceManager::createFace(const Interest& request,
-                        ControlParameters& parameters)
-{
-  const Name& requestName = request.getName();
-  ndn::nfd::FaceCreateCommand command;
-
-  if (!validateParameters(command, parameters))
-    {
-      sendResponse(requestName, 400, "Malformed command");
-      NFD_LOG_TRACE("invalid control parameters URI");
-      return;
-    }
-
-  FaceUri uri;
-  if (!uri.parse(parameters.getUri()))
-    {
-      sendResponse(requestName, 400, "Malformed command");
-      NFD_LOG_TRACE("failed to parse URI");
-      return;
-    }
-
-  if (!uri.isCanonical())
-    {
-      sendResponse(requestName, 400, "Non-canonical URI");
-      NFD_LOG_TRACE("received non-canonical URI");
-      return;
-    }
-
-  FactoryMap::iterator factory = m_factories.find(uri.getScheme());
-  if (factory == m_factories.end())
-    {
-      sendResponse(requestName, 501, "Unsupported protocol");
-      return;
-    }
-
-  try
-    {
-      factory->second->createFace(uri,
-                                  parameters.getFacePersistency(),
-                                  bind(&FaceManager::onCreated,
-                                       this, requestName, parameters, _1),
-                                  bind(&FaceManager::onConnectFailed,
-                                       this, requestName, _1));
-    }
-  catch (const std::runtime_error& error)
-    {
-      std::string errorMessage = "NFD error: ";
-      errorMessage += error.what();
-
-      NFD_LOG_ERROR(errorMessage);
-      sendResponse(requestName, 500, errorMessage);
-    }
-  catch (const std::logic_error& error)
-    {
-      std::string errorMessage = "NFD error: ";
-      errorMessage += error.what();
-
-      NFD_LOG_ERROR(errorMessage);
-      sendResponse(requestName, 500, errorMessage);
-    }
-}
-
-
-void
-FaceManager::destroyFace(const Interest& request,
-                         ControlParameters& parameters)
-{
-  const Name& requestName = request.getName();
-  ndn::nfd::FaceDestroyCommand command;
-
-  if (!validateParameters(command, parameters))
-    {
-      sendResponse(requestName, 400, "Malformed command");
-      return;
-    }
-
-  shared_ptr<Face> target = m_faceTable.get(parameters.getFaceId());
-  if (static_cast<bool>(target))
-    {
-      target->close();
-    }
-
-  sendResponse(requestName, 200, "Success", parameters.wireEncode());
-
-}
-
-void
-FaceManager::onAddFace(shared_ptr<Face> face)
-{
-  ndn::nfd::FaceEventNotification notification;
-  notification.setKind(ndn::nfd::FACE_EVENT_CREATED);
-  face->copyStatusTo(notification);
-
-  m_notificationStream.postNotification(notification);
-}
-
-void
-FaceManager::onRemoveFace(shared_ptr<Face> face)
-{
-  ndn::nfd::FaceEventNotification notification;
-  notification.setKind(ndn::nfd::FACE_EVENT_DESTROYED);
-  face->copyStatusTo(notification);
-
-  m_notificationStream.postNotification(notification);
-}
-
-bool
-FaceManager::extractLocalControlParameters(const Interest& request,
-                                           ControlParameters& parameters,
-                                           ControlCommand& command,
-                                           shared_ptr<LocalFace>& outFace,
-                                           LocalControlFeature& outFeature)
-{
-  if (!validateParameters(command, parameters))
-    {
-      sendResponse(request.getName(), 400, "Malformed command");
-      return false;
-    }
-
-  shared_ptr<Face> face = m_faceTable.get(request.getIncomingFaceId());
-
-  if (!static_cast<bool>(face))
-    {
-      NFD_LOG_DEBUG("command result: faceid " << request.getIncomingFaceId() << " not found");
-      sendResponse(request.getName(), 410, "Face not found");
-      return false;
-    }
-  else if (!face->isLocal())
-    {
-      NFD_LOG_DEBUG("command result: cannot enable local control on non-local faceid " <<
-                    face->getId());
-      sendResponse(request.getName(), 412, "Face is non-local");
-      return false;
-    }
-
-  outFace = dynamic_pointer_cast<LocalFace>(face);
-  outFeature = static_cast<LocalControlFeature>(parameters.getLocalControlFeature());
-
-  return true;
-}
-
-void
-FaceManager::enableLocalControl(const Interest& request,
-                                ControlParameters& parameters)
-{
-  ndn::nfd::FaceEnableLocalControlCommand command;
-
-
-  shared_ptr<LocalFace> face;
-  LocalControlFeature feature;
-
-  if (extractLocalControlParameters(request, parameters, command, face, feature))
-    {
-      face->setLocalControlHeaderFeature(feature, true);
-      sendResponse(request.getName(), 200, "Success", parameters.wireEncode());
-    }
-}
-
-void
-FaceManager::disableLocalControl(const Interest& request,
-                                 ControlParameters& parameters)
-{
-  ndn::nfd::FaceDisableLocalControlCommand command;
-  shared_ptr<LocalFace> face;
-  LocalControlFeature feature;
-
-  if (extractLocalControlParameters(request, parameters, command, face, feature))
-    {
-      face->setLocalControlHeaderFeature(feature, false);
-      sendResponse(request.getName(), 200, "Success", parameters.wireEncode());
-    }
-}
-
-void
-FaceManager::listFaces(const Interest& request)
-{
-  const Name& command = request.getName();
-  const size_t commandNComps = command.size();
-
-  if (commandNComps < FACES_LIST_DATASET_NCOMPS ||
-      !FACES_LIST_DATASET_PREFIX.isPrefixOf(command))
-    {
-      NFD_LOG_DEBUG("command result: malformed");
-      sendResponse(command, 400, "Malformed command");
-      return;
-    }
-
-  m_faceStatusPublisher.publish();
-}
-
-void
-FaceManager::listChannels(const Interest& request)
-{
-  NFD_LOG_DEBUG("in listChannels");
-  const Name& command = request.getName();
-  const size_t commandNComps = command.size();
-
-  if (commandNComps < CHANNELS_LIST_DATASET_NCOMPS ||
-      !CHANNELS_LIST_DATASET_PREFIX.isPrefixOf(command))
-    {
-      NFD_LOG_DEBUG("command result: malformed");
-      sendResponse(command, 400, "Malformed command");
-      return;
-    }
-
-  NFD_LOG_DEBUG("publishing");
-  m_channelStatusPublisher.publish();
-}
-
-void
-FaceManager::listQueriedFaces(const Interest& request)
-{
-  NFD_LOG_DEBUG("in listQueriedFaces");
-  const Name& query = request.getName();
-  const size_t queryNComps = query.size();
-
-  if (queryNComps < FACES_QUERY_DATASET_NCOMPS ||
-      !FACES_QUERY_DATASET_PREFIX.isPrefixOf(query))
-    {
-      NFD_LOG_DEBUG("query result: malformed");
-      sendNack(query);
-      return;
-    }
-
-  ndn::nfd::FaceQueryFilter faceFilter;
-  try
-    {
-      faceFilter.wireDecode(query[-1].blockFromValue());
-    }
-  catch (tlv::Error&)
-    {
-      NFD_LOG_DEBUG("query result: malformed filter");
-      sendNack(query);
-      return;
-    }
-
-  FaceQueryStatusPublisher
-    faceQueryStatusPublisher(m_faceTable, *m_face, query, faceFilter, m_keyChain);
-
-  faceQueryStatusPublisher.publish();
-}
-
-shared_ptr<ProtocolFactory>
-FaceManager::findFactory(const std::string& protocol)
-{
-  FactoryMap::iterator factory = m_factories.find(protocol);
-  if (factory != m_factories.end())
-    return factory->second;
-  else
-    return shared_ptr<ProtocolFactory>();
-}
-
-} // namespace nfd
+} // namespace
diff --git a/daemon/mgmt/face-manager.hpp b/daemon/mgmt/face-manager.hpp
index 74dc0dd..f5283d6 100644
--- a/daemon/mgmt/face-manager.hpp
+++ b/daemon/mgmt/face-manager.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  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
+ * 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.
@@ -26,118 +26,105 @@
 #ifndef NFD_DAEMON_MGMT_FACE_MANAGER_HPP
 #define NFD_DAEMON_MGMT_FACE_MANAGER_HPP
 
-#include "common.hpp"
-#include "core/notification-stream.hpp"
+#include "manager-base.hpp"
 #include "face/local-face.hpp"
-#include "mgmt/manager-base.hpp"
-#include "mgmt/face-status-publisher.hpp"
-#include "mgmt/channel-status-publisher.hpp"
-#include "mgmt/face-query-status-publisher.hpp"
-
-#include <ndn-cxx/management/nfd-control-parameters.hpp>
-#include <ndn-cxx/management/nfd-control-response.hpp>
+#include <ndn-cxx/management/nfd-face-query-filter.hpp>
 
 namespace nfd {
 
-const std::string FACE_MANAGER_PRIVILEGE = "faces";
-
-class ConfigFile;
-class Face;
 class FaceTable;
-class LocalFace;
 class NetworkInterfaceInfo;
 class ProtocolFactory;
 
+/**
+ * @brief implement the Face Management of NFD Management Protocol.
+ * @sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt
+ */
 class FaceManager : public ManagerBase
 {
 public:
-  class Error : public ManagerBase::Error
-  {
-  public:
-    Error(const std::string& what) : ManagerBase::Error(what) {}
-  };
+  FaceManager(FaceTable& faceTable,
+              Dispatcher& dispatcher,
+              CommandValidator& validator);
 
   /**
-   * \throws FaceManager::Error if localPort is an invalid port number
-   */
-  FaceManager(FaceTable& faceTable,
-              shared_ptr<InternalFace> face,
-              ndn::KeyChain& keyChain);
-
-  virtual
-  ~FaceManager();
-
-  /** \brief Subscribe to a face management section(s) for the config file
+   * @brief Subscribe to face_system section for the config file
    */
   void
   setConfigFile(ConfigFile& configFile);
 
+PUBLIC_WITH_TESTS_ELSE_PRIVATE: // ControlCommand
   void
-  onFaceRequest(const Interest& request);
-
-PUBLIC_WITH_TESTS_ELSE_PRIVATE:
-  void
-  listFaces(const Interest& request);
+  createFace(const Name& topPrefix, const Interest& interest,
+             const ControlParameters& parameters,
+             const ndn::mgmt::CommandContinuation& done);
 
   void
-  listChannels(const Interest& request);
+  destroyFace(const Name& topPrefix, const Interest& interest,
+              const ControlParameters& parameters,
+              const ndn::mgmt::CommandContinuation& done);
 
   void
-  listQueriedFaces(const Interest& request);
+  enableLocalControl(const Name& topPrefix, const Interest& interest,
+                     const ControlParameters& parameters,
+                     const ndn::mgmt::CommandContinuation& done);
 
-  shared_ptr<ProtocolFactory>
-  findFactory(const std::string& protocol);
-
-PROTECTED_WITH_TESTS_ELSE_PRIVATE:
   void
-  onValidatedFaceRequest(const shared_ptr<const Interest>& request);
+  disableLocalControl(const Name& topPrefix, const Interest& interest,
+                      const ControlParameters& parameters,
+                      const ndn::mgmt::CommandContinuation& done);
 
-  VIRTUAL_WITH_TESTS void
-  createFace(const Interest& request,
-             ControlParameters& parameters);
+PUBLIC_WITH_TESTS_ELSE_PRIVATE: // helpers for ControlCommand
+  void
+  afterCreateFaceSuccess(ControlParameters& parameters,
+                         const shared_ptr<Face>& newFace,
+                         const ndn::mgmt::CommandContinuation& done);
 
-  VIRTUAL_WITH_TESTS void
-  destroyFace(const Interest& request,
-              ControlParameters& parameters);
+  void
+  afterCreateFaceFailure(const std::string& reason,
+                         const ndn::mgmt::CommandContinuation& done);
 
-  VIRTUAL_WITH_TESTS bool
+  struct ExtractLocalControlParametersResult
+  {
+    bool isValid;
+    shared_ptr<LocalFace> face;
+    LocalControlFeature feature;
+  };
+
+  ExtractLocalControlParametersResult
   extractLocalControlParameters(const Interest& request,
-                                ControlParameters& parameters,
-                                ControlCommand& command,
-                                shared_ptr<LocalFace>& outFace,
-                                LocalControlFeature& outFeature);
+                                const ControlParameters& parameters,
+                                const ndn::mgmt::CommandContinuation& done);
 
-  VIRTUAL_WITH_TESTS void
-  enableLocalControl(const Interest& request,
-                     ControlParameters& parambeters);
-
-  VIRTUAL_WITH_TESTS void
-  disableLocalControl(const Interest& request,
-                      ControlParameters& parameters);
+PUBLIC_WITH_TESTS_ELSE_PRIVATE: // StatusDataset
+  void
+  listFaces(const Name& topPrefix, const Interest& interest,
+            ndn::mgmt::StatusDatasetContext& context);
 
   void
-  ignoreUnsignedVerb(const Interest& request);
+  listChannels(const Name& topPrefix, const Interest& interest,
+               ndn::mgmt::StatusDatasetContext& context);
 
   void
-  addCreatedFaceToForwarder(const shared_ptr<Face>& newFace);
+  queryFaces(const Name& topPrefix, const Interest& interest,
+             ndn::mgmt::StatusDatasetContext& context);
+
+private: // helpers for StatusDataset handler
+  bool
+  doesMatchFilter(const ndn::nfd::FaceQueryFilter& filter, shared_ptr<Face> face);
+
+private: // NotificationStream
+  void
+  afterFaceAdded(shared_ptr<Face> face,
+                 const ndn::mgmt::PostNotification& post);
 
   void
-  onCreated(const Name& requestName,
-            ControlParameters& parameters,
-            const shared_ptr<Face>& newFace);
+  afterFaceRemoved(shared_ptr<Face> face,
+                   const ndn::mgmt::PostNotification& post);
 
+private: // configuration
   void
-  onConnectFailed(const Name& requestName, const std::string& reason);
-
-  void
-  onAddFace(shared_ptr<Face> face);
-
-  void
-  onRemoveFace(shared_ptr<Face> face);
-
-private:
-  void
-  onConfig(const ConfigSection& configSection, bool isDryRun, const std::string& filename);
+  processConfig(const ConfigSection& configSection, bool isDryRun, const std::string& filename);
 
   void
   processSectionUnix(const ConfigSection& configSection, bool isDryRun);
@@ -158,14 +145,9 @@
   void
   processSectionWebSocket(const ConfigSection& configSection, bool isDryRun);
 
-  /** \brief parse a config option that can be either "yes" or "no"
-   *  \throw ConfigFile::Error value is neither "yes" nor "no"
-   *  \return true if "yes", false if "no"
-   */
-  bool
-  parseYesNo(const ConfigSection::const_iterator& i,
-             const std::string& optionName,
-             const std::string& sectionName);
+private: // helpers for configuration
+  void
+  addCreatedFaceToForwarder(shared_ptr<Face> newFace);
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   typedef std::map<std::string/*protocol*/, shared_ptr<ProtocolFactory>> FactoryMap;
@@ -175,76 +157,8 @@
   FaceTable& m_faceTable;
   signal::ScopedConnection m_faceAddConn;
   signal::ScopedConnection m_faceRemoveConn;
-  FaceStatusPublisher m_faceStatusPublisher;
-  ChannelStatusPublisher m_channelStatusPublisher;
-  NotificationStream<AppFace> m_notificationStream;
-
-  typedef function<void(FaceManager*,
-                        const Interest&,
-                        ControlParameters&)> SignedVerbProcessor;
-
-  typedef std::map<Name::Component, SignedVerbProcessor> SignedVerbDispatchTable;
-  typedef std::pair<Name::Component, SignedVerbProcessor> SignedVerbAndProcessor;
-
-  typedef function<void(FaceManager*, const Interest&)> UnsignedVerbProcessor;
-
-  typedef std::map<Name::Component, UnsignedVerbProcessor> UnsignedVerbDispatchTable;
-  typedef std::pair<Name::Component, UnsignedVerbProcessor> UnsignedVerbAndProcessor;
-
-  const SignedVerbDispatchTable m_signedVerbDispatch;
-  const UnsignedVerbDispatchTable m_unsignedVerbDispatch;
-
-  static const Name COMMAND_PREFIX; // /localhost/nfd/faces
-
-  // number of components in an invalid signed command (i.e. should be signed, but isn't)
-  // (/localhost/nfd/faces + verb + parameters) = 5
-  static const size_t COMMAND_UNSIGNED_NCOMPS;
-
-  // number of components in a valid signed command.
-  // (see UNSIGNED_NCOMPS), 9 with signed Interest support.
-  static const size_t COMMAND_SIGNED_NCOMPS;
-
-  static const SignedVerbAndProcessor SIGNED_COMMAND_VERBS[];
-  static const UnsignedVerbAndProcessor UNSIGNED_COMMAND_VERBS[];
-
-  static const Name FACES_LIST_DATASET_PREFIX;
-  static const size_t FACES_LIST_DATASET_NCOMPS;
-
-  static const Name CHANNELS_LIST_DATASET_PREFIX;
-  static const size_t CHANNELS_LIST_DATASET_NCOMPS;
-
-  static const Name FACES_QUERY_DATASET_PREFIX;
-  static const size_t FACES_QUERY_DATASET_NCOMPS;
-
-  static const Name FACE_EVENTS_PREFIX;
 };
 
-inline bool
-FaceManager::parseYesNo(const ConfigSection::const_iterator& i,
-                        const std::string& optionName,
-                        const std::string& sectionName)
-{
-  const std::string value = i->second.get_value<std::string>();
-  if (value == "yes")
-    {
-      return true;
-    }
-  else if (value == "no")
-    {
-      return false;
-    }
-
-   BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
-                                           optionName + "\" in \"" +
-                                           sectionName + "\" section"));
-}
-
-inline void
-FaceManager::ignoreUnsignedVerb(const Interest& request)
-{
-  // do nothing
-}
-
 } // namespace nfd
 
 #endif // NFD_DAEMON_MGMT_FACE_MANAGER_HPP
diff --git a/daemon/mgmt/face-query-status-publisher.cpp b/daemon/mgmt/face-query-status-publisher.cpp
deleted file mode 100644
index e60b949..0000000
--- a/daemon/mgmt/face-query-status-publisher.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014,  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-query-status-publisher.hpp"
-
-#include <boost/range/adaptor/reversed.hpp>
-#include <ndn-cxx/management/nfd-face-status.hpp>
-
-namespace nfd {
-
-FaceQueryStatusPublisher::FaceQueryStatusPublisher(const FaceTable& faceTable,
-                                                   AppFace& face,
-                                                   const Name& prefix,
-                                                   const ndn::nfd::FaceQueryFilter& filter,
-                                                   ndn::KeyChain& keyChain)
-  : SegmentPublisher(face, prefix, keyChain)
-  , m_faceTable(faceTable)
-  , m_faceFilter(filter)
-{
-}
-
-FaceQueryStatusPublisher::~FaceQueryStatusPublisher()
-{
-}
-
-bool
-FaceQueryStatusPublisher::doesMatchFilter(const shared_ptr<Face>& face)
-{
-  if (m_faceFilter.hasFaceId() &&
-      m_faceFilter.getFaceId() != static_cast<uint64_t>(face->getId())) {
-    return false;
-  }
-
-  if (m_faceFilter.hasUriScheme() &&
-      (m_faceFilter.getUriScheme() != face->getRemoteUri().getScheme() ||
-       m_faceFilter.getUriScheme() != face->getLocalUri().getScheme())) {
-    return false;
-  }
-
-  if (m_faceFilter.hasRemoteUri() &&
-      m_faceFilter.getRemoteUri() != face->getRemoteUri().toString()) {
-    return false;
-  }
-
-  if (m_faceFilter.hasLocalUri() && m_faceFilter.getLocalUri() != face->getLocalUri().toString()) {
-    return false;
-  }
-
-  if (m_faceFilter.hasFaceScope() &&
-      (m_faceFilter.getFaceScope() == ndn::nfd::FACE_SCOPE_LOCAL) != face->isLocal()) {
-    return false;
-  }
-
-  if (m_faceFilter.hasFacePersistency() &&
-      m_faceFilter.getFacePersistency() != face->getPersistency()) {
-    return false;
-  }
-
-  if (m_faceFilter.hasLinkType() &&
-      (m_faceFilter.getLinkType() == ndn::nfd::LINK_TYPE_MULTI_ACCESS) != face->isMultiAccess()) {
-    return false;
-  }
-
-  return true;
-}
-
-size_t
-FaceQueryStatusPublisher::generate(ndn::EncodingBuffer& outBuffer)
-{
-  size_t totalLength = 0;
-
-  for (const shared_ptr<Face>& face : m_faceTable | boost::adaptors::reversed) {
-    if (doesMatchFilter(face)) {
-      ndn::nfd::FaceStatus status = face->getFaceStatus();
-      totalLength += status.wireEncode(outBuffer);
-    }
-  }
-  return totalLength;
-}
-
-} // namespace nfd
diff --git a/daemon/mgmt/face-query-status-publisher.hpp b/daemon/mgmt/face-query-status-publisher.hpp
deleted file mode 100644
index 237cecb..0000000
--- a/daemon/mgmt/face-query-status-publisher.hpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014,  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_MGMT_QUERIED_FACE_STATUS_PUBLISHER_HPP
-#define NFD_DAEMON_MGMT_QUERIED_FACE_STATUS_PUBLISHER_HPP
-
-#include "core/segment-publisher.hpp"
-#include "mgmt/app-face.hpp"
-#include "fw/face-table.hpp"
-
-#include <ndn-cxx/management/nfd-face-query-filter.hpp>
-
-namespace nfd {
-
-class FaceQueryStatusPublisher : public SegmentPublisher<AppFace>
-{
-public:
-  FaceQueryStatusPublisher(const FaceTable& faceTable,
-                           AppFace& face,
-                           const Name& prefix,
-                           const ndn::nfd::FaceQueryFilter& filter,
-                           ndn::KeyChain& keyChain);
-
-  virtual
-  ~FaceQueryStatusPublisher();
-
-  bool
-  doesMatchFilter(const shared_ptr<Face>& face);
-
-protected:
-
-  virtual size_t
-  generate(ndn::EncodingBuffer& outBuffer);
-
-private:
-  const FaceTable& m_faceTable;
-  const ndn::nfd::FaceQueryFilter& m_faceFilter;
-};
-
-} // namespace nfd
-
-#endif // NFD_DAEMON_MGMT_QUERIED_FACE_STATUS_PUBLISHER_HPP
diff --git a/daemon/mgmt/face-status-publisher.cpp b/daemon/mgmt/face-status-publisher.cpp
deleted file mode 100644
index ff061fa..0000000
--- a/daemon/mgmt/face-status-publisher.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014  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
- *
- * 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-status-publisher.hpp"
-#include "fw/face-table.hpp"
-
-#include <boost/range/adaptor/reversed.hpp>
-#include <ndn-cxx/management/nfd-face-status.hpp>
-
-namespace nfd {
-
-FaceStatusPublisher::FaceStatusPublisher(const FaceTable& faceTable,
-                                         AppFace& face,
-                                         const Name& prefix,
-                                         ndn::KeyChain& keyChain)
-  : SegmentPublisher(face, prefix, keyChain)
-  , m_faceTable(faceTable)
-{
-}
-
-FaceStatusPublisher::~FaceStatusPublisher()
-{
-}
-
-size_t
-FaceStatusPublisher::generate(ndn::EncodingBuffer& outBuffer)
-{
-  size_t totalLength = 0;
-
-  for (const shared_ptr<Face>& face : m_faceTable | boost::adaptors::reversed) {
-    ndn::nfd::FaceStatus status = face->getFaceStatus();
-    totalLength += status.wireEncode(outBuffer);
-  }
-  return totalLength;
-}
-
-} // namespace nfd
diff --git a/daemon/mgmt/face-status-publisher.hpp b/daemon/mgmt/face-status-publisher.hpp
deleted file mode 100644
index 5091165..0000000
--- a/daemon/mgmt/face-status-publisher.hpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014,  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_MGMT_FACE_STATUS_PUBLISHER_HPP
-#define NFD_DAEMON_MGMT_FACE_STATUS_PUBLISHER_HPP
-
-#include "core/segment-publisher.hpp"
-#include "mgmt/app-face.hpp"
-
-namespace nfd {
-
-class FaceTable;
-
-class FaceStatusPublisher : public SegmentPublisher<AppFace>
-{
-public:
-  FaceStatusPublisher(const FaceTable& faceTable,
-                      AppFace& face,
-                      const Name& prefix,
-                      ndn::KeyChain& keyChain);
-
-  virtual
-  ~FaceStatusPublisher();
-
-protected:
-
-  virtual size_t
-  generate(ndn::EncodingBuffer& outBuffer);
-
-private:
-  const FaceTable& m_faceTable;
-};
-
-} // namespace nfd
-
-#endif // NFD_DAEMON_MGMT_FACE_STATUS_PUBLISHER_HPP
diff --git a/daemon/mgmt/fib-enumeration-publisher.cpp b/daemon/mgmt/fib-enumeration-publisher.cpp
deleted file mode 100644
index 5123a58..0000000
--- a/daemon/mgmt/fib-enumeration-publisher.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014  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
- *
- * 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 "fib-enumeration-publisher.hpp"
-#include "core/logger.hpp"
-#include "table/fib.hpp"
-
-#include <ndn-cxx/management/nfd-fib-entry.hpp>
-
-namespace nfd {
-
-NFD_LOG_INIT("FibEnumerationPublisher");
-
-FibEnumerationPublisher::FibEnumerationPublisher(const Fib& fib,
-                                                 AppFace& face,
-                                                 const Name& prefix,
-                                                 ndn::KeyChain& keyChain)
-  : SegmentPublisher(face, prefix, keyChain)
-  , m_fib(fib)
-{
-}
-
-FibEnumerationPublisher::~FibEnumerationPublisher()
-{
-}
-
-size_t
-FibEnumerationPublisher::generate(ndn::EncodingBuffer& outBuffer)
-{
-  size_t totalLength = 0;
-
-  /// \todo Enable use of Fib::const_reverse_iterator (when it is available)
-  for (Fib::const_iterator i = m_fib.begin(); i != m_fib.end(); ++i)
-    {
-      const fib::Entry& entry = *i;
-      const Name& prefix = entry.getPrefix();
-      size_t fibEntryLength = 0;
-
-      ndn::nfd::FibEntry tlvEntry;
-      const fib::NextHopList& nextHops = entry.getNextHops();
-
-      for (fib::NextHopList::const_iterator j = nextHops.begin();
-           j != nextHops.end();
-           ++j)
-        {
-          const fib::NextHop& next = *j;
-          ndn::nfd::NextHopRecord nextHopRecord;
-          nextHopRecord.setFaceId(next.getFace()->getId());
-          nextHopRecord.setCost(next.getCost());
-
-          tlvEntry.addNextHopRecord(nextHopRecord);
-        }
-
-      tlvEntry.setPrefix(prefix);
-      fibEntryLength += tlvEntry.wireEncode(outBuffer);
-
-      NFD_LOG_DEBUG("generate: fib entry length = " << fibEntryLength);
-
-      totalLength += fibEntryLength;
-    }
-
-  NFD_LOG_DEBUG("generate: Total length = " << totalLength);
-  return totalLength;
-}
-
-} // namespace nfd
diff --git a/daemon/mgmt/fib-enumeration-publisher.hpp b/daemon/mgmt/fib-enumeration-publisher.hpp
deleted file mode 100644
index 2ff5c31..0000000
--- a/daemon/mgmt/fib-enumeration-publisher.hpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014,  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_MGMT_FIB_ENUMERATION_PUBLISHER_HPP
-#define NFD_DAEMON_MGMT_FIB_ENUMERATION_PUBLISHER_HPP
-
-#include "core/segment-publisher.hpp"
-#include "mgmt/app-face.hpp"
-
-namespace nfd {
-
-class Fib;
-
-class FibEnumerationPublisher : public SegmentPublisher<AppFace>
-{
-public:
-  FibEnumerationPublisher(const Fib& fib,
-                          AppFace& face,
-                          const Name& prefix,
-                          ndn::KeyChain& keyChain);
-
-  virtual
-  ~FibEnumerationPublisher();
-
-protected:
-
-  virtual size_t
-  generate(ndn::EncodingBuffer& outBuffer);
-
-private:
-  const Fib& m_fib;
-};
-
-} // namespace nfd
-
-#endif // NFD_DAEMON_MGMT_FIB_ENUMERATION_PUBLISHER_HPP
diff --git a/daemon/mgmt/fib-manager.cpp b/daemon/mgmt/fib-manager.cpp
index 3db26e7..12374f1 100644
--- a/daemon/mgmt/fib-manager.cpp
+++ b/daemon/mgmt/fib-manager.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  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
+ * 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.
@@ -24,171 +24,34 @@
  */
 
 #include "fib-manager.hpp"
-
-#include "core/logger.hpp"
-#include "table/fib.hpp"
-#include "fw/forwarder.hpp"
-#include "mgmt/internal-face.hpp"
-#include "mgmt/app-face.hpp"
-
-#include <ndn-cxx/encoding/tlv.hpp>
+#include <ndn-cxx/management/nfd-fib-entry.hpp>
 
 namespace nfd {
 
 NFD_LOG_INIT("FibManager");
 
-const Name FibManager::COMMAND_PREFIX = "/localhost/nfd/fib";
-
-const size_t FibManager::COMMAND_UNSIGNED_NCOMPS =
-  FibManager::COMMAND_PREFIX.size() +
-  1 + // verb
-  1;  // verb parameters
-
-const size_t FibManager::COMMAND_SIGNED_NCOMPS =
-  FibManager::COMMAND_UNSIGNED_NCOMPS +
-  4; // (timestamp, nonce, signed info tlv, signature tlv)
-
-const FibManager::SignedVerbAndProcessor FibManager::SIGNED_COMMAND_VERBS[] =
-  {
-
-    SignedVerbAndProcessor(
-                           Name::Component("add-nexthop"),
-                           &FibManager::addNextHop
-                           ),
-
-    SignedVerbAndProcessor(
-                           Name::Component("remove-nexthop"),
-                           &FibManager::removeNextHop
-                           ),
-
-  };
-
-const FibManager::UnsignedVerbAndProcessor FibManager::UNSIGNED_COMMAND_VERBS[] =
-  {
-    UnsignedVerbAndProcessor(
-                             Name::Component("list"),
-                             &FibManager::listEntries
-                             ),
-  };
-
-const Name FibManager::LIST_COMMAND_PREFIX("/localhost/nfd/fib/list");
-const size_t FibManager::LIST_COMMAND_NCOMPS = LIST_COMMAND_PREFIX.size();
-
-
 FibManager::FibManager(Fib& fib,
                        function<shared_ptr<Face>(FaceId)> getFace,
-                       shared_ptr<InternalFace> face,
-                       ndn::KeyChain& keyChain)
-  : ManagerBase(face, FIB_PRIVILEGE, keyChain)
-  , m_managedFib(fib)
+                       Dispatcher& dispatcher,
+                       CommandValidator& validator)
+  : ManagerBase(dispatcher, validator, "fib")
+  , m_fib(fib)
   , m_getFace(getFace)
-  , m_fibEnumerationPublisher(fib, *face, LIST_COMMAND_PREFIX, keyChain)
-  , m_signedVerbDispatch(SIGNED_COMMAND_VERBS,
-                         SIGNED_COMMAND_VERBS +
-                         (sizeof(SIGNED_COMMAND_VERBS) / sizeof(SignedVerbAndProcessor)))
-  , m_unsignedVerbDispatch(UNSIGNED_COMMAND_VERBS,
-                           UNSIGNED_COMMAND_VERBS +
-                           (sizeof(UNSIGNED_COMMAND_VERBS) / sizeof(UnsignedVerbAndProcessor)))
 {
-  face->setInterestFilter("/localhost/nfd/fib",
-                          bind(&FibManager::onFibRequest, this, _2));
-}
+  registerCommandHandler<ndn::nfd::FibAddNextHopCommand>("add-nexthop",
+    bind(&FibManager::addNextHop, this, _2, _3, _4, _5));
+  registerCommandHandler<ndn::nfd::FibRemoveNextHopCommand>("remove-nexthop",
+    bind(&FibManager::removeNextHop, this, _2, _3, _4, _5));
 
-FibManager::~FibManager()
-{
-
+  registerStatusDatasetHandler("list", bind(&FibManager::listEntries, this, _1, _2, _3));
 }
 
 void
-FibManager::onFibRequest(const Interest& request)
+FibManager::addNextHop(const Name& topPrefix, const Interest& interest,
+                       ControlParameters parameters,
+                       const ndn::mgmt::CommandContinuation& done)
 {
-  const Name& command = request.getName();
-  const size_t commandNComps = command.size();
-
-  if (commandNComps <= COMMAND_PREFIX.size())
-    {
-      // command is too short to have a verb
-      NFD_LOG_DEBUG("command result: malformed");
-      sendResponse(command, 400, "Malformed command");
-      return;
-    }
-
-  const Name::Component& verb = command.at(COMMAND_PREFIX.size());
-
-  const auto unsignedVerbProcessor = m_unsignedVerbDispatch.find(verb);
-  if (unsignedVerbProcessor != m_unsignedVerbDispatch.end())
-    {
-      NFD_LOG_DEBUG("command result: processing verb: " << verb);
-      (unsignedVerbProcessor->second)(this, request);
-    }
-  else if (COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
-           commandNComps < COMMAND_SIGNED_NCOMPS)
-    {
-      NFD_LOG_DEBUG("command result: unsigned verb: " << command);
-      sendResponse(command, 401, "Signature required");
-    }
-  else if (commandNComps < COMMAND_SIGNED_NCOMPS ||
-           !COMMAND_PREFIX.isPrefixOf(command))
-    {
-      NFD_LOG_DEBUG("command result: malformed");
-      sendResponse(command, 400, "Malformed command");
-    }
-  else
-    {
-      validate(request,
-               bind(&FibManager::onValidatedFibRequest, this, _1),
-               bind(&ManagerBase::onCommandValidationFailed, this, _1, _2));
-    }
-}
-
-void
-FibManager::onValidatedFibRequest(const shared_ptr<const Interest>& request)
-{
-  const Name& command = request->getName();
-  const Name::Component& verb = command[COMMAND_PREFIX.size()];
-  const Name::Component& parameterComponent = command[COMMAND_PREFIX.size() + 1];
-
-  SignedVerbDispatchTable::const_iterator verbProcessor = m_signedVerbDispatch.find(verb);
-  if (verbProcessor != m_signedVerbDispatch.end())
-    {
-      ControlParameters parameters;
-      if (!extractParameters(parameterComponent, parameters))
-        {
-          NFD_LOG_DEBUG("command result: malformed verb: " << verb);
-          sendResponse(command, 400, "Malformed command");
-          return;
-        }
-
-      bool isSelfRegistration = (!parameters.hasFaceId() || parameters.getFaceId() == 0);
-      if (isSelfRegistration)
-        {
-          parameters.setFaceId(request->getIncomingFaceId());
-        }
-
-      NFD_LOG_DEBUG("command result: processing verb: " << verb);
-      ControlResponse response;
-      (verbProcessor->second)(this, parameters, response);
-      sendResponse(command, response);
-    }
-  else
-    {
-      NFD_LOG_DEBUG("command result: unsupported verb: " << verb);
-      sendResponse(command, 501, "Unsupported command");
-    }
-}
-
-void
-FibManager::addNextHop(ControlParameters& parameters,
-                       ControlResponse& response)
-{
-  ndn::nfd::FibAddNextHopCommand command;
-
-  if (!validateParameters(command, parameters))
-    {
-      NFD_LOG_DEBUG("add-nexthop result: FAIL reason: malformed");
-      setResponse(response, 400, "Malformed command");
-      return;
-    }
+  setFaceForSelfRegistration(interest, parameters);
 
   const Name& prefix = parameters.getName();
   FaceId faceId = parameters.getFaceId();
@@ -198,87 +61,89 @@
                 << " faceid: " << faceId
                 << " cost: " << cost);
 
-  shared_ptr<Face> nextHopFace = m_getFace(faceId);
-  if (static_cast<bool>(nextHopFace))
-    {
-      shared_ptr<fib::Entry> entry = m_managedFib.insert(prefix).first;
+  auto face = m_getFace(faceId);
+  if (static_cast<bool>(face)) {
+    auto entry = m_fib.insert(prefix).first;
 
-      entry->addNextHop(nextHopFace, cost);
+    entry->addNextHop(face, cost);
 
-      NFD_LOG_DEBUG("add-nexthop result: OK"
-                    << " prefix:" << prefix
-                    << " faceid: " << faceId
-                    << " cost: " << cost);
+    NFD_LOG_DEBUG("add-nexthop result: OK"
+                  << " prefix:" << prefix
+                  << " faceid: " << faceId
+                  << " cost: " << cost);
 
-      setResponse(response, 200, "Success", parameters.wireEncode());
-    }
-  else
-    {
-      NFD_LOG_DEBUG("add-nexthop result: FAIL reason: unknown-faceid: " << faceId);
-      setResponse(response, 410, "Face not found");
-    }
+    return done(ControlResponse(200, "Success").setBody(parameters.wireEncode()));
+  }
+  else {
+    NFD_LOG_INFO("add-nexthop result: FAIL reason: unknown-faceid: " << faceId);
+    return done(ControlResponse(410, "Face not found"));
+  }
 }
 
 void
-FibManager::removeNextHop(ControlParameters& parameters,
-                          ControlResponse& response)
+FibManager::removeNextHop(const Name& topPrefix, const Interest& interest,
+                          ControlParameters parameters,
+                          const ndn::mgmt::CommandContinuation& done)
 {
-  ndn::nfd::FibRemoveNextHopCommand command;
-  if (!validateParameters(command, parameters))
-    {
-      NFD_LOG_DEBUG("remove-nexthop result: FAIL reason: malformed");
-      setResponse(response, 400, "Malformed command");
-      return;
-    }
+  setFaceForSelfRegistration(interest, parameters);
 
   NFD_LOG_TRACE("remove-nexthop prefix: " << parameters.getName()
                 << " faceid: " << parameters.getFaceId());
 
-  shared_ptr<Face> faceToRemove = m_getFace(parameters.getFaceId());
-  if (static_cast<bool>(faceToRemove))
-    {
-      shared_ptr<fib::Entry> entry = m_managedFib.findExactMatch(parameters.getName());
-      if (static_cast<bool>(entry))
-        {
-          entry->removeNextHop(faceToRemove);
-          NFD_LOG_DEBUG("remove-nexthop result: OK prefix: " << parameters.getName()
-                        << " faceid: " << parameters.getFaceId());
+  auto face = m_getFace(parameters.getFaceId());
+  if (static_cast<bool>(face)) {
+    auto entry = m_fib.findExactMatch(parameters.getName());
+    if (static_cast<bool>(entry)) {
+      entry->removeNextHop(face);
+      NFD_LOG_DEBUG("remove-nexthop result: OK prefix: " << parameters.getName()
+                    << " faceid: " << parameters.getFaceId());
 
-          if (!entry->hasNextHops())
-            {
-              m_managedFib.erase(*entry);
-            }
-        }
-      else
-        {
-          NFD_LOG_DEBUG("remove-nexthop result: OK, but entry for face id "
-                        << parameters.getFaceId() << " not found");
-        }
+      if (!entry->hasNextHops()) {
+        m_fib.erase(*entry);
+      }
     }
-  else
-    {
-      NFD_LOG_DEBUG("remove-nexthop result: OK, but face id "
-                    << parameters.getFaceId() << " not found");
+    else {
+      NFD_LOG_DEBUG("remove-nexthop result: OK");
     }
+  }
+  else {
+    NFD_LOG_DEBUG("remove-nexthop result: OK");
+  }
 
-  setResponse(response, 200, "Success", parameters.wireEncode());
+  done(ControlResponse(200, "Success").setBody(parameters.wireEncode()));
 }
 
 void
-FibManager::listEntries(const Interest& request)
+FibManager::listEntries(const Name& topPrefix, const Interest& interest,
+                        ndn::mgmt::StatusDatasetContext& context)
 {
-  const Name& command = request.getName();
-  const size_t commandNComps = command.size();
+  for (auto&& entry : m_fib) {
+    auto prefix = entry.getPrefix();
+    ndn::nfd::FibEntry record;
+    const auto& nextHops = entry.getNextHops();
 
-  if (commandNComps < LIST_COMMAND_NCOMPS ||
-      !LIST_COMMAND_PREFIX.isPrefixOf(command))
-    {
-      NFD_LOG_DEBUG("command result: malformed");
-      sendResponse(command, 400, "Malformed command");
-      return;
+    for (auto&& next : nextHops) {
+      ndn::nfd::NextHopRecord nextHopRecord;
+      nextHopRecord.setFaceId(next.getFace()->getId());
+      nextHopRecord.setCost(next.getCost());
+
+      record.addNextHopRecord(nextHopRecord);
     }
 
-  m_fibEnumerationPublisher.publish();
+    record.setPrefix(prefix);
+    context.append(record.wireEncode());
+  }
+
+  context.end();
 }
 
-} // namespace nfd
+void
+FibManager::setFaceForSelfRegistration(const Interest& request, ControlParameters& parameters)
+{
+  bool isSelfRegistration = (parameters.getFaceId() == 0);
+  if (isSelfRegistration) {
+    parameters.setFaceId(request.getIncomingFaceId());
+  }
+}
+
+} // namespace
diff --git a/daemon/mgmt/fib-manager.hpp b/daemon/mgmt/fib-manager.hpp
index c8c21ea..7c66138 100644
--- a/daemon/mgmt/fib-manager.hpp
+++ b/daemon/mgmt/fib-manager.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  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
+ * 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.
@@ -26,87 +26,55 @@
 #ifndef NFD_DAEMON_MGMT_FIB_MANAGER_HPP
 #define NFD_DAEMON_MGMT_FIB_MANAGER_HPP
 
-#include "common.hpp"
-#include "mgmt/manager-base.hpp"
-#include "mgmt/fib-enumeration-publisher.hpp"
+#include "manager-base.hpp"
+#include "core/logger.hpp"
+#include "table/fib.hpp"
+#include "fw/forwarder.hpp"
 
 namespace nfd {
 
-class Face;
-class Forwarder;
-class Fib;
-
-const std::string FIB_PRIVILEGE = "fib"; // config file privilege name
-
+/**
+ * @brief implement the FIB Management of NFD Management Protocol.
+ * @sa http://redmine.named-data.net/projects/nfd/wiki/FibMgmt
+ */
 class FibManager : public ManagerBase
 {
 public:
-
+  /**
+   * @brief construct a FibManger
+   *
+   * @param fib the managed FIB
+   * @param getFace a function used to retrive a face by FaceId from the face table
+   * @param dispatcher the management dispatcher
+   * @param validator the command validator
+   */
   FibManager(Fib& fib,
              function<shared_ptr<Face>(FaceId)> getFace,
-             shared_ptr<InternalFace> face,
-             ndn::KeyChain& keyChain);
-
-  virtual
-  ~FibManager();
-
-  void
-  onFibRequest(const Interest& request);
+             Dispatcher& dispatcher,
+             CommandValidator& validator);
 
 private:
+  void
+  addNextHop(const Name& topPrefix, const Interest& interest,
+             ControlParameters parameters,
+             const ndn::mgmt::CommandContinuation& done);
 
   void
-  onValidatedFibRequest(const shared_ptr<const Interest>& request);
+  removeNextHop(const Name& topPrefix, const Interest& interest,
+                ControlParameters parameters,
+                const ndn::mgmt::CommandContinuation& done);
 
   void
-  addNextHop(ControlParameters& parameters,
-             ControlResponse& response);
-
-  void
-  removeNextHop(ControlParameters& parameters,
-                ControlResponse& response);
-
-  void
-  listEntries(const Interest& request);
+  listEntries(const Name& topPrefix, const Interest& interest,
+              ndn::mgmt::StatusDatasetContext& context);
 
 private:
+  void
+  setFaceForSelfRegistration(const Interest& request, ControlParameters& parameters);
 
-  Fib& m_managedFib;
+private:
+  Fib& m_fib;
   function<shared_ptr<Face>(FaceId)> m_getFace;
-  FibEnumerationPublisher m_fibEnumerationPublisher;
-
-  typedef function<void(FibManager*,
-                        ControlParameters&,
-                        ControlResponse&)> SignedVerbProcessor;
-
-  typedef std::map<Name::Component, SignedVerbProcessor> SignedVerbDispatchTable;
-
-  typedef std::pair<Name::Component, SignedVerbProcessor> SignedVerbAndProcessor;
-
-  typedef function<void(FibManager*, const Interest&)> UnsignedVerbProcessor;
-
-  typedef std::map<Name::Component, UnsignedVerbProcessor> UnsignedVerbDispatchTable;
-  typedef std::pair<Name::Component, UnsignedVerbProcessor> UnsignedVerbAndProcessor;
-
-
-  const SignedVerbDispatchTable m_signedVerbDispatch;
-  const UnsignedVerbDispatchTable m_unsignedVerbDispatch;
-
-  static const Name COMMAND_PREFIX; // /localhost/nfd/fib
-
-  // number of components in an invalid, but not malformed, unsigned command.
-  // (/localhost/nfd/fib + verb + parameters) = 5
-  static const size_t COMMAND_UNSIGNED_NCOMPS;
-
-  // number of components in a valid signed Interest.
-  // UNSIGNED_NCOMPS + 4 command Interest components = 9
-  static const size_t COMMAND_SIGNED_NCOMPS;
-
-  static const SignedVerbAndProcessor SIGNED_COMMAND_VERBS[];
-  static const UnsignedVerbAndProcessor UNSIGNED_COMMAND_VERBS[];
-
-  static const Name LIST_COMMAND_PREFIX;
-  static const size_t LIST_COMMAND_NCOMPS;
 };
 
 } // namespace nfd
diff --git a/daemon/mgmt/forwarder-status-manager.cpp b/daemon/mgmt/forwarder-status-manager.cpp
new file mode 100644
index 0000000..bf2372b
--- /dev/null
+++ b/daemon/mgmt/forwarder-status-manager.cpp
@@ -0,0 +1,70 @@
+/* -*- 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 "forwarder-status-manager.hpp"
+#include "fw/forwarder.hpp"
+#include "version.hpp"
+
+namespace nfd {
+
+const time::milliseconds STATUS_SERVER_DEFAULT_FRESHNESS = time::milliseconds(5000);
+
+ForwarderStatusManager::ForwarderStatusManager(Forwarder& forwarder, Dispatcher& dispatcher)
+  : m_forwarder(forwarder)
+  , m_dispatcher(dispatcher)
+  , m_startTimestamp(time::system_clock::now())
+{
+  m_dispatcher.addStatusDataset("status", ndn::mgmt::makeAcceptAllAuthorization(),
+                                bind(&ForwarderStatusManager::listStatus, this, _1, _2, _3));
+}
+
+void
+ForwarderStatusManager::listStatus(const Name& topPrefix, const Interest& interest,
+                                   ndn::mgmt::StatusDatasetContext& context)
+{
+  ndn::nfd::ForwarderStatus status;
+
+  status.setNfdVersion(NFD_VERSION_BUILD_STRING);
+  status.setStartTimestamp(m_startTimestamp);
+  status.setCurrentTimestamp(time::system_clock::now());
+
+  status.setNNameTreeEntries(m_forwarder.getNameTree().size());
+  status.setNFibEntries(m_forwarder.getFib().size());
+  status.setNPitEntries(m_forwarder.getPit().size());
+  status.setNMeasurementsEntries(m_forwarder.getMeasurements().size());
+  status.setNCsEntries(m_forwarder.getCs().size());
+
+  m_forwarder.getCounters().copyTo(status);
+
+  context.setExpiry(STATUS_SERVER_DEFAULT_FRESHNESS);
+
+  status.wireEncode().parse();
+  for (const auto& subblock : status.wireEncode().elements()) {
+    context.append(subblock);
+  }
+  context.end();
+}
+
+} // namespace nfd
diff --git a/daemon/mgmt/forwarder-status-manager.hpp b/daemon/mgmt/forwarder-status-manager.hpp
new file mode 100644
index 0000000..57d89c8
--- /dev/null
+++ b/daemon/mgmt/forwarder-status-manager.hpp
@@ -0,0 +1,58 @@
+/* -*- 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_MGMT_FORWARDER_STATUS_MANAGER_HPP
+#define NFD_DAEMON_MGMT_FORWARDER_STATUS_MANAGER_HPP
+
+#include "manager-base.hpp"
+#include <ndn-cxx/management/nfd-forwarder-status.hpp>
+
+namespace nfd {
+
+class Forwarder;
+
+/**
+ * @brief implement the Forwarder Status of NFD Management Protocol.
+ * @sa http://redmine.named-data.net/projects/nfd/wiki/ForwarderStatus
+ */
+class ForwarderStatusManager : noncopyable
+{
+public:
+  ForwarderStatusManager(Forwarder& forwarder, Dispatcher& dispatcher);
+
+private:
+  void
+  listStatus(const Name& topPrefix, const Interest& interest,
+             ndn::mgmt::StatusDatasetContext& context);
+
+private:
+  Forwarder&  m_forwarder;
+  Dispatcher& m_dispatcher;
+  time::system_clock::TimePoint m_startTimestamp;
+};
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_MGMT_FORWARDER_STATUS_MANAGER_HPP
diff --git a/daemon/mgmt/internal-face.cpp b/daemon/mgmt/internal-face.cpp
deleted file mode 100644
index 7569fb9..0000000
--- a/daemon/mgmt/internal-face.cpp
+++ /dev/null
@@ -1,153 +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 "internal-face.hpp"
-#include "core/logger.hpp"
-#include "core/global-io.hpp"
-
-namespace nfd {
-
-NFD_LOG_INIT("InternalFace");
-
-InternalFace::InternalFace()
-  : Face(FaceUri("internal://"), FaceUri("internal://"), true)
-{
-}
-
-void
-InternalFace::sendInterest(const Interest& interest)
-{
-  this->emitSignal(onSendInterest, interest);
-
-  // Invoke .processInterest a bit later,
-  // to avoid potential problems in forwarding pipelines.
-  getGlobalIoService().post(bind(&InternalFace::processInterest,
-                                 this, interest.shared_from_this()));
-}
-
-void
-InternalFace::processInterest(const shared_ptr<const Interest>& interest)
-{
-  if (m_interestFilters.size() == 0)
-    {
-      NFD_LOG_DEBUG("no Interest filters to match against");
-      return;
-    }
-
-  const Name& interestName(interest->getName());
-  NFD_LOG_DEBUG("received Interest: " << interestName);
-
-  std::map<Name, OnInterest>::const_iterator filter =
-    m_interestFilters.lower_bound(interestName);
-
-  // lower_bound gives us the first Name that is
-  // an exact match OR ordered after interestName.
-  //
-  // If we reach the end of the map, then we need
-  // only check if the before-end element is a match.
-  //
-  // If we match an element, then the current
-  // position or the previous element are potential
-  // matches.
-  //
-  // If we hit begin, the element is either an exact
-  // match or there is no matching prefix in the map.
-
-
-  if (filter == m_interestFilters.end() && filter != m_interestFilters.begin())
-    {
-      // We hit the end, check if the previous element
-      // is a match
-      --filter;
-      if (filter->first.isPrefixOf(interestName))
-        {
-          NFD_LOG_DEBUG("found Interest filter for " << filter->first << " (before end match)");
-          filter->second(interestName, *interest);
-        }
-      else
-        {
-          NFD_LOG_DEBUG("no Interest filter found for " << interestName << " (before end)");
-        }
-    }
-  else if (filter->first == interestName)
-    {
-      NFD_LOG_DEBUG("found Interest filter for " << filter->first << " (exact match)");
-      filter->second(interestName, *interest);
-    }
-  else if (filter != m_interestFilters.begin())
-    {
-      // the element we found is canonically
-      // ordered after interestName.
-      // Check the previous element.
-      --filter;
-      if (filter->first.isPrefixOf(interestName))
-        {
-          NFD_LOG_DEBUG("found Interest filter for " << filter->first << " (previous match)");
-          filter->second(interestName, *interest);
-        }
-      else
-        {
-          NFD_LOG_DEBUG("no Interest filter found for " << interestName << " (previous)");
-        }
-    }
-  else
-    {
-      NFD_LOG_DEBUG("no Interest filter found for " << interestName << " (begin)");
-    }
-  //Drop Interest
-}
-
-void
-InternalFace::sendData(const Data& data)
-{
-  this->emitSignal(onSendData, data);
-}
-
-void
-InternalFace::close()
-{
-  BOOST_THROW_EXCEPTION(Error("Internal face cannot be closed"));
-}
-
-void
-InternalFace::setInterestFilter(const Name& filter,
-                                OnInterest onInterest)
-{
-  NFD_LOG_INFO("registering callback for " << filter);
-  m_interestFilters[filter] = onInterest;
-}
-
-void
-InternalFace::put(const Data& data)
-{
-  this->emitSignal(onReceiveData, data);
-}
-
-InternalFace::~InternalFace()
-{
-
-}
-
-} // namespace nfd
diff --git a/daemon/mgmt/internal-face.hpp b/daemon/mgmt/internal-face.hpp
deleted file mode 100644
index c35676f..0000000
--- a/daemon/mgmt/internal-face.hpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014,  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_MGMT_INTERNAL_FACE_HPP
-#define NFD_DAEMON_MGMT_INTERNAL_FACE_HPP
-
-#include "face/face.hpp"
-#include "app-face.hpp"
-
-#include "command-validator.hpp"
-
-namespace nfd {
-
-class InternalFace : public Face, public AppFace
-{
-public:
-  /**
-   * \brief InternalFace-related error
-   */
-  class Error : public Face::Error
-  {
-  public:
-    explicit
-    Error(const std::string& what)
-      : Face::Error(what)
-    {
-    }
-  };
-
-  InternalFace();
-
-  CommandValidator&
-  getValidator();
-
-  virtual
-  ~InternalFace();
-
-  // Overridden Face methods for forwarder
-
-  virtual void
-  sendInterest(const Interest& interest);
-
-  virtual void
-  sendData(const Data& data);
-
-  virtual void
-  close();
-
-  // Methods implementing AppFace interface. Do not invoke from forwarder.
-
-  virtual void
-  setInterestFilter(const Name& filter,
-                    OnInterest onInterest);
-
-  virtual void
-  put(const Data& data);
-
-private:
-  void
-  processInterest(const shared_ptr<const Interest>& interest);
-
-private:
-  std::map<Name, OnInterest> m_interestFilters;
-  CommandValidator m_validator;
-};
-
-inline CommandValidator&
-InternalFace::getValidator()
-{
-  return m_validator;
-}
-
-
-} // namespace nfd
-
-#endif // NFD_DAEMON_MGMT_INTERNAL_FACE_HPP
diff --git a/daemon/mgmt/manager-base.cpp b/daemon/mgmt/manager-base.cpp
index cf75d04..418c3d6 100644
--- a/daemon/mgmt/manager-base.cpp
+++ b/daemon/mgmt/manager-base.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  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
+ * 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.
@@ -24,120 +24,96 @@
  */
 
 #include "manager-base.hpp"
-#include "core/logger.hpp"
 
 namespace nfd {
 
-NFD_LOG_INIT("ManagerBase");
+using ndn::mgmt::ValidateParameters;
+using ndn::mgmt::Authorization;
 
-ManagerBase::ManagerBase(shared_ptr<InternalFace> face, const std::string& privilege,
-                         ndn::KeyChain& keyChain)
-  : m_face(face)
-  , m_keyChain(keyChain)
+ManagerBase::ManagerBase(Dispatcher& dispatcher,
+                         CommandValidator& validator,
+                         const std::string& module)
+  : m_dispatcher(dispatcher)
+  , m_validator(validator)
+  , m_mgmtModuleName(module)
 {
-  face->getValidator().addSupportedPrivilege(privilege);
+  m_validator.addSupportedPrivilege(module);
 }
 
-ManagerBase::~ManagerBase()
+void
+ManagerBase::registerStatusDatasetHandler(const std::string& verb,
+                                          const ndn::mgmt::StatusDatasetHandler& handler)
 {
+  m_dispatcher.addStatusDataset(makeRelPrefix(verb),
+                                ndn::mgmt::makeAcceptAllAuthorization(),
+                                handler);
+}
 
+ndn::mgmt::PostNotification
+ManagerBase::registerNotificationStream(const std::string& verb)
+{
+  return m_dispatcher.addNotificationStream(makeRelPrefix(verb));
+}
+
+void
+ManagerBase::authorize(const Name& prefix, const Interest& interest,
+                       const ndn::mgmt::ControlParameters* params,
+                       ndn::mgmt::AcceptContinuation accept,
+                       ndn::mgmt::RejectContinuation reject)
+{
+  BOOST_ASSERT(params != nullptr);
+  BOOST_ASSERT(typeid(*params) == typeid(ndn::nfd::ControlParameters));
+
+  m_validator.validate(interest,
+                       bind(&ManagerBase::extractRequester, this, interest, accept),
+                       bind([&] { reject(ndn::mgmt::RejectReply::STATUS403); }));
+}
+
+void
+ManagerBase::extractRequester(const Interest& interest,
+                              ndn::mgmt::AcceptContinuation accept)
+{
+  const Name& interestName = interest.getName();
+
+  try {
+    ndn::SignatureInfo sigInfo(interestName.at(ndn::signed_interest::POS_SIG_INFO).blockFromValue());
+    if (!sigInfo.hasKeyLocator() ||
+        sigInfo.getKeyLocator().getType() != ndn::KeyLocator::KeyLocator_Name) {
+      return accept("");
+    }
+
+    accept(sigInfo.getKeyLocator().getName().toUri());
+  }
+  catch (const tlv::Error&) {
+    accept("");
+  }
 }
 
 bool
-ManagerBase::extractParameters(const Name::Component& parameterComponent,
-                               ControlParameters& extractedParameters)
+ManagerBase::validateParameters(const nfd::ControlCommand& command, const ndn::mgmt::ControlParameters& parameters)
 {
-  try
-    {
-      Block rawParameters = parameterComponent.blockFromValue();
-      extractedParameters.wireDecode(rawParameters);
-    }
-  catch (const tlv::Error&)
-    {
-      return false;
-    }
+  BOOST_ASSERT(dynamic_cast<const ControlParameters*>(&parameters) != nullptr);
 
-  NFD_LOG_DEBUG("Parameters parsed OK");
+  try {
+    command.validateRequest(static_cast<const ControlParameters&>(parameters));
+  }
+  catch (const ControlCommand::ArgumentError&) {
+    return false;
+  }
   return true;
 }
 
 void
-ManagerBase::sendResponse(const Name& name,
-                          uint32_t code,
-                          const std::string& text)
+ManagerBase::handleCommand(shared_ptr<nfd::ControlCommand> command,
+                           const ControlCommandHandler& handler,
+                           const Name& prefix, const Interest& interest,
+                           const ndn::mgmt::ControlParameters& params,
+                           ndn::mgmt::CommandContinuation done)
 {
-  ControlResponse response(code, text);
-  sendResponse(name, response);
-}
-
-void
-ManagerBase::sendResponse(const Name& name,
-                          uint32_t code,
-                          const std::string& text,
-                          const Block& body)
-{
-  ControlResponse response(code, text);
-  response.setBody(body);
-  sendResponse(name, response);
-}
-
-void
-ManagerBase::sendResponse(const Name& name,
-                          const ControlResponse& response)
-{
-  NFD_LOG_DEBUG("responding"
-                << " name: " << name
-                << " code: " << response.getCode()
-                << " text: " << response.getText());
-
-  const Block& encodedControl = response.wireEncode();
-
-  shared_ptr<Data> responseData(make_shared<Data>(name));
-  responseData->setContent(encodedControl);
-
-  m_keyChain.sign(*responseData);
-  m_face->put(*responseData);
-}
-
-void
-ManagerBase::sendNack(const Name& name)
-{
-  NFD_LOG_DEBUG("responding NACK to " << name);
-
-  ndn::MetaInfo meta;
-  meta.setType(tlv::ContentType_Nack);
-
-  shared_ptr<Data> responseData(make_shared<Data>(name));
-  responseData->setMetaInfo(meta);
-
-  m_keyChain.sign(*responseData);
-  m_face->put(*responseData);
-}
-
-bool
-ManagerBase::validateParameters(const ControlCommand& command,
-                                ControlParameters& parameters)
-{
-  try
-    {
-      command.validateRequest(parameters);
-    }
-  catch (const ControlCommand::ArgumentError& error)
-    {
-      return false;
-    }
-
-  command.applyDefaultsToRequest(parameters);
-
-  return true;
-}
-
-void
-ManagerBase::onCommandValidationFailed(const shared_ptr<const Interest>& command,
-                                       const std::string& error)
-{
-  NFD_LOG_DEBUG("command result: unauthorized command: " << *command << " (" << error << ")");
-  sendResponse(command->getName(), 403, "Unauthorized command");
+  BOOST_ASSERT(dynamic_cast<const ControlParameters*>(&params) != nullptr);
+  ControlParameters parameters = static_cast<const ControlParameters&>(params);
+  command->applyDefaultsToRequest(parameters);
+  handler(*command, prefix, interest, parameters, done);
 }
 
 
diff --git a/daemon/mgmt/manager-base.hpp b/daemon/mgmt/manager-base.hpp
index 1611615..988fff1 100644
--- a/daemon/mgmt/manager-base.hpp
+++ b/daemon/mgmt/manager-base.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  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
+ * 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.
@@ -27,143 +27,154 @@
 #define NFD_DAEMON_MGMT_MANAGER_BASE_HPP
 
 #include "common.hpp"
-
 #include "mgmt/command-validator.hpp"
-#include "mgmt/internal-face.hpp"
 
+#include <ndn-cxx/mgmt/dispatcher.hpp>
 #include <ndn-cxx/management/nfd-control-command.hpp>
 #include <ndn-cxx/management/nfd-control-response.hpp>
 #include <ndn-cxx/management/nfd-control-parameters.hpp>
 
 namespace nfd {
 
+using ndn::mgmt::Dispatcher;
+
 using ndn::nfd::ControlCommand;
 using ndn::nfd::ControlResponse;
 using ndn::nfd::ControlParameters;
 
-class InternalFace;
-
-class ManagerBase
+/**
+ * @brief a collection of common functions shared by all NFD managers,
+ *        such as communicating with the dispatcher and command validator.
+ */
+class ManagerBase : public noncopyable
 {
 public:
-
-  struct Error : public std::runtime_error
+  class Error : public std::runtime_error
   {
-    Error(const std::string& what) : std::runtime_error(what) {}
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
   };
 
-  ManagerBase(shared_ptr<InternalFace> face,
-              const std::string& privilege,
-              ndn::KeyChain& keyChain);
+public:
+  ManagerBase(Dispatcher& dispatcher,
+              CommandValidator& validator,
+              const std::string& module);
 
-  virtual
-  ~ManagerBase();
+PUBLIC_WITH_TESTS_ELSE_PROTECTED: // registrations to the dispatcher
+
+  // difference from mgmt::ControlCommand: accepts nfd::ControlParameters
+  typedef function<void(const ControlCommand& command,
+                        const Name& prefix, const Interest& interest,
+                        const ControlParameters& parameters,
+                        const ndn::mgmt::CommandContinuation done)> ControlCommandHandler;
+
+  template<typename Command>
+  void
+  registerCommandHandler(const std::string& verb,
+                         const ControlCommandHandler& handler);
 
   void
-  onCommandValidationFailed(const shared_ptr<const Interest>& command,
-                            const std::string& error);
+  registerStatusDatasetHandler(const std::string& verb,
+                               const ndn::mgmt::StatusDatasetHandler& handler);
 
-protected:
+  ndn::mgmt::PostNotification
+  registerNotificationStream(const std::string& verb);
 
+PUBLIC_WITH_TESTS_ELSE_PRIVATE: // command validation
+  /**
+   * @brief validate a request for ControlCommand.
+   *
+   * This is called by the dispatcher.
+   *
+   * @pre params != null
+   * @pre typeid(*params) == typeid(ndn::nfd::ControlParameters)
+   *
+   * @param prefix the top prefix
+   * @param interest a request for ControlCommand
+   * @param params the parameters for ControlCommand
+   * @param accept callback of successful validation, take the requester string as a argument
+   * @param reject callback of failure in validation, take the action code as a argument
+   */
+  void
+  authorize(const Name& prefix, const Interest& interest,
+            const ndn::mgmt::ControlParameters* params,
+            ndn::mgmt::AcceptContinuation accept,
+            ndn::mgmt::RejectContinuation reject);
+
+  /**
+   * @brief extract a requester from a ControlCommand request
+   *
+   * This is called after the signature is validated.
+   *
+   * @param interest a request for ControlCommand
+   * @param accept callback of successful validation, take the requester string as a argument
+   */
+  void
+  extractRequester(const Interest& interest,
+                   ndn::mgmt::AcceptContinuation accept);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE: // helpers
+  /**
+   * @brief validate the @p parameters for a given @p command
+   *
+   * @param parameters the original ControlParameters
+   *
+   * @return whether the original ControlParameters can be validated
+   */
   static bool
-  extractParameters(const Name::Component& parameterComponent,
-                    ControlParameters& extractedParameters);
+  validateParameters(const nfd::ControlCommand& command,
+                     const ndn::mgmt::ControlParameters& parameters);
 
-  void
-  setResponse(ControlResponse& response,
-              uint32_t code,
-              const std::string& text);
-  void
-  setResponse(ControlResponse& response,
-              uint32_t code,
-              const std::string& text,
-              const Block& body);
+  /** @brief Handle control command
+   */
+  static void
+  handleCommand(shared_ptr<nfd::ControlCommand> command,
+                const ControlCommandHandler& handler,
+                const Name& prefix, const Interest& interest,
+                const ndn::mgmt::ControlParameters& params,
+                ndn::mgmt::CommandContinuation done);
 
-  void
-  sendResponse(const Name& name,
-               const ControlResponse& response);
+  /**
+   * @brief generate the relative prefix for a handler,
+   *        by appending the verb name to the module name.
+   *
+   * @param verb the verb name
+   *
+   * @return the generated relative prefix
+   */
+  PartialName
+  makeRelPrefix(const std::string& verb);
 
-  void
-  sendResponse(const Name& name,
-               uint32_t code,
-               const std::string& text);
-
-  void
-  sendResponse(const Name& name,
-               uint32_t code,
-               const std::string& text,
-               const Block& body);
-
-  void
-  sendNack(const Name& name);
-
-  virtual bool
-  validateParameters(const ControlCommand& command,
-                     ControlParameters& parameters);
-
-PUBLIC_WITH_TESTS_ELSE_PROTECTED:
-  void
-  addInterestRule(const std::string& regex,
-                  const ndn::IdentityCertificate& certificate);
-
-  void
-  addInterestRule(const std::string& regex,
-                  const Name& keyName,
-                  const ndn::PublicKey& publicKey);
-
-  void
-  validate(const Interest& interest,
-           const ndn::OnInterestValidated& onValidated,
-           const ndn::OnInterestValidationFailed& onValidationFailed);
-
-protected:
-  shared_ptr<InternalFace> m_face;
-  ndn::KeyChain& m_keyChain;
+private:
+  Dispatcher&       m_dispatcher;
+  CommandValidator& m_validator;
+  std::string       m_mgmtModuleName;
 };
 
-inline void
-ManagerBase::setResponse(ControlResponse& response,
-                         uint32_t code,
-                         const std::string& text)
+inline PartialName
+ManagerBase::makeRelPrefix(const std::string& verb)
 {
-  response.setCode(code);
-  response.setText(text);
+  return PartialName(m_mgmtModuleName).append(verb);
 }
 
+template<typename Command>
 inline void
-ManagerBase::setResponse(ControlResponse& response,
-                         uint32_t code,
-                         const std::string& text,
-                         const Block& body)
+ManagerBase::registerCommandHandler(const std::string& verb,
+                                    const ControlCommandHandler& handler)
 {
-  setResponse(response, code, text);
-  response.setBody(body);
-}
+  auto command = make_shared<Command>();
 
-inline void
-ManagerBase::addInterestRule(const std::string& regex,
-                             const ndn::IdentityCertificate& certificate)
-{
-  m_face->getValidator().addInterestRule(regex, certificate);
+  m_dispatcher.addControlCommand<ControlParameters>(
+    makeRelPrefix(verb),
+    bind(&ManagerBase::authorize, this, _1, _2, _3, _4, _5),
+    bind(&ManagerBase::validateParameters, cref(*command), _1),
+    bind(&ManagerBase::handleCommand, command, handler, _1, _2, _3, _4));
 }
 
-inline void
-ManagerBase::addInterestRule(const std::string& regex,
-                             const Name& keyName,
-                             const ndn::PublicKey& publicKey)
-{
-  m_face->getValidator().addInterestRule(regex, keyName, publicKey);
-}
-
-inline void
-ManagerBase::validate(const Interest& interest,
-                      const ndn::OnInterestValidated& onValidated,
-                      const ndn::OnInterestValidationFailed& onValidationFailed)
-{
-  m_face->getValidator().validate(interest, onValidated, onValidationFailed);
-}
-
-
 } // namespace nfd
 
 #endif // NFD_DAEMON_MGMT_MANAGER_BASE_HPP
diff --git a/daemon/mgmt/status-server.cpp b/daemon/mgmt/status-server.cpp
deleted file mode 100644
index c8ce5b4..0000000
--- a/daemon/mgmt/status-server.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014,  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 "status-server.hpp"
-#include "fw/forwarder.hpp"
-#include "version.hpp"
-
-namespace nfd {
-
-const Name StatusServer::DATASET_PREFIX = "ndn:/localhost/nfd/status";
-const time::milliseconds StatusServer::RESPONSE_FRESHNESS = time::milliseconds(5000);
-
-StatusServer::StatusServer(shared_ptr<AppFace> face, Forwarder& forwarder, ndn::KeyChain& keyChain)
-  : m_face(face)
-  , m_forwarder(forwarder)
-  , m_startTimestamp(time::system_clock::now())
-  , m_keyChain(keyChain)
-{
-  m_face->setInterestFilter(DATASET_PREFIX, bind(&StatusServer::onInterest, this, _2));
-}
-
-void
-StatusServer::onInterest(const Interest& interest) const
-{
-  Name name(DATASET_PREFIX);
-  name.appendVersion();
-  name.appendSegment(0);
-
-  shared_ptr<Data> data = make_shared<Data>(name);
-  data->setFreshnessPeriod(RESPONSE_FRESHNESS);
-
-  shared_ptr<ndn::nfd::ForwarderStatus> status = this->collectStatus();
-  data->setContent(status->wireEncode());
-
-  m_keyChain.sign(*data);
-  m_face->put(*data);
-}
-
-shared_ptr<ndn::nfd::ForwarderStatus>
-StatusServer::collectStatus() const
-{
-  shared_ptr<ndn::nfd::ForwarderStatus> status = make_shared<ndn::nfd::ForwarderStatus>();
-
-  status->setNfdVersion(NFD_VERSION_BUILD_STRING);
-  status->setStartTimestamp(m_startTimestamp);
-  status->setCurrentTimestamp(time::system_clock::now());
-
-  status->setNNameTreeEntries(m_forwarder.getNameTree().size());
-  status->setNFibEntries(m_forwarder.getFib().size());
-  status->setNPitEntries(m_forwarder.getPit().size());
-  status->setNMeasurementsEntries(m_forwarder.getMeasurements().size());
-  status->setNCsEntries(m_forwarder.getCs().size());
-
-  m_forwarder.getCounters().copyTo(*status);
-
-  return status;
-}
-
-} // namespace nfd
diff --git a/daemon/mgmt/status-server.hpp b/daemon/mgmt/status-server.hpp
deleted file mode 100644
index 16dbf8e..0000000
--- a/daemon/mgmt/status-server.hpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014,  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_MGMT_STATUS_SERVER_HPP
-#define NFD_DAEMON_MGMT_STATUS_SERVER_HPP
-
-#include "mgmt/app-face.hpp"
-#include <ndn-cxx/management/nfd-forwarder-status.hpp>
-
-namespace nfd {
-
-class Forwarder;
-
-class StatusServer : noncopyable
-{
-public:
-  StatusServer(shared_ptr<AppFace> face, Forwarder& forwarder, ndn::KeyChain& keyChain);
-
-private:
-  void
-  onInterest(const Interest& interest) const;
-
-  shared_ptr<ndn::nfd::ForwarderStatus>
-  collectStatus() const;
-
-private:
-  static const Name DATASET_PREFIX;
-  static const time::milliseconds RESPONSE_FRESHNESS;
-
-  shared_ptr<AppFace> m_face;
-  Forwarder& m_forwarder;
-  time::system_clock::TimePoint m_startTimestamp;
-  ndn::KeyChain& m_keyChain;
-};
-
-} // namespace nfd
-
-#endif // NFD_DAEMON_MGMT_STATUS_SERVER_HPP
diff --git a/daemon/mgmt/strategy-choice-manager.cpp b/daemon/mgmt/strategy-choice-manager.cpp
index 4f2728c..fba7f4c 100644
--- a/daemon/mgmt/strategy-choice-manager.cpp
+++ b/daemon/mgmt/strategy-choice-manager.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  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
+ * 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.
@@ -25,190 +25,75 @@
 
 #include "strategy-choice-manager.hpp"
 #include "table/strategy-choice.hpp"
-#include "core/logger.hpp"
-#include "mgmt/app-face.hpp"
+#include <ndn-cxx/management/nfd-strategy-choice.hpp>
 
 namespace nfd {
 
 NFD_LOG_INIT("StrategyChoiceManager");
 
-const Name StrategyChoiceManager::COMMAND_PREFIX = "/localhost/nfd/strategy-choice";
-
-const size_t StrategyChoiceManager::COMMAND_UNSIGNED_NCOMPS =
-  StrategyChoiceManager::COMMAND_PREFIX.size() +
-  1 + // verb
-  1;  // verb parameters
-
-const size_t StrategyChoiceManager::COMMAND_SIGNED_NCOMPS =
-  StrategyChoiceManager::COMMAND_UNSIGNED_NCOMPS +
-  4; // (timestamp, nonce, signed info tlv, signature tlv)
-
-const Name StrategyChoiceManager::LIST_DATASET_PREFIX("/localhost/nfd/strategy-choice/list");
-
 StrategyChoiceManager::StrategyChoiceManager(StrategyChoice& strategyChoice,
-                                             shared_ptr<InternalFace> face,
-                                             ndn::KeyChain& keyChain)
-  : ManagerBase(face, STRATEGY_CHOICE_PRIVILEGE, keyChain)
+                                             Dispatcher& dispatcher,
+                                             CommandValidator& validator)
+  : ManagerBase(dispatcher, validator, "strategy-choice")
   , m_strategyChoice(strategyChoice)
-  , m_listPublisher(strategyChoice, *m_face, LIST_DATASET_PREFIX, keyChain)
 {
-  face->setInterestFilter("/localhost/nfd/strategy-choice",
-                          bind(&StrategyChoiceManager::onStrategyChoiceRequest, this, _2));
-}
+  registerCommandHandler<ndn::nfd::StrategyChoiceSetCommand>("set",
+    bind(&StrategyChoiceManager::setStrategy, this, _2, _3, _4, _5));
+  registerCommandHandler<ndn::nfd::StrategyChoiceUnsetCommand>("unset",
+    bind(&StrategyChoiceManager::unsetStrategy, this, _2, _3, _4, _5));
 
-StrategyChoiceManager::~StrategyChoiceManager()
-{
-
+  registerStatusDatasetHandler("list",
+    bind(&StrategyChoiceManager::listChoices, this, _1, _2, _3));
 }
 
 void
-StrategyChoiceManager::onStrategyChoiceRequest(const Interest& request)
+StrategyChoiceManager::setStrategy(const Name& topPrefix, const Interest& interest,
+                                   ControlParameters parameters,
+                                   const ndn::mgmt::CommandContinuation& done)
 {
-  const Name& command = request.getName();
-  const size_t commandNComps = command.size();
-
-  if (command == LIST_DATASET_PREFIX)
-    {
-      listStrategies(request);
-      return;
-    }
-  else if (commandNComps <= COMMAND_PREFIX.size())
-    {
-      // command is too short to have a verb
-      NFD_LOG_DEBUG("command result: malformed");
-      sendResponse(command, 400, "Malformed command");
-      return;
-    }
-
-  if (COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
-      commandNComps < COMMAND_SIGNED_NCOMPS)
-    {
-      NFD_LOG_DEBUG("command result: unsigned verb: " << command);
-      sendResponse(command, 401, "Signature required");
-
-      return;
-    }
-  else if (commandNComps < COMMAND_SIGNED_NCOMPS ||
-           !COMMAND_PREFIX.isPrefixOf(command))
-    {
-      NFD_LOG_DEBUG("command result: malformed");
-      sendResponse(command, 400, "Malformed command");
-      return;
-    }
-
-  validate(request,
-           bind(&StrategyChoiceManager::onValidatedStrategyChoiceRequest, this, _1),
-           bind(&ManagerBase::onCommandValidationFailed, this, _1, _2));
-}
-
-void
-StrategyChoiceManager::listStrategies(const Interest& request)
-{
-  m_listPublisher.publish();
-}
-
-void
-StrategyChoiceManager::onValidatedStrategyChoiceRequest(const shared_ptr<const Interest>& request)
-{
-  static const Name::Component VERB_SET("set");
-  static const Name::Component VERB_UNSET("unset");
-
-  const Name& command = request->getName();
-  const Name::Component& parameterComponent = command[COMMAND_PREFIX.size() + 1];
-
-  ControlParameters parameters;
-  if (!extractParameters(parameterComponent, parameters))
-    {
-      sendResponse(command, 400, "Malformed command");
-      return;
-    }
-
-  const Name::Component& verb = command.at(COMMAND_PREFIX.size());
-  ControlResponse response;
-  if (verb == VERB_SET)
-    {
-      setStrategy(parameters, response);
-    }
-  else if (verb == VERB_UNSET)
-    {
-      unsetStrategy(parameters, response);
-    }
-  else
-    {
-      NFD_LOG_DEBUG("command result: unsupported verb: " << verb);
-      setResponse(response, 501, "Unsupported command");
-    }
-
-  sendResponse(command, response);
-}
-
-void
-StrategyChoiceManager::setStrategy(ControlParameters& parameters,
-                                   ControlResponse& response)
-{
-  ndn::nfd::StrategyChoiceSetCommand command;
-
-  if (!validateParameters(command, parameters))
-    {
-      NFD_LOG_DEBUG("strategy-choice result: FAIL reason: malformed");
-      setResponse(response, 400, "Malformed command");
-      return;
-    }
-
   const Name& prefix = parameters.getName();
   const Name& selectedStrategy = parameters.getStrategy();
 
-  if (!m_strategyChoice.hasStrategy(selectedStrategy))
-    {
-      NFD_LOG_DEBUG("strategy-choice result: FAIL reason: unknown-strategy: "
-                    << parameters.getStrategy());
-      setResponse(response, 504, "Unsupported strategy");
-      return;
-    }
+  if (!m_strategyChoice.hasStrategy(selectedStrategy)) {
+    NFD_LOG_DEBUG("strategy-choice result: FAIL reason: unknown-strategy: "
+                  << parameters.getStrategy());
+    return done(ControlResponse(504, "Unsupported strategy"));
+  }
 
-  if (m_strategyChoice.insert(prefix, selectedStrategy))
-    {
-      NFD_LOG_DEBUG("strategy-choice result: SUCCESS");
-      auto currentStrategyChoice = m_strategyChoice.get(prefix);
-      BOOST_ASSERT(currentStrategyChoice.first);
-      parameters.setStrategy(currentStrategyChoice.second);
-      setResponse(response, 200, "Success", parameters.wireEncode());
-    }
-  else
-    {
-      NFD_LOG_DEBUG("strategy-choice result: FAIL reason: not-installed");
-      setResponse(response, 405, "Strategy not installed");
-    }
+  if (m_strategyChoice.insert(prefix, selectedStrategy)) {
+    NFD_LOG_DEBUG("strategy-choice result: SUCCESS");
+    auto currentStrategyChoice = m_strategyChoice.get(prefix);
+    BOOST_ASSERT(currentStrategyChoice.first);
+    parameters.setStrategy(currentStrategyChoice.second);
+    return done(ControlResponse(200, "OK").setBody(parameters.wireEncode()));
+  }
+  else {
+    NFD_LOG_DEBUG("strategy-choice result: FAIL reason: not-installed");
+    return done(ControlResponse(405, "Strategy not installed"));
+  }
 }
 
 void
-StrategyChoiceManager::unsetStrategy(ControlParameters& parameters,
-                                     ControlResponse& response)
+StrategyChoiceManager::unsetStrategy(const Name& topPrefix, const Interest& interest,
+                                     ControlParameters parameters,
+                                     const ndn::mgmt::CommandContinuation& done)
 {
-  ndn::nfd::StrategyChoiceUnsetCommand command;
-
-  if (!validateParameters(command, parameters))
-    {
-      static const Name ROOT_PREFIX;
-      if (parameters.hasName() && parameters.getName() == ROOT_PREFIX)
-        {
-          NFD_LOG_DEBUG("strategy-choice result: FAIL reason: unset-root");
-          setResponse(response, 403, "Cannot unset root prefix strategy");
-        }
-      else
-        {
-          NFD_LOG_DEBUG("strategy-choice result: FAIL reason: malformed");
-          setResponse(response, 400, "Malformed command");
-        }
-      return;
-    }
-
   m_strategyChoice.erase(parameters.getName());
 
   NFD_LOG_DEBUG("strategy-choice result: SUCCESS");
-  setResponse(response, 200, "Success", parameters.wireEncode());
+  done(ControlResponse(200, "OK").setBody(parameters.wireEncode()));
 }
 
+void
+StrategyChoiceManager::listChoices(const Name& topPrefix, const Interest& interest,
+                                   ndn::mgmt::StatusDatasetContext& context)
+{
+  for (auto&& i : m_strategyChoice) {
+    ndn::nfd::StrategyChoice entry;
+    entry.setName(i.getPrefix()).setStrategy(i.getStrategyName());
+    context.append(entry.wireEncode());
+  }
+  context.end();
+}
 
-
-} // namespace nfd
+} // namespace
diff --git a/daemon/mgmt/strategy-choice-manager.hpp b/daemon/mgmt/strategy-choice-manager.hpp
index c2ccd53..74c54db 100644
--- a/daemon/mgmt/strategy-choice-manager.hpp
+++ b/daemon/mgmt/strategy-choice-manager.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  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
+ * 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.
@@ -26,64 +26,40 @@
 #ifndef NFD_DAEMON_MGMT_STRATEGY_CHOICE_MANAGER_HPP
 #define NFD_DAEMON_MGMT_STRATEGY_CHOICE_MANAGER_HPP
 
-#include "mgmt/manager-base.hpp"
-#include "mgmt/strategy-choice-publisher.hpp"
-
-#include <ndn-cxx/management/nfd-control-parameters.hpp>
+#include "manager-base.hpp"
 
 namespace nfd {
 
-const std::string STRATEGY_CHOICE_PRIVILEGE = "strategy-choice";
-
 class StrategyChoice;
 
+/**
+ * @brief implement the Strategy Choice Management of NFD Management Protocol.
+ * @sa http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice
+ */
 class StrategyChoiceManager : public ManagerBase
 {
 public:
   StrategyChoiceManager(StrategyChoice& strategyChoice,
-                        shared_ptr<InternalFace> face,
-                        ndn::KeyChain& keyChain);
-
-  virtual
-  ~StrategyChoiceManager();
-
-  void
-  onStrategyChoiceRequest(const Interest& request);
-
-PUBLIC_WITH_TESTS_ELSE_PRIVATE:
-
-  void
-  listStrategies(const Interest& request);
-
-  void
-  onValidatedStrategyChoiceRequest(const shared_ptr<const Interest>& request);
-
-  void
-  setStrategy(ControlParameters& parameters,
-              ControlResponse& response);
-
-  void
-  unsetStrategy(ControlParameters& parameters,
-                ControlResponse& response);
+                        Dispatcher& dispatcher,
+                        CommandValidator& validator);
 
 private:
+  void
+  setStrategy(const Name& topPrefix, const Interest& interest,
+              ControlParameters parameters,
+              const ndn::mgmt::CommandContinuation& done);
 
+  void
+  unsetStrategy(const Name& topPrefix, const Interest& interest,
+                ControlParameters parameters,
+                const ndn::mgmt::CommandContinuation& done);
+
+  void
+  listChoices(const Name& topPrefix, const Interest& interest,
+              ndn::mgmt::StatusDatasetContext& context);
+
+private:
   StrategyChoice& m_strategyChoice;
-
-  StrategyChoicePublisher m_listPublisher;
-
-  static const Name COMMAND_PREFIX; // /localhost/nfd/strategy-choice
-
-  // number of components in an invalid, but not malformed, unsigned command.
-  // (/localhost/nfd/strategy-choice + verb + parameters) = 5
-  static const size_t COMMAND_UNSIGNED_NCOMPS;
-
-  // number of components in a valid signed Interest.
-  // (see UNSIGNED_NCOMPS), 9 with signed Interest support.
-  static const size_t COMMAND_SIGNED_NCOMPS;
-
-  static const Name LIST_DATASET_PREFIX; // /localhost/nfd/strategy-choice/list
-
 };
 
 } // namespace nfd
diff --git a/daemon/mgmt/strategy-choice-publisher.cpp b/daemon/mgmt/strategy-choice-publisher.cpp
deleted file mode 100644
index 8db7124..0000000
--- a/daemon/mgmt/strategy-choice-publisher.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014  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 "strategy-choice-publisher.hpp"
-#include "table/strategy-choice.hpp"
-
-#include <ndn-cxx/management/nfd-strategy-choice.hpp>
-
-namespace nfd {
-
-StrategyChoicePublisher::StrategyChoicePublisher(const StrategyChoice& strategyChoice,
-                                                 AppFace& face,
-                                                 const Name& prefix,
-                                                 ndn::KeyChain& keyChain)
-  : SegmentPublisher(face, prefix, keyChain)
-  , m_strategyChoice(strategyChoice)
-{
-}
-
-StrategyChoicePublisher::~StrategyChoicePublisher()
-{
-}
-
-size_t
-StrategyChoicePublisher::generate(ndn::EncodingBuffer& outBuffer)
-{
-  size_t totalLength = 0;
-
-  for (StrategyChoice::const_iterator i = m_strategyChoice.begin();
-       i != m_strategyChoice.end();
-       ++i)
-    {
-      ndn::nfd::StrategyChoice entry;
-
-      entry.setName(i->getPrefix())
-           .setStrategy(i->getStrategyName());
-
-      totalLength += entry.wireEncode(outBuffer);
-    }
-
-  return totalLength;
-}
-
-} // namespace nfd
diff --git a/daemon/mgmt/strategy-choice-publisher.hpp b/daemon/mgmt/strategy-choice-publisher.hpp
deleted file mode 100644
index 8322b63..0000000
--- a/daemon/mgmt/strategy-choice-publisher.hpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014  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_MGMT_STRATEGY_CHOICE_PUBLISHER_HPP
-#define NFD_DAEMON_MGMT_STRATEGY_CHOICE_PUBLISHER_HPP
-
-#include "core/segment-publisher.hpp"
-#include "mgmt/app-face.hpp"
-
-namespace nfd {
-
-class StrategyChoice;
-
-class StrategyChoicePublisher : public SegmentPublisher<AppFace>
-{
-public:
-  StrategyChoicePublisher(const StrategyChoice& strategyChoice,
-                          AppFace& face,
-                          const Name& prefix,
-                          ndn::KeyChain& keyChain);
-
-  virtual
-  ~StrategyChoicePublisher();
-
-protected:
-
-  virtual size_t
-  generate(ndn::EncodingBuffer& outBuffer);
-
-private:
-
-  const StrategyChoice& m_strategyChoice;
-
-};
-
-} // namespace nfd
-
-#endif // NFD_DAEMON_MGMT_STRATEGY_CHOICE_PUBLISHER_HPP
diff --git a/daemon/nfd.cpp b/daemon/nfd.cpp
index 262bbf5..7d68de3 100644
--- a/daemon/nfd.cpp
+++ b/daemon/nfd.cpp
@@ -31,13 +31,17 @@
 #include "core/config-file.hpp"
 #include "fw/forwarder.hpp"
 #include "face/null-face.hpp"
-#include "mgmt/internal-face.hpp"
+#include "face/internal-face.hpp"
+#include "face/internal-client-face.hpp"
 #include "mgmt/fib-manager.hpp"
 #include "mgmt/face-manager.hpp"
 #include "mgmt/strategy-choice-manager.hpp"
-#include "mgmt/status-server.hpp"
+#include "mgmt/forwarder-status-manager.hpp"
 #include "mgmt/general-config-section.hpp"
 #include "mgmt/tables-config-section.hpp"
+#include "mgmt/command-validator.hpp"
+
+#include <ndn-cxx/mgmt/dispatcher.hpp>
 
 namespace nfd {
 
@@ -128,17 +132,26 @@
 Nfd::initializeManagement()
 {
   m_internalFace = make_shared<InternalFace>();
+  m_forwarder->getFaceTable().addReserved(m_internalFace, FACEID_INTERNAL_FACE);
+  m_internalClientFace = makeInternalClientFace(m_internalFace, m_keyChain);
+  m_dispatcher.reset(new ndn::mgmt::Dispatcher(*m_internalClientFace, m_keyChain));
+
+  m_validator.reset(new CommandValidator());
 
   m_fibManager.reset(new FibManager(m_forwarder->getFib(),
                                     bind(&Forwarder::getFace, m_forwarder.get(), _1),
-                                    m_internalFace, m_keyChain));
+                                    *m_dispatcher,
+                                    *m_validator));
 
-  m_faceManager.reset(new FaceManager(m_forwarder->getFaceTable(), m_internalFace, m_keyChain));
+  m_faceManager.reset(new FaceManager(m_forwarder->getFaceTable(),
+                                      *m_dispatcher,
+                                      *m_validator));
 
   m_strategyChoiceManager.reset(new StrategyChoiceManager(m_forwarder->getStrategyChoice(),
-                                                          m_internalFace, m_keyChain));
+                                                          *m_dispatcher,
+                                                          *m_validator));
 
-  m_statusServer.reset(new StatusServer(m_internalFace, *m_forwarder, m_keyChain));
+  m_forwarderStatusManager.reset(new ForwarderStatusManager(*m_forwarder, *m_dispatcher));
 
   ConfigFile config(&ignoreRibAndLogSections);
   general::setConfigFile(config);
@@ -151,9 +164,7 @@
                                    m_forwarder->getNetworkRegionTable());
   tablesConfig.setConfigFile(config);
 
-  m_internalFace->getValidator().setConfigFile(config);
-
-  m_forwarder->getFaceTable().addReserved(m_internalFace, FACEID_INTERNAL_FACE);
+  m_validator->setConfigFile(config);
 
   m_faceManager->setConfigFile(config);
 
@@ -170,8 +181,10 @@
   tablesConfig.ensureTablesAreConfigured();
 
   // add FIB entry for NFD Management Protocol
-  shared_ptr<fib::Entry> entry = m_forwarder->getFib().insert("/localhost/nfd").first;
+  Name topPrefix("/localhost/nfd");
+  auto entry = m_forwarder->getFib().insert(topPrefix).first;
   entry->addNextHop(m_internalFace, 0);
+  m_dispatcher->addTopPrefix(topPrefix, false);
 }
 
 void
@@ -195,7 +208,7 @@
 
   tablesConfig.setConfigFile(config);
 
-  m_internalFace->getValidator().setConfigFile(config);
+  m_validator->setConfigFile(config);
   m_faceManager->setConfigFile(config);
 
   if (!m_configFile.empty()) {
diff --git a/daemon/nfd.hpp b/daemon/nfd.hpp
index 4710554..34111b3 100644
--- a/daemon/nfd.hpp
+++ b/daemon/nfd.hpp
@@ -33,6 +33,14 @@
 #include <ndn-cxx/security/key-chain.hpp>
 #include <ndn-cxx/util/network-monitor.hpp>
 
+namespace ndn {
+namespace mgmt {
+
+class Dispatcher;
+
+}
+}
+
 namespace nfd {
 
 class Forwarder;
@@ -40,7 +48,9 @@
 class FibManager;
 class FaceManager;
 class StrategyChoiceManager;
-class StatusServer;
+class ForwarderStatusManager;
+class InternalClientFace;
+class CommandValidator;
 
 /**
  * \brief Class representing NFD instance
@@ -97,16 +107,19 @@
 
   unique_ptr<Forwarder> m_forwarder;
 
-  shared_ptr<InternalFace>          m_internalFace;
-  unique_ptr<FibManager>            m_fibManager;
-  unique_ptr<FaceManager>           m_faceManager;
-  unique_ptr<StrategyChoiceManager> m_strategyChoiceManager;
-  unique_ptr<StatusServer>          m_statusServer;
+  ndn::KeyChain&                     m_keyChain;
+  shared_ptr<InternalFace>           m_internalFace;
+  shared_ptr<InternalClientFace>     m_internalClientFace;
+  unique_ptr<CommandValidator>       m_validator;
 
-  ndn::KeyChain&                    m_keyChain;
+  unique_ptr<ndn::mgmt::Dispatcher>  m_dispatcher;
+  unique_ptr<FibManager>             m_fibManager;
+  unique_ptr<FaceManager>            m_faceManager;
+  unique_ptr<StrategyChoiceManager>  m_strategyChoiceManager;
+  unique_ptr<ForwarderStatusManager> m_forwarderStatusManager;
 
-  ndn::util::NetworkMonitor         m_networkMonitor;
-  scheduler::ScopedEventId          m_reloadConfigEvent;
+  ndn::util::NetworkMonitor          m_networkMonitor;
+  scheduler::ScopedEventId           m_reloadConfigEvent;
 };
 
 } // namespace nfd
diff --git a/tests/daemon/face/internal-client-face.t.cpp b/tests/daemon/face/internal-client-face.t.cpp
new file mode 100644
index 0000000..0b2295c
--- /dev/null
+++ b/tests/daemon/face/internal-client-face.t.cpp
@@ -0,0 +1,134 @@
+/* -*- 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/internal-client-face.hpp"
+#include "face/internal-face.hpp"
+#include "tests/test-common.hpp"
+#include "tests/identity-management-fixture.hpp"
+
+namespace nfd {
+namespace tests {
+
+class InternalClientFaceFixture : public UnitTestTimeFixture
+                                , public IdentityManagementFixture
+{
+public:
+  InternalClientFaceFixture()
+    : m_internalFace(make_shared<InternalFace>())
+    , m_face(makeInternalClientFace(m_internalFace, m_keyChain))
+  {
+  }
+
+protected:
+  shared_ptr<InternalFace> m_internalFace;
+  shared_ptr<InternalClientFace> m_face;
+};
+
+BOOST_FIXTURE_TEST_SUITE(FaceInternalClientFace, InternalClientFaceFixture)
+
+BOOST_AUTO_TEST_CASE(ExpressInterest)
+{
+  bool didReceiveDataBack = false;
+  bool didTimeoutCallbackFire = false;
+  Data receivedData;
+
+  auto expressInterest = [&] (shared_ptr<Interest> interest) {
+    didReceiveDataBack = false;
+    didTimeoutCallbackFire = false;
+    m_face->expressInterest(interest->setInterestLifetime(time::milliseconds(100)),
+                            [&] (const Interest& interest, Data& data) {
+                              didReceiveDataBack = true;
+                              receivedData = data;
+                            },
+                            bind([&] { didTimeoutCallbackFire = true; }));
+    advanceClocks(time::milliseconds(1));
+  };
+
+  expressInterest(makeInterest("/test/timeout"));
+  advanceClocks(time::milliseconds(1), 200); // wait for time out
+  BOOST_CHECK(didTimeoutCallbackFire);
+
+  auto dataToSend = makeData("/test/data")->setContent(Block("\x81\x01\0x01", 3));
+  expressInterest(makeInterest("/test/data"));
+  m_internalFace->sendData(dataToSend); // send data to satisfy the expressed interest
+  advanceClocks(time::milliseconds(1));
+  BOOST_CHECK(didReceiveDataBack);
+  BOOST_CHECK(receivedData.wireEncode() == dataToSend.wireEncode());
+}
+
+BOOST_AUTO_TEST_CASE(InterestFilter)
+{
+  bool didOnInterestCallbackFire = false;
+  Block receivedBlock, expectedBlock;
+
+  auto testSendInterest = [&] (shared_ptr<Interest> interest) {
+    didOnInterestCallbackFire = false;
+    expectedBlock = interest->wireEncode();
+    m_internalFace->sendInterest(*interest);
+  };
+
+  testSendInterest(makeInterest("/test/filter")); // no filter is set now
+  BOOST_CHECK(!didOnInterestCallbackFire);
+
+  // set filter
+  auto filter = m_face->setInterestFilter("/test/filter",
+                                          bind([&](const Interest& interset) {
+                                              didOnInterestCallbackFire = true;
+                                              receivedBlock = interset.wireEncode();
+                                            }, _2));
+  advanceClocks(time::milliseconds(1));
+
+  testSendInterest(makeInterest("/test/filter"));
+  BOOST_CHECK(didOnInterestCallbackFire);
+  BOOST_CHECK(receivedBlock == expectedBlock);
+
+  // unset filter
+  didOnInterestCallbackFire = false;
+  m_face->unsetInterestFilter(filter);
+  advanceClocks(time::milliseconds(1));
+  testSendInterest(makeInterest("/test/filter"));
+  BOOST_CHECK(!didOnInterestCallbackFire);
+}
+
+BOOST_AUTO_TEST_CASE(PutData)
+{
+  bool didInternalFaceReceiveData = false;
+  Data receivedData;
+  m_internalFace->onReceiveData.connect([&] (const Data& data) {
+      didInternalFaceReceiveData = true;
+      receivedData = data;
+    });
+
+  auto dataToPut = makeData("/test/put/data");
+  m_face->put(*dataToPut);
+  advanceClocks(time::milliseconds(1));
+  BOOST_CHECK(didInternalFaceReceiveData);
+  BOOST_CHECK(receivedData.wireEncode() == dataToPut->wireEncode());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/face/internal-face.t.cpp b/tests/daemon/face/internal-face.t.cpp
new file mode 100644
index 0000000..44f5e7b
--- /dev/null
+++ b/tests/daemon/face/internal-face.t.cpp
@@ -0,0 +1,143 @@
+/* -*- 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/internal-face.hpp"
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+class InternalFaceFixture : protected BaseFixture
+{
+public:
+  InternalFaceFixture()
+  {
+    m_face.onReceiveInterest.connect([this] (const Interest& interest) {
+        inInterests.push_back(interest);
+      });
+    m_face.onSendInterest.connect([this] (const Interest& interest) {
+        outInterests.push_back(interest);
+      });
+    m_face.onReceiveData.connect([this] (const Data& data) {
+        inData.push_back(data);
+      });
+    m_face.onSendData.connect([this] (const Data& data) {
+        outData.push_back(data);
+      });
+  }
+
+protected:
+  InternalFace m_face;
+  std::vector<Interest> inInterests;
+  std::vector<Interest> outInterests;
+  std::vector<Data> inData;
+  std::vector<Data> outData;
+};
+
+BOOST_FIXTURE_TEST_SUITE(FaceInternalFace, InternalFaceFixture)
+
+BOOST_AUTO_TEST_CASE(SendInterest)
+{
+  BOOST_CHECK(outInterests.empty());
+
+  auto interest = makeInterest("/test/send/interest");
+  Block expectedBlock = interest->wireEncode(); // assign a value to nonce
+  m_face.sendInterest(*interest);
+
+  BOOST_CHECK_EQUAL(outInterests.size(), 1);
+  BOOST_CHECK(outInterests[0].wireEncode() == expectedBlock);
+}
+
+BOOST_AUTO_TEST_CASE(SendData)
+{
+  BOOST_CHECK(outData.empty());
+
+  auto data = makeData("/test/send/data");
+  m_face.sendData(*data);
+
+  BOOST_CHECK_EQUAL(outData.size(), 1);
+  BOOST_CHECK(outData[0].wireEncode() == data->wireEncode());
+}
+
+BOOST_AUTO_TEST_CASE(ReceiveInterest)
+{
+  BOOST_CHECK(inInterests.empty());
+
+  auto interest = makeInterest("/test/receive/interest");
+  Block expectedBlock = interest->wireEncode(); // assign a value to nonce
+  m_face.receiveInterest(*interest);
+
+  BOOST_CHECK_EQUAL(inInterests.size(), 1);
+  BOOST_CHECK(inInterests[0].wireEncode() == expectedBlock);
+}
+
+BOOST_AUTO_TEST_CASE(ReceiveData)
+{
+  BOOST_CHECK(inData.empty());
+
+  auto data = makeData("/test/send/data");
+  m_face.receiveData(*data);
+
+  BOOST_CHECK_EQUAL(inData.size(), 1);
+  BOOST_CHECK(inData[0].wireEncode() == data->wireEncode());
+}
+
+BOOST_AUTO_TEST_CASE(ReceiveBlock)
+{
+  BOOST_CHECK(inInterests.empty());
+  BOOST_CHECK(inData.empty());
+
+  Block interestBlock = makeInterest("test/receive/interest")->wireEncode();
+  m_face.receive(interestBlock);
+  BOOST_CHECK_EQUAL(inInterests.size(), 1);
+  BOOST_CHECK(inInterests[0].wireEncode() == interestBlock);
+
+  Block dataBlock = makeData("test/receive/data")->wireEncode();
+  m_face.receive(dataBlock);
+  BOOST_CHECK_EQUAL(inData.size(), 1);
+  BOOST_CHECK(inData[0].wireEncode() == dataBlock);
+}
+
+BOOST_AUTO_TEST_CASE(ExtractPacketFromBlock)
+{
+  {
+    Block block = makeInterest("/test/interest")->wireEncode();
+    const Block& payload = ndn::nfd::LocalControlHeader::getPayload(block);
+    auto interest = m_face.extractPacketFromBlock<Interest>(block, payload);
+    BOOST_CHECK(interest->wireEncode() == block);
+  }
+
+  {
+    Block block = makeData("/test/data")->wireEncode();
+    const Block& payload = ndn::nfd::LocalControlHeader::getPayload(block);
+    auto data = m_face.extractPacketFromBlock<Data>(block, payload);
+    BOOST_CHECK(data->wireEncode() == block);
+  }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/channel-status-common.hpp b/tests/daemon/mgmt/channel-status-common.hpp
deleted file mode 100644
index e04c0be..0000000
--- a/tests/daemon/mgmt/channel-status-common.hpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014,  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_TESTS_NFD_MGMT_CHANNEL_STATUS_COMMON_HPP
-#define NFD_TESTS_NFD_MGMT_CHANNEL_STATUS_COMMON_HPP
-
-#include "face/protocol-factory.hpp"
-#include "face/channel.hpp"
-
-#include "tests/test-common.hpp"
-
-#include <ndn-cxx/management/nfd-channel-status.hpp>
-
-
-
-namespace nfd {
-namespace tests {
-
-class DummyChannel : public Channel
-{
-public:
-
-  DummyChannel(const std::string& uri)
-  {
-    setUri(FaceUri(uri));
-  }
-
-  virtual
-  ~DummyChannel()
-  {
-  }
-};
-
-class DummyProtocolFactory : public ProtocolFactory
-{
-public:
-
-  DummyProtocolFactory()
-  {
-
-  }
-
-  virtual void
-  createFace(const FaceUri& uri,
-             ndn::nfd::FacePersistency persistency,
-             const FaceCreatedCallback& onCreated,
-             const FaceConnectFailedCallback& onConnectFailed)
-  {
-  }
-
-  virtual void
-  addChannel(const std::string& channelUri)
-  {
-    m_channels.push_back(make_shared<DummyChannel>(channelUri));
-  }
-
-  virtual std::list<shared_ptr<const Channel> >
-  getChannels() const
-  {
-    return m_channels;
-  }
-
-  virtual size_t
-  getNChannels() const
-  {
-    return m_channels.size();
-  }
-
-private:
-  std::list<shared_ptr<const Channel> > m_channels;
-};
-
-} // namespace tests
-} // namespace nfd
-
-#endif // NFD_TESTS_NFD_MGMT_CHANNEL_STATUS_COMMON_HPP
diff --git a/tests/daemon/mgmt/channel-status-publisher.t.cpp b/tests/daemon/mgmt/channel-status-publisher.t.cpp
deleted file mode 100644
index 6027137..0000000
--- a/tests/daemon/mgmt/channel-status-publisher.t.cpp
+++ /dev/null
@@ -1,180 +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 "mgmt/channel-status-publisher.hpp"
-#include "mgmt/internal-face.hpp"
-
-#include "channel-status-common.hpp"
-
-namespace nfd {
-namespace tests {
-
-class ChannelStatusPublisherFixture : BaseFixture
-{
-public:
-  ChannelStatusPublisherFixture()
-    : m_face(make_shared<InternalFace>())
-    , m_publisher(m_factories, *m_face, "/localhost/nfd/faces/channels", m_keyChain)
-    , m_finished(false)
-  {
-  }
-
-  virtual
-  ~ChannelStatusPublisherFixture()
-  {
-  }
-
-  // virtual shared_ptr<DummyProtocolFactory>
-  // addProtocolFactory(const std::string& protocol)
-  // {
-  //   shared_ptr<DummyProtocolFactory> factory(make_shared<DummyProtocolFactory>());
-  //   m_factories[protocol] = factory;
-
-  //   return factory;
-  // }
-
-  void
-  validatePublish(const Data& data)
-  {
-    Block payload = data.getContent();
-
-    m_buffer.appendByteArray(payload.value(), payload.value_size());
-
-    BOOST_CHECK_NO_THROW(data.getName()[-1].toSegment());
-    if (data.getFinalBlockId() != data.getName()[-1])
-      {
-        return;
-      }
-
-    // wrap the Channel Status entries in a single Content TLV for easy parsing
-    m_buffer.prependVarNumber(m_buffer.size());
-    m_buffer.prependVarNumber(tlv::Content);
-
-    ndn::Block parser(m_buffer.buf(), m_buffer.size());
-    parser.parse();
-
-    BOOST_REQUIRE_EQUAL(parser.elements_size(), m_expectedEntries.size());
-
-    for (Block::element_const_iterator i = parser.elements_begin();
-         i != parser.elements_end();
-         ++i)
-      {
-        if (i->type() != ndn::tlv::nfd::ChannelStatus)
-          {
-            BOOST_FAIL("expected ChannelStatus, got type #" << i->type());
-          }
-
-        ndn::nfd::ChannelStatus entry(*i);
-
-        std::map<std::string, ndn::nfd::ChannelStatus>::const_iterator expectedEntryPos =
-          m_expectedEntries.find(entry.getLocalUri());
-
-        BOOST_REQUIRE(expectedEntryPos != m_expectedEntries.end());
-        const ndn::nfd::ChannelStatus& expectedEntry = expectedEntryPos->second;
-
-        BOOST_CHECK_EQUAL(entry.getLocalUri(), expectedEntry.getLocalUri());
-
-        m_matchedEntries.insert(entry.getLocalUri());
-      }
-
-    BOOST_CHECK_EQUAL(m_matchedEntries.size(), m_expectedEntries.size());
-
-    m_finished = true;
-  }
-
-protected:
-  ChannelStatusPublisher::FactoryMap m_factories;
-  shared_ptr<InternalFace> m_face;
-  ChannelStatusPublisher m_publisher;
-
-  ndn::EncodingBuffer m_buffer;
-
-  std::map<std::string, ndn::nfd::ChannelStatus> m_expectedEntries;
-  std::set<std::string> m_matchedEntries;
-
-  bool m_finished;
-
-  ndn::KeyChain m_keyChain;
-};
-
-BOOST_FIXTURE_TEST_SUITE(MgmtChannelStatusPublisher, ChannelStatusPublisherFixture)
-
-BOOST_AUTO_TEST_CASE(Publish)
-{
-  const std::string protocol = "dummy";
-
-  shared_ptr<DummyProtocolFactory> factory(make_shared<DummyProtocolFactory>());
-  m_factories[protocol] = factory;
-
-  for (int i = 0; i < 10; ++i)
-    {
-      const std::string uri = protocol + "://path" + boost::lexical_cast<std::string>(i);
-      factory->addChannel(uri);
-
-      ndn::nfd::ChannelStatus expectedEntry;
-      expectedEntry.setLocalUri(DummyChannel(uri).getUri().toString());
-
-      m_expectedEntries[expectedEntry.getLocalUri()] = expectedEntry;
-    }
-
-  m_face->onReceiveData.connect(bind(&ChannelStatusPublisherFixture::validatePublish, this, _1));
-
-  m_publisher.publish();
-  BOOST_REQUIRE(m_finished);
-}
-
-BOOST_AUTO_TEST_CASE(DuplicateFactories)
-{
-  const std::string protocol1 = "dummy1";
-  const std::string protocol2 = "dummy2";
-
-  shared_ptr<DummyProtocolFactory> factory(make_shared<DummyProtocolFactory>());
-  m_factories[protocol1] = factory;
-  m_factories[protocol2] = factory;
-
-  for (int i = 0; i < 10; ++i)
-    {
-      ndn::nfd::ChannelStatus expectedEntry;
-      const std::string uri = protocol1 + "://path" + boost::lexical_cast<std::string>(i);
-
-      factory->addChannel(uri);
-
-      expectedEntry.setLocalUri(DummyChannel(uri).getUri().toString());
-      m_expectedEntries[expectedEntry.getLocalUri()] = expectedEntry;
-    }
-
-  m_face->onReceiveData.connect(bind(&ChannelStatusPublisherFixture::validatePublish, this, _1));
-
-  m_publisher.publish();
-  BOOST_REQUIRE(m_finished);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-} // namespace tests
-
-
-
-} // namespace nfd
diff --git a/tests/daemon/mgmt/face-manager/create-face.t.cpp b/tests/daemon/mgmt/face-manager-create-face.t.cpp
similarity index 85%
rename from tests/daemon/mgmt/face-manager/create-face.t.cpp
rename to tests/daemon/mgmt/face-manager-create-face.t.cpp
index 3a70619..ffee5aa 100644
--- a/tests/daemon/mgmt/face-manager/create-face.t.cpp
+++ b/tests/daemon/mgmt/face-manager-create-face.t.cpp
@@ -24,27 +24,34 @@
  */
 
 #include "mgmt/face-manager.hpp"
-#include "mgmt/internal-face.hpp"
 #include "fw/forwarder.hpp"
 
 #include "tests/test-common.hpp"
 
+#include <ndn-cxx/mgmt/dispatcher.hpp>
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
 #include <boost/property_tree/info_parser.hpp>
 
 namespace nfd {
 namespace tests {
 
-BOOST_FIXTURE_TEST_SUITE(MgmtFaceManager, BaseFixture)
 
-BOOST_AUTO_TEST_SUITE(CreateFace)
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(TestFaceManager)
+
+BOOST_FIXTURE_TEST_SUITE(CreateFace, BaseFixture)
 
 class FaceManagerNode
 {
 public:
   FaceManagerNode(ndn::KeyChain& keyChain, const std::string& port = "6363")
-    : face(make_shared<InternalFace>())
-    , manager(forwarder.getFaceTable(), face, keyChain)
+    : face(ndn::util::makeDummyClientFace(getGlobalIoService(), {true, true}))
+    , dispatcher(*face, keyChain, ndn::security::SigningInfo())
+    , manager(forwarder.getFaceTable(), dispatcher, validator)
   {
+    dispatcher.addTopPrefix("/localhost/nfd");
+
     std::string basicConfig =
       "face_system\n"
       "{\n"
@@ -75,7 +82,7 @@
 
     ConfigFile config;
     manager.setConfigFile(config);
-    face->getValidator().setConfigFile(config);
+    validator.setConfigFile(config);
     config.parse(configSection, false, "dummy-config");
   }
 
@@ -92,7 +99,9 @@
 
 public:
   Forwarder forwarder;
-  shared_ptr<InternalFace> face;
+  shared_ptr<ndn::util::DummyClientFace> face;
+  ndn::mgmt::Dispatcher dispatcher;
+  CommandValidator validator;
   FaceManager manager;
 };
 
@@ -103,6 +112,7 @@
     : node1(keyChain, "16363")
     , node2(keyChain, "26363")
   {
+    advanceClocks(time::milliseconds(1), 100);
   }
 
   ~FaceManagerFixture()
@@ -166,6 +176,18 @@
   }
 };
 
+class UdpFaceCannotConnect // face that will cause afterCreateFaceFailure to be invoked
+{
+public:
+  ControlParameters
+  getParameters()
+  {
+    return ControlParameters()
+      .setUri("udp4://0.0.0.0:16363"); // cannot connect to self
+  }
+};
+
+
 class UdpFacePersistent
 {
 public:
@@ -198,7 +220,7 @@
   {
     return ControlResponse()
       .setCode(200)
-      .setText("Success");
+      .setText("OK");
   }
 };
 
@@ -223,7 +245,8 @@
                     mpl::pair<TcpFacePermanent, Failure<500>>,
                     mpl::pair<UdpFaceOnDemand, Failure<500>>,
                     mpl::pair<UdpFacePersistent, Success>,
-                    mpl::pair<UdpFacePermanent, Success>> Faces;
+                    mpl::pair<UdpFacePermanent, Success>,
+                    mpl::pair<UdpFaceCannotConnect, Failure<408>>> Faces;
 
 BOOST_FIXTURE_TEST_CASE_TEMPLATE(NewFace, T, Faces, FaceManagerFixture)
 {
@@ -238,7 +261,8 @@
   this->keyChain.sign(*command);
 
   bool hasCallbackFired = false;
-  this->node1.face->onReceiveData.connect([this, command, &hasCallbackFired] (const Data& response) {
+  this->node1.face->onSendData.connect([this, command, &hasCallbackFired] (const Data& response) {
+      // std::cout << response << std::endl;
       if (!command->getName().isPrefixOf(response.getName())) {
         return;
       }
@@ -258,8 +282,8 @@
       hasCallbackFired = true;
     });
 
-  this->node1.face->sendInterest(*command);
-  this->advanceClocks(time::milliseconds(1), 10);
+  this->node1.face->receive(*command);
+  this->advanceClocks(time::milliseconds(1), 100);
 
   BOOST_CHECK(hasCallbackFired);
 }
@@ -287,7 +311,7 @@
     shared_ptr<Interest> command(make_shared<Interest>(commandName));
     this->keyChain.sign(*command);
 
-    this->node1.face->sendInterest(*command);
+    this->node1.face->receive(*command);
     this->advanceClocks(time::milliseconds(1), 10);
   }
 
@@ -303,7 +327,7 @@
     this->keyChain.sign(*command);
 
     bool hasCallbackFired = false;
-    this->node1.face->onReceiveData.connect([this, command, &hasCallbackFired] (const Data& response) {
+    this->node1.face->onSendData.connect([this, command, &hasCallbackFired] (const Data& response) {
         if (!command->getName().isPrefixOf(response.getName())) {
           return;
         }
@@ -318,7 +342,7 @@
         hasCallbackFired = true;
       });
 
-    this->node1.face->sendInterest(*command);
+    this->node1.face->receive(*command);
     this->advanceClocks(time::milliseconds(1), 10);
 
     BOOST_CHECK(hasCallbackFired);
@@ -326,18 +350,6 @@
 }
 
 
-// class TcpFace
-// {
-// public:
-//   ControlParameters
-//   getParameters()
-//   {
-//     return ControlParameters()
-//       .setUri("tcp4://127.0.0.1:16363")
-//       .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
-//   }
-// };
-
 class UdpFace
 {
 public:
@@ -376,13 +388,13 @@
     this->keyChain.sign(*command);
 
     ndn::util::signal::ScopedConnection connection =
-      this->node2.face->onReceiveData.connect([this, command] (const Data& response) {
+      this->node2.face->onSendData.connect([this, command] (const Data& response) {
           if (!command->getName().isPrefixOf(response.getName())) {
             return;
           }
 
           ControlResponse controlResponse(response.getContent().blockFromValue());
-          BOOST_REQUIRE_EQUAL(controlResponse.getText(), "Success");
+          BOOST_REQUIRE_EQUAL(controlResponse.getText(), "OK");
           BOOST_REQUIRE_EQUAL(controlResponse.getCode(), 200);
           uint64_t faceId = ControlParameters(controlResponse.getBody()).getFaceId();
           auto face = this->node2.forwarder.getFace(static_cast<FaceId>(faceId));
@@ -392,7 +404,7 @@
           face->sendInterest(*dummyInterest);
         });
 
-    this->node2.face->sendInterest(*command);
+    this->node2.face->receive(*command);
     this->advanceClocks(time::milliseconds(1), 10);
   }
 
@@ -419,7 +431,7 @@
     this->keyChain.sign(*command);
 
     bool hasCallbackFired = false;
-    this->node1.face->onReceiveData.connect([this, command, &hasCallbackFired] (const Data& response) {
+    this->node1.face->onSendData.connect([this, command, &hasCallbackFired] (const Data& response) {
         if (!command->getName().isPrefixOf(response.getName())) {
           return;
         }
@@ -434,7 +446,7 @@
         hasCallbackFired = true;
       });
 
-    this->node1.face->sendInterest(*command);
+    this->node1.face->receive(*command);
     this->advanceClocks(time::milliseconds(1), 10);
 
     BOOST_CHECK(hasCallbackFired);
@@ -442,8 +454,8 @@
 }
 
 BOOST_AUTO_TEST_SUITE_END() // CreateFace
-
-BOOST_AUTO_TEST_SUITE_END() // MgmtFaceManager
+BOOST_AUTO_TEST_SUITE_END() // TestFaceManager
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
 
 } // tests
 } // nfd
diff --git a/tests/daemon/mgmt/face-manager-process-config.t.cpp b/tests/daemon/mgmt/face-manager-process-config.t.cpp
new file mode 100644
index 0000000..9463ac4
--- /dev/null
+++ b/tests/daemon/mgmt/face-manager-process-config.t.cpp
@@ -0,0 +1,438 @@
+/* -*- 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 "mgmt/face-manager.hpp"
+#include "face/udp-factory.hpp"
+
+#ifdef HAVE_LIBPCAP
+#include "face/ethernet-factory.hpp"
+#endif // HAVE_LIBPCAP
+
+#include "manager-common-fixture.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(TestFaceManager)
+
+class FaceManagerProcessConfigFixture : public ManagerCommonFixture
+{
+public:
+  FaceManagerProcessConfigFixture()
+    : m_manager(m_forwarder.getFaceTable(), m_dispatcher, m_validator)
+  {
+    m_manager.setConfigFile(m_config);
+  }
+
+public:
+  void
+  parseConfig(const std::string& type, bool isDryRun)
+  {
+    m_config.parse(type, isDryRun, "test-config");
+  }
+
+protected:
+  FaceManager m_manager;
+  ConfigFile m_config;
+};
+
+
+BOOST_FIXTURE_TEST_SUITE(ProcessConfig, FaceManagerProcessConfigFixture)
+
+#ifdef HAVE_UNIX_SOCKETS
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUnix)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  unix\n"
+    "  {\n"
+    "    path /tmp/nfd.sock\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUnixUnknownOption)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  unix\n"
+    "  {\n"
+    "    hello\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+#endif // HAVE_UNIX_SOCKETS
+
+BOOST_AUTO_TEST_CASE(ProcessSectionTcp)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  tcp\n"
+    "  {\n"
+    "    listen yes\n"
+    "    port 16363\n"
+    "    enable_v4 yes\n"
+    "    enable_v6 yes\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionTcpBadListen)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  tcp\n"
+    "  {\n"
+    "    listen hello\n"
+    "  }\n"
+    "}\n";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionTcpChannelsDisabled)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  tcp\n"
+    "  {\n"
+    "    port 6363\n"
+    "    enable_v4 no\n"
+    "    enable_v6 no\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionTcpUnknownOption)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  tcp\n"
+    "  {\n"
+    "    hello\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdp)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    port 6363\n"
+    "    enable_v4 yes\n"
+    "    enable_v6 yes\n"
+    "    idle_timeout 30\n"
+    "    keep_alive_interval 25\n"
+    "    mcast yes\n"
+    "    mcast_port 56363\n"
+    "    mcast_group 224.0.23.170\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdpBadIdleTimeout)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    idle_timeout hello\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdpBadMcast)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    mcast hello\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdpBadMcastGroup)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    mcast no\n"
+    "    mcast_port 50\n"
+    "    mcast_group hello\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdpBadMcastGroupV6)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    mcast no\n"
+    "    mcast_port 50\n"
+    "    mcast_group ::1\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdpChannelsDisabled)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    port 6363\n"
+    "    enable_v4 no\n"
+    "    enable_v6 no\n"
+    "    idle_timeout 30\n"
+    "    keep_alive_interval 25\n"
+    "    mcast yes\n"
+    "    mcast_port 56363\n"
+    "    mcast_group 224.0.23.170\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdpConflictingMcast)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    port 6363\n"
+    "    enable_v4 no\n"
+    "    enable_v6 yes\n"
+    "    idle_timeout 30\n"
+    "    keep_alive_interval 25\n"
+    "    mcast yes\n"
+    "    mcast_port 56363\n"
+    "    mcast_group 224.0.23.170\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdpUnknownOption)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    hello\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdpMulticastReinit)
+{
+  const std::string CONFIG_WITH_MCAST =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    mcast yes\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITH_MCAST, false));
+
+  BOOST_REQUIRE(m_manager.m_factories.find("udp") != m_manager.m_factories.end());
+  auto factory = dynamic_pointer_cast<UdpFactory>(m_manager.m_factories.find("udp")->second);
+  BOOST_REQUIRE(factory != nullptr);
+
+  if (factory->getMulticastFaces().size() == 0) {
+    BOOST_TEST_MESSAGE("Destroying multicast faces is not tested because "
+                       "no UDP multicast faces are available");
+    return;
+  }
+  BOOST_CHECK_GT(factory->getMulticastFaces().size(), 0);
+
+  const std::string CONFIG_WITHOUT_MCAST =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    mcast no\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITHOUT_MCAST, false));
+  BOOST_CHECK_EQUAL(factory->getMulticastFaces().size(), 0);
+}
+
+#ifdef HAVE_LIBPCAP
+
+BOOST_AUTO_TEST_CASE(ProcessSectionEther)
+{
+
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  ether\n"
+    "  {\n"
+    "    mcast yes\n"
+    "    mcast_group 01:00:5E:00:17:AA\n"
+    "  }\n"
+    "}\n";
+
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionEtherBadMcast)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  ether\n"
+    "  {\n"
+    "    mcast hello\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionEtherBadMcastGroup)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  ether\n"
+    "  {\n"
+    "    mcast yes\n"
+    "    mcast_group\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionEtherUnknownOption)
+{
+  const std::string CONFIG =
+    "face_system\n"
+    "{\n"
+    "  ether\n"
+    "  {\n"
+    "    hello\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionEtherMulticastReinit)
+{
+  const std::string CONFIG_WITH_MCAST =
+    "face_system\n"
+    "{\n"
+    "  ether\n"
+    "  {\n"
+    "    mcast yes\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITH_MCAST, false));
+
+  BOOST_REQUIRE(m_manager.m_factories.find("ether") != m_manager.m_factories.end());
+  auto factory = dynamic_pointer_cast<EthernetFactory>(m_manager.m_factories.find("ether")->second);
+  BOOST_REQUIRE(factory != nullptr);
+
+  if (factory->getMulticastFaces().size() == 0) {
+    BOOST_TEST_MESSAGE("Destroying multicast faces is not tested because "
+                       "no Ethernet multicast faces are available");
+    return;
+  }
+  BOOST_CHECK_GT(factory->getMulticastFaces().size(), 0);
+
+  const std::string CONFIG_WITHOUT_MCAST =
+    "face_system\n"
+    "{\n"
+    "  ether\n"
+    "  {\n"
+    "    mcast no\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITHOUT_MCAST, false));
+  BOOST_CHECK_EQUAL(factory->getMulticastFaces().size(), 0);
+}
+
+#endif // HAVE_LIBPCAP
+
+BOOST_AUTO_TEST_SUITE_END() // ProcessConfig
+BOOST_AUTO_TEST_SUITE_END() // TestFaceManager
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/face-manager.t.cpp b/tests/daemon/mgmt/face-manager.t.cpp
index 36e9e8a..db24029 100644
--- a/tests/daemon/mgmt/face-manager.t.cpp
+++ b/tests/daemon/mgmt/face-manager.t.cpp
@@ -24,1853 +24,426 @@
  */
 
 #include "mgmt/face-manager.hpp"
-#include "mgmt/internal-face.hpp"
-#include "mgmt/face-status-publisher.hpp"
-
-#include "face/face.hpp"
+#include "manager-common-fixture.hpp"
 #include "../face/dummy-face.hpp"
-#include "fw/face-table.hpp"
-#include "fw/forwarder.hpp"
+#include "face/tcp-factory.hpp"
 #include "face/udp-factory.hpp"
 
-#ifdef HAVE_LIBPCAP
-#include "face/ethernet-factory.hpp"
-#endif // HAVE_LIBPCAP
-
-#include "common.hpp"
-#include "tests/test-common.hpp"
-#include "validation-common.hpp"
-#include "face-status-publisher-common.hpp"
-#include "face-query-status-publisher-common.hpp"
-#include "channel-status-common.hpp"
-
+#include <ndn-cxx/util/random.hpp>
 #include <ndn-cxx/encoding/tlv.hpp>
+#include <ndn-cxx/management/nfd-channel-status.hpp>
 #include <ndn-cxx/management/nfd-face-event-notification.hpp>
 
 namespace nfd {
 namespace tests {
 
-NFD_LOG_INIT("FaceManagerTest");
-
-class FaceManagerTestFace : public DummyFace
-{
-public:
-
-  FaceManagerTestFace()
-    : m_closeFired(false)
-  {
-
-  }
-
-  virtual
-  ~FaceManagerTestFace()
-  {
-
-  }
-
-  virtual void
-  close()
-  {
-    m_closeFired = true;
-  }
-
-  bool
-  didCloseFire() const
-  {
-    return m_closeFired;
-  }
-
-private:
-  bool m_closeFired;
-};
-
-class TestFaceTable : public FaceTable
-{
-public:
-  TestFaceTable(Forwarder& forwarder)
-    : FaceTable(forwarder),
-      m_addFired(false),
-      m_getFired(false),
-      m_dummy(make_shared<FaceManagerTestFace>())
-  {
-
-  }
-
-  virtual
-  ~TestFaceTable()
-  {
-
-  }
-
-  virtual void
-  add(shared_ptr<Face> face)
-  {
-    m_addFired = true;
-  }
-
-  virtual shared_ptr<Face>
-  get(FaceId id) const
-  {
-    m_getFired = true;
-    return m_dummy;
-  }
-
-  bool
-  didAddFire() const
-  {
-    return m_addFired;
-  }
-
-  bool
-  didGetFire() const
-  {
-    return m_getFired;
-  }
-
-  void
-  reset()
-  {
-    m_addFired = false;
-    m_getFired = false;
-  }
-
-  shared_ptr<FaceManagerTestFace>&
-  getDummyFace()
-  {
-    return m_dummy;
-  }
-
-private:
-  bool m_addFired;
-  mutable bool m_getFired;
-  shared_ptr<FaceManagerTestFace> m_dummy;
-};
-
-
-class TestFaceTableFixture : public BaseFixture
-{
-public:
-  TestFaceTableFixture()
-    : m_faceTable(m_forwarder)
-  {
-
-  }
-
-  virtual
-  ~TestFaceTableFixture()
-  {
-
-  }
-
-protected:
-  Forwarder m_forwarder;
-  TestFaceTable m_faceTable;
-};
-
-class TestFaceManagerCommon
-{
-public:
-  TestFaceManagerCommon()
-    : m_face(make_shared<InternalFace>()),
-      m_callbackFired(false)
-  {
-
-  }
-
-  virtual
-  ~TestFaceManagerCommon()
-  {
-
-  }
-
-  shared_ptr<InternalFace>&
-  getFace()
-  {
-    return m_face;
-  }
-
-  void
-  validateControlResponseCommon(const Data& response,
-                                const Name& expectedName,
-                                uint32_t expectedCode,
-                                const std::string& expectedText,
-                                ControlResponse& control)
-  {
-    m_callbackFired = true;
-    Block controlRaw = response.getContent().blockFromValue();
-
-    control.wireDecode(controlRaw);
-
-    // NFD_LOG_DEBUG("received control response"
-    //               << " Name: " << response.getName()
-    //               << " code: " << control.getCode()
-    //               << " text: " << control.getText());
-
-    BOOST_CHECK_EQUAL(response.getName(), expectedName);
-    BOOST_CHECK_EQUAL(control.getCode(), expectedCode);
-    BOOST_CHECK_EQUAL(control.getText(), expectedText);
-  }
-
-  void
-  validateControlResponse(const Data& response,
-                          const Name& expectedName,
-                          uint32_t expectedCode,
-                          const std::string& expectedText)
-  {
-    ControlResponse control;
-    validateControlResponseCommon(response, expectedName,
-                                  expectedCode, expectedText, control);
-
-    if (!control.getBody().empty())
-      {
-        BOOST_FAIL("found unexpected control response body");
-      }
-  }
-
-  void
-  validateControlResponse(const Data& response,
-                          const Name& expectedName,
-                          uint32_t expectedCode,
-                          const std::string& expectedText,
-                          const Block& expectedBody)
-  {
-    ControlResponse control;
-    validateControlResponseCommon(response, expectedName,
-                                  expectedCode, expectedText, control);
-
-    BOOST_REQUIRE(!control.getBody().empty());
-    BOOST_REQUIRE(control.getBody().value_size() == expectedBody.value_size());
-
-    BOOST_CHECK(memcmp(control.getBody().value(), expectedBody.value(),
-                       expectedBody.value_size()) == 0);
-
-  }
-
-  bool
-  didCallbackFire() const
-  {
-    return m_callbackFired;
-  }
-
-  void
-  resetCallbackFired()
-  {
-    m_callbackFired = false;
-  }
-
-protected:
-  shared_ptr<InternalFace> m_face;
-  bool m_callbackFired;
-  ndn::KeyChain m_testKeyChain;
-};
-
-class FaceManagerFixture : public TestFaceTableFixture, public TestFaceManagerCommon
+class FaceManagerFixture : public ManagerCommonFixture
 {
 public:
   FaceManagerFixture()
-    : m_manager(m_faceTable, m_face, m_testKeyChain)
+    : m_faceTable(m_forwarder.getFaceTable())
+    , m_manager(m_faceTable, m_dispatcher, m_validator)
   {
-    m_manager.setConfigFile(m_config);
+    setTopPrefixAndPrivilege("/localhost/nfd", "faces");
   }
 
-  virtual
-  ~FaceManagerFixture()
+public:
+  template<typename Face>
+  shared_ptr<Face>
+  addFace(bool wantRemoveLastNotification = false)
   {
-
-  }
-
-  void
-  parseConfig(const std::string configuration, bool isDryRun)
-  {
-    m_config.parse(configuration, isDryRun, "dummy-config");
-  }
-
-  FaceManager&
-  getManager()
-  {
-    return m_manager;
-  }
-
-  void
-  addInterestRule(const std::string& regex,
-                  ndn::IdentityCertificate& certificate)
-  {
-    m_manager.addInterestRule(regex, certificate);
-  }
-
-  bool
-  didFaceTableAddFire() const
-  {
-    return m_faceTable.didAddFire();
-  }
-
-  bool
-  didFaceTableGetFire() const
-  {
-    return m_faceTable.didGetFire();
-  }
-
-  void
-  resetFaceTable()
-  {
-    m_faceTable.reset();
+    auto face = make_shared<Face>();
+    m_faceTable.add(face);
+    advanceClocks(time::milliseconds(1), 10); // wait for notification posted
+    if (wantRemoveLastNotification) {
+      m_responses.pop_back();
+    }
+    return face;
   }
 
 protected:
+  FaceTable& m_faceTable;
   FaceManager m_manager;
-  ConfigFile m_config;
 };
 
-BOOST_FIXTURE_TEST_SUITE(MgmtFaceManager, FaceManagerFixture)
+BOOST_FIXTURE_TEST_SUITE(Mgmt, FaceManagerFixture)
+BOOST_AUTO_TEST_SUITE(TestFaceManager)
+
+BOOST_AUTO_TEST_SUITE(DestroyFace)
+
+BOOST_AUTO_TEST_CASE(Existing)
+{
+  auto addedFace = addFace<DummyFace>(true); // clear notification for creation
+
+  auto parameters = ControlParameters().setFaceId(addedFace->getId());
+  auto command = makeControlCommandRequest("/localhost/nfd/faces/destroy", parameters);
+
+  receiveInterest(command);
+
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 2); // one response and one notification
+  // notification is already tested, so ignore it
+
+  BOOST_CHECK_EQUAL(checkResponse(1, command->getName(), makeResponse(200, "OK", parameters)),
+                    CheckResponseResult::OK);
+
+  BOOST_CHECK_EQUAL(addedFace->getId(), -1);
+}
+
+BOOST_AUTO_TEST_CASE(NonExisting)
+{
+  auto parameters = ControlParameters().setFaceId(65535);
+  auto command = makeControlCommandRequest("/localhost/nfd/faces/destroy", parameters);
+
+  receiveInterest(command);
+
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+
+  BOOST_CHECK_EQUAL(checkResponse(0, command->getName(), makeResponse(200, "OK", parameters)),
+                    CheckResponseResult::OK);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // DestroyFace
+
+BOOST_AUTO_TEST_CASE(FaceEvents)
+{
+  auto addedFace = addFace<DummyFace>(); // trigger FACE_EVENT_CREATED notification
+  BOOST_CHECK_NE(addedFace->getId(), -1);
+  int64_t faceId = addedFace->getId();
+
+  // check notification
+  {
+    Block payload;
+    ndn::nfd::FaceEventNotification notification;
+    BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+    BOOST_CHECK_NO_THROW(payload = m_responses[0].getContent().blockFromValue());
+    BOOST_CHECK_EQUAL(payload.type(), ndn::tlv::nfd::FaceEventNotification);
+    BOOST_CHECK_NO_THROW(notification.wireDecode(payload));
+    BOOST_CHECK_EQUAL(notification.getKind(), ndn::nfd::FACE_EVENT_CREATED);
+    BOOST_CHECK_EQUAL(notification.getFaceId(), faceId);
+    BOOST_CHECK_EQUAL(notification.getRemoteUri(), addedFace->getRemoteUri().toString());
+    BOOST_CHECK_EQUAL(notification.getLocalUri(), addedFace->getLocalUri().toString());
+    BOOST_CHECK_EQUAL(notification.getFaceScope(), ndn::nfd::FACE_SCOPE_NON_LOCAL);
+    BOOST_CHECK_EQUAL(notification.getFacePersistency(), ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
+    BOOST_CHECK_EQUAL(notification.getLinkType(), ndn::nfd::LinkType::LINK_TYPE_POINT_TO_POINT);
+  }
+
+  addedFace->close(); // trigger FaceDestroy FACE_EVENT_DESTROYED
+  advanceClocks(time::milliseconds(1), 10);
+
+  // check notification
+  {
+    Block payload;
+    ndn::nfd::FaceEventNotification notification;
+    BOOST_REQUIRE_EQUAL(m_responses.size(), 2);
+    BOOST_CHECK_NO_THROW(payload = m_responses[1].getContent().blockFromValue());
+    BOOST_CHECK_EQUAL(payload.type(), ndn::tlv::nfd::FaceEventNotification);
+    BOOST_CHECK_NO_THROW(notification.wireDecode(payload));
+    BOOST_CHECK_EQUAL(notification.getKind(), ndn::nfd::FACE_EVENT_DESTROYED);
+    BOOST_CHECK_EQUAL(notification.getFaceId(), faceId);
+    BOOST_CHECK_EQUAL(notification.getRemoteUri(), addedFace->getRemoteUri().toString());
+    BOOST_CHECK_EQUAL(notification.getLocalUri(), addedFace->getLocalUri().toString());
+    BOOST_CHECK_EQUAL(notification.getFaceScope(), ndn::nfd::FACE_SCOPE_NON_LOCAL);
+    BOOST_CHECK_EQUAL(notification.getFacePersistency(), ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
+    BOOST_CHECK_EQUAL(notification.getLinkType(), ndn::nfd::LinkType::LINK_TYPE_POINT_TO_POINT);
+  }
+  BOOST_CHECK_EQUAL(addedFace->getId(), -1);
+}
+
+BOOST_AUTO_TEST_CASE(EnableDisableLocalControl)
+{
+  auto nonLocalFace = addFace<DummyFace>(true); // clear notification
+  auto localFace = addFace<DummyLocalFace>(true); // clear notification
+  BOOST_CHECK(localFace->isLocal());
+  BOOST_CHECK(!nonLocalFace->isLocal());
+
+  std::vector<Name> commandNames;
+  auto testLocalControl = [&] (const Name& name, const ControlParameters& params, uint64_t faceId) {
+    auto command = makeControlCommandRequest(name, params,
+                                             [faceId] (shared_ptr<Interest> interest) {
+                                               interest->setIncomingFaceId(faceId);
+                                             });
+    receiveInterest(command);
+    commandNames.push_back(command->getName());
+  };
+
+  auto paramsF = ControlParameters().setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+  auto paramsN = ControlParameters().setLocalControlFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+
+  // non-existing face: 0~3
+  testLocalControl("/localhost/nfd/faces/enable-local-control", paramsF, FACEID_NULL);
+  testLocalControl("/localhost/nfd/faces/disable-local-control", paramsF, FACEID_NULL);
+  testLocalControl("/localhost/nfd/faces/enable-local-control", paramsN, FACEID_NULL);
+  testLocalControl("/localhost/nfd/faces/disable-local-control", paramsN, FACEID_NULL);
+
+  // non-local face: 4~7
+  testLocalControl("/localhost/nfd/faces/enable-local-control", paramsF, nonLocalFace->getId());
+  testLocalControl("/localhost/nfd/faces/disable-local-control", paramsF, nonLocalFace->getId());
+  testLocalControl("/localhost/nfd/faces/enable-local-control", paramsN, nonLocalFace->getId());
+  testLocalControl("/localhost/nfd/faces/disable-local-control", paramsN, nonLocalFace->getId());
+
+  // enableLocalControl for Incoming FaceId on existing local face:
+  testLocalControl("/localhost/nfd/faces/enable-local-control", paramsF, localFace->getId()); // 8
+  BOOST_CHECK(localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+  BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+  // disableLocalControl for Incoming FaceId on existing local face
+  testLocalControl("/localhost/nfd/faces/disable-local-control", paramsF, localFace->getId()); // 9
+  BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+  BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+  // enableLocalControl for NextHop ID on existing local face
+  testLocalControl("/localhost/nfd/faces/enable-local-control", paramsN, localFace->getId()); // 10
+  BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+  BOOST_CHECK(localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+  // disableLocalControl for NextHop ID on existing local face
+  testLocalControl("/localhost/nfd/faces/disable-local-control", paramsN, localFace->getId()); // 11
+  BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+  BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+  // check responses
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 12);
+  BOOST_CHECK_EQUAL(checkResponse(0,  commandNames[0],  ControlResponse(410, "Face not found")),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkResponse(1,  commandNames[1],  ControlResponse(410, "Face not found")),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkResponse(2,  commandNames[2],  ControlResponse(410, "Face not found")),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkResponse(3,  commandNames[3],  ControlResponse(410, "Face not found")),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkResponse(4,  commandNames[4],  ControlResponse(412, "Face is non-local")),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkResponse(5,  commandNames[5],  ControlResponse(412, "Face is non-local")),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkResponse(6,  commandNames[6],  ControlResponse(412, "Face is non-local")),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkResponse(7,  commandNames[7],  ControlResponse(412, "Face is non-local")),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkResponse(8,  commandNames[8],  makeResponse(200, "OK", paramsF)),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkResponse(9,  commandNames[9],  makeResponse(200, "OK", paramsF)),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkResponse(10, commandNames[10], makeResponse(200, "OK", paramsN)),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkResponse(11, commandNames[11], makeResponse(200, "OK", paramsN)),
+                    CheckResponseResult::OK);
+}
+
+class TestFace : public DummyFace
+{
+public:
+  explicit
+  TestFace(const std::string& uri = "test://")
+    : DummyFace(uri, uri)
+  {
+    getMutableCounters().getNInInterests().set(ndn::random::generateWord64());
+    getMutableCounters().getNInDatas().set(ndn::random::generateWord64());
+    getMutableCounters().getNOutInterests().set(ndn::random::generateWord64());
+    getMutableCounters().getNOutDatas().set(ndn::random::generateWord64());
+    getMutableCounters().getNInBytes().set(ndn::random::generateWord64());
+    getMutableCounters().getNOutBytes().set(ndn::random::generateWord64());
+  }
+};
+
+// @todo Refactor when ndn::nfd::FaceStatus implementes operator!= and operator<<
+class FaceStatus : public ndn::nfd::FaceStatus
+{
+public:
+  FaceStatus(const ndn::nfd::FaceStatus& s)
+    : ndn::nfd::FaceStatus(s)
+  {
+  }
+};
 
 bool
-isExpectedException(const ConfigFile::Error& error, const std::string& expectedMessage)
+operator!=(const FaceStatus& left, const FaceStatus& right)
 {
-  if (error.what() != expectedMessage)
-    {
-      NFD_LOG_ERROR("expected: " << expectedMessage << "\tgot: " << error.what());
-    }
-  return error.what() == expectedMessage;
+  return left.getRemoteUri() != right.getRemoteUri() ||
+    left.getLocalUri() != right.getLocalUri() ||
+    left.getFaceScope() != right.getFaceScope() ||
+    left.getFacePersistency() != right.getFacePersistency() ||
+    left.getLinkType() != right.getLinkType() ||
+    left.getNInInterests() != right.getNInInterests() ||
+    left.getNInDatas() != right.getNInDatas() ||
+    left.getNOutInterests() != right.getNOutInterests() ||
+    left.getNOutDatas() != right.getNOutDatas() ||
+    left.getNInBytes() != right.getNInBytes() ||
+    left.getNOutBytes() != right.getNOutBytes();
 }
 
-#ifdef HAVE_UNIX_SOCKETS
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionUnix)
+std::ostream&
+operator<<(std::ostream &os, const FaceStatus& status)
 {
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  unix\n"
-    "  {\n"
-    "    path /tmp/nfd.sock\n"
-    "  }\n"
-    "}\n";
-  BOOST_TEST_CHECKPOINT("Calling parse");
-  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+  os << "[" << status.getRemoteUri() << ", "
+     << status.getLocalUri() << ", "
+     << status.getFacePersistency() << ", "
+     << status.getLinkType() << ", "
+     << status.getNInInterests() << ", "
+     << status.getNInDatas() << ", "
+     << status.getNOutInterests() << ", "
+     << status.getNOutDatas() << ", "
+     << status.getNInBytes() << ", "
+     << status.getNOutBytes() << "]";
+  return os;
 }
 
-BOOST_AUTO_TEST_CASE(TestProcessSectionUnixDryRun)
+BOOST_AUTO_TEST_CASE(FaceDataset)
 {
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  unix\n"
-    "  {\n"
-    "    path /var/run/nfd.sock\n"
-    "  }\n"
-    "}\n";
-
-  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionUnixUnknownOption)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  unix\n"
-    "  {\n"
-    "    hello\n"
-    "  }\n"
-    "}\n";
-  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
-                        bind(&isExpectedException, _1,
-                             "Unrecognized option \"hello\" in \"unix\" section"));
-}
-
-#endif // HAVE_UNIX_SOCKETS
-
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionTcp)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  tcp\n"
-    "  {\n"
-    "    listen yes\n"
-    "    port 6363\n"
-    "    enable_v4 yes\n"
-    "    enable_v6 yes\n"
-    "  }\n"
-    "}\n";
-  try
-    {
-      parseConfig(CONFIG, false);
-    }
-  catch (const std::runtime_error& e)
-    {
-      const std::string reason = e.what();
-      if (reason.find("Address in use") != std::string::npos)
-        {
-          BOOST_FAIL(reason);
-        }
-    }
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionTcpDryRun)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  tcp\n"
-    "  {\n"
-    "    listen yes\n"
-    "    port 6363\n"
-    "    enable_v4 yes\n"
-    "    enable_v6 yes\n"
-    "  }\n"
-    "}\n";
-  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionTcpBadListen)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  tcp\n"
-    "  {\n"
-    "    listen hello\n"
-    "  }\n"
-    "}\n";
-  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
-                        bind(&isExpectedException, _1,
-                             "Invalid value for option \"listen\" in \"tcp\" section"));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionTcpChannelsDisabled)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  tcp\n"
-    "  {\n"
-    "    port 6363\n"
-    "    enable_v4 no\n"
-    "    enable_v6 no\n"
-    "  }\n"
-    "}\n";
-  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
-                        bind(&isExpectedException, _1,
-                             "IPv4 and IPv6 channels have been disabled."
-                             " Remove \"tcp\" section to disable TCP channels or"
-                             " re-enable at least one channel type."));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionTcpUnknownOption)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  tcp\n"
-    "  {\n"
-    "    hello\n"
-    "  }\n"
-    "}\n";
-  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
-                        bind(&isExpectedException, _1,
-                             "Unrecognized option \"hello\" in \"tcp\" section"));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionUdp)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  udp\n"
-    "  {\n"
-    "    port 6363\n"
-    "    enable_v4 yes\n"
-    "    enable_v6 yes\n"
-    "    idle_timeout 30\n"
-    "    keep_alive_interval 25\n"
-    "    mcast yes\n"
-    "    mcast_port 56363\n"
-    "    mcast_group 224.0.23.170\n"
-    "  }\n"
-    "}\n";
-  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionUdpDryRun)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  udp\n"
-    "  {\n"
-    "    port 6363\n"
-    "    idle_timeout 30\n"
-    "    keep_alive_interval 25\n"
-    "    mcast yes\n"
-    "    mcast_port 56363\n"
-    "    mcast_group 224.0.23.170\n"
-    "  }\n"
-    "}\n";
-  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionUdpBadIdleTimeout)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  udp\n"
-    "  {\n"
-    "    idle_timeout hello\n"
-    "  }\n"
-    "}\n";
-
-  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
-                        bind(&isExpectedException, _1,
-                             "Invalid value for option \"idle_timeout\" in \"udp\" section"));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionUdpBadMcast)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  udp\n"
-    "  {\n"
-    "    mcast hello\n"
-    "  }\n"
-    "}\n";
-
-  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
-                        bind(&isExpectedException, _1,
-                             "Invalid value for option \"mcast\" in \"udp\" section"));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionUdpBadMcastGroup)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  udp\n"
-    "  {\n"
-    "    mcast no\n"
-    "    mcast_port 50\n"
-    "    mcast_group hello\n"
-    "  }\n"
-    "}\n";
-
-  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
-                        bind(&isExpectedException, _1,
-                             "Invalid value for option \"mcast_group\" in \"udp\" section"));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionUdpBadMcastGroupV6)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  udp\n"
-    "  {\n"
-    "    mcast no\n"
-    "    mcast_port 50\n"
-    "    mcast_group ::1\n"
-    "  }\n"
-    "}\n";
-
-  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
-                        bind(&isExpectedException, _1,
-                             "Invalid value for option \"mcast_group\" in \"udp\" section"));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionUdpChannelsDisabled)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  udp\n"
-    "  {\n"
-    "    port 6363\n"
-    "    enable_v4 no\n"
-    "    enable_v6 no\n"
-    "    idle_timeout 30\n"
-    "    keep_alive_interval 25\n"
-    "    mcast yes\n"
-    "    mcast_port 56363\n"
-    "    mcast_group 224.0.23.170\n"
-    "  }\n"
-    "}\n";
-  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
-                        bind(&isExpectedException, _1,
-                             "IPv4 and IPv6 channels have been disabled."
-                             " Remove \"udp\" section to disable UDP channels or"
-                             " re-enable at least one channel type."));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionUdpConflictingMcast)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  udp\n"
-    "  {\n"
-    "    port 6363\n"
-    "    enable_v4 no\n"
-    "    enable_v6 yes\n"
-    "    idle_timeout 30\n"
-    "    keep_alive_interval 25\n"
-    "    mcast yes\n"
-    "    mcast_port 56363\n"
-    "    mcast_group 224.0.23.170\n"
-    "  }\n"
-    "}\n";
-  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
-                        bind(&isExpectedException, _1,
-                             "IPv4 multicast requested, but IPv4 channels"
-                             " have been disabled (conflicting configuration options set)"));
-}
-
-
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionUdpUnknownOption)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  udp\n"
-    "  {\n"
-    "    hello\n"
-    "  }\n"
-    "}\n";
-  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
-                        bind(&isExpectedException, _1,
-                             "Unrecognized option \"hello\" in \"udp\" section"));
-}
-
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionUdpMulticastReinit)
-{
-  const std::string CONFIG_WITH_MCAST =
-    "face_system\n"
-    "{\n"
-    "  udp\n"
-    "  {\n"
-    "    mcast yes\n"
-    "  }\n"
-    "}\n";
-  BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITH_MCAST, false));
-
-  shared_ptr<UdpFactory> factory = static_pointer_cast<UdpFactory>(getManager().findFactory("udp"));
-  BOOST_REQUIRE(static_cast<bool>(factory));
-
-  if (factory->getMulticastFaces().size() == 0) {
-    BOOST_TEST_MESSAGE("Destroying multicast faces is not tested because "
-                       "no UDP multicast faces are available");
+  size_t nEntries = 303;
+  for (size_t i = 0 ; i < nEntries ; i ++) {
+    addFace<TestFace>(true);
   }
 
-  const std::string CONFIG_WITHOUT_MCAST =
-    "face_system\n"
-    "{\n"
-    "  udp\n"
-    "  {\n"
-    "    mcast no\n"
-    "  }\n"
-    "}\n";
-  BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITHOUT_MCAST, false));
-  BOOST_CHECK_EQUAL(factory->getMulticastFaces().size(), 0);
-}
+  receiveInterest(makeInterest("/localhost/nfd/faces/list"));
 
+  Block content;
+  BOOST_CHECK_NO_THROW(content = concatenateResponses());
+  BOOST_CHECK_NO_THROW(content.parse());
+  BOOST_REQUIRE_EQUAL(content.elements().size(), nEntries);
 
-#ifdef HAVE_LIBPCAP
+  std::vector<FaceStatus> expectedStatuses, receivedStatuses;
+  std::set<FaceId> faceIds;
+  for (size_t idx = 0; idx < nEntries; ++idx) {
+    BOOST_TEST_MESSAGE("processing element: " << idx);
 
-BOOST_AUTO_TEST_CASE(TestProcessSectionEther)
-{
-
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  ether\n"
-    "  {\n"
-    "    mcast yes\n"
-    "    mcast_group 01:00:5E:00:17:AA\n"
-    "  }\n"
-    "}\n";
-
-  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionEtherDryRun)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  ether\n"
-    "  {\n"
-    "    mcast yes\n"
-    "    mcast_group 01:00:5E:00:17:AA\n"
-    "  }\n"
-    "}\n";
-
-  BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionEtherBadMcast)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  ether\n"
-    "  {\n"
-    "    mcast hello\n"
-    "  }\n"
-    "}\n";
-
-  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
-                        bind(&isExpectedException, _1,
-                             "Invalid value for option \"mcast\" in \"ether\" section"));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionEtherBadMcastGroup)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  ether\n"
-    "  {\n"
-    "    mcast yes\n"
-    "    mcast_group\n"
-    "  }\n"
-    "}\n";
-
-  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
-                        bind(&isExpectedException, _1,
-                             "Invalid value for option \"mcast_group\" in \"ether\" section"));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionEtherUnknownOption)
-{
-  const std::string CONFIG =
-    "face_system\n"
-    "{\n"
-    "  ether\n"
-    "  {\n"
-    "    hello\n"
-    "  }\n"
-    "}\n";
-  BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
-                        bind(&isExpectedException, _1,
-                             "Unrecognized option \"hello\" in \"ether\" section"));
-}
-
-BOOST_AUTO_TEST_CASE(TestProcessSectionEtherMulticastReinit)
-{
-  const std::string CONFIG_WITH_MCAST =
-    "face_system\n"
-    "{\n"
-    "  ether\n"
-    "  {\n"
-    "    mcast yes\n"
-    "  }\n"
-    "}\n";
-  BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITH_MCAST, false));
-
-  shared_ptr<EthernetFactory> factory =
-    static_pointer_cast<EthernetFactory>(getManager().findFactory("ether"));
-  BOOST_REQUIRE(static_cast<bool>(factory));
-
-  if (factory->getMulticastFaces().size() == 0) {
-    BOOST_TEST_MESSAGE("Destroying multicast faces is not tested because "
-                       "no Ethernet multicast faces are available");
+    ndn::nfd::FaceStatus decodedStatus;
+    BOOST_REQUIRE_NO_THROW(decodedStatus.wireDecode(content.elements()[idx]));
+    BOOST_REQUIRE(m_faceTable.get(decodedStatus.getFaceId()) != nullptr);
+    faceIds.insert(decodedStatus.getFaceId());
+    receivedStatuses.push_back(decodedStatus);
+    expectedStatuses.push_back(m_faceTable.get(decodedStatus.getFaceId())->getFaceStatus());
   }
 
-  const std::string CONFIG_WITHOUT_MCAST =
-    "face_system\n"
-    "{\n"
-    "  ether\n"
-    "  {\n"
-    "    mcast no\n"
-    "  }\n"
-    "}\n";
-  BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITHOUT_MCAST, false));
-  BOOST_CHECK_EQUAL(factory->getMulticastFaces().size(), 0);
+  BOOST_CHECK_EQUAL(faceIds.size(), nEntries);
+  BOOST_CHECK_EQUAL_COLLECTIONS(receivedStatuses.begin(), receivedStatuses.end(),
+                                expectedStatuses.begin(), expectedStatuses.end());
 }
 
-#endif // HAVE_LIBPCAP
-
-BOOST_AUTO_TEST_CASE(ShortName)
+BOOST_AUTO_TEST_CASE(FaceQuery)
 {
-  shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/faces"));
+  auto face1 = addFace<DummyFace>(true); // dummy://
+  auto face2 = addFace<DummyLocalFace>(true); // dummy://, local
+  auto face3 = addFace<TestFace>(true); // test://
 
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  });
+  auto generateQueryName = [] (const ndn::nfd::FaceQueryFilter& filter) {
+    return Name("/localhost/nfd/faces/query").append(filter.wireEncode());
+  };
 
-  getFace()->sendInterest(*command);
-  g_io.run_one();
+  auto querySchemeName =
+    generateQueryName(ndn::nfd::FaceQueryFilter().setUriScheme("dummy"));
+  auto queryIdName =
+    generateQueryName(ndn::nfd::FaceQueryFilter().setFaceId(face1->getId()));
+  auto queryScopeName =
+    generateQueryName(ndn::nfd::FaceQueryFilter().setFaceScope(ndn::nfd::FACE_SCOPE_NON_LOCAL));
+  auto invalidQueryName =
+    Name("/localhost/nfd/faces/query").append(ndn::makeStringBlock(tlv::Content, "invalid"));
 
-  BOOST_REQUIRE(didCallbackFire());
+  receiveInterest(makeInterest(querySchemeName)); // face1 and face2 expected
+  receiveInterest(makeInterest(queryIdName)); // face1 expected
+  receiveInterest(makeInterest(queryScopeName)); // face1 and face3 expected
+  receiveInterest(makeInterest(invalidQueryName)); // nack expected
+
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 4);
+
+  Block content;
+  ndn::nfd::FaceStatus status;
+
+  content = m_responses[0].getContent();
+  BOOST_CHECK_NO_THROW(content.parse());
+  BOOST_CHECK_EQUAL(content.elements().size(), 2); // face1 and face2
+  BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[0]));
+  BOOST_CHECK_EQUAL(face1->getId(), status.getFaceId());
+  BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[1]));
+  BOOST_CHECK_EQUAL(face2->getId(), status.getFaceId());
+
+  content = m_responses[1].getContent();
+  BOOST_CHECK_NO_THROW(content.parse());
+  BOOST_CHECK_EQUAL(content.elements().size(), 1); // face1
+  BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[0]));
+  BOOST_CHECK_EQUAL(face1->getId(), status.getFaceId());
+
+  content = m_responses[2].getContent();
+  BOOST_CHECK_NO_THROW(content.parse());
+  BOOST_CHECK_EQUAL(content.elements().size(), 2); // face1 and face3
+  BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[0]));
+  BOOST_CHECK_EQUAL(face1->getId(), status.getFaceId());
+  BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[1]));
+  BOOST_CHECK_EQUAL(face3->getId(), status.getFaceId());
+
+  ControlResponse expectedResponse(400, "malformed filter"); // nack, 400, malformed filter
+  BOOST_CHECK_EQUAL(checkResponse(3, invalidQueryName, expectedResponse, tlv::ContentType_Nack),
+                    CheckResponseResult::OK);
 }
 
-BOOST_AUTO_TEST_CASE(MalformedCommmand)
-{
-  shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/faces"));
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  });
-
-  getManager().onFaceRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_AUTO_TEST_CASE(UnsignedCommand)
-{
-  ControlParameters parameters;
-  parameters.setUri("tcp4://127.0.0.1:6363");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/faces");
-  commandName.append("create");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 401, "Signature required");
-  });
-
-  getManager().onFaceRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand, UnauthorizedCommandFixture<FaceManagerFixture>)
-{
-  ControlParameters parameters;
-  parameters.setUri("tcp4://127.0.0.1:6363");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/faces");
-  commandName.append("create");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 403, "Unauthorized command");
-  });
-
-  getManager().onFaceRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-template <typename T> class AuthorizedCommandFixture : public CommandFixture<T>
+class TestChannel : public Channel
 {
 public:
-  AuthorizedCommandFixture()
+  TestChannel(const std::string& uri)
   {
-    const std::string regex = "^<localhost><nfd><faces>";
-    T::addInterestRule(regex, *CommandFixture<T>::m_certificate);
-  }
-
-  virtual
-  ~AuthorizedCommandFixture()
-  {
-
+    setUri(FaceUri(uri));
   }
 };
 
-BOOST_FIXTURE_TEST_CASE(UnsupportedCommand, AuthorizedCommandFixture<FaceManagerFixture>)
-{
-  ControlParameters parameters;
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/faces");
-  commandName.append("unsupported");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 501, "Unsupported command");
-  });
-
-  getManager().onFaceRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-class ValidatedFaceRequestFixture : public TestFaceTableFixture,
-                                    public TestFaceManagerCommon,
-                                    public FaceManager
+class TestProtocolFactory : public ProtocolFactory
 {
 public:
-
-  ValidatedFaceRequestFixture()
-    : FaceManager(TestFaceTableFixture::m_faceTable, getFace(), m_testKeyChain),
-      m_createFaceFired(false),
-      m_destroyFaceFired(false)
-  {
-
-  }
-
   virtual void
-  createFace(const Interest& request,
-             ControlParameters& parameters)
+  createFace(const FaceUri& uri,
+             ndn::nfd::FacePersistency persistency,
+             const FaceCreatedCallback& onCreated,
+             const FaceConnectFailedCallback& onConnectFailed) DECL_OVERRIDE
   {
-    m_createFaceFired = true;
   }
 
-  virtual void
-  destroyFace(const Interest& request,
-              ControlParameters& parameters)
+  virtual std::list<shared_ptr<const Channel>>
+  getChannels() const DECL_OVERRIDE
   {
-    m_destroyFaceFired = true;
+    return m_channels;
   }
 
-  virtual
-  ~ValidatedFaceRequestFixture()
+public:
+  shared_ptr<TestChannel>
+  addChannel(const std::string& channelUri)
   {
-
-  }
-
-  bool
-  didCreateFaceFire() const
-  {
-    return m_createFaceFired;
-  }
-
-  bool
-  didDestroyFaceFire() const
-  {
-    return m_destroyFaceFired;
+    auto channel = make_shared<TestChannel>(channelUri);
+    m_channels.push_back(channel);
+    return channel;
   }
 
 private:
-  bool m_createFaceFired;
-  bool m_destroyFaceFired;
+  std::list<shared_ptr<const Channel> > m_channels;
 };
 
-BOOST_FIXTURE_TEST_CASE(ValidatedFaceRequestBadOptionParse,
-                        AuthorizedCommandFixture<ValidatedFaceRequestFixture>)
+BOOST_AUTO_TEST_CASE(ChannelDataset)
 {
-  Name commandName("/localhost/nfd/faces");
-  commandName.append("create");
-  commandName.append("NotReallyParameters");
+  auto factory = make_shared<TestProtocolFactory>();
+  m_manager.m_factories["test"] = factory;
 
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  });
-
-  onValidatedFaceRequest(command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_FIXTURE_TEST_CASE(ValidatedFaceRequestCreateFace,
-                        AuthorizedCommandFixture<ValidatedFaceRequestFixture>)
-{
-  ControlParameters parameters;
-  parameters.setUri("tcp4://127.0.0.1:6363");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/faces");
-  commandName.append("create");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  onValidatedFaceRequest(command);
-  BOOST_CHECK(didCreateFaceFire());
-}
-
-BOOST_FIXTURE_TEST_CASE(ValidatedFaceRequestDestroyFace,
-                        AuthorizedCommandFixture<ValidatedFaceRequestFixture>)
-{
-  ControlParameters parameters;
-  parameters.setUri("tcp4://127.0.0.1:6363");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/faces");
-  commandName.append("destroy");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  onValidatedFaceRequest(command);
-  BOOST_CHECK(didDestroyFaceFire());
-}
-
-class FaceTableFixture
-{
-public:
-  FaceTableFixture()
-    : m_faceTable(m_forwarder)
-  {
+  std::map<std::string, shared_ptr<TestChannel>> addedChannels;
+  size_t nEntries = 404;
+  for (size_t i = 0 ; i < nEntries ; i ++) {
+    auto channel = factory->addChannel("test" + boost::lexical_cast<std::string>(i) + "://");
+    addedChannels[channel->getUri().toString()] = channel;
   }
 
-  virtual
-  ~FaceTableFixture()
-  {
+  receiveInterest(makeInterest("/localhost/nfd/faces/channels"));
+
+  Block content;
+  BOOST_CHECK_NO_THROW(content = concatenateResponses());
+  BOOST_CHECK_NO_THROW(content.parse());
+  BOOST_REQUIRE_EQUAL(content.elements().size(), nEntries);
+
+  for (size_t idx = 0; idx < nEntries; ++idx) {
+    BOOST_TEST_MESSAGE("processing element: " << idx);
+
+    ndn::nfd::ChannelStatus decodedStatus;
+    BOOST_CHECK_NO_THROW(decodedStatus.wireDecode(content.elements()[idx]));
+    BOOST_CHECK(addedChannels.find(decodedStatus.getLocalUri()) != addedChannels.end());
   }
-
-protected:
-  Forwarder m_forwarder;
-  FaceTable m_faceTable;
-};
-
-class LocalControlFixture : public FaceTableFixture,
-                            public TestFaceManagerCommon,
-                            public FaceManager
-{
-public:
-  LocalControlFixture()
-    : FaceManager(FaceTableFixture::m_faceTable, getFace(), m_testKeyChain)
-  {
-  }
-};
-
-BOOST_FIXTURE_TEST_CASE(LocalControlInFaceId,
-                        AuthorizedCommandFixture<LocalControlFixture>)
-{
-  shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
-  BOOST_REQUIRE(dummy->isLocal());
-  FaceTableFixture::m_faceTable.add(dummy);
-
-  ControlParameters parameters;
-  parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name enable("/localhost/nfd/faces/enable-local-control");
-  enable.append(encodedParameters);
-
-  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
-  enableCommand->setIncomingFaceId(dummy->getId());
-
-  generateCommand(*enableCommand);
-
-  signal::Connection conn = getFace()->onReceiveData.connect(
-      [this, enableCommand, encodedParameters] (const Data& response) {
-        this->validateControlResponse(response, enableCommand->getName(),
-                                      200, "Success", encodedParameters);
-      });
-
-  onValidatedFaceRequest(enableCommand);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
-  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
-
-  conn.disconnect();
-  resetCallbackFired();
-
-  Name disable("/localhost/nfd/faces/disable-local-control");
-  disable.append(encodedParameters);
-
-  shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
-  disableCommand->setIncomingFaceId(dummy->getId());
-
-  generateCommand(*disableCommand);
-
-  getFace()->onReceiveData.connect(
-      [this, disableCommand, encodedParameters] (const Data& response) {
-        this->validateControlResponse(response, disableCommand->getName(),
-                                      200, "Success", encodedParameters);
-      });
-
-  onValidatedFaceRequest(disableCommand);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
-  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
 }
 
-BOOST_FIXTURE_TEST_CASE(LocalControlInFaceIdFaceNotFound,
-                        AuthorizedCommandFixture<LocalControlFixture>)
-{
-  shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
-  BOOST_REQUIRE(dummy->isLocal());
-  FaceTableFixture::m_faceTable.add(dummy);
-
-  ControlParameters parameters;
-  parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name enable("/localhost/nfd/faces/enable-local-control");
-  enable.append(encodedParameters);
-
-  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
-  enableCommand->setIncomingFaceId(dummy->getId() + 100);
-
-  generateCommand(*enableCommand);
-
-  signal::Connection conn = getFace()->onReceiveData.connect(
-      [this, enableCommand] (const Data& response) {
-        this->validateControlResponse(response, enableCommand->getName(), 410, "Face not found");
-      });
-
-  onValidatedFaceRequest(enableCommand);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
-  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
-
-  conn.disconnect();
-  resetCallbackFired();
-
-  Name disable("/localhost/nfd/faces/disable-local-control");
-  disable.append(encodedParameters);
-
-  shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
-  disableCommand->setIncomingFaceId(dummy->getId() + 100);
-
-  generateCommand(*disableCommand);
-
-  getFace()->onReceiveData.connect(
-      [this, disableCommand] (const Data& response) {
-        this->validateControlResponse(response, disableCommand->getName(), 410, "Face not found");
-      });
-
-  onValidatedFaceRequest(disableCommand);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
-  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
-}
-
-BOOST_FIXTURE_TEST_CASE(LocalControlMissingFeature,
-                        AuthorizedCommandFixture<LocalControlFixture>)
-{
-  shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
-  BOOST_REQUIRE(dummy->isLocal());
-  FaceTableFixture::m_faceTable.add(dummy);
-
-  ControlParameters parameters;
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name enable("/localhost/nfd/faces/enable-local-control");
-  enable.append(encodedParameters);
-
-  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
-  enableCommand->setIncomingFaceId(dummy->getId());
-
-  generateCommand(*enableCommand);
-
-  signal::Connection conn = getFace()->onReceiveData.connect(
-      [this, enableCommand] (const Data& response) {
-        this->validateControlResponse(response, enableCommand->getName(),
-                                      400, "Malformed command");
-      });
-
-  onValidatedFaceRequest(enableCommand);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
-  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
-
-  conn.disconnect();
-  resetCallbackFired();
-
-  Name disable("/localhost/nfd/faces/disable-local-control");
-  disable.append(encodedParameters);
-
-  shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
-  disableCommand->setIncomingFaceId(dummy->getId());
-
-  generateCommand(*disableCommand);
-
-  getFace()->onReceiveData.connect(
-      [this, disableCommand] (const Data& response) {
-        this->validateControlResponse(response, disableCommand->getName(),
-                                      400, "Malformed command");
-      });
-
-  onValidatedFaceRequest(disableCommand);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
-  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
-}
-
-BOOST_FIXTURE_TEST_CASE(LocalControlInFaceIdNonLocal,
-                        AuthorizedCommandFixture<LocalControlFixture>)
-{
-  shared_ptr<DummyFace> dummy = make_shared<DummyFace>();
-  BOOST_REQUIRE(!dummy->isLocal());
-  FaceTableFixture::m_faceTable.add(dummy);
-
-  ControlParameters parameters;
-  parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name enable("/localhost/nfd/faces/enable-local-control");
-  enable.append(encodedParameters);
-
-  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
-  enableCommand->setIncomingFaceId(dummy->getId());
-
-  generateCommand(*enableCommand);
-
-  signal::Connection conn = getFace()->onReceiveData.connect(
-      [this, enableCommand] (const Data& response) {
-        this->validateControlResponse(response, enableCommand->getName(),
-                                      412, "Face is non-local");
-      });
-
-  onValidatedFaceRequest(enableCommand);
-
-  BOOST_REQUIRE(didCallbackFire());
-
-  conn.disconnect();
-  resetCallbackFired();
-
-  Name disable("/localhost/nfd/faces/disable-local-control");
-  enable.append(encodedParameters);
-
-  shared_ptr<Interest> disableCommand(make_shared<Interest>(enable));
-  disableCommand->setIncomingFaceId(dummy->getId());
-
-  generateCommand(*disableCommand);
-
-  getFace()->onReceiveData.connect(
-      [this, disableCommand] (const Data& response) {
-        this->validateControlResponse(response, disableCommand->getName(),
-                                      412, "Face is non-local");
-      });
-
-  onValidatedFaceRequest(disableCommand);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_FIXTURE_TEST_CASE(LocalControlNextHopFaceId,
-                        AuthorizedCommandFixture<LocalControlFixture>)
-{
-  shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
-  BOOST_REQUIRE(dummy->isLocal());
-  FaceTableFixture::m_faceTable.add(dummy);
-
-  ControlParameters parameters;
-  parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name enable("/localhost/nfd/faces/enable-local-control");
-  enable.append(encodedParameters);
-
-  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
-  enableCommand->setIncomingFaceId(dummy->getId());
-
-  generateCommand(*enableCommand);
-
-  signal::Connection conn = getFace()->onReceiveData.connect(
-      [this, enableCommand, encodedParameters] (const Data& response) {
-        this->validateControlResponse(response, enableCommand->getName(),
-                                      200, "Success", encodedParameters);
-      });
-
-  onValidatedFaceRequest(enableCommand);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
-  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
-
-
-  conn.disconnect();
-  resetCallbackFired();
-
-  Name disable("/localhost/nfd/faces/disable-local-control");
-  disable.append(encodedParameters);
-
-  shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
-  disableCommand->setIncomingFaceId(dummy->getId());
-
-  generateCommand(*disableCommand);
-
-  getFace()->onReceiveData.connect(
-      [this, disableCommand, encodedParameters] (const Data& response) {
-        this->validateControlResponse(response, disableCommand->getName(),
-                                      200, "Success", encodedParameters);
-      });
-
-  onValidatedFaceRequest(disableCommand);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
-  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
-}
-
-BOOST_FIXTURE_TEST_CASE(LocalControlNextHopFaceIdFaceNotFound,
-                        AuthorizedCommandFixture<LocalControlFixture>)
-{
-  shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
-  BOOST_REQUIRE(dummy->isLocal());
-  FaceTableFixture::m_faceTable.add(dummy);
-
-  ControlParameters parameters;
-  parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name enable("/localhost/nfd/faces/enable-local-control");
-  enable.append(encodedParameters);
-
-  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
-  enableCommand->setIncomingFaceId(dummy->getId() + 100);
-
-  generateCommand(*enableCommand);
-
-  signal::Connection conn = getFace()->onReceiveData.connect(
-      [this, enableCommand] (const Data& response) {
-        this->validateControlResponse(response, enableCommand->getName(), 410, "Face not found");
-      });
-
-  onValidatedFaceRequest(enableCommand);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
-  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
-
-
-  conn.disconnect();
-  resetCallbackFired();
-
-  Name disable("/localhost/nfd/faces/disable-local-control");
-  disable.append(encodedParameters);
-
-  shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
-  disableCommand->setIncomingFaceId(dummy->getId() + 100);
-
-  generateCommand(*disableCommand);
-
-  getFace()->onReceiveData.connect(
-      [this, disableCommand] (const Data& response) {
-        this->validateControlResponse(response, disableCommand->getName(),
-                                      410, "Face not found");
-      });
-
-  onValidatedFaceRequest(disableCommand);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
-  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
-}
-
-BOOST_FIXTURE_TEST_CASE(LocalControlNextHopFaceIdNonLocal,
-                        AuthorizedCommandFixture<LocalControlFixture>)
-{
-  shared_ptr<DummyFace> dummy = make_shared<DummyFace>();
-  BOOST_REQUIRE(!dummy->isLocal());
-  FaceTableFixture::m_faceTable.add(dummy);
-
-  ControlParameters parameters;
-  parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name enable("/localhost/nfd/faces/enable-local-control");
-  enable.append(encodedParameters);
-
-  shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
-  enableCommand->setIncomingFaceId(dummy->getId());
-
-  generateCommand(*enableCommand);
-
-  signal::Connection conn = getFace()->onReceiveData.connect(
-      [this, enableCommand] (const Data& response) {
-        this->validateControlResponse(response, enableCommand->getName(),
-                                      412, "Face is non-local");
-      });
-
-  onValidatedFaceRequest(enableCommand);
-
-  BOOST_REQUIRE(didCallbackFire());
-
-  conn.disconnect();
-  resetCallbackFired();
-
-  Name disable("/localhost/nfd/faces/disable-local-control");
-  disable.append(encodedParameters);
-
-  shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
-  disableCommand->setIncomingFaceId(dummy->getId());
-
-  generateCommand(*disableCommand);
-
-  getFace()->onReceiveData.connect(
-      [this, disableCommand] (const Data& response) {
-        this->validateControlResponse(response, disableCommand->getName(),
-                                      412, "Face is non-local");
-      });
-
-  onValidatedFaceRequest(disableCommand);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-class FaceFixture : public FaceTableFixture,
-                    public TestFaceManagerCommon,
-                    public FaceManager
-{
-public:
-  FaceFixture()
-    : FaceManager(FaceTableFixture::m_faceTable,
-                  getFace(),
-                  m_testKeyChain)
-    , m_receivedNotification(false)
-  {
-
-  }
-
-  virtual
-  ~FaceFixture()
-  {
-
-  }
-
-  void
-  callbackDispatch(const Data& response,
-                   const Name& expectedName,
-                   uint32_t expectedCode,
-                   const std::string& expectedText,
-                   const Block& expectedBody,
-                   const ndn::nfd::FaceEventNotification& expectedFaceEvent)
-  {
-    Block payload = response.getContent().blockFromValue();
-    if (payload.type() == ndn::tlv::nfd::ControlResponse)
-      {
-        validateControlResponse(response, expectedName, expectedCode,
-                                expectedText, expectedBody);
-      }
-    else if (payload.type() == ndn::tlv::nfd::FaceEventNotification)
-      {
-        validateFaceEvent(payload, expectedFaceEvent);
-      }
-    else
-      {
-        BOOST_FAIL("Received unknown message type: #" << payload.type());
-      }
-  }
-
-  void
-  callbackDispatch(const Data& response,
-                   const Name& expectedName,
-                   uint32_t expectedCode,
-                   const std::string& expectedText,
-                   const ndn::nfd::FaceEventNotification& expectedFaceEvent)
-  {
-    Block payload = response.getContent().blockFromValue();
-    if (payload.type() == ndn::tlv::nfd::ControlResponse)
-      {
-        validateControlResponse(response, expectedName,
-                                expectedCode, expectedText);
-      }
-    else if (payload.type() == ndn::tlv::nfd::FaceEventNotification)
-      {
-        validateFaceEvent(payload, expectedFaceEvent);
-      }
-    else
-      {
-        BOOST_FAIL("Received unknown message type: #" << payload.type());
-      }
-  }
-
-  void
-  validateFaceEvent(const Block& wire,
-                    const ndn::nfd::FaceEventNotification& expectedFaceEvent)
-  {
-
-    m_receivedNotification = true;
-
-    ndn::nfd::FaceEventNotification notification(wire);
-
-    BOOST_CHECK_EQUAL(notification.getKind(), expectedFaceEvent.getKind());
-    BOOST_CHECK_EQUAL(notification.getFaceId(), expectedFaceEvent.getFaceId());
-    BOOST_CHECK_EQUAL(notification.getRemoteUri(), expectedFaceEvent.getRemoteUri());
-    BOOST_CHECK_EQUAL(notification.getLocalUri(), expectedFaceEvent.getLocalUri());
-    BOOST_CHECK_EQUAL(notification.getFaceScope(), expectedFaceEvent.getFaceScope());
-    BOOST_CHECK_EQUAL(notification.getFacePersistency(), expectedFaceEvent.getFacePersistency());
-    BOOST_CHECK_EQUAL(notification.getLinkType(), expectedFaceEvent.getLinkType());
-  }
-
-  bool
-  didReceiveNotication() const
-  {
-    return m_receivedNotification;
-  }
-
-protected:
-  bool m_receivedNotification;
-};
-
-BOOST_FIXTURE_TEST_CASE(CreateFaceBadUri, AuthorizedCommandFixture<FaceFixture>)
-{
-  ControlParameters parameters;
-  parameters.setUri("tcp4:/127.0.0.1:6363");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/faces");
-  commandName.append("create");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  });
-
-  createFace(*command, parameters);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-BOOST_FIXTURE_TEST_CASE(CreateFaceNoncanonicalUri, AuthorizedCommandFixture<FaceFixture>)
-{
-  ControlParameters parameters;
-  parameters.setUri("tcp://127.0.0.1");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/faces");
-  commandName.append("create");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 400, "Non-canonical URI");
-  });
-
-  createFace(*command, parameters);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_FIXTURE_TEST_CASE(CreateFaceMissingUri, AuthorizedCommandFixture<FaceFixture>)
-{
-  ControlParameters parameters;
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/faces");
-  commandName.append("create");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  });
-
-  createFace(*command, parameters);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_FIXTURE_TEST_CASE(CreateFaceUnknownScheme, AuthorizedCommandFixture<FaceFixture>)
-{
-  ControlParameters parameters;
-  // this will be an unsupported protocol because no factories have been
-  // added to the face manager
-  parameters.setUri("tcp4://127.0.0.1:6363");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/faces");
-  commandName.append("create");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 501, "Unsupported protocol");
-  });
-
-  createFace(*command, parameters);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_FIXTURE_TEST_CASE(OnCreated, AuthorizedCommandFixture<FaceFixture>)
-{
-  ControlParameters parameters;
-  parameters.setUri("tcp4://127.0.0.1:6363");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/faces");
-  commandName.append("create");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  ControlParameters resultParameters;
-  resultParameters.setUri("dummy://");
-  resultParameters.setFaceId(FACEID_RESERVED_MAX + 1);
-  resultParameters.setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
-
-  shared_ptr<DummyFace> dummy(make_shared<DummyFace>());
-
-  ndn::nfd::FaceEventNotification expectedFaceEvent;
-  expectedFaceEvent.setKind(ndn::nfd::FACE_EVENT_CREATED)
-                   .setFaceId(FACEID_RESERVED_MAX + 1)
-                   .setRemoteUri(dummy->getRemoteUri().toString())
-                   .setLocalUri(dummy->getLocalUri().toString())
-                   .setFaceScope(ndn::nfd::FACE_SCOPE_NON_LOCAL)
-                   .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
-
-  Block encodedResultParameters(resultParameters.wireEncode());
-
-  getFace()->onReceiveData.connect(
-      [this, command, encodedResultParameters, expectedFaceEvent] (const Data& response) {
-        this->callbackDispatch(response,command->getName(), 200, "Success",
-                               encodedResultParameters, expectedFaceEvent);
-      });
-
-  onCreated(command->getName(), parameters, dummy);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(didReceiveNotication());
-}
-
-BOOST_FIXTURE_TEST_CASE(OnConnectFailed, AuthorizedCommandFixture<FaceFixture>)
-{
-  ControlParameters parameters;
-  parameters.setUri("tcp4://127.0.0.1:6363");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/faces");
-  commandName.append("create");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 408, "unit-test-reason");
-  });
-
-  onConnectFailed(command->getName(), "unit-test-reason");
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_CHECK_EQUAL(didReceiveNotication(), false);
-}
-
-
-BOOST_FIXTURE_TEST_CASE(DestroyFace, AuthorizedCommandFixture<FaceFixture>)
-{
-  shared_ptr<DummyFace> dummy(make_shared<DummyFace>());
-  FaceTableFixture::m_faceTable.add(dummy);
-
-  ControlParameters parameters;
-  parameters.setFaceId(dummy->getId());
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/faces");
-  commandName.append("destroy");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  ndn::nfd::FaceEventNotification expectedFaceEvent;
-  expectedFaceEvent.setKind(ndn::nfd::FACE_EVENT_DESTROYED)
-                   .setFaceId(dummy->getId())
-                   .setRemoteUri(dummy->getRemoteUri().toString())
-                   .setLocalUri(dummy->getLocalUri().toString())
-                   .setFaceScope(ndn::nfd::FACE_SCOPE_NON_LOCAL)
-                   .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
-
-  getFace()->onReceiveData.connect(
-      [this, command, encodedParameters, expectedFaceEvent] (const Data& response) {
-        this->callbackDispatch(response,command->getName(), 200, "Success",
-                               encodedParameters, expectedFaceEvent);
-      });
-
-  destroyFace(*command, parameters);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(didReceiveNotication());
-}
-
-class FaceListFixture : public FaceStatusPublisherFixture
-{
-public:
-  FaceListFixture()
-    : m_manager(m_table, m_face, m_testKeyChain)
-  {
-
-  }
-
-  virtual
-  ~FaceListFixture()
-  {
-
-  }
-
-protected:
-  FaceManager m_manager;
-  ndn::KeyChain m_testKeyChain;
-};
-
-BOOST_FIXTURE_TEST_CASE(TestFaceList, FaceListFixture)
-{
-  Name commandName("/localhost/nfd/faces/list");
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-
-  // MAX_SEGMENT_SIZE == 4400, FaceStatus size with filler counters is 75
-  // use 59 FaceStatuses to force a FaceStatus to span Data packets
-  for (int i = 0; i < 59; i++)
-    {
-      shared_ptr<TestCountersFace> dummy(make_shared<TestCountersFace>());
-
-      uint64_t filler = std::numeric_limits<uint64_t>::max() - 1;
-      dummy->setCounters(filler, filler, filler, filler, filler, filler);
-
-      m_referenceFaces.push_back(dummy);
-
-      add(dummy);
-    }
-
-  ndn::EncodingBuffer buffer;
-
-  m_face->onReceiveData.connect(bind(&FaceStatusPublisherFixture::decodeFaceStatusBlock,
-                                     this, _1));
-
-  m_manager.listFaces(*command);
-  BOOST_REQUIRE(m_finished);
-}
-
-class ChannelStatusFixture : public FaceManagerFixture
-{
-public:
-  void
-  validatePublish(const Data& data, const ndn::nfd::ChannelStatus& expectedEntry)
-  {
-    m_callbackFired = true;
-    Block b = data.getContent().blockFromValue();
-    ndn::nfd::ChannelStatus entry(b);
-    BOOST_CHECK_EQUAL(entry.getLocalUri(), expectedEntry.getLocalUri());
-  }
-
-  virtual shared_ptr<DummyProtocolFactory>
-  addProtocolFactory(const std::string& protocol)
-  {
-    shared_ptr<DummyProtocolFactory> factory(make_shared<DummyProtocolFactory>());
-    m_manager.m_factories[protocol] = factory;
-
-    return factory;
-  }
-};
-
-BOOST_FIXTURE_TEST_CASE(TestChannelStatus, ChannelStatusFixture)
-{
-  shared_ptr<DummyProtocolFactory> factory(addProtocolFactory("dummy"));
-  factory->addChannel("dummy://");
-
-  Name requestName("/localhost/nfd/faces/channels");
-  shared_ptr<Interest> request(make_shared<Interest>(requestName));
-
-  ndn::nfd::ChannelStatus expectedEntry;
-  expectedEntry.setLocalUri(DummyChannel("dummy://").getUri().toString());
-
-  m_face->onReceiveData.connect(bind(&ChannelStatusFixture::validatePublish,
-                                     this, _1, expectedEntry));
-
-  m_manager.listChannels(*request);
-  BOOST_REQUIRE(m_callbackFired);
-}
-
-class FaceQueryListFixture : public FaceQueryStatusPublisherFixture
-{
-public:
-  FaceQueryListFixture()
-    : m_manager(m_table, m_face, m_testKeyChain)
-  {
-
-  }
-
-  virtual
-  ~FaceQueryListFixture()
-  {
-
-  }
-
-protected:
-  FaceManager m_manager;
-  ndn::KeyChain m_testKeyChain;
-};
-
-BOOST_FIXTURE_TEST_CASE(TestValidQueryFilter, FaceQueryListFixture)
-{
-  Name queryName("/localhost/nfd/faces/query");
-  ndn::nfd::FaceQueryFilter queryFilter;
-  queryFilter.setUriScheme("dummy");
-  queryName.append(queryFilter.wireEncode());
-
-  shared_ptr<Interest> query(make_shared<Interest>(queryName));
-
-  // add expected faces
-  shared_ptr<DummyLocalFace> expectedFace1(make_shared<DummyLocalFace>());
-  m_referenceFaces.push_back(expectedFace1);
-  add(expectedFace1);
-
-  shared_ptr<DummyFace> expectedFace2(make_shared<DummyFace>());
-  m_referenceFaces.push_back(expectedFace2);
-  add(expectedFace2);
-
-  // add other faces
-  shared_ptr<DummyFace> face1(make_shared<DummyFace>("udp://", "udp://"));
-  add(face1);
-  shared_ptr<DummyLocalFace> face2(make_shared<DummyLocalFace>("tcp://", "tcp://"));
-  add(face2);
-
-  m_face->onReceiveData.connect(bind(&FaceQueryStatusPublisherFixture::decodeFaceStatusBlock,
-                                     this, _1));
-
-  m_manager.listQueriedFaces(*query);
-  BOOST_REQUIRE(m_finished);
-}
-
-BOOST_FIXTURE_TEST_CASE(TestInvalidQueryFilter, FaceQueryListFixture)
-{
-  Name queryName("/localhost/nfd/faces/query");
-  ndn::nfd::FaceStatus queryFilter;
-  queryName.append(queryFilter.wireEncode());
-
-  shared_ptr<Interest> query(make_shared<Interest>(queryName));
-
-  shared_ptr<DummyLocalFace> face(make_shared<DummyLocalFace>());
-  add(face);
-
-  m_face->onReceiveData.connect(bind(&FaceQueryStatusPublisherFixture::decodeNackBlock, this, _1));
-
-  m_manager.listQueriedFaces(*query);
-  BOOST_REQUIRE(m_finished);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END() // TestFaceManager
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
 
 } // namespace tests
 } // namespace nfd
diff --git a/tests/daemon/mgmt/face-query-status-publisher-common.hpp b/tests/daemon/mgmt/face-query-status-publisher-common.hpp
deleted file mode 100644
index 09582c9..0000000
--- a/tests/daemon/mgmt/face-query-status-publisher-common.hpp
+++ /dev/null
@@ -1,158 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014,  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_TESTS_NFD_MGMT_FACE_QUERY_STATUS_PUBLISHER_COMMON_HPP
-#define NFD_TESTS_NFD_MGMT_FACE_QUERY_STATUS_PUBLISHER_COMMON_HPP
-
-#include "mgmt/face-query-status-publisher.hpp"
-#include "mgmt/app-face.hpp"
-#include "mgmt/internal-face.hpp"
-#include "fw/forwarder.hpp"
-#include "face/udp-factory.hpp"
-
-#include "tests/test-common.hpp"
-#include "tests/daemon/face/dummy-face.hpp"
-
-#include <ndn-cxx/management/nfd-face-status.hpp>
-
-namespace nfd {
-namespace tests {
-
-class FaceQueryStatusPublisherFixture : public BaseFixture
-{
-public:
-
-  FaceQueryStatusPublisherFixture()
-    : m_table(m_forwarder)
-    , m_face(make_shared<InternalFace>())
-    , m_dummyFace(make_shared<DummyFace>())
-    , m_dummyLocalFace(make_shared<DummyLocalFace>())
-    , m_dummyUri(make_shared<DummyFace>("dummy://remoteUri", "dummy://localUri"))
-    , m_factory(UdpFactory())
-    , m_finished(false)
-  {
-  }
-
-  virtual
-  ~FaceQueryStatusPublisherFixture()
-  {
-  }
-
-  void
-  add(shared_ptr<Face> face)
-  {
-    m_table.add(face);
-  }
-
-  void
-  validateFaceStatus(const Block& statusBlock, const shared_ptr<Face>& reference)
-  {
-    ndn::nfd::FaceStatus status;
-    BOOST_REQUIRE_NO_THROW(status.wireDecode(statusBlock));
-
-    BOOST_CHECK_EQUAL(status.getFaceId(), reference->getId());
-    BOOST_CHECK_EQUAL(status.getRemoteUri(), reference->getRemoteUri().toString());
-    BOOST_CHECK_EQUAL(status.getLocalUri(), reference->getLocalUri().toString());
-
-    if (reference->isLocal()) {
-      BOOST_CHECK_EQUAL(status.getFaceScope(), ndn::nfd::FACE_SCOPE_LOCAL);
-    }
-    else {
-      BOOST_CHECK_EQUAL(status.getFaceScope(), ndn::nfd::FACE_SCOPE_NON_LOCAL);
-    }
-
-    BOOST_CHECK_EQUAL(status.getFacePersistency(), reference->getPersistency());
-
-    if (reference->isMultiAccess()) {
-      BOOST_CHECK_EQUAL(status.getLinkType(), ndn::nfd::LINK_TYPE_MULTI_ACCESS);
-    }
-    else {
-      BOOST_CHECK_EQUAL(status.getLinkType(), ndn::nfd::LINK_TYPE_POINT_TO_POINT);
-    }
-  }
-
-  void
-  decodeFaceStatusBlock(const Data& data)
-  {
-    BOOST_REQUIRE_EQUAL(data.getContentType(), tlv::ContentType_Blob);
-
-    Block payload = data.getContent();
-    m_buffer.appendByteArray(payload.value(), payload.value_size());
-
-    BOOST_CHECK_NO_THROW(data.getName()[-1].toSegment());
-    if (data.getFinalBlockId() != data.getName()[-1]) {
-        return;
-    }
-
-    // wrap the Face Statuses in a single Content TLV for easy parsing
-    m_buffer.prependVarNumber(m_buffer.size());
-    m_buffer.prependVarNumber(tlv::Content);
-
-    ndn::Block parser(m_buffer.buf(), m_buffer.size());
-    parser.parse();
-
-    BOOST_REQUIRE_EQUAL(parser.elements_size(), m_referenceFaces.size());
-
-    std::list<shared_ptr<Face> >::const_iterator iReference = m_referenceFaces.begin();
-    for (Block::element_const_iterator i = parser.elements_begin();
-         i != parser.elements_end();
-         ++i) {
-      if (i->type() != ndn::tlv::nfd::FaceStatus) {
-          BOOST_FAIL("expected face status, got type #" << i->type());
-      }
-      validateFaceStatus(*i, *iReference);
-      ++iReference;
-    }
-    m_finished = true;
-  }
-
-  void
-  decodeNackBlock(const Data& data)
-  {
-    BOOST_REQUIRE_EQUAL(data.getContentType(), tlv::ContentType_Nack);
-
-    m_finished = true;
-  }
-
-protected:
-  Forwarder m_forwarder;
-  FaceTable m_table;
-  shared_ptr<InternalFace> m_face;
-  ndn::EncodingBuffer m_buffer;
-  std::list<shared_ptr<Face> > m_referenceFaces;
-  ndn::KeyChain m_keyChain;
-  shared_ptr<DummyFace> m_dummyFace;
-  shared_ptr<DummyLocalFace> m_dummyLocalFace;
-  shared_ptr<DummyFace> m_dummyUri;
-  UdpFactory m_factory;
-
-protected:
-  bool m_finished;
-};
-
-} // namespace tests
-} // namespace nfd
-
-#endif // NFD_TESTS_NFD_MGMT_FACE_QUERY_STATUS_PUBLISHER_COMMON_HPP
diff --git a/tests/daemon/mgmt/face-query-status-publisher.t.cpp b/tests/daemon/mgmt/face-query-status-publisher.t.cpp
deleted file mode 100644
index 09f6804..0000000
--- a/tests/daemon/mgmt/face-query-status-publisher.t.cpp
+++ /dev/null
@@ -1,129 +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-query-status-publisher-common.hpp"
-
-namespace nfd {
-namespace tests {
-
-BOOST_FIXTURE_TEST_SUITE(MgmtFaceQueryStatusPublisher, FaceQueryStatusPublisherFixture)
-
-BOOST_AUTO_TEST_CASE(NoConditionFilter)
-{
-  // filter without conditions matches all faces
-  ndn::nfd::FaceQueryFilter filter;
-  FaceQueryStatusPublisher faceQueryStatusPublisher(m_table, *m_face,
-                                                    "/localhost/nfd/FaceStatusPublisherFixture",
-                                                    filter, m_keyChain);
-
-  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyFace), true);
-  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyLocalFace), true);
-}
-
-BOOST_AUTO_TEST_CASE(AllConditionFilter)
-{
-  ndn::nfd::FaceQueryFilter filter;
-  filter.setUriScheme("dummy")
-        .setRemoteUri("dummy://")
-        .setLocalUri("dummy://")
-        .setFaceScope(ndn::nfd::FACE_SCOPE_NON_LOCAL)
-        .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT)
-        .setLinkType(ndn::nfd::LINK_TYPE_POINT_TO_POINT);
-
-  FaceQueryStatusPublisher faceQueryStatusPublisher(m_table, *m_face,
-                                                    "/localhost/nfd/FaceStatusPublisherFixture",
-                                                    filter, m_keyChain);
-  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyFace), true);
-  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyLocalFace), false);
-}
-
-BOOST_AUTO_TEST_CASE(UriSchemeFilter)
-{
-  ndn::nfd::FaceQueryFilter filter;
-  filter.setUriScheme("dummyurischeme");
-  FaceQueryStatusPublisher faceQueryStatusPublisher(m_table, *m_face,
-                                                    "/localhost/nfd/FaceStatusPublisherFixture",
-                                                    filter, m_keyChain);
-  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyFace), false);
-  auto dummyUriScheme = make_shared<DummyFace>("dummyurischeme://", "dummyurischeme://");
-  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(dummyUriScheme), true);
-}
-
-BOOST_AUTO_TEST_CASE(RemoteUriFilter)
-{
-  ndn::nfd::FaceQueryFilter filter;
-  filter.setRemoteUri("dummy://remoteUri");
-  FaceQueryStatusPublisher faceQueryStatusPublisher(m_table, *m_face,
-                                                    "/localhost/nfd/FaceStatusPublisherFixture",
-                                                    filter, m_keyChain);
-  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyFace), false);
-  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyUri), true);
-}
-
-BOOST_AUTO_TEST_CASE(LocalUriFilter)
-{
-  ndn::nfd::FaceQueryFilter filter;
-  filter.setLocalUri("dummy://localUri");
-  FaceQueryStatusPublisher faceQueryStatusPublisher(m_table, *m_face,
-                                                    "/localhost/nfd/FaceStatusPublisherFixture",
-                                                    filter, m_keyChain);
-  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyFace), false);
-  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyUri), true);
-}
-
-
-BOOST_AUTO_TEST_CASE(LinkTypeFilter)
-{
-  shared_ptr<MulticastUdpFace> multicastFace = m_factory.createMulticastFace("0.0.0.0",
-                                                                             "224.0.0.1",
-                                                                             "20070");
-  ndn::nfd::FaceQueryFilter filter;
-  filter.setLinkType(ndn::nfd::LINK_TYPE_MULTI_ACCESS);
-  FaceQueryStatusPublisher faceQueryStatusPublisher(m_table, *m_face,
-                                                    "/localhost/nfd/FaceStatusPublisherFixture",
-                                                    filter, m_keyChain);
-  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyFace), false);
-  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(multicastFace), true);
-}
-
-BOOST_AUTO_TEST_CASE(PersistencyFilter)
-{
-  shared_ptr<MulticastUdpFace> multicastFace = m_factory.createMulticastFace("0.0.0.0",
-                                                                             "224.0.0.1",
-                                                                             "20070");
-  ndn::nfd::FaceQueryFilter filter;
-  filter.setFacePersistency(ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
-  FaceQueryStatusPublisher faceQueryStatusPublisher(m_table, *m_face,
-                                                    "/localhost/nfd/FaceStatusPublisherFixture",
-                                                    filter, m_keyChain);
-  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(m_dummyFace), false);
-  multicastFace->setPersistency(ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
-  BOOST_CHECK_EQUAL(faceQueryStatusPublisher.doesMatchFilter(multicastFace), true);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-} // namespace tests
-} // namespace nfd
diff --git a/tests/daemon/mgmt/face-status-publisher-common.hpp b/tests/daemon/mgmt/face-status-publisher-common.hpp
deleted file mode 100644
index 882c976..0000000
--- a/tests/daemon/mgmt/face-status-publisher-common.hpp
+++ /dev/null
@@ -1,215 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014,  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_TESTS_NFD_MGMT_FACE_STATUS_PUBLISHER_COMMON_HPP
-#define NFD_TESTS_NFD_MGMT_FACE_STATUS_PUBLISHER_COMMON_HPP
-
-#include "mgmt/face-status-publisher.hpp"
-#include "mgmt/app-face.hpp"
-#include "mgmt/internal-face.hpp"
-#include "fw/forwarder.hpp"
-
-#include "tests/test-common.hpp"
-#include "tests/daemon/face/dummy-face.hpp"
-
-#include <ndn-cxx/management/nfd-face-status.hpp>
-
-namespace nfd {
-namespace tests {
-
-class TestCountersFace : public DummyFace
-{
-public:
-
-  TestCountersFace()
-  {
-  }
-
-  virtual
-  ~TestCountersFace()
-  {
-  }
-
-  void
-  setCounters(PacketCounter::rep nInInterests,
-              PacketCounter::rep nInDatas,
-              PacketCounter::rep nOutInterests,
-              PacketCounter::rep nOutDatas,
-              ByteCounter::rep nInBytes,
-              ByteCounter::rep nOutBytes)
-  {
-    FaceCounters& counters = getMutableCounters();
-    counters.getNInInterests().set(nInInterests);
-    counters.getNInDatas().set(nInDatas);
-    counters.getNOutInterests().set(nOutInterests);
-    counters.getNOutDatas().set(nOutDatas);
-    counters.getNInBytes().set(nInBytes);
-    counters.getNOutBytes().set(nOutBytes);
-  }
-
-
-};
-
-static inline uint64_t
-readNonNegativeIntegerType(const Block& block,
-                           uint32_t type)
-{
-  if (block.type() == type)
-    {
-      return readNonNegativeInteger(block);
-    }
-  std::stringstream error;
-  error << "expected type " << type << " got " << block.type();
-  BOOST_THROW_EXCEPTION(tlv::Error(error.str()));
-}
-
-static inline uint64_t
-checkedReadNonNegativeIntegerType(Block::element_const_iterator& i,
-                                  Block::element_const_iterator end,
-                                  uint32_t type)
-{
-  if (i != end)
-    {
-      const Block& block = *i;
-      ++i;
-      return readNonNegativeIntegerType(block, type);
-    }
-  BOOST_THROW_EXCEPTION(tlv::Error("Unexpected end of FaceStatus"));
-}
-
-class FaceStatusPublisherFixture : public BaseFixture
-{
-public:
-
-  FaceStatusPublisherFixture()
-    : m_table(m_forwarder)
-    , m_face(make_shared<InternalFace>())
-    , m_publisher(m_table, *m_face, "/localhost/nfd/FaceStatusPublisherFixture", m_keyChain)
-    , m_finished(false)
-  {
-
-  }
-
-  virtual
-  ~FaceStatusPublisherFixture()
-  {
-
-  }
-
-  void
-  add(shared_ptr<Face> face)
-  {
-    m_table.add(face);
-  }
-
-  void
-  validateFaceStatus(const Block& statusBlock, const shared_ptr<Face>& reference)
-  {
-    ndn::nfd::FaceStatus status;
-    BOOST_REQUIRE_NO_THROW(status.wireDecode(statusBlock));
-    const FaceCounters& counters = reference->getCounters();
-
-    BOOST_CHECK_EQUAL(status.getFaceId(), reference->getId());
-    BOOST_CHECK_EQUAL(status.getRemoteUri(), reference->getRemoteUri().toString());
-    BOOST_CHECK_EQUAL(status.getLocalUri(), reference->getLocalUri().toString());
-
-    if (reference->isLocal()) {
-      BOOST_CHECK_EQUAL(status.getFaceScope(), ndn::nfd::FACE_SCOPE_LOCAL);
-    }
-    else {
-      BOOST_CHECK_EQUAL(status.getFaceScope(), ndn::nfd::FACE_SCOPE_NON_LOCAL);
-    }
-
-    BOOST_CHECK_EQUAL(status.getFacePersistency(), reference->getPersistency());
-
-    if (reference->isMultiAccess()) {
-      BOOST_CHECK_EQUAL(status.getLinkType(), ndn::nfd::LINK_TYPE_MULTI_ACCESS);
-    }
-    else {
-      BOOST_CHECK_EQUAL(status.getLinkType(), ndn::nfd::LINK_TYPE_POINT_TO_POINT);
-    }
-
-    BOOST_CHECK_EQUAL(status.getNInInterests(), counters.getNInInterests());
-    BOOST_CHECK_EQUAL(status.getNInDatas(), counters.getNInDatas());
-    BOOST_CHECK_EQUAL(status.getNOutInterests(), counters.getNOutInterests());
-    BOOST_CHECK_EQUAL(status.getNOutDatas(), counters.getNOutDatas());
-    BOOST_CHECK_EQUAL(status.getNInBytes(), counters.getNInBytes());
-    BOOST_CHECK_EQUAL(status.getNOutBytes(), counters.getNOutBytes());
-  }
-
-  void
-  decodeFaceStatusBlock(const Data& data)
-  {
-    Block payload = data.getContent();
-
-    m_buffer.appendByteArray(payload.value(), payload.value_size());
-
-    BOOST_CHECK_NO_THROW(data.getName()[-1].toSegment());
-    if (data.getFinalBlockId() != data.getName()[-1])
-      {
-        return;
-      }
-
-    // wrap the Face Statuses in a single Content TLV for easy parsing
-    m_buffer.prependVarNumber(m_buffer.size());
-    m_buffer.prependVarNumber(tlv::Content);
-
-    ndn::Block parser(m_buffer.buf(), m_buffer.size());
-    parser.parse();
-
-    BOOST_REQUIRE_EQUAL(parser.elements_size(), m_referenceFaces.size());
-
-    std::list<shared_ptr<Face> >::const_iterator iReference = m_referenceFaces.begin();
-    for (Block::element_const_iterator i = parser.elements_begin();
-         i != parser.elements_end();
-         ++i)
-      {
-        if (i->type() != ndn::tlv::nfd::FaceStatus)
-          {
-            BOOST_FAIL("expected face status, got type #" << i->type());
-          }
-        validateFaceStatus(*i, *iReference);
-        ++iReference;
-      }
-    m_finished = true;
-  }
-
-protected:
-  Forwarder m_forwarder;
-  FaceTable m_table;
-  shared_ptr<InternalFace> m_face;
-  FaceStatusPublisher m_publisher;
-  ndn::EncodingBuffer m_buffer;
-  std::list<shared_ptr<Face> > m_referenceFaces;
-  ndn::KeyChain m_keyChain;
-
-protected:
-  bool m_finished;
-};
-
-} // namespace tests
-} // namespace nfd
-
-#endif // NFD_TESTS_NFD_MGMT_FACE_STATUS_PUBLISHER_COMMON_HPP
diff --git a/tests/daemon/mgmt/face-status-publisher.t.cpp b/tests/daemon/mgmt/face-status-publisher.t.cpp
deleted file mode 100644
index 383dba8..0000000
--- a/tests/daemon/mgmt/face-status-publisher.t.cpp
+++ /dev/null
@@ -1,64 +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-status-publisher-common.hpp"
-
-namespace nfd {
-namespace tests {
-
-BOOST_FIXTURE_TEST_SUITE(MgmtFaceStatusPublisher, FaceStatusPublisherFixture)
-
-BOOST_AUTO_TEST_CASE(EncodingDecoding)
-{
-  Name commandName("/localhost/nfd/faces/list");
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-
-  // MAX_SEGMENT_SIZE == 4400, FaceStatus size with filler counters is 75
-  // use 59 FaceStatuses to force a FaceStatus to span Data packets
-  for (int i = 0; i < 59; i++)
-    {
-      shared_ptr<TestCountersFace> dummy(make_shared<TestCountersFace>());
-
-      uint64_t filler = std::numeric_limits<uint64_t>::max() - 1;
-      dummy->setCounters(filler, filler, filler, filler, filler, filler);
-
-      m_referenceFaces.push_back(dummy);
-
-      add(dummy);
-    }
-
-  ndn::EncodingBuffer buffer;
-
-  m_face->onReceiveData.connect(
-      bind(&FaceStatusPublisherFixture::decodeFaceStatusBlock, this, _1));
-
-  m_publisher.publish();
-  BOOST_REQUIRE(m_finished);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-} // namespace tests
-} // namespace nfd
diff --git a/tests/daemon/mgmt/fib-enumeration-publisher-common.hpp b/tests/daemon/mgmt/fib-enumeration-publisher-common.hpp
deleted file mode 100644
index 9f9150b..0000000
--- a/tests/daemon/mgmt/fib-enumeration-publisher-common.hpp
+++ /dev/null
@@ -1,221 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014,  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_TESTS_NFD_MGMT_FIB_ENUMERATION_PUBLISHER_COMMON_HPP
-#define NFD_TESTS_NFD_MGMT_FIB_ENUMERATION_PUBLISHER_COMMON_HPP
-
-#include "mgmt/fib-enumeration-publisher.hpp"
-
-#include "mgmt/app-face.hpp"
-#include "mgmt/internal-face.hpp"
-#include "table/fib.hpp"
-#include "table/name-tree.hpp"
-
-#include "tests/test-common.hpp"
-#include "../face/dummy-face.hpp"
-
-#include <ndn-cxx/encoding/tlv.hpp>
-
-namespace nfd {
-namespace tests {
-
-static inline uint64_t
-readNonNegativeIntegerType(const Block& block,
-                           uint32_t type)
-{
-  if (block.type() == type)
-    {
-      return readNonNegativeInteger(block);
-    }
-  std::stringstream error;
-  error << "Expected type " << type << " got " << block.type();
-  BOOST_THROW_EXCEPTION(tlv::Error(error.str()));
-}
-
-static inline uint64_t
-checkedReadNonNegativeIntegerType(Block::element_const_iterator& i,
-                                  Block::element_const_iterator end,
-                                  uint32_t type)
-{
-  if (i != end)
-    {
-      const Block& block = *i;
-      ++i;
-      return readNonNegativeIntegerType(block, type);
-    }
-  std::stringstream error;
-  error << "Unexpected end of Block while attempting to read type #"
-        << type;
-  BOOST_THROW_EXCEPTION(tlv::Error(error.str()));
-}
-
-class FibEnumerationPublisherFixture : public BaseFixture
-{
-public:
-
-  FibEnumerationPublisherFixture()
-    : m_fib(m_nameTree)
-    , m_face(make_shared<InternalFace>())
-    , m_publisher(m_fib, *m_face, "/localhost/nfd/FibEnumerationPublisherFixture", m_keyChain)
-    , m_finished(false)
-  {
-  }
-
-  virtual
-  ~FibEnumerationPublisherFixture()
-  {
-  }
-
-  bool
-  hasNextHopWithCost(const fib::NextHopList& nextHops,
-                     FaceId faceId,
-                     uint64_t cost)
-  {
-    for (fib::NextHopList::const_iterator i = nextHops.begin();
-         i != nextHops.end();
-         ++i)
-      {
-        if (i->getFace()->getId() == faceId && i->getCost() == cost)
-          {
-            return true;
-          }
-      }
-    return false;
-  }
-
-  bool
-  entryHasPrefix(const shared_ptr<fib::Entry> entry, const Name& prefix)
-  {
-    return entry->getPrefix() == prefix;
-  }
-
-  void
-  validateFibEntry(const Block& entry)
-  {
-    entry.parse();
-
-    Block::element_const_iterator i = entry.elements_begin();
-    BOOST_REQUIRE(i != entry.elements_end());
-
-
-    BOOST_REQUIRE(i->type() == tlv::Name);
-    Name prefix(*i);
-    ++i;
-
-    std::set<shared_ptr<fib::Entry> >::const_iterator referenceIter =
-      std::find_if(m_referenceEntries.begin(), m_referenceEntries.end(),
-                   bind(&FibEnumerationPublisherFixture::entryHasPrefix,
-                        this, _1, prefix));
-
-    BOOST_REQUIRE(referenceIter != m_referenceEntries.end());
-
-    const shared_ptr<fib::Entry>& reference = *referenceIter;
-    BOOST_REQUIRE_EQUAL(prefix, reference->getPrefix());
-
-    // 0 or more next hop records
-    size_t nRecords = 0;
-    const fib::NextHopList& referenceNextHops = reference->getNextHops();
-    for (; i != entry.elements_end(); ++i)
-      {
-        const ndn::Block& nextHopRecord = *i;
-        BOOST_REQUIRE(nextHopRecord.type() == ndn::tlv::nfd::NextHopRecord);
-        nextHopRecord.parse();
-
-        Block::element_const_iterator j = nextHopRecord.elements_begin();
-
-        FaceId faceId =
-          checkedReadNonNegativeIntegerType(j,
-                                            entry.elements_end(),
-                                            ndn::tlv::nfd::FaceId);
-
-        uint64_t cost =
-          checkedReadNonNegativeIntegerType(j,
-                                            entry.elements_end(),
-                                            ndn::tlv::nfd::Cost);
-
-        BOOST_REQUIRE(hasNextHopWithCost(referenceNextHops, faceId, cost));
-
-        BOOST_REQUIRE(j == nextHopRecord.elements_end());
-        nRecords++;
-      }
-    BOOST_REQUIRE_EQUAL(nRecords, referenceNextHops.size());
-
-    BOOST_REQUIRE(i == entry.elements_end());
-    m_referenceEntries.erase(referenceIter);
-  }
-
-  void
-  decodeFibEntryBlock(const Data& data)
-  {
-    Block payload = data.getContent();
-
-    m_buffer.appendByteArray(payload.value(), payload.value_size());
-
-    BOOST_CHECK_NO_THROW(data.getName()[-1].toSegment());
-    if (data.getFinalBlockId() != data.getName()[-1])
-      {
-        return;
-      }
-
-    // wrap the FIB Entry blocks in a single Content TLV for easy parsing
-    m_buffer.prependVarNumber(m_buffer.size());
-    m_buffer.prependVarNumber(tlv::Content);
-
-    ndn::Block parser(m_buffer.buf(), m_buffer.size());
-    parser.parse();
-
-    BOOST_REQUIRE_EQUAL(parser.elements_size(), m_referenceEntries.size());
-
-    for (Block::element_const_iterator i = parser.elements_begin();
-         i != parser.elements_end();
-         ++i)
-      {
-        if (i->type() != ndn::tlv::nfd::FibEntry)
-          {
-            BOOST_FAIL("expected fib entry, got type #" << i->type());
-          }
-
-        validateFibEntry(*i);
-      }
-    m_finished = true;
-  }
-
-protected:
-  NameTree m_nameTree;
-  Fib m_fib;
-  shared_ptr<InternalFace> m_face;
-  FibEnumerationPublisher m_publisher;
-  ndn::EncodingBuffer m_buffer;
-  std::set<shared_ptr<fib::Entry> > m_referenceEntries;
-  ndn::KeyChain m_keyChain;
-
-protected:
-  bool m_finished;
-};
-
-} // namespace tests
-} // namespace nfd
-
-#endif // NFD_TESTS_NFD_MGMT_FIB_ENUMERATION_PUBLISHER_COMMON_HPP
diff --git a/tests/daemon/mgmt/fib-enumeration-publisher.t.cpp b/tests/daemon/mgmt/fib-enumeration-publisher.t.cpp
deleted file mode 100644
index 34cb582..0000000
--- a/tests/daemon/mgmt/fib-enumeration-publisher.t.cpp
+++ /dev/null
@@ -1,86 +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 "mgmt/fib-enumeration-publisher.hpp"
-
-#include "mgmt/app-face.hpp"
-#include "mgmt/internal-face.hpp"
-
-#include "tests/test-common.hpp"
-#include "../face/dummy-face.hpp"
-
-#include "fib-enumeration-publisher-common.hpp"
-
-#include <ndn-cxx/encoding/tlv.hpp>
-
-namespace nfd {
-namespace tests {
-
-BOOST_FIXTURE_TEST_SUITE(MgmtFibEnumerationPublisher, FibEnumerationPublisherFixture)
-
-BOOST_AUTO_TEST_CASE(TestFibEnumerationPublisher)
-{
-  for (int i = 0; i < 87; i++)
-    {
-      Name prefix("/test");
-      prefix.appendSegment(i);
-
-      shared_ptr<DummyFace> dummy1(make_shared<DummyFace>());
-      shared_ptr<DummyFace> dummy2(make_shared<DummyFace>());
-
-      shared_ptr<fib::Entry> entry = m_fib.insert(prefix).first;
-      entry->addNextHop(dummy1, std::numeric_limits<uint64_t>::max() - 1);
-      entry->addNextHop(dummy2, std::numeric_limits<uint64_t>::max() - 2);
-
-      m_referenceEntries.insert(entry);
-    }
-  for (int i = 0; i < 2; i++)
-    {
-      Name prefix("/test2");
-      prefix.appendSegment(i);
-
-      shared_ptr<DummyFace> dummy1(make_shared<DummyFace>());
-      shared_ptr<DummyFace> dummy2(make_shared<DummyFace>());
-
-      shared_ptr<fib::Entry> entry = m_fib.insert(prefix).first;
-      entry->addNextHop(dummy1, std::numeric_limits<uint8_t>::max() - 1);
-      entry->addNextHop(dummy2, std::numeric_limits<uint8_t>::max() - 2);
-
-      m_referenceEntries.insert(entry);
-    }
-
-  ndn::EncodingBuffer buffer;
-
-  m_face->onReceiveData.connect(
-      bind(&FibEnumerationPublisherFixture::decodeFibEntryBlock, this, _1));
-
-  m_publisher.publish();
-  BOOST_REQUIRE(m_finished);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-} // namespace tests
-} // namespace nfd
diff --git a/tests/daemon/mgmt/fib-manager.t.cpp b/tests/daemon/mgmt/fib-manager.t.cpp
index 82e70b9..513536c 100644
--- a/tests/daemon/mgmt/fib-manager.t.cpp
+++ b/tests/daemon/mgmt/fib-manager.t.cpp
@@ -22,904 +22,470 @@
  * 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 "mgmt/fib-manager.hpp"
-#include "table/fib.hpp"
+
+#include "manager-common-fixture.hpp"
 #include "table/fib-nexthop.hpp"
-#include "face/face.hpp"
-#include "mgmt/internal-face.hpp"
-#include "tests/daemon/face/dummy-face.hpp"
-
-#include "validation-common.hpp"
-#include "tests/test-common.hpp"
-
-#include "fib-enumeration-publisher-common.hpp"
+#include "../face/dummy-face.hpp"
+#include <ndn-cxx/management/nfd-fib-entry.hpp>
 
 namespace nfd {
 namespace tests {
 
-NFD_LOG_INIT("FibManagerTest");
-
-class FibManagerFixture : public FibEnumerationPublisherFixture
+class FibManagerFixture : public ManagerCommonFixture
 {
 public:
-
-  virtual
-  ~FibManagerFixture()
-  {
-  }
-
-  shared_ptr<Face>
-  getFace(FaceId id)
-  {
-    if (id > 0 && static_cast<size_t>(id) <= m_faces.size())
-      {
-        return m_faces[id - 1];
-      }
-    NFD_LOG_DEBUG("No face found returning NULL");
-    return shared_ptr<DummyFace>();
-  }
-
-  void
-  addFace(shared_ptr<Face> face)
-  {
-    m_faces.push_back(face);
-  }
-
-  void
-  validateControlResponseCommon(const Data& response,
-                                const Name& expectedName,
-                                uint32_t expectedCode,
-                                const std::string& expectedText,
-                                ControlResponse& control)
-  {
-    m_callbackFired = true;
-    Block controlRaw = response.getContent().blockFromValue();
-
-    control.wireDecode(controlRaw);
-
-    // NFD_LOG_DEBUG("received control response"
-    //               << " Name: " << response.getName()
-    //               << " code: " << control.getCode()
-    //               << " text: " << control.getText());
-
-    BOOST_CHECK_EQUAL(response.getName(), expectedName);
-    BOOST_CHECK_EQUAL(control.getCode(), expectedCode);
-    BOOST_CHECK_EQUAL(control.getText(), expectedText);
-  }
-
-  void
-  validateControlResponse(const Data& response,
-                          const Name& expectedName,
-                          uint32_t expectedCode,
-                          const std::string& expectedText)
-  {
-    ControlResponse control;
-    validateControlResponseCommon(response, expectedName,
-                                  expectedCode, expectedText, control);
-
-    if (!control.getBody().empty())
-      {
-        BOOST_FAIL("found unexpected control response body");
-      }
-  }
-
-  void
-  validateControlResponse(const Data& response,
-                          const Name& expectedName,
-                          uint32_t expectedCode,
-                          const std::string& expectedText,
-                          const Block& expectedBody)
-  {
-    ControlResponse control;
-    validateControlResponseCommon(response, expectedName,
-                                  expectedCode, expectedText, control);
-
-    BOOST_REQUIRE(!control.getBody().empty());
-    BOOST_REQUIRE_EQUAL(control.getBody().value_size(), expectedBody.value_size());
-
-    BOOST_CHECK(memcmp(control.getBody().value(), expectedBody.value(),
-                       expectedBody.value_size()) == 0);
-
-  }
-
-  bool
-  didCallbackFire()
-  {
-    return m_callbackFired;
-  }
-
-  void
-  resetCallbackFired()
-  {
-    m_callbackFired = false;
-  }
-
-  shared_ptr<InternalFace>
-  getInternalFace()
-  {
-    return m_face;
-  }
-
-  FibManager&
-  getFibManager()
-  {
-    return m_manager;
-  }
-
-  Fib&
-  getFib()
-  {
-    return m_fib;
-  }
-
-  void
-  addInterestRule(const std::string& regex,
-                  ndn::IdentityCertificate& certificate)
-  {
-    m_manager.addInterestRule(regex, certificate);
-  }
-
-protected:
   FibManagerFixture()
-    : m_manager(ref(m_fib), bind(&FibManagerFixture::getFace, this, _1), m_face, m_keyChain)
-    , m_callbackFired(false)
+    : m_fib(m_forwarder.getFib())
+    , m_faceTable(m_forwarder.getFaceTable())
+    , m_manager(m_fib, bind(&Forwarder::getFace, &m_forwarder, _1), m_dispatcher, m_validator)
   {
+    setTopPrefixAndPrivilege("/localhost/nfd", "fib");
+  }
+
+public: // for test
+  ControlParameters
+  makeParameters(const Name& name, const FaceId& id)
+  {
+    return ControlParameters().setName(name).setFaceId(id);
+  }
+
+  ControlParameters
+  makeParameters(const Name& name, const FaceId& id, const uint32_t& cost)
+  {
+    return ControlParameters().setName(name).setFaceId(id).setCost(cost);
+  }
+
+  FaceId
+  addFace()
+  {
+    auto face = make_shared<DummyFace>();
+    m_faceTable.add(face);
+    advanceClocks(time::milliseconds(1), 10);
+    m_responses.clear(); // clear all event notifications, if any
+    return face->getId();
+  }
+
+public: // for check
+  enum class CheckNextHopResult
+  {
+    OK,
+    NO_FIB_ENTRY,
+    WRONG_N_NEXTHOPS,
+    NO_NEXTHOP,
+    WRONG_COST
+   };
+
+  /**
+   * @brief check whether the nexthop record is added / removed properly
+   *
+   * @param expectedNNextHops use -1 to skip this check
+   * @param faceId use FACEID_NULL to skip NextHopRecord checks
+   * @param expectedCost use -1 to skip this check
+   *
+   * @retval OK FIB entry is found by exact match and has the expected number of nexthops;
+   *            NextHopRe record for faceId is found and has the expected cost
+   * @retval NO_FIB_ENTRY FIB entry is not found
+   * @retval WRONG_N_NEXTHOPS FIB entry is found but has wrong number of nexthops
+   * @retval NO_NEXTHOP NextHopRecord for faceId is not found
+   * @retval WRONG_COST NextHopRecord for faceId has wrong cost
+   */
+  CheckNextHopResult
+  checkNextHop(const Name& prefix, ssize_t expectedNNextHops = -1,
+               FaceId faceId = FACEID_NULL, int32_t expectedCost = -1)
+  {
+    auto entry = m_fib.findExactMatch(prefix);
+    if (!static_cast<bool>(entry)) {
+      return CheckNextHopResult::NO_FIB_ENTRY;
+    }
+
+    auto nextHops = entry->getNextHops();
+    if (expectedNNextHops != -1 && nextHops.size() != static_cast<size_t>(expectedNNextHops)) {
+      return CheckNextHopResult::WRONG_N_NEXTHOPS;
+    }
+
+    if (faceId != FACEID_NULL) {
+      for (auto&& record : nextHops) {
+        if (record.getFace()->getId() == faceId) {
+          return expectedCost != -1 && record.getCost() != static_cast<uint32_t>(expectedCost) ?
+            CheckNextHopResult::WRONG_COST : CheckNextHopResult::OK;
+        }
+      }
+
+      return CheckNextHopResult::NO_NEXTHOP;
+    }
+
+    return CheckNextHopResult::OK;
   }
 
 protected:
+  Fib&       m_fib;
+  FaceTable& m_faceTable;
   FibManager m_manager;
-
-  std::vector<shared_ptr<Face> > m_faces;
-  bool m_callbackFired;
-  ndn::KeyChain m_keyChain;
 };
 
-template <typename T>
-class AuthorizedCommandFixture : public CommandFixture<T>
+std::ostream&
+operator<<(std::ostream &os, const FibManagerFixture::CheckNextHopResult& result)
 {
-public:
-  AuthorizedCommandFixture()
-  {
-    const std::string regex = "^<localhost><nfd><fib>";
-    T::addInterestRule(regex, *CommandFixture<T>::m_certificate);
-  }
+  switch (result) {
+  case FibManagerFixture::CheckNextHopResult::OK:
+    os << "OK";
+    break;
+  case FibManagerFixture::CheckNextHopResult::NO_FIB_ENTRY:
+    os << "NO_FIB_ENTRY";
+    break;
+  case FibManagerFixture::CheckNextHopResult::WRONG_N_NEXTHOPS:
+    os << "WRONG_N_NEXTHOPS";
+    break;
+  case FibManagerFixture::CheckNextHopResult::NO_NEXTHOP:
+    os << "NO_NEXTHOP";
+    break;
+  case FibManagerFixture::CheckNextHopResult::WRONG_COST:
+    os << "WRONG_COST";
+    break;
+  default:
+    break;
+  };
 
-  virtual
-  ~AuthorizedCommandFixture()
-  {
-  }
-};
-
-BOOST_FIXTURE_TEST_SUITE(MgmtFibManager, AuthorizedCommandFixture<FibManagerFixture>)
-
-bool
-foundNextHop(FaceId id, uint32_t cost, const fib::NextHop& next)
-{
-  return id == next.getFace()->getId() && next.getCost() == cost;
+  return os;
 }
 
-bool
-addedNextHopWithCost(const Fib& fib, const Name& prefix, size_t oldSize, uint32_t cost)
-{
-  shared_ptr<fib::Entry> entry = fib.findExactMatch(prefix);
+BOOST_FIXTURE_TEST_SUITE(Mgmt, FibManagerFixture)
+BOOST_AUTO_TEST_SUITE(TestFibManager)
 
-  if (static_cast<bool>(entry))
-    {
-      const fib::NextHopList& hops = entry->getNextHops();
-      return hops.size() == oldSize + 1 &&
-        std::find_if(hops.begin(), hops.end(), bind(&foundNextHop, -1, cost, _1)) != hops.end();
-    }
-  return false;
-}
-
-bool
-foundNextHopWithFace(FaceId id, uint32_t cost,
-                     shared_ptr<Face> face, const fib::NextHop& next)
-{
-  return id == next.getFace()->getId() && next.getCost() == cost && face == next.getFace();
-}
-
-bool
-addedNextHopWithFace(const Fib& fib, const Name& prefix, size_t oldSize,
-                     uint32_t cost, shared_ptr<Face> face)
-{
-  shared_ptr<fib::Entry> entry = fib.findExactMatch(prefix);
-
-  if (static_cast<bool>(entry))
-    {
-      const fib::NextHopList& hops = entry->getNextHops();
-      return hops.size() == oldSize + 1 &&
-        std::find_if(hops.begin(), hops.end(), bind(&foundNextHop, -1, cost, _1)) != hops.end();
-    }
-  return false;
-}
-
-BOOST_AUTO_TEST_CASE(ShortName)
-{
-  shared_ptr<InternalFace> face = getInternalFace();
-
-  shared_ptr<Interest> command = makeInterest("/localhost/nfd/fib");
-
-  face->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  });
-
-  face->sendInterest(*command);
-  g_io.run_one();
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_AUTO_TEST_CASE(MalformedCommmand)
-{
-  shared_ptr<InternalFace> face = getInternalFace();
-
-  BOOST_REQUIRE(didCallbackFire() == false);
-
-  Interest command("/localhost/nfd/fib");
-
-  face->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command.getName(), 400, "Malformed command");
-  });
-
-  getFibManager().onFibRequest(command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_AUTO_TEST_CASE(UnsupportedVerb)
-{
-  shared_ptr<InternalFace> face = getInternalFace();
-
-  ControlParameters parameters;
-  parameters.setName("/hello");
-  parameters.setFaceId(1);
-  parameters.setCost(1);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/fib");
-  commandName.append("unsupported");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  face->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 501, "Unsupported command");
-  });
-
-  getFibManager().onFibRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_AUTO_TEST_CASE(UnsignedCommand)
-{
-  addFace(make_shared<DummyFace>());
-
-  shared_ptr<InternalFace> face = getInternalFace();
-
-  ControlParameters parameters;
-  parameters.setName("/hello");
-  parameters.setFaceId(1);
-  parameters.setCost(101);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/fib");
-  commandName.append("add-nexthop");
-  commandName.append(encodedParameters);
-
-  Interest command(commandName);
-
-  face->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command.getName(), 401, "Signature required");
-  });
-
-  getFibManager().onFibRequest(command);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(!addedNextHopWithCost(getFib(), "/hello", 0, 101));
-}
-
-BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand, UnauthorizedCommandFixture<FibManagerFixture>)
-{
-  addFace(make_shared<DummyFace>());
-
-  shared_ptr<InternalFace> face = getInternalFace();
-
-  ControlParameters parameters;
-  parameters.setName("/hello");
-  parameters.setFaceId(1);
-  parameters.setCost(101);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/fib");
-  commandName.append("add-nexthop");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  face->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 403, "Unauthorized command");
-  });
-
-  getFibManager().onFibRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(!addedNextHopWithCost(getFib(), "/hello", 0, 101));
-}
-
-BOOST_AUTO_TEST_CASE(BadOptionParse)
-{
-  addFace(make_shared<DummyFace>());
-
-  shared_ptr<InternalFace> face = getInternalFace();
-
-  Name commandName("/localhost/nfd/fib");
-  commandName.append("add-nexthop");
-  commandName.append("NotReallyParameters");
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  face->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  });
-
-  getFibManager().onFibRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
+BOOST_AUTO_TEST_SUITE(AddNextHop)
 
 BOOST_AUTO_TEST_CASE(UnknownFaceId)
 {
-  addFace(make_shared<DummyFace>());
+  auto command = makeControlCommandRequest("/localhost/nfd/fib/add-nexthop",
+                                           makeParameters("hello", FACEID_NULL, 101));
+  receiveInterest(command);
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
 
-  shared_ptr<InternalFace> face = getInternalFace();
+  // check response
+  BOOST_CHECK_EQUAL(checkResponse(0, command->getName(), ControlResponse(410, "Face not found")),
+                    CheckResponseResult::OK);
 
-  ControlParameters parameters;
-  parameters.setName("/hello");
-  parameters.setFaceId(1000);
-  parameters.setCost(101);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/fib");
-  commandName.append("add-nexthop");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  face->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 410, "Face not found");
-  });
-
-  getFibManager().onFibRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(addedNextHopWithCost(getFib(), "/hello", 0, 101) == false);
+  // double check that the next hop was not added
+  BOOST_CHECK_EQUAL(checkNextHop("/hello", -1, FACEID_NULL, 101), CheckNextHopResult::NO_FIB_ENTRY);
 }
 
-BOOST_AUTO_TEST_CASE(AddNextHopVerbImplicitFaceId)
+BOOST_AUTO_TEST_CASE(ImplicitFaceId)
 {
-  addFace(make_shared<DummyFace>());
+  auto face1 = addFace();
+  auto face2 = addFace();
+  BOOST_REQUIRE(face1 != INVALID_FACEID && face2 != INVALID_FACEID);
 
-  shared_ptr<InternalFace> face = getInternalFace();
+  Name expectedName;
+  ControlResponse expectedResponse;
+  auto testAddNextHop = [&] (ControlParameters parameters, const FaceId& faceId) {
+    auto command = makeControlCommandRequest("/localhost/nfd/fib/add-nexthop", parameters,
+                                             [&faceId] (shared_ptr<Interest> interest) {
+                                               interest->setIncomingFaceId(faceId);
+                                             });
+    m_responses.clear();
+    expectedName = command->getName();
+    expectedResponse = makeResponse(200, "Success", parameters.setFaceId(faceId));
+    receiveInterest(command);
+  };
 
-  std::vector<ControlParameters> testedParameters;
-  testedParameters.push_back(ControlParameters().setName("/hello").setCost(101).setFaceId(0));
-  testedParameters.push_back(ControlParameters().setName("/hello").setCost(101));
+  testAddNextHop(ControlParameters().setName("/hello").setCost(100).setFaceId(0), face1);
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkNextHop("/hello", 1, face1, 100), CheckNextHopResult::OK);
 
-  for (std::vector<ControlParameters>::iterator parameters = testedParameters.begin();
-       parameters != testedParameters.end(); ++parameters) {
-
-    Block encodedParameters(parameters->wireEncode());
-
-    Name commandName("/localhost/nfd/fib");
-    commandName.append("add-nexthop");
-    commandName.append(encodedParameters);
-
-    ControlParameters expectedParameters;
-    expectedParameters.setName("/hello");
-    expectedParameters.setFaceId(1);
-    expectedParameters.setCost(101);
-
-    Block encodedExpectedParameters(expectedParameters.wireEncode());
-
-    shared_ptr<Interest> command(make_shared<Interest>(commandName));
-    command->setIncomingFaceId(1);
-    generateCommand(*command);
-
-    signal::Connection conn = face->onReceiveData.connect(
-        [this, command, encodedExpectedParameters] (const Data& response) {
-          this->validateControlResponse(response, command->getName(),
-                                        200, "Success", encodedExpectedParameters);
-        });
-
-    getFibManager().onFibRequest(*command);
-
-    BOOST_REQUIRE(didCallbackFire());
-    BOOST_REQUIRE(addedNextHopWithFace(getFib(), "/hello", 0, 101, getFace(1)));
-
-    conn.disconnect();
-    getFib().erase("/hello");
-    BOOST_REQUIRE_EQUAL(getFib().size(), 0);
-  }
+  testAddNextHop(ControlParameters().setName("/hello").setCost(100), face2);
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkNextHop("/hello", 2, face2, 100), CheckNextHopResult::OK);
 }
 
-BOOST_AUTO_TEST_CASE(AddNextHopVerbInitialAdd)
+BOOST_AUTO_TEST_CASE(InitialAdd)
 {
-  addFace(make_shared<DummyFace>());
+  FaceId addedFaceId = addFace();
+  BOOST_REQUIRE(addedFaceId != INVALID_FACEID);
 
-  shared_ptr<InternalFace> face = getInternalFace();
+  auto parameters = makeParameters("hello", addedFaceId, 101);
+  auto command = makeControlCommandRequest("/localhost/nfd/fib/add-nexthop", parameters);
 
-  ControlParameters parameters;
-  parameters.setName("/hello");
-  parameters.setFaceId(1);
-  parameters.setCost(101);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/fib");
-  commandName.append("add-nexthop");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  face->onReceiveData.connect([this, command, encodedParameters] (const Data& response) {
-    this->validateControlResponse(response, command->getName(),
-                                  200, "Success", encodedParameters);
-  });
-
-  getFibManager().onFibRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(addedNextHopWithCost(getFib(), "/hello", 0, 101));
+  receiveInterest(command);
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, command->getName(), makeResponse(200, "Success", parameters)),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkNextHop("/hello", 1, addedFaceId, 101), CheckNextHopResult::OK);
 }
 
-BOOST_AUTO_TEST_CASE(AddNextHopVerbImplicitCost)
+BOOST_AUTO_TEST_CASE(ImplicitCost)
 {
-  addFace(make_shared<DummyFace>());
+  FaceId addedFaceId = addFace();
+  BOOST_REQUIRE(addedFaceId != INVALID_FACEID);
 
-  shared_ptr<InternalFace> face = getInternalFace();
+  auto originalParameters = ControlParameters().setName("/hello").setFaceId(addedFaceId);
+  auto parameters = makeParameters("/hello", addedFaceId, 0);
+  auto command = makeControlCommandRequest("/localhost/nfd/fib/add-nexthop", originalParameters);
 
-  ControlParameters parameters;
-  parameters.setName("/hello");
-  parameters.setFaceId(1);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/fib");
-  commandName.append("add-nexthop");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  ControlParameters resultParameters;
-  resultParameters.setName("/hello");
-  resultParameters.setFaceId(1);
-  resultParameters.setCost(0);
-
-  face->onReceiveData.connect([this, command, resultParameters] (const Data& response) {
-    this->validateControlResponse(response, command->getName(),
-                                  200, "Success", resultParameters.wireEncode());
-  });
-
-  getFibManager().onFibRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-  BOOST_REQUIRE(addedNextHopWithCost(getFib(), "/hello", 0, 0));
+  receiveInterest(command);
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, command->getName(), makeResponse(200, "Success", parameters)),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkNextHop("/hello", 1, addedFaceId, 0), CheckNextHopResult::OK);
 }
 
-BOOST_AUTO_TEST_CASE(AddNextHopVerbAddToExisting)
+BOOST_AUTO_TEST_CASE(AddToExisting)
 {
-  addFace(make_shared<DummyFace>());
-  shared_ptr<InternalFace> face = getInternalFace();
+  FaceId face = addFace();
+  BOOST_CHECK(face != INVALID_FACEID);
 
-  for (int i = 1; i <= 2; i++)
-    {
+  Name expectedName;
+  ControlResponse expectedResponse;
+  auto testAddNextHop = [&] (const ControlParameters& parameters) {
+    m_responses.clear();
+    auto command = makeControlCommandRequest("/localhost/nfd/fib/add-nexthop", parameters);
+    expectedName = command->getName();
+    expectedResponse = makeResponse(200, "Success", parameters);
+    receiveInterest(command);
+  };
 
-      ControlParameters parameters;
-      parameters.setName("/hello");
-      parameters.setFaceId(1);
-      parameters.setCost(100 + i);
+  // add initial, succeeds
+  testAddNextHop(makeParameters("/hello", face, 101));
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
 
-      Block encodedParameters(parameters.wireEncode());
+  // add to existing --> update cost, succeeds
+  testAddNextHop(makeParameters("/hello", face, 102));
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
 
-      Name commandName("/localhost/nfd/fib");
-      commandName.append("add-nexthop");
-      commandName.append(encodedParameters);
-
-      shared_ptr<Interest> command(make_shared<Interest>(commandName));
-      generateCommand(*command);
-
-      signal::Connection conn = face->onReceiveData.connect(
-          [this, command, encodedParameters] (const Data& response) {
-            this->validateControlResponse(response, command->getName(),
-                                          200, "Success", encodedParameters);
-          });
-
-      getFibManager().onFibRequest(*command);
-      BOOST_REQUIRE(didCallbackFire());
-      resetCallbackFired();
-
-      shared_ptr<fib::Entry> entry = getFib().findExactMatch("/hello");
-
-      if (static_cast<bool>(entry))
-        {
-          const fib::NextHopList& hops = entry->getNextHops();
-          BOOST_REQUIRE(hops.size() == 1);
-          BOOST_REQUIRE(std::find_if(hops.begin(), hops.end(),
-                                     bind(&foundNextHop, -1, 100 + i, _1)) != hops.end());
-
-        }
-      else
-        {
-          BOOST_FAIL("Failed to find expected fib entry");
-        }
-
-      conn.disconnect();
-    }
+  BOOST_CHECK_EQUAL(checkNextHop("/hello", 2, face, 102), CheckNextHopResult::WRONG_N_NEXTHOPS);
+  BOOST_CHECK_EQUAL(checkNextHop("/hello", 1, face, 101), CheckNextHopResult::WRONG_COST);
+  BOOST_CHECK_EQUAL(checkNextHop("/hello", 1, face, 102), CheckNextHopResult::OK);
 }
 
-BOOST_AUTO_TEST_CASE(AddNextHopVerbUpdateFaceCost)
+BOOST_AUTO_TEST_SUITE_END() // AddNextHop
+
+BOOST_AUTO_TEST_SUITE(RemoveNextHop)
+
+BOOST_AUTO_TEST_CASE(Basic)
 {
-  addFace(make_shared<DummyFace>());
-  shared_ptr<InternalFace> face = getInternalFace();
+  Name expectedName;
+  ControlResponse expectedResponse;
+  auto testRemoveNextHop = [&] (const ControlParameters& parameters) {
+    m_responses.clear();
+    auto command = makeControlCommandRequest("/localhost/nfd/fib/remove-nexthop", parameters);
+    expectedName = command->getName();
+    expectedResponse = makeResponse(200, "Success", parameters);
+    receiveInterest(command);
+  };
 
-  ControlParameters parameters;
-  parameters.setName("/hello");
-  parameters.setFaceId(1);
+  FaceId face1 = addFace();
+  FaceId face2 = addFace();
+  FaceId face3 = addFace();
+  BOOST_REQUIRE(face1 != INVALID_FACEID && face2 != INVALID_FACEID && face3 != INVALID_FACEID);
 
+  shared_ptr<fib::Entry> entry = m_fib.insert("/hello").first;
+  entry->addNextHop(m_faceTable.get(face1), 101);
+  entry->addNextHop(m_faceTable.get(face2), 202);
+  entry->addNextHop(m_faceTable.get(face3), 303);
+
+  testRemoveNextHop(makeParameters("/hello", face1));
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkNextHop("/hello", 2, face1, 101), CheckNextHopResult::NO_NEXTHOP);
+
+  testRemoveNextHop(makeParameters("/hello", face2));
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkNextHop("/hello", 1, face2, 202), CheckNextHopResult::NO_NEXTHOP);
+
+  testRemoveNextHop(makeParameters("/hello", face3));
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkNextHop("/hello", 0, face3, 303), CheckNextHopResult::NO_FIB_ENTRY);
+}
+
+BOOST_AUTO_TEST_CASE(PrefixNotFound)
+{
+  FaceId addedFaceId = addFace();
+  BOOST_CHECK(addedFaceId != INVALID_FACEID);
+
+  auto parameters = makeParameters("hello", addedFaceId);
+  auto command = makeControlCommandRequest("/localhost/nfd/fib/remove-nexthop", parameters);
+  auto response = makeResponse(200, "Success", parameters);
+
+  receiveInterest(command);
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, command->getName(), response), CheckResponseResult::OK);
+}
+
+BOOST_AUTO_TEST_CASE(ImplicitFaceId)
+{
+  auto face1 = addFace();
+  auto face2 = addFace();
+  BOOST_REQUIRE(face1 != INVALID_FACEID && face2 != INVALID_FACEID);
+
+  Name expectedName;
+  ControlResponse expectedResponse;
+  auto testWithImplicitFaceId = [&] (ControlParameters parameters, FaceId face) {
+    m_responses.clear();
+    auto command = makeControlCommandRequest("/localhost/nfd/fib/remove-nexthop", parameters,
+                                             [face] (shared_ptr<Interest> interest) {
+                                               interest->setIncomingFaceId(face);
+                                             });
+    expectedName = command->getName();
+    expectedResponse = makeResponse(200, "Success", parameters.setFaceId(face));
+    receiveInterest(command);
+  };
+
+  shared_ptr<fib::Entry> entry = m_fib.insert("/hello").first;
+  entry->addNextHop(m_faceTable.get(face1), 101);
+  entry->addNextHop(m_faceTable.get(face2), 202);
+
+  testWithImplicitFaceId(ControlParameters().setName("/hello").setFaceId(0), face1);
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkNextHop("/hello", 1, face1, 101), CheckNextHopResult::NO_NEXTHOP);
+
+  testWithImplicitFaceId(ControlParameters().setName("/hello"), face2);
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkNextHop("/hello", 0, face2, 202), CheckNextHopResult::NO_FIB_ENTRY);
+}
+
+BOOST_AUTO_TEST_CASE(RecordNotExist)
+{
+  auto face1 = addFace();
+  auto face2 = addFace();
+  BOOST_REQUIRE(face1 != INVALID_FACEID && face2 != INVALID_FACEID);
+
+  Name expectedName;
+  ControlResponse expectedResponse;
+  auto testRemoveNextHop = [&] (ControlParameters parameters) {
+    m_responses.clear();
+    auto command = makeControlCommandRequest("/localhost/nfd/fib/remove-nexthop", parameters);
+    expectedName = command->getName();
+    expectedResponse = makeResponse(200, "Success", parameters);
+    receiveInterest(command);
+  };
+
+  m_fib.insert("/hello").first->addNextHop(m_faceTable.get(face1), 101);
+
+  testRemoveNextHop(makeParameters("/hello", face2 + 100));
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1); // face does not exist
+  BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkNextHop("/hello", -1, face2 + 100), CheckNextHopResult::NO_NEXTHOP);
+
+  testRemoveNextHop(makeParameters("/hello", face2));
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1); // record does not exist
+  BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(checkNextHop("/hello", -1, face2), CheckNextHopResult::NO_NEXTHOP);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // RemoveNextHop
+
+// @todo Remove when ndn::nfd::FibEntry implements operator!= and operator<<
+class FibEntry : public ndn::nfd::FibEntry
+{
+public:
+  FibEntry() = default;
+
+  FibEntry(const ndn::nfd::FibEntry& entry)
+    : ndn::nfd::FibEntry(entry)
   {
-    parameters.setCost(1);
-
-    Block encodedParameters(parameters.wireEncode());
-
-    Name commandName("/localhost/nfd/fib");
-    commandName.append("add-nexthop");
-    commandName.append(encodedParameters);
-
-    shared_ptr<Interest> command(make_shared<Interest>(commandName));
-    generateCommand(*command);
-
-    signal::Connection conn = face->onReceiveData.connect(
-        [this, command, encodedParameters] (const Data& response) {
-          this->validateControlResponse(response, command->getName(),
-                                        200, "Success", encodedParameters);
-        });
-
-    getFibManager().onFibRequest(*command);
-
-    BOOST_REQUIRE(didCallbackFire());
-
-    resetCallbackFired();
-    conn.disconnect();
   }
-
-  {
-    parameters.setCost(102);
-
-    Block encodedParameters(parameters.wireEncode());
-
-    Name commandName("/localhost/nfd/fib");
-    commandName.append("add-nexthop");
-    commandName.append(encodedParameters);
-
-    shared_ptr<Interest> command(make_shared<Interest>(commandName));
-    generateCommand(*command);
-
-    face->onReceiveData.connect([this, command, encodedParameters] (const Data& response) {
-      this->validateControlResponse(response, command->getName(),
-                                    200, "Success", encodedParameters);
-    });
-
-    getFibManager().onFibRequest(*command);
-
-    BOOST_REQUIRE(didCallbackFire());
-  }
-
-  shared_ptr<fib::Entry> entry = getFib().findExactMatch("/hello");
-
-  // Add faces with cost == FaceID for the name /hello
-  // This test assumes:
-  //   FaceIDs are -1 because we don't add them to a forwarder
-  if (static_cast<bool>(entry))
-    {
-      const fib::NextHopList& hops = entry->getNextHops();
-      BOOST_REQUIRE(hops.size() == 1);
-      BOOST_REQUIRE(std::find_if(hops.begin(),
-                                 hops.end(),
-                                 bind(&foundNextHop, -1, 102, _1)) != hops.end());
-    }
-  else
-    {
-      BOOST_FAIL("Failed to find expected fib entry");
-    }
-}
-
-BOOST_AUTO_TEST_CASE(AddNextHopVerbMissingPrefix)
-{
-  addFace(make_shared<DummyFace>());
-
-  shared_ptr<InternalFace> face = getInternalFace();
-
-  ControlParameters parameters;
-  parameters.setFaceId(1);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/fib");
-  commandName.append("add-nexthop");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  face->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  });
-
-  getFibManager().onFibRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
+};
 
 bool
-removedNextHopWithCost(const Fib& fib, const Name& prefix, size_t oldSize, uint32_t cost)
+operator!=(const FibEntry& left, const FibEntry& right)
 {
-  shared_ptr<fib::Entry> entry = fib.findExactMatch(prefix);
+  if (left.getPrefix() != right.getPrefix()) {
+    return true;
+  }
 
-  if (static_cast<bool>(entry))
-    {
-      const fib::NextHopList& hops = entry->getNextHops();
-      return hops.size() == oldSize - 1 &&
-        std::find_if(hops.begin(), hops.end(), bind(&foundNextHop, -1, cost, _1)) == hops.end();
+  auto leftNextHops = left.getNextHopRecords();
+  auto rightNextHops = right.getNextHopRecords();
+  if (leftNextHops.size() != rightNextHops.size()) {
+    return true;
+  }
+
+  for (auto&& nexthop : leftNextHops) {
+    auto hitEntry =
+      std::find_if(rightNextHops.begin(), rightNextHops.end(), [&] (const ndn::nfd::NextHopRecord& record) {
+          return nexthop.getCost() == record.getCost() && nexthop.getFaceId() == record.getFaceId();
+        });
+
+    if (hitEntry == rightNextHops.end()) {
+      return true;
     }
+  }
+
   return false;
 }
 
-void
-testRemoveNextHop(CommandFixture<FibManagerFixture>* fixture,
-                  FibManager& manager,
-                  Fib& fib,
-                  shared_ptr<Face> face,
-                  const Name& targetName,
-                  FaceId targetFace)
+std::ostream&
+operator<<(std::ostream &os, const FibEntry& entry)
 {
-  ControlParameters parameters;
-  parameters.setName(targetName);
-  parameters.setFaceId(targetFace);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/fib");
-  commandName.append("remove-nexthop");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  fixture->generateCommand(*command);
-
-  signal::Connection conn = face->onReceiveData.connect(
-      [fixture, command, encodedParameters] (const Data& response) {
-        fixture->validateControlResponse(response, command->getName(),
-                                         200, "Success", encodedParameters);
-      });
-
-  manager.onFibRequest(*command);
-
-  BOOST_REQUIRE(fixture->didCallbackFire());
-
-  fixture->resetCallbackFired();
-  conn.disconnect();
-}
-
-BOOST_AUTO_TEST_CASE(RemoveNextHop)
-{
-  shared_ptr<Face> face1 = make_shared<DummyFace>();
-  shared_ptr<Face> face2 = make_shared<DummyFace>();
-  shared_ptr<Face> face3 = make_shared<DummyFace>();
-
-  addFace(face1);
-  addFace(face2);
-  addFace(face3);
-
-  shared_ptr<InternalFace> face = getInternalFace();
-  FibManager& manager = getFibManager();
-  Fib& fib = getFib();
-
-  shared_ptr<fib::Entry> entry = fib.insert("/hello").first;
-
-  entry->addNextHop(face1, 101);
-  entry->addNextHop(face2, 202);
-  entry->addNextHop(face3, 303);
-
-  testRemoveNextHop(this, manager, fib, face, "/hello", 2);
-  BOOST_REQUIRE(removedNextHopWithCost(fib, "/hello", 3, 202));
-
-  testRemoveNextHop(this, manager, fib, face, "/hello", 3);
-  BOOST_REQUIRE(removedNextHopWithCost(fib, "/hello", 2, 303));
-
-  testRemoveNextHop(this, manager, fib, face, "/hello", 1);
-  // BOOST_REQUIRE(removedNextHopWithCost(fib, "/hello", 1, 101));
-
-  BOOST_CHECK(!static_cast<bool>(getFib().findExactMatch("/hello")));
-}
-
-BOOST_AUTO_TEST_CASE(RemoveFaceNotFound)
-{
-  shared_ptr<InternalFace> face = getInternalFace();
-
-  ControlParameters parameters;
-  parameters.setName("/hello");
-  parameters.setFaceId(1);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/fib");
-  commandName.append("remove-nexthop");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  face->onReceiveData.connect([this, command, encodedParameters] (const Data& response) {
-    this->validateControlResponse(response, command->getName(),
-                                  200, "Success", encodedParameters);
-  });
-
-  getFibManager().onFibRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_AUTO_TEST_CASE(RemovePrefixNotFound)
-{
-  addFace(make_shared<DummyFace>());
-
-  shared_ptr<InternalFace> face = getInternalFace();
-
-  ControlParameters parameters;
-  parameters.setName("/hello");
-  parameters.setFaceId(1);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/fib");
-  commandName.append("remove-nexthop");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  face->onReceiveData.connect([this, command, encodedParameters] (const Data& response) {
-    this->validateControlResponse(response, command->getName(),
-                                  200, "Success", encodedParameters);
-  });
-
-  getFibManager().onFibRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_AUTO_TEST_CASE(RemoveMissingPrefix)
-{
-  addFace(make_shared<DummyFace>());
-
-  shared_ptr<InternalFace> face = getInternalFace();
-
-  ControlParameters parameters;
-  parameters.setFaceId(1);
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/fib");
-  commandName.append("remove-nexthop");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  face->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  });
-
-  getFibManager().onFibRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_AUTO_TEST_CASE(RemoveImplicitFaceId)
-{
-  addFace(make_shared<DummyFace>());
-
-  shared_ptr<InternalFace> face = getInternalFace();
-
-  std::vector<ControlParameters> testedParameters;
-  testedParameters.push_back(ControlParameters().setName("/hello").setFaceId(0));
-  testedParameters.push_back(ControlParameters().setName("/hello"));
-
-  for (std::vector<ControlParameters>::iterator parameters = testedParameters.begin();
-       parameters != testedParameters.end(); ++parameters) {
-    Block encodedParameters(parameters->wireEncode());
-
-    Name commandName("/localhost/nfd/fib");
-    commandName.append("remove-nexthop");
-    commandName.append(encodedParameters);
-
-    shared_ptr<Interest> command(make_shared<Interest>(commandName));
-    command->setIncomingFaceId(1);
-    generateCommand(*command);
-
-    ControlParameters resultParameters;
-    resultParameters.setFaceId(1);
-    resultParameters.setName("/hello");
-
-    signal::Connection conn = face->onReceiveData.connect(
-        [this, command, resultParameters] (const Data& response) {
-          this->validateControlResponse(response, command->getName(),
-                                        200, "Success", resultParameters.wireEncode());
-        });
-
-    getFibManager().onFibRequest(*command);
-
-    BOOST_REQUIRE(didCallbackFire());
-
-    conn.disconnect();
+  const auto& nexthops = entry.getNextHopRecords();
+  os << "[" << entry.getPrefix() << ", " << nexthops.size() << ": ";
+  for (auto record : nexthops) {
+    os << "{" << record.getFaceId() << ", " << record.getCost() << "} ";
   }
+  os << "]";
+
+  return os;
 }
 
-BOOST_FIXTURE_TEST_CASE(TestFibEnumerationRequest, FibManagerFixture)
+BOOST_AUTO_TEST_CASE(FibDataset)
 {
-  for (int i = 0; i < 87; i++)
-    {
-      Name prefix("/test");
-      prefix.appendSegment(i);
+  const size_t nEntries = 108;
+  std::set<Name> actualPrefixes;
+  for (size_t i = 0 ; i < nEntries ; i ++) {
+    Name prefix = Name("test").appendSegment(i);
+    actualPrefixes.insert(prefix);
+    auto fibEntry = m_fib.insert(prefix).first;
+    fibEntry->addNextHop(m_faceTable.get(addFace()), std::numeric_limits<uint8_t>::max() - 1);
+    fibEntry->addNextHop(m_faceTable.get(addFace()), std::numeric_limits<uint8_t>::max() - 2);
+  }
 
-      shared_ptr<DummyFace> dummy1(make_shared<DummyFace>());
-      shared_ptr<DummyFace> dummy2(make_shared<DummyFace>());
+  receiveInterest(makeInterest("/localhost/nfd/fib/list"));
 
-      shared_ptr<fib::Entry> entry = m_fib.insert(prefix).first;
-      entry->addNextHop(dummy1, std::numeric_limits<uint64_t>::max() - 1);
-      entry->addNextHop(dummy2, std::numeric_limits<uint64_t>::max() - 2);
+  Block content;
+  BOOST_CHECK_NO_THROW(content = concatenateResponses());
+  BOOST_CHECK_NO_THROW(content.parse());
+  BOOST_REQUIRE_EQUAL(content.elements().size(), nEntries);
 
-      m_referenceEntries.insert(entry);
+  std::vector<FibEntry> receivedRecords, expectedRecords;
+  for (size_t idx = 0; idx < nEntries; ++idx) {
+    BOOST_TEST_MESSAGE("processing element: " << idx);
+
+    FibEntry decodedEntry;
+    BOOST_REQUIRE_NO_THROW(decodedEntry.wireDecode(content.elements()[idx]));
+    receivedRecords.push_back(decodedEntry);
+
+    actualPrefixes.erase(decodedEntry.getPrefix());
+
+    auto matchedEntry = m_fib.findExactMatch(decodedEntry.getPrefix());
+    BOOST_REQUIRE(matchedEntry != nullptr);
+
+    FibEntry record;
+    record.setPrefix(matchedEntry->getPrefix());
+    const auto& nextHops = matchedEntry->getNextHops();
+    for (auto&& next : nextHops) {
+      ndn::nfd::NextHopRecord nextHopRecord;
+      nextHopRecord.setFaceId(next.getFace()->getId());
+      nextHopRecord.setCost(next.getCost());
+      record.addNextHopRecord(nextHopRecord);
     }
-  for (int i = 0; i < 2; i++)
-    {
-      Name prefix("/test2");
-      prefix.appendSegment(i);
+    expectedRecords.push_back(record);
+  }
 
-      shared_ptr<DummyFace> dummy1(make_shared<DummyFace>());
-      shared_ptr<DummyFace> dummy2(make_shared<DummyFace>());
+  BOOST_CHECK_EQUAL(actualPrefixes.size(), 0);
 
-      shared_ptr<fib::Entry> entry = m_fib.insert(prefix).first;
-      entry->addNextHop(dummy1, std::numeric_limits<uint8_t>::max() - 1);
-      entry->addNextHop(dummy2, std::numeric_limits<uint8_t>::max() - 2);
-
-      m_referenceEntries.insert(entry);
-    }
-
-  ndn::EncodingBuffer buffer;
-
-  m_face->onReceiveData.connect(bind(&FibEnumerationPublisherFixture::decodeFibEntryBlock,
-                                     this, _1));
-
-  shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/fib/list"));
-
-  m_manager.onFibRequest(*command);
-  BOOST_REQUIRE(m_finished);
+  BOOST_CHECK_EQUAL_COLLECTIONS(receivedRecords.begin(), receivedRecords.end(),
+                                expectedRecords.begin(), expectedRecords.end());
 }
 
-BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END() // TestFibManager
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
 
 } // namespace tests
 } // namespace nfd
diff --git a/tests/daemon/mgmt/status-server.t.cpp b/tests/daemon/mgmt/forwarder-status-manager.t.cpp
similarity index 78%
rename from tests/daemon/mgmt/status-server.t.cpp
rename to tests/daemon/mgmt/forwarder-status-manager.t.cpp
index 4cf7c13..104adaa 100644
--- a/tests/daemon/mgmt/status-server.t.cpp
+++ b/tests/daemon/mgmt/forwarder-status-manager.t.cpp
@@ -23,39 +23,35 @@
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "mgmt/status-server.hpp"
+#include "mgmt/forwarder-status-manager.hpp"
 #include "fw/forwarder.hpp"
 #include "version.hpp"
-#include "mgmt/internal-face.hpp"
 
 #include "tests/test-common.hpp"
 #include "tests/daemon/face/dummy-face.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+#include <ndn-cxx/mgmt/dispatcher.hpp>
 
 namespace nfd {
 namespace tests {
 
-BOOST_FIXTURE_TEST_SUITE(MgmtStatusServer, BaseFixture)
-
-shared_ptr<const Data> g_response;
-
-void
-interceptResponse(const Data& data)
-{
-  g_response = data.shared_from_this();
-}
+BOOST_FIXTURE_TEST_SUITE(Mgmt, UnitTestTimeFixture)
+BOOST_AUTO_TEST_SUITE(TestForwarderStatusManager)
 
 BOOST_AUTO_TEST_CASE(Status)
 {
   // initialize
   time::system_clock::TimePoint t1 = time::system_clock::now();
   Forwarder forwarder;
-  shared_ptr<InternalFace> internalFace = make_shared<InternalFace>();
-  internalFace->onReceiveData.connect(&interceptResponse);
+  auto face = ndn::util::makeDummyClientFace(g_io, {true, true});
   ndn::KeyChain keyChain;
-  StatusServer statusServer(internalFace, ref(forwarder), keyChain);
-  time::system_clock::TimePoint t2 = time::system_clock::now();
+  ndn::mgmt::Dispatcher dispatcher(*face, ref(keyChain));
+  ForwarderStatusManager statusServer(ref(forwarder), ref(dispatcher));
+  dispatcher.addTopPrefix("/localhost/nfd");
+  advanceClocks(time::milliseconds(1));
 
   // populate tables
+  time::system_clock::TimePoint t2 = time::system_clock::now();
   forwarder.getFib().insert("ndn:/fib1");
   forwarder.getPit().insert(*makeInterest("ndn:/pit1"));
   forwarder.getPit().insert(*makeInterest("ndn:/pit2"));
@@ -69,20 +65,19 @@
   BOOST_CHECK_GE(forwarder.getMeasurements().size(), 3);
 
   // request
-  shared_ptr<Interest> request = makeInterest("ndn:/localhost/nfd/status");
+  time::system_clock::TimePoint t3 = time::system_clock::now();
+  auto request = makeInterest("ndn:/localhost/nfd/status");
   request->setMustBeFresh(true);
   request->setChildSelector(1);
 
-  g_response.reset();
-  time::system_clock::TimePoint t3 = time::system_clock::now();
-  internalFace->sendInterest(*request);
-  g_io.run_one();
-  time::system_clock::TimePoint t4 = time::system_clock::now();
-  BOOST_REQUIRE(static_cast<bool>(g_response));
+  face->receive<Interest>(*request);
+  advanceClocks(time::milliseconds(1));
 
   // verify
+  time::system_clock::TimePoint t4 = time::system_clock::now();
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
   ndn::nfd::ForwarderStatus status;
-  BOOST_REQUIRE_NO_THROW(status.wireDecode(g_response->getContent()));
+  BOOST_REQUIRE_NO_THROW(status.wireDecode(face->sentDatas[0].getContent()));
 
   BOOST_CHECK_EQUAL(status.getNfdVersion(), NFD_VERSION_BUILD_STRING);
   BOOST_CHECK_GE(time::toUnixTimestamp(status.getStartTimestamp()), time::toUnixTimestamp(t1));
@@ -90,7 +85,7 @@
   BOOST_CHECK_GE(time::toUnixTimestamp(status.getCurrentTimestamp()), time::toUnixTimestamp(t3));
   BOOST_CHECK_LE(time::toUnixTimestamp(status.getCurrentTimestamp()), time::toUnixTimestamp(t4));
 
-  // StatusServer under test isn't added to Forwarder,
+  // Interest/Data toward ForwarderStatusManager don't go through Forwarder,
   // so request and response won't affect table size
   BOOST_CHECK_EQUAL(status.getNNameTreeEntries(), forwarder.getNameTree().size());
   BOOST_CHECK_EQUAL(status.getNFibEntries(), forwarder.getFib().size());
@@ -99,7 +94,8 @@
   BOOST_CHECK_EQUAL(status.getNCsEntries(), forwarder.getCs().size());
 }
 
-BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END() // TestForwarderStatusManager
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
 
 } // namespace tests
 } // namespace nfd
diff --git a/tests/daemon/mgmt/internal-face.t.cpp b/tests/daemon/mgmt/internal-face.t.cpp
deleted file mode 100644
index 29b602c..0000000
--- a/tests/daemon/mgmt/internal-face.t.cpp
+++ /dev/null
@@ -1,238 +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 "mgmt/internal-face.hpp"
-#include "tests/daemon/face/dummy-face.hpp"
-
-#include "tests/test-common.hpp"
-
-namespace nfd {
-namespace tests {
-
-class InternalFaceFixture : protected BaseFixture
-{
-public:
-
-  InternalFaceFixture()
-    : m_onInterestFired(false),
-      m_noOnInterestFired(false)
-  {
-
-  }
-
-  void
-  validateOnInterestCallback(const Name& name, const Interest& interest)
-  {
-    m_onInterestFired = true;
-  }
-
-  void
-  validateNoOnInterestCallback(const Name& name, const Interest& interest)
-  {
-    m_noOnInterestFired = true;
-  }
-
-  void
-  addFace(shared_ptr<Face> face)
-  {
-    m_faces.push_back(face);
-  }
-
-  bool
-  didOnInterestFire()
-  {
-    return m_onInterestFired;
-  }
-
-  bool
-  didNoOnInterestFire()
-  {
-    return m_noOnInterestFired;
-  }
-
-  void
-  resetOnInterestFired()
-  {
-    m_onInterestFired = false;
-  }
-
-  void
-  resetNoOnInterestFired()
-  {
-    m_noOnInterestFired = false;
-  }
-
-protected:
-  ndn::KeyChain m_keyChain;
-
-private:
-  std::vector<shared_ptr<Face> > m_faces;
-  bool m_onInterestFired;
-  bool m_noOnInterestFired;
-};
-
-BOOST_FIXTURE_TEST_SUITE(MgmtInternalFace, InternalFaceFixture)
-
-void
-validatePutData(bool& called, const Name& expectedName, const Data& data)
-{
-  called = true;
-  BOOST_CHECK_EQUAL(expectedName, data.getName());
-}
-
-BOOST_AUTO_TEST_CASE(PutData)
-{
-  addFace(make_shared<DummyFace>());
-
-  shared_ptr<InternalFace> face(new InternalFace);
-
-  bool didPutData = false;
-  Name dataName("/hello");
-  face->onReceiveData.connect(bind(&validatePutData, ref(didPutData), dataName, _1));
-
-  Data testData(dataName);
-  m_keyChain.sign(testData);
-  face->put(testData);
-
-  BOOST_REQUIRE(didPutData);
-
-  BOOST_CHECK_THROW(face->close(), InternalFace::Error);
-}
-
-BOOST_AUTO_TEST_CASE(SendInterestHitEnd)
-{
-  addFace(make_shared<DummyFace>());
-
-  shared_ptr<InternalFace> face(new InternalFace);
-
-  face->setInterestFilter("/localhost/nfd/fib",
-                          bind(&InternalFaceFixture::validateOnInterestCallback,
-                               this, _1, _2));
-
-  // generate command whose name is canonically
-  // ordered after /localhost/nfd/fib so that
-  // we hit the end of the std::map
-
-  Name commandName("/localhost/nfd/fib/end");
-  shared_ptr<Interest> command = makeInterest(commandName);
-  face->sendInterest(*command);
-  g_io.run_one();
-
-  BOOST_REQUIRE(didOnInterestFire());
-  BOOST_REQUIRE(didNoOnInterestFire() == false);
-}
-
-BOOST_AUTO_TEST_CASE(SendInterestHitBegin)
-{
-  addFace(make_shared<DummyFace>());
-
-  shared_ptr<InternalFace> face(new InternalFace);
-
-  face->setInterestFilter("/localhost/nfd/fib",
-                          bind(&InternalFaceFixture::validateNoOnInterestCallback,
-                               this, _1, _2));
-
-  // generate command whose name is canonically
-  // ordered before /localhost/nfd/fib so that
-  // we hit the beginning of the std::map
-
-  Name commandName("/localhost/nfd");
-  shared_ptr<Interest> command = makeInterest(commandName);
-  face->sendInterest(*command);
-  g_io.run_one();
-
-  BOOST_REQUIRE(didNoOnInterestFire() == false);
-}
-
-BOOST_AUTO_TEST_CASE(SendInterestHitExact)
-{
-  addFace(make_shared<DummyFace>());
-
-  shared_ptr<InternalFace> face(new InternalFace);
-
-  face->setInterestFilter("/localhost/nfd/eib",
-                          bind(&InternalFaceFixture::validateNoOnInterestCallback,
-                               this, _1, _2));
-
-  face->setInterestFilter("/localhost/nfd/fib",
-                          bind(&InternalFaceFixture::validateOnInterestCallback,
-                           this, _1, _2));
-
-  face->setInterestFilter("/localhost/nfd/gib",
-                          bind(&InternalFaceFixture::validateNoOnInterestCallback,
-                               this, _1, _2));
-
-  // generate command whose name exactly matches
-  // /localhost/nfd/fib
-
-  Name commandName("/localhost/nfd/fib");
-  shared_ptr<Interest> command = makeInterest(commandName);
-  face->sendInterest(*command);
-  g_io.run_one();
-
-  BOOST_REQUIRE(didOnInterestFire());
-  BOOST_REQUIRE(didNoOnInterestFire() == false);
-}
-
-BOOST_AUTO_TEST_CASE(SendInterestHitPrevious)
-{
-  addFace(make_shared<DummyFace>());
-
-  shared_ptr<InternalFace> face(new InternalFace);
-
-  face->setInterestFilter("/localhost/nfd/fib",
-                          bind(&InternalFaceFixture::validateOnInterestCallback,
-                               this, _1, _2));
-
-  face->setInterestFilter("/localhost/nfd/fib/zzzzzzzzzzzzz/",
-                          bind(&InternalFaceFixture::validateNoOnInterestCallback,
-                               this, _1, _2));
-
-  // generate command whose name exactly matches
-  // an Interest filter
-
-  Name commandName("/localhost/nfd/fib/previous");
-  shared_ptr<Interest> command = makeInterest(commandName);
-  face->sendInterest(*command);
-  g_io.run_one();
-
-  BOOST_REQUIRE(didOnInterestFire());
-  BOOST_REQUIRE(didNoOnInterestFire() == false);
-}
-
-BOOST_AUTO_TEST_CASE(InterestGone)
-{
-  shared_ptr<InternalFace> face = make_shared<InternalFace>();
-  shared_ptr<Interest> interest = makeInterest("ndn:/localhost/nfd/gone");
-  face->sendInterest(*interest);
-
-  interest.reset();
-  BOOST_CHECK_NO_THROW(g_io.poll());
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-} // namespace tests
-} // namespace nfd
diff --git a/tests/daemon/mgmt/manager-base.t.cpp b/tests/daemon/mgmt/manager-base.t.cpp
index 1b78175..f3ea152 100644
--- a/tests/daemon/mgmt/manager-base.t.cpp
+++ b/tests/daemon/mgmt/manager-base.t.cpp
@@ -24,177 +24,164 @@
  */
 
 #include "mgmt/manager-base.hpp"
-#include "mgmt/internal-face.hpp"
+#include "manager-common-fixture.hpp"
 
-#include "tests/test-common.hpp"
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/management/nfd-control-command.hpp>
 
 namespace nfd {
 namespace tests {
 
 NFD_LOG_INIT("ManagerBaseTest");
 
-class ManagerBaseTest : public ManagerBase, protected BaseFixture
+class TestCommandVoidParameters : public ndn::nfd::ControlCommand
 {
-
 public:
-
-  ManagerBaseTest()
-    : ManagerBase(make_shared<InternalFace>(), "TEST-PRIVILEGE", m_keyChain),
-      m_callbackFired(false)
+  TestCommandVoidParameters()
+    : ndn::nfd::ControlCommand("test-module", "test-void-parameters")
   {
-
   }
-
-  void
-  testSetResponse(ControlResponse& response,
-                  uint32_t code,
-                  const std::string& text)
-  {
-    setResponse(response, code, text);
-  }
-
-  void
-  testSendResponse(const Name& name,
-                   uint32_t code,
-                   const std::string& text,
-                   const Block& body)
-  {
-    sendResponse(name, code, text, body);
-  }
-
-  void
-  testSendResponse(const Name& name,
-                   uint32_t code,
-                   const std::string& text)
-  {
-    sendResponse(name, code, text);
-  }
-
-  void
-  testSendResponse(const Name& name,
-                   const ControlResponse& response)
-  {
-    sendResponse(name, response);
-  }
-
-  shared_ptr<InternalFace>
-  getInternalFace()
-  {
-    return static_pointer_cast<InternalFace>(m_face);
-  }
-
-  void
-  validateControlResponse(const Data& response,
-                          const Name& expectedName,
-                          uint32_t expectedCode,
-                          const std::string& expectedText)
-  {
-    m_callbackFired = true;
-    Block controlRaw = response.getContent().blockFromValue();
-
-    ControlResponse control;
-    control.wireDecode(controlRaw);
-
-    NFD_LOG_DEBUG("received control response"
-                  << " name: " << response.getName()
-                  << " code: " << control.getCode()
-                  << " text: " << control.getText());
-
-    BOOST_REQUIRE(response.getName() == expectedName);
-    BOOST_REQUIRE(control.getCode() == expectedCode);
-    BOOST_REQUIRE(control.getText() == expectedText);
-  }
-
-  void
-  validateControlResponse(const Data& response,
-                          const Name& expectedName,
-                          uint32_t expectedCode,
-                          const std::string& expectedText,
-                          const Block& expectedBody)
-  {
-    m_callbackFired = true;
-    Block controlRaw = response.getContent().blockFromValue();
-
-    ControlResponse control;
-    control.wireDecode(controlRaw);
-
-    NFD_LOG_DEBUG("received control response"
-                  << " name: " << response.getName()
-                  << " code: " << control.getCode()
-                  << " text: " << control.getText());
-
-    BOOST_REQUIRE(response.getName() == expectedName);
-    BOOST_REQUIRE(control.getCode() == expectedCode);
-    BOOST_REQUIRE(control.getText() == expectedText);
-
-    BOOST_REQUIRE(control.getBody().value_size() == expectedBody.value_size());
-
-    BOOST_CHECK(memcmp(control.getBody().value(), expectedBody.value(),
-                       expectedBody.value_size()) == 0);
-  }
-
-  bool
-  didCallbackFire()
-  {
-    return m_callbackFired;
-  }
-
-private:
-
-  bool m_callbackFired;
-  ndn::KeyChain m_keyChain;
-
 };
 
-BOOST_FIXTURE_TEST_SUITE(MgmtManagerBase, ManagerBaseTest)
-
-BOOST_AUTO_TEST_CASE(SetResponse)
+class TestCommandRequireName : public ndn::nfd::ControlCommand
 {
-  ControlResponse response(200, "OK");
+public:
+  TestCommandRequireName()
+    : ndn::nfd::ControlCommand("test-module", "test-require-name")
+  {
+    m_requestValidator.required(ndn::nfd::CONTROL_PARAMETER_NAME);
+  }
+};
 
-  BOOST_CHECK_EQUAL(response.getCode(), 200);
-  BOOST_CHECK_EQUAL(response.getText(), "OK");
+class ManagerBaseFixture : public ManagerCommonFixture
+{
+public:
+  ManagerBaseFixture()
+    : m_manager(m_dispatcher, m_validator, "test-module")
+  {
+  }
 
-  testSetResponse(response, 100, "test");
+protected:
+  ManagerBase m_manager;
+};
 
-  BOOST_CHECK_EQUAL(response.getCode(), 100);
-  BOOST_CHECK_EQUAL(response.getText(), "test");
+BOOST_FIXTURE_TEST_SUITE(MgmtManagerBase, ManagerBaseFixture)
+
+BOOST_AUTO_TEST_CASE(AddSupportedPrivilegeInConstructor)
+{
+  BOOST_CHECK_NO_THROW(m_validator.addSupportedPrivilege("other-module"));
+  // test-module has already been added by the constructor of ManagerBase
+  BOOST_CHECK_THROW(m_validator.addSupportedPrivilege("test-module"), CommandValidator::Error);
 }
 
-BOOST_AUTO_TEST_CASE(SendResponse4Arg)
+BOOST_AUTO_TEST_CASE(RegisterCommandHandler)
 {
-  ndn::nfd::ControlParameters parameters;
-  parameters.setName("/test/body");
+  bool wasCommandHandlerCalled = false;
+  auto handler = bind([&] { wasCommandHandlerCalled = true; });
 
-  getInternalFace()->onReceiveData.connect([this, parameters] (const Data& response) {
-    this->validateControlResponse(response, "/response", 100, "test", parameters.wireEncode());
-  });
+  m_manager.registerCommandHandler<TestCommandVoidParameters>("test-void", handler);
+  m_manager.registerCommandHandler<TestCommandRequireName>("test-require-name", handler);
+  setTopPrefixAndPrivilege("/localhost/nfd", "test-module");
 
-  testSendResponse("/response", 100, "test", parameters.wireEncode());
-  BOOST_REQUIRE(didCallbackFire());
+  auto testRegisterCommandHandler = [&wasCommandHandlerCalled, this] (const Name& commandName) {
+    wasCommandHandlerCalled = false;
+    receiveInterest(makeControlCommandRequest(commandName, ControlParameters()));
+  };
+
+  testRegisterCommandHandler("/localhost/nfd/test-module/test-void");
+  BOOST_CHECK(wasCommandHandlerCalled);
+
+  testRegisterCommandHandler("/localhost/nfd/test-module/test-require-name");
+  BOOST_CHECK(!wasCommandHandlerCalled);
 }
 
-
-BOOST_AUTO_TEST_CASE(SendResponse3Arg)
+BOOST_AUTO_TEST_CASE(RegisterStatusDataset)
 {
-  getInternalFace()->onReceiveData.connect([this] (const Data& response) {
-    this->validateControlResponse(response, "/response", 100, "test");
-  });
+  bool isStatusDatasetCalled = false;
+  auto handler = bind([&] { isStatusDatasetCalled = true; });
 
-  testSendResponse("/response", 100, "test");
-  BOOST_REQUIRE(didCallbackFire());
+  m_manager.registerStatusDatasetHandler("test-status", handler);
+  m_dispatcher.addTopPrefix("/localhost/nfd");
+  advanceClocks(time::milliseconds(1));
+
+  receiveInterest(makeInterest("/localhost/nfd/test-module/test-status"));
+  BOOST_CHECK(isStatusDatasetCalled);
 }
 
-BOOST_AUTO_TEST_CASE(SendResponse2Arg)
+BOOST_AUTO_TEST_CASE(RegisterNotificationStream)
 {
-  getInternalFace()->onReceiveData.connect([this] (const Data& response) {
-    this->validateControlResponse(response, "/response", 100, "test");
-  });
+  auto post = m_manager.registerNotificationStream("test-notification");
+  m_dispatcher.addTopPrefix("/localhost/nfd");
+  advanceClocks(time::milliseconds(1));
 
-  ControlResponse response(100, "test");
+  post(Block("\x82\x01\x02", 3));
+  advanceClocks(time::milliseconds(1));
 
-  testSendResponse("/response", 100, "test");
-  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(m_responses[0].getName(),
+                    Name("/localhost/nfd/test-module/test-notification/%FE%00"));
+}
+
+BOOST_AUTO_TEST_CASE(CommandAuthorization)
+{
+  bool didAcceptCallbackFire = false;
+  bool didRejectCallbackFire = false;
+  auto testAuthorization = [&] {
+    didAcceptCallbackFire = false;
+    didRejectCallbackFire = false;
+
+    auto command = makeControlCommandRequest("/localhost/nfd/test-module/test-verb",
+                                             ControlParameters());
+    ndn::nfd::ControlParameters params;
+    m_manager.authorize("/top/prefix", *command, &params,
+                        bind([&] { didAcceptCallbackFire = true; }),
+                        bind([&] { didRejectCallbackFire = true; }));
+  };
+
+  testAuthorization();
+  BOOST_CHECK(!didAcceptCallbackFire);
+  BOOST_CHECK(didRejectCallbackFire);
+
+  m_validator.addInterestRule("^<localhost><nfd><test-module>", *m_certificate);
+  testAuthorization();
+  BOOST_CHECK(didAcceptCallbackFire);
+  BOOST_CHECK(!didRejectCallbackFire);
+}
+
+BOOST_AUTO_TEST_CASE(ExtractRequester)
+{
+  std::string requesterName;
+  auto testAccept = [&] (const std::string& requester) { requesterName = requester; };
+
+  auto unsignedCommand = makeInterest("/test/interest/unsigned");
+  auto signedCommand = makeControlCommandRequest("/test/interest/signed", ControlParameters());
+
+  m_manager.extractRequester(*unsignedCommand, testAccept);
+  BOOST_CHECK(requesterName.empty());
+
+  requesterName = "";
+  m_manager.extractRequester(*signedCommand, testAccept);
+  auto keyLocator = m_keyChain.getDefaultCertificateNameForIdentity(m_identityName).getPrefix(-1);
+  BOOST_CHECK_EQUAL(requesterName, keyLocator.toUri());
+}
+
+BOOST_AUTO_TEST_CASE(ValidateParameters)
+{
+  ControlParameters params;
+  TestCommandVoidParameters commandVoidParams;
+  TestCommandRequireName commandRequireName;
+
+  BOOST_CHECK_EQUAL(ManagerBase::validateParameters(commandVoidParams, params), true); // succeeds
+  BOOST_CHECK_EQUAL(ManagerBase::validateParameters(commandRequireName, params), false); // fails
+
+  params.setName("test-name");
+  BOOST_CHECK_EQUAL(ManagerBase::validateParameters(commandRequireName, params), true); // succeeds
+}
+
+BOOST_AUTO_TEST_CASE(MakeRelPrefix)
+{
+  auto generatedRelPrefix = m_manager.makeRelPrefix("test-verb");
+  BOOST_CHECK_EQUAL(generatedRelPrefix, PartialName("/test-module/test-verb"));
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/daemon/mgmt/manager-common-fixture.cpp b/tests/daemon/mgmt/manager-common-fixture.cpp
new file mode 100644
index 0000000..b03449e
--- /dev/null
+++ b/tests/daemon/mgmt/manager-common-fixture.cpp
@@ -0,0 +1,193 @@
+/* -*- 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 "manager-common-fixture.hpp"
+
+namespace nfd {
+namespace tests {
+
+ManagerCommonFixture::ManagerCommonFixture()
+  : m_face(ndn::util::makeDummyClientFace(UnitTestTimeFixture::g_io, {true, true}))
+  , m_dispatcher(*m_face, m_keyChain, ndn::security::SigningInfo())
+  , m_responses(m_face->sentDatas)
+  , m_identityName("/unit-test/ManagerCommonFixture/identity")
+  , m_certificate(m_keyChain.getCertificate(m_keyChain.createIdentity(m_identityName)))
+{
+}
+
+void
+ManagerCommonFixture::setTopPrefixAndPrivilege(const Name& topPrefix,
+                                               const std::string& privilege)
+{
+  m_dispatcher.addTopPrefix(topPrefix); // such that all filters are added
+  advanceClocks(time::milliseconds(1));
+
+  std::string regex("^");
+  for (auto component : topPrefix) {
+    regex += "<" + component.toUri() + ">";
+  }
+  m_validator.addInterestRule(regex + "<" + privilege + ">", *m_certificate);
+}
+
+shared_ptr<Interest>
+ManagerCommonFixture::makeControlCommandRequest(Name commandName,
+                                                const ControlParameters& parameters,
+                                                const InterestHandler& beforeSigning)
+{
+  shared_ptr<Interest> command = makeInterest(commandName.append(parameters.wireEncode()));
+
+  if (beforeSigning != nullptr) {
+    beforeSigning(command);
+  }
+
+  m_keyChain.sign(*command, ndn::security::SigningInfo(ndn::security::SigningInfo::SIGNER_TYPE_ID,
+                                                       m_identityName));
+  return command;
+}
+
+void
+ManagerCommonFixture::receiveInterest(shared_ptr<Interest> interest)
+{
+  m_face->receive<Interest>(*interest);
+  advanceClocks(time::milliseconds(1));
+}
+
+ControlResponse
+ManagerCommonFixture::makeResponse(uint32_t code, const std::string& text,
+                                   const ControlParameters& parameters)
+{
+  return ControlResponse(code, text).setBody(parameters.wireEncode());
+}
+
+ManagerCommonFixture::CheckResponseResult
+ManagerCommonFixture::checkResponse(size_t idx,
+                                    const Name& expectedName,
+                                    const ControlResponse& expectedResponse,
+                                    int expectedContentType /*= -1*/)
+{
+  Data data;
+  try {
+    data = m_responses.at(idx);
+  }
+  catch (const std::out_of_range&) {
+    return CheckResponseResult::OUT_OF_BOUNDARY;
+  }
+
+  if (data.getName() != expectedName) {
+    return CheckResponseResult::WRONG_NAME;
+  }
+
+  if (expectedContentType != -1 &&
+      data.getContentType() != static_cast<uint32_t>(expectedContentType)) {
+    return CheckResponseResult::WRONG_CONTENT_TYPE;
+  }
+
+  ControlResponse response;
+  try {
+    response.wireDecode(data.getContent().blockFromValue());
+  }
+  catch (const tlv::Error&) {
+    return CheckResponseResult::INVALID_RESPONSE;
+  }
+
+  if (response.getCode() != expectedResponse.getCode()) {
+    return CheckResponseResult::WRONG_CODE;
+  }
+
+  if (response.getText() != expectedResponse.getText()) {
+    return CheckResponseResult::WRONG_TEXT;
+  }
+
+  const Block& body = response.getBody();
+  const Block& expectedBody = expectedResponse.getBody();
+  if (body.value_size() != expectedBody.value_size()) {
+    return CheckResponseResult::WRONG_BODY_SIZE;
+  }
+  if (body.value_size() > 0 && memcmp(body.value(), expectedBody.value(), body.value_size()) != 0) {
+    return CheckResponseResult::WRONG_BODY_VALUE;
+  }
+
+  return CheckResponseResult::OK;
+}
+
+Block
+ManagerCommonFixture::concatenateResponses(size_t startIndex, size_t nResponses)
+{
+  size_t endIndex = startIndex + nResponses; // not included
+  if (nResponses == startIndex || endIndex > m_responses.size()) {
+    endIndex = m_responses.size();
+  }
+
+  ndn::EncodingBuffer encoder;
+  size_t valueLength = 0;
+  for (size_t i = startIndex; i < endIndex ; i ++) {
+    valueLength += encoder.appendByteArray(m_responses[i].getContent().value(),
+                                           m_responses[i].getContent().value_size());
+  }
+  encoder.prependVarNumber(valueLength);
+  encoder.prependVarNumber(tlv::Content);
+  return encoder.block();
+}
+
+std::ostream&
+operator<<(std::ostream &os, const ManagerCommonFixture::CheckResponseResult& result)
+{
+  switch (result) {
+  case ManagerCommonFixture::CheckResponseResult::OK:
+    os << "OK";
+    break;
+  case ManagerCommonFixture::CheckResponseResult::OUT_OF_BOUNDARY:
+    os << "OUT_OF_BOUNDARY";
+    break;
+  case ManagerCommonFixture::CheckResponseResult::WRONG_NAME:
+    os << "WRONG_NAME";
+    break;
+  case ManagerCommonFixture::CheckResponseResult::WRONG_CONTENT_TYPE:
+    os << "WRONG_CONTENT_TYPE";
+    break;
+  case ManagerCommonFixture::CheckResponseResult::INVALID_RESPONSE:
+    os << "INVALID_RESPONSE";
+    break;
+  case ManagerCommonFixture::CheckResponseResult::WRONG_CODE:
+    os << "WRONG_CODE";
+    break;
+  case ManagerCommonFixture::CheckResponseResult::WRONG_TEXT:
+    os << "WRONG_TEXT";
+    break;
+  case ManagerCommonFixture::CheckResponseResult::WRONG_BODY_SIZE:
+    os << "WRONG_BODY_SIZE";
+    break;
+  case ManagerCommonFixture::CheckResponseResult::WRONG_BODY_VALUE:
+    os << "WRONG_BODY_VALUE";
+    break;
+  default:
+    break;
+  };
+
+  return os;
+}
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/manager-common-fixture.hpp b/tests/daemon/mgmt/manager-common-fixture.hpp
new file mode 100644
index 0000000..ffdb579
--- /dev/null
+++ b/tests/daemon/mgmt/manager-common-fixture.hpp
@@ -0,0 +1,163 @@
+/* -*- 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_TESTS_NFD_MGMT_MANAGER_COMMON_HPP
+#define NFD_TESTS_NFD_MGMT_MANAGER_COMMON_HPP
+
+#include "tests/test-common.hpp"
+#include "tests/identity-management-fixture.hpp"
+#include "mgmt/manager-base.hpp"
+#include "fw/forwarder.hpp"
+
+#include <ndn-cxx/mgmt/dispatcher.hpp>
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+namespace nfd {
+namespace tests {
+
+/**
+ * @brief a collection of common functions shared by all manager's testing fixtures.
+ */
+class ManagerCommonFixture : public UnitTestTimeFixture
+                           , public IdentityManagementFixture
+{
+public: // initialize
+  ManagerCommonFixture();
+
+  /**
+   * @brief set topPrefix to the dispatcher and configure an interest rule for the module.
+   *
+   * after setting @param topPrefix, call advanceClocks to ensure all added filters take effects.
+   *
+   * @param topPrefix top prefix for the dispatcher
+   * @param privilege the module name
+   */
+  void
+  setTopPrefixAndPrivilege(const Name& topPrefix, const std::string& privilege);
+
+public: // test
+  typedef std::function<void(shared_ptr<Interest> interest)> InterestHandler;
+
+  /**
+   * @brief create a ControlCommand request
+   *
+   * step1: append the @param parameters to the @param commandName and make an Interest.
+   * step2: call @param beforeSinging to do something with the Interest.
+   * step3: sign the generated command
+   *
+   * @param commandName the command name
+   * @param parameters the ControlParameters
+   * @param beforeSigning a callback that can modify the Interest before it's signed
+   *
+   * @return the signed ControlCommand request
+   */
+  shared_ptr<Interest>
+  makeControlCommandRequest(Name commandName,
+                            const ControlParameters& parameters,
+                            const InterestHandler& beforeSigning = nullptr);
+
+  /**
+   * @brief cause management to receive an Interest
+   *
+   * call DummyClientFace::receive to receive Interest and then call advanceClocks to ensure
+   * the Interest dispatched
+   *
+   * @param interest the Interest to receive
+   */
+  void
+  receiveInterest(shared_ptr<Interest> interest);
+
+public: // verify
+  ControlResponse
+  makeResponse(uint32_t code, const std::string& text,
+               const ControlParameters& parameters);
+
+  enum class CheckResponseResult
+  {
+    OK,
+    OUT_OF_BOUNDARY,
+    WRONG_NAME,
+    WRONG_CONTENT_TYPE,
+    INVALID_RESPONSE,
+    WRONG_CODE,
+    WRONG_TEXT,
+    WRONG_BODY_SIZE,
+    WRONG_BODY_VALUE
+   };
+
+  /**
+   * @brief check a specified response data with the expected ControlResponse
+   *
+   * @param idx the index of the specified Data in m_responses
+   * @param expectedDataName the expected name of this Data
+   * @param expectedResponse the expected ControlResponse
+   * @param expectedContentType the expected content type of this Data, use -1 to skip this check
+   *
+   * @retval OK the response at the specified index can be decoded from the response data,
+   *            and its code, text and response body are all matched with the expected response
+   * @retval OUT_OF_BOUNDARY the specified index out of boundary
+   * @retval WRONG_NAME the name of the specified response data does not match
+   * @retval WRONG_CONTENT_TYPE the content type of the specified response data does not match
+   * @retval INVALID_RESPONSE the data name matches but it fails in decoding a ControlResponse from
+   *                          the content of the specified response data
+   * @retval WRONG_CODE a valid ControlResponse can be decoded but has a wrong code
+   * @retval WRONG_TEXT a valid ControlResponse can be decoded but has a wrong text
+   * @retval WRONG_BODY_SIZE the body size of decoded ControlResponse does not match
+   * @retval WRONT_BODY_VALUE the body value of decoded ControlResponse does not match
+   */
+  CheckResponseResult
+  checkResponse(size_t idx,
+                const Name& expectedName,
+                const ControlResponse& expectedResponse,
+                int expectedContentType = -1);
+
+  /**
+   * @brief concatenate specified response Data into a single block
+   *
+   * @param startIndex the start index in m_responses
+   * @param nResponses the number of response to concatenate
+   *
+   * @return the generated block
+   */
+  Block
+  concatenateResponses(size_t startIndex = 0, size_t nResponses = 0);
+
+protected:
+  shared_ptr<ndn::util::DummyClientFace> m_face;
+  ndn::mgmt::Dispatcher                  m_dispatcher;
+  CommandValidator                       m_validator;
+  Forwarder                              m_forwarder;
+  std::vector<Data>&                     m_responses; // a reference of m_face->sentDatas
+  Name                                   m_identityName; // the identity used to sign request
+  shared_ptr<ndn::IdentityCertificate>   m_certificate; // the certificate used to sign request
+};
+
+std::ostream&
+operator<<(std::ostream &os, const ManagerCommonFixture::CheckResponseResult& result);
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_NFD_MGMT_MANAGER_COMMON_HPP
diff --git a/tests/daemon/mgmt/strategy-choice-manager.t.cpp b/tests/daemon/mgmt/strategy-choice-manager.t.cpp
index d0c425d..9e7a8c2 100644
--- a/tests/daemon/mgmt/strategy-choice-manager.t.cpp
+++ b/tests/daemon/mgmt/strategy-choice-manager.t.cpp
@@ -24,623 +24,225 @@
  */
 
 #include "mgmt/strategy-choice-manager.hpp"
+#include "manager-common-fixture.hpp"
+
 #include "face/face.hpp"
-#include "mgmt/internal-face.hpp"
+#include "face/internal-face.hpp"
 #include "table/name-tree.hpp"
 #include "table/strategy-choice.hpp"
-#include "fw/forwarder.hpp"
 #include "fw/strategy.hpp"
 #include "tests/daemon/face/dummy-face.hpp"
 #include "tests/daemon/fw/dummy-strategy.hpp"
 
+#include <ndn-cxx/util/random.hpp>
 #include <ndn-cxx/management/nfd-strategy-choice.hpp>
 
-#include "tests/test-common.hpp"
-#include "validation-common.hpp"
-
 namespace nfd {
 namespace tests {
 
-NFD_LOG_INIT("MgmtStrategyChoiceManager");
-
-class StrategyChoiceManagerFixture : protected BaseFixture
+class StrategyChoiceManagerFixture : public ManagerCommonFixture
 {
 public:
-
   StrategyChoiceManagerFixture()
     : m_strategyChoice(m_forwarder.getStrategyChoice())
-    , m_face(make_shared<InternalFace>())
-    , m_manager(m_strategyChoice, m_face, m_keyChain)
-    , m_callbackFired(false)
+    , m_manager(m_strategyChoice, m_dispatcher, m_validator)
   {
-    m_strategyChoice.install(make_shared<DummyStrategy>(ref(m_forwarder),
-                                                        "/localhost/nfd/strategy/test-strategy-a"));
-    m_strategyChoice.insert("ndn:/", "/localhost/nfd/strategy/test-strategy-a");
+    setTopPrefixAndPrivilege("/localhost/nfd", "strategy-choice");
   }
 
-  virtual
-  ~StrategyChoiceManagerFixture()
-  {
-
-  }
-
+public:
   void
-  validateControlResponseCommon(const Data& response,
-                                const Name& expectedName,
-                                uint32_t expectedCode,
-                                const std::string& expectedText,
-                                ControlResponse& control)
+  installStrategy(const Name& strategy)
   {
-    m_callbackFired = true;
-    Block controlRaw = response.getContent().blockFromValue();
-
-    control.wireDecode(controlRaw);
-
-    NFD_LOG_DEBUG("received control response"
-                  << " Name: " << response.getName()
-                  << " code: " << control.getCode()
-                  << " text: " << control.getText());
-
-    BOOST_CHECK_EQUAL(response.getName(), expectedName);
-    BOOST_CHECK_EQUAL(control.getCode(), expectedCode);
-    BOOST_CHECK_EQUAL(control.getText(), expectedText);
+    m_strategyChoice.install(make_shared<DummyStrategy>(ref(m_forwarder), strategy));
   }
 
-  void
-  validateControlResponse(const Data& response,
-                          const Name& expectedName,
-                          uint32_t expectedCode,
-                          const std::string& expectedText)
+  const Name&
+  findStrategy(const Name& name)
   {
-    ControlResponse control;
-    validateControlResponseCommon(response, expectedName,
-                                  expectedCode, expectedText, control);
-
-    if (!control.getBody().empty())
-      {
-        BOOST_FAIL("found unexpected control response body");
-      }
+    return m_strategyChoice.findEffectiveStrategy(name).getName();
   }
 
-  void
-  validateControlResponse(const Data& response,
-                          const Name& expectedName,
-                          uint32_t expectedCode,
-                          const std::string& expectedText,
-                          const Block& expectedBody)
+  ControlParameters
+  makeParameters(const Name& name, const Name& strategy)
   {
-    ControlResponse control;
-    validateControlResponseCommon(response, expectedName,
-                                  expectedCode, expectedText, control);
-
-    BOOST_REQUIRE(!control.getBody().empty());
-    BOOST_REQUIRE(control.getBody().value_size() == expectedBody.value_size());
-
-    BOOST_CHECK(memcmp(control.getBody().value(), expectedBody.value(),
-                       expectedBody.value_size()) == 0);
-
-  }
-
-  void
-  validateList(const Data& data, const ndn::nfd::StrategyChoice& expectedChoice)
-  {
-    m_callbackFired = true;
-    ndn::nfd::StrategyChoice choice(data.getContent().blockFromValue());
-    BOOST_CHECK_EQUAL(choice.getStrategy(), expectedChoice.getStrategy());
-    BOOST_CHECK_EQUAL(choice.getName(), expectedChoice.getName());
-  }
-
-  bool
-  didCallbackFire()
-  {
-    return m_callbackFired;
-  }
-
-  void
-  resetCallbackFired()
-  {
-    m_callbackFired = false;
-  }
-
-  shared_ptr<InternalFace>&
-  getFace()
-  {
-    return m_face;
-  }
-
-  StrategyChoiceManager&
-  getManager()
-  {
-    return m_manager;
-  }
-
-  StrategyChoice&
-  getStrategyChoice()
-  {
-    return m_strategyChoice;
-  }
-
-  void
-  addInterestRule(const std::string& regex,
-                  ndn::IdentityCertificate& certificate)
-  {
-    m_manager.addInterestRule(regex, certificate);
+    return ControlParameters().setName(name).setStrategy(strategy);
   }
 
 protected:
-  Forwarder m_forwarder;
   StrategyChoice& m_strategyChoice;
-  shared_ptr<InternalFace> m_face;
   StrategyChoiceManager m_manager;
-  ndn::KeyChain m_keyChain;
-
-private:
-  bool m_callbackFired;
 };
 
-class AllStrategiesFixture : public StrategyChoiceManagerFixture
+BOOST_FIXTURE_TEST_SUITE(Mgmt, StrategyChoiceManagerFixture)
+BOOST_AUTO_TEST_SUITE(TestStrategyChoiceManager)
+
+BOOST_AUTO_TEST_CASE(SetStrategy)
+{
+  auto testSetStrategy = [this] (const ControlParameters& parameters) -> Name {
+    m_responses.clear();
+    auto command = makeControlCommandRequest("/localhost/nfd/strategy-choice/set", parameters);
+    receiveInterest(command);
+    return command->getName();
+  };
+
+  installStrategy("/localhost/nfd/strategy/test-strategy-a");
+  installStrategy("/localhost/nfd/strategy/test-strategy-c/%FD%01"); // version 1
+  installStrategy("/localhost/nfd/strategy/test-strategy-c/%FD%02"); // version 2
+
+  auto parametersA  = makeParameters("test", "/localhost/nfd/strategy/test-strategy-a");
+  auto parametersB  = makeParameters("test", "/localhost/nfd/strategy/test-strategy-b");
+  auto parametersC1 = makeParameters("test", "/localhost/nfd/strategy/test-strategy-c/%FD%01");
+  auto parametersC  = makeParameters("test", "/localhost/nfd/strategy/test-strategy-c");
+
+  auto commandNameA = testSetStrategy(parametersA); // succeed
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, commandNameA, makeResponse(200, "OK", parametersA)),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(findStrategy("/test"), "/localhost/nfd/strategy/test-strategy-a");
+
+  auto commandNameB = testSetStrategy(parametersB); // not installed
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, commandNameB, ControlResponse(504, "Unsupported strategy")),
+                    CheckResponseResult::OK);
+
+  auto commandNameC1 = testSetStrategy(parametersC1); // specified version
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, commandNameC1, makeResponse(200, "OK", parametersC1)),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(findStrategy("/test"), "/localhost/nfd/strategy/test-strategy-c/%FD%01");
+
+  auto commandNameC = testSetStrategy(parametersC); // latest version
+  parametersC.setStrategy("/localhost/nfd/strategy/test-strategy-c/%FD%02"); // change to latest
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, commandNameC, makeResponse(200, "OK", parametersC)),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(findStrategy("/test"), "/localhost/nfd/strategy/test-strategy-c/%FD%02");
+}
+
+BOOST_AUTO_TEST_CASE(UnsetStrategy)
+{
+  auto testUnsetStrategy = [this] (const ControlParameters& parameters) -> Name {
+    m_responses.clear();
+    auto command = makeControlCommandRequest("/localhost/nfd/strategy-choice/unset", parameters);
+    receiveInterest(command);
+    return command->getName();
+  };
+
+  installStrategy("/localhost/nfd/strategy/test-strategy-a");
+  installStrategy("/localhost/nfd/strategy/test-strategy-b");
+  installStrategy("/localhost/nfd/strategy/test-strategy-c");
+
+  BOOST_CHECK(m_strategyChoice.insert("ndn:/", "/localhost/nfd/strategy/test-strategy-a")); // root
+  BOOST_CHECK(m_strategyChoice.insert("/test", "/localhost/nfd/strategy/test-strategy-b")); // test
+  BOOST_CHECK_EQUAL(findStrategy("/"), "/localhost/nfd/strategy/test-strategy-a");
+
+  auto parametersRoot = ControlParameters().setName("ndn:/"); // root prefix
+  auto parametersNone = ControlParameters().setName("/none"); // no such entry
+  auto parametersTest = ControlParameters().setName("/test"); // has such entry
+
+  BOOST_CHECK_EQUAL(findStrategy("/"), "/localhost/nfd/strategy/test-strategy-a"); // root
+  auto commandRootName = testUnsetStrategy(parametersRoot);
+  auto expectedResponse = ControlResponse(400, "failed in validating parameters");
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, commandRootName, expectedResponse), CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(findStrategy("/"), "/localhost/nfd/strategy/test-strategy-a"); // keep as root
+
+  BOOST_CHECK_EQUAL(findStrategy("/none"), "/localhost/nfd/strategy/test-strategy-a"); // root
+  auto commandNoneName = testUnsetStrategy(parametersNone);
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, commandNoneName, makeResponse(200, "OK", parametersNone)),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(findStrategy("/none"), "/localhost/nfd/strategy/test-strategy-a"); // root
+
+  BOOST_CHECK_EQUAL(findStrategy("/test"), "/localhost/nfd/strategy/test-strategy-b"); // self
+  auto commandTestName = testUnsetStrategy(parametersTest);
+  BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+  BOOST_CHECK_EQUAL(checkResponse(0, commandTestName, makeResponse(200, "OK", parametersTest)),
+                    CheckResponseResult::OK);
+  BOOST_CHECK_EQUAL(findStrategy("/test"), "/localhost/nfd/strategy/test-strategy-a"); // parent
+}
+
+// @todo Remove when ndn::nfd::StrategyChoice implements operator!= and operator<<
+class StrategyChoice : public ndn::nfd::StrategyChoice
 {
 public:
-  AllStrategiesFixture()
+  StrategyChoice() = default;
+
+  StrategyChoice(const ndn::nfd::StrategyChoice& entry)
+    : ndn::nfd::StrategyChoice(entry)
   {
-    m_strategyChoice.install(make_shared<DummyStrategy>(ref(m_forwarder),
-                                                        "/localhost/nfd/strategy/test-strategy-b"));
-
-    const Name strategyCVersion1("/localhost/nfd/strategy/test-strategy-c/%FD%01");
-    m_strategyChoice.install(make_shared<DummyStrategy>(ref(m_forwarder),
-                                                        strategyCVersion1));
-
-    const Name strategyCVersion2("/localhost/nfd/strategy/test-strategy-c/%FD%02");
-    m_strategyChoice.install(make_shared<DummyStrategy>(ref(m_forwarder),
-                                                        strategyCVersion2));
-  }
-
-  virtual
-  ~AllStrategiesFixture()
-  {
-
   }
 };
 
-template <typename T> class AuthorizedCommandFixture : public CommandFixture<T>
+bool
+operator!=(const StrategyChoice& left, const StrategyChoice& right)
 {
-public:
-  AuthorizedCommandFixture()
-  {
-    const std::string regex = "^<localhost><nfd><strategy-choice>";
-    T::addInterestRule(regex, *CommandFixture<T>::m_certificate);
+  return left.getName() != right.getName() || left.getStrategy() != right.getStrategy();
+}
+
+std::ostream&
+operator<<(std::ostream &os, const StrategyChoice& entry)
+{
+  os << "[ " << entry.getName() << ", " << entry.getStrategy() << " ]";
+  return os;
+}
+
+BOOST_AUTO_TEST_CASE(ListChoices)
+{
+  size_t nPreInsertedStrategies = m_strategyChoice.size(); // the best-route strategy
+  std::set<Name> actualNames, actualStrategies;
+  for (auto&& entry : m_strategyChoice) {
+    actualNames.insert(entry.getPrefix());
+    actualStrategies.insert(entry.getStrategyName());
   }
 
-  virtual
-  ~AuthorizedCommandFixture()
-  {
-
+  size_t nEntries = 1024;
+  for (size_t i = 0 ; i < nEntries ; i++) {
+    auto name = Name("test-name").appendSegment(i);
+    auto strategy = Name("test-strategy").appendSegment(ndn::random::generateWord64());
+    auto entry = ndn::nfd::StrategyChoice().setName(name).setStrategy(strategy);
+    actualNames.insert(name);
+    actualStrategies.insert(strategy);
+    installStrategy(strategy);
+    m_strategyChoice.insert(name, strategy);
   }
-};
+  nEntries += nPreInsertedStrategies;
 
-BOOST_FIXTURE_TEST_SUITE(MgmtStrategyChoiceManager,
-                         AuthorizedCommandFixture<AllStrategiesFixture>)
+  receiveInterest(makeInterest("/localhost/nfd/strategy-choice/list"));
 
-BOOST_FIXTURE_TEST_CASE(ShortName, AllStrategiesFixture)
-{
-  shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/strategy-choice"));
+  Block content;
+  BOOST_CHECK_NO_THROW(content = concatenateResponses());
+  BOOST_CHECK_NO_THROW(content.parse());
+  BOOST_CHECK_EQUAL(content.elements().size(), nEntries);
 
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  });
+  std::vector<StrategyChoice> receivedRecords, expectedRecords;
+  for (size_t idx = 0; idx < nEntries; ++idx) {
+    BOOST_TEST_MESSAGE("processing element: " << idx);
 
-  getFace()->sendInterest(*command);
-  g_io.run_one();
+    StrategyChoice decodedEntry;
+    BOOST_REQUIRE_NO_THROW(decodedEntry.wireDecode(content.elements()[idx]));
+    receivedRecords.push_back(decodedEntry);
 
-  BOOST_REQUIRE(didCallbackFire());
-}
+    actualNames.erase(decodedEntry.getName());
+    actualStrategies.erase(decodedEntry.getStrategy());
 
-BOOST_FIXTURE_TEST_CASE(MalformedCommmand, AllStrategiesFixture)
-{
-  shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/strategy-choice"));
+    auto result = m_strategyChoice.get(decodedEntry.getName());
+    BOOST_REQUIRE(result.first);
 
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  });
-
-  getManager().onStrategyChoiceRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_FIXTURE_TEST_CASE(UnsignedCommand, AllStrategiesFixture)
-{
-  ControlParameters parameters;
-  parameters.setName("/test");
-  parameters.setStrategy("/localhost/nfd/strategy/best-route");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/strategy-choice");
-  commandName.append("set");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 401, "Signature required");
-  });
-
-  getManager().onStrategyChoiceRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand,
-                        UnauthorizedCommandFixture<StrategyChoiceManagerFixture>)
-{
-  ControlParameters parameters;
-  parameters.setName("/test");
-  parameters.setStrategy("/localhost/nfd/strategy/best-route");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/strategy-choice");
-  commandName.append("set");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 403, "Unauthorized command");
-  });
-
-  getManager().onStrategyChoiceRequest(*command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_AUTO_TEST_CASE(UnsupportedVerb)
-{
-  ControlParameters parameters;
-  parameters.setName("/test");
-  parameters.setStrategy("/localhost/nfd/strategy/test-strategy-b");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/strategy-choice");
-  commandName.append("unsupported");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 501, "Unsupported command");
-  });
-
-  getManager().onValidatedStrategyChoiceRequest(command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_AUTO_TEST_CASE(BadOptionParse)
-{
-  Name commandName("/localhost/nfd/strategy-choice");
-  commandName.append("set");
-  commandName.append("NotReallyParameters");
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  });
-
-  getManager().onValidatedStrategyChoiceRequest(command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_AUTO_TEST_CASE(SetStrategies)
-{
-  ControlParameters parameters;
-  parameters.setName("/test");
-  parameters.setStrategy("/localhost/nfd/strategy/test-strategy-b");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/strategy-choice");
-  commandName.append("set");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-
-  getFace()->onReceiveData.connect([this, command, encodedParameters] (const Data& response) {
-    this->validateControlResponse(response, command->getName(),
-                                  200, "Success", encodedParameters);
-  });
-
-  getManager().onValidatedStrategyChoiceRequest(command);
-
-  BOOST_REQUIRE(didCallbackFire());
-  fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
-  BOOST_REQUIRE_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-b");
-}
-
-BOOST_AUTO_TEST_CASE(SetStrategySpecifiedVersion)
-{
-  ControlParameters parameters;
-  parameters.setName("/test");
-  parameters.setStrategy("/localhost/nfd/strategy/test-strategy-c/%FD%01");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/strategy-choice");
-  commandName.append("set");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-
-  getFace()->onReceiveData.connect([this, command, encodedParameters] (const Data& response) {
-    this->validateControlResponse(response, command->getName(),
-                                  200, "Success", encodedParameters);
-  });
-
-  getManager().onValidatedStrategyChoiceRequest(command);
-
-  BOOST_REQUIRE(didCallbackFire());
-  fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
-  BOOST_REQUIRE_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-c/%FD%01");
-}
-
-BOOST_AUTO_TEST_CASE(SetStrategyLatestVersion)
-{
-  ControlParameters parameters;
-  parameters.setName("/test");
-  parameters.setStrategy("/localhost/nfd/strategy/test-strategy-c");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/strategy-choice");
-  commandName.append("set");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-
-  ControlParameters responseParameters;
-  responseParameters.setName("/test");
-  responseParameters.setStrategy("/localhost/nfd/strategy/test-strategy-c/%FD%02");
-
-  getFace()->onReceiveData.connect([this, command, responseParameters] (const Data& response) {
-    this->validateControlResponse(response, command->getName(),
-                                  200, "Success", responseParameters.wireEncode());
-  });
-
-  getManager().onValidatedStrategyChoiceRequest(command);
-
-  BOOST_REQUIRE(didCallbackFire());
-  fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
-  BOOST_REQUIRE_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-c/%FD%02");
-}
-
-BOOST_AUTO_TEST_CASE(SetStrategiesMissingName)
-{
-  ControlParameters parameters;
-  parameters.setStrategy("/localhost/nfd/strategy/test-strategy-b");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/strategy-choice");
-  commandName.append("set");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  });
-
-  getManager().onValidatedStrategyChoiceRequest(command);
-
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_AUTO_TEST_CASE(SetStrategiesMissingStrategy)
-{
-  ControlParameters parameters;
-  parameters.setName("/test");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/strategy-choice");
-  commandName.append("set");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  });
-
-  getManager().onValidatedStrategyChoiceRequest(command);
-
-  BOOST_REQUIRE(didCallbackFire());
-  fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
-  BOOST_REQUIRE_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-a");
-}
-
-BOOST_AUTO_TEST_CASE(SetUnsupportedStrategy)
-{
-  ControlParameters parameters;
-  parameters.setName("/test");
-  parameters.setStrategy("/localhost/nfd/strategy/unit-test-doesnotexist");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/strategy-choice");
-  commandName.append("set");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 504, "Unsupported strategy");
-  });
-
-  getManager().onValidatedStrategyChoiceRequest(command);
-
-  BOOST_REQUIRE(didCallbackFire());
-  fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
-  BOOST_CHECK_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-a");
-}
-
-class DefaultStrategyOnlyFixture : public StrategyChoiceManagerFixture
-{
-public:
-  DefaultStrategyOnlyFixture()
-    : StrategyChoiceManagerFixture()
-  {
-
+    auto record = StrategyChoice().setName(decodedEntry.getName()).setStrategy(result.second);
+    expectedRecords.push_back(record);
   }
 
-  virtual
-  ~DefaultStrategyOnlyFixture()
-  {
+  BOOST_CHECK_EQUAL(actualNames.size(), 0);
+  BOOST_CHECK_EQUAL(actualStrategies.size(), 0);
 
-  }
-};
-
-
-/// \todo I'm not sure this code branch (code 405) can happen. The manager tests for the strategy first and will return 504.
-// BOOST_FIXTURE_TEST_CASE(SetNotInstalled, DefaultStrategyOnlyFixture)
-// {
-//   BOOST_REQUIRE(!getStrategyChoice().hasStrategy("/localhost/nfd/strategy/test-strategy-b"));
-//   ControlParameters parameters;
-//   parameters.setName("/test");
-//   parameters.setStrategy("/localhost/nfd/strategy/test-strategy-b");
-
-//   Block encodedParameters(parameters.wireEncode());
-
-//   Name commandName("/localhost/nfd/strategy-choice");
-//   commandName.append("set");
-//   commandName.append(encodedParameters);
-
-//   shared_ptr<Interest> command(make_shared<Interest>(commandName));
-//   generateCommand(*command);
-
-//   getFace()->onReceiveData +=
-//     bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
-//          command->getName(), 405, "Strategy not installed");
-
-//   getManager().onValidatedStrategyChoiceRequest(command);
-
-//   BOOST_REQUIRE(didCallbackFire());
-//   fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
-//   BOOST_CHECK_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-a");
-// }
-
-BOOST_AUTO_TEST_CASE(Unset)
-{
-  ControlParameters parameters;
-  parameters.setName("/test");
-
-  BOOST_REQUIRE(m_strategyChoice.insert("/test", "/localhost/nfd/strategy/test-strategy-b"));
-  BOOST_REQUIRE_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
-                      "/localhost/nfd/strategy/test-strategy-b");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/strategy-choice");
-  commandName.append("unset");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  getFace()->onReceiveData.connect([this, command, encodedParameters] (const Data& response) {
-    this->validateControlResponse(response, command->getName(),
-                                  200, "Success", encodedParameters);
-  });
-
-  getManager().onValidatedStrategyChoiceRequest(command);
-
-  BOOST_REQUIRE(didCallbackFire());
-
-  BOOST_CHECK_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
-                    "/localhost/nfd/strategy/test-strategy-a");
+  BOOST_CHECK_EQUAL_COLLECTIONS(receivedRecords.begin(), receivedRecords.end(),
+                                expectedRecords.begin(), expectedRecords.end());
 }
 
-BOOST_AUTO_TEST_CASE(UnsetRoot)
-{
-  ControlParameters parameters;
-  parameters.setName("/");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/strategy-choice");
-  commandName.append("unset");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(),
-                                  403, "Cannot unset root prefix strategy");
-  });
-
-  getManager().onValidatedStrategyChoiceRequest(command);
-
-  BOOST_REQUIRE(didCallbackFire());
-
-  BOOST_CHECK_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
-                    "/localhost/nfd/strategy/test-strategy-a");
-}
-
-BOOST_AUTO_TEST_CASE(UnsetMissingName)
-{
-  ControlParameters parameters;
-
-  BOOST_REQUIRE(m_strategyChoice.insert("/test", "/localhost/nfd/strategy/test-strategy-b"));
-  BOOST_REQUIRE_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
-                      "/localhost/nfd/strategy/test-strategy-b");
-
-  Block encodedParameters(parameters.wireEncode());
-
-  Name commandName("/localhost/nfd/strategy-choice");
-  commandName.append("unset");
-  commandName.append(encodedParameters);
-
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-  generateCommand(*command);
-
-  getFace()->onReceiveData.connect([this, command] (const Data& response) {
-    this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  });
-
-  getManager().onValidatedStrategyChoiceRequest(command);
-
-  BOOST_REQUIRE(didCallbackFire());
-
-  BOOST_CHECK_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
-                    "/localhost/nfd/strategy/test-strategy-b");
-}
-
-BOOST_AUTO_TEST_CASE(Publish)
-{
-  Name commandName("/localhost/nfd/strategy-choice/list");
-  shared_ptr<Interest> command(make_shared<Interest>(commandName));
-
-  ndn::nfd::StrategyChoice expectedChoice;
-  expectedChoice.setStrategy("/localhost/nfd/strategy/test-strategy-a");
-  expectedChoice.setName("/");
-
-  getFace()->onReceiveData.connect(bind(&StrategyChoiceManagerFixture::validateList,
-                                        this, _1, expectedChoice));
-
-  m_manager.onStrategyChoiceRequest(*command);
-  BOOST_REQUIRE(didCallbackFire());
-}
-
-BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END() // TestStrategyChoiceManager
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
 
 } // namespace tests
 } // namespace nfd
diff --git a/tests/daemon/mgmt/strategy-choice-publisher.t.cpp b/tests/daemon/mgmt/strategy-choice-publisher.t.cpp
deleted file mode 100644
index 8f98cb5..0000000
--- a/tests/daemon/mgmt/strategy-choice-publisher.t.cpp
+++ /dev/null
@@ -1,168 +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/>.
- */
-
-#ifndef NFD_TESTS_NFD_MGMT_STRATEGY_CHOICE_PUBLISHER_HPP
-#define NFD_TESTS_NFD_MGMT_STRATEGY_CHOICE_PUBLISHER_HPP
-
-#include "mgmt/strategy-choice-publisher.hpp"
-#include "mgmt/app-face.hpp"
-#include "mgmt/internal-face.hpp"
-#include "fw/forwarder.hpp"
-#include "../fw/dummy-strategy.hpp"
-
-#include "tests/test-common.hpp"
-
-#include <ndn-cxx/management/nfd-strategy-choice.hpp>
-
-namespace nfd {
-namespace tests {
-
-class StrategyChoicePublisherFixture : BaseFixture
-{
-public:
-
-  StrategyChoicePublisherFixture()
-    : m_strategyChoice(m_forwarder.getStrategyChoice())
-    , m_face(make_shared<InternalFace>())
-    , m_publisher(m_strategyChoice, *m_face, "/localhost/nfd/strategy-choice/list", m_keyChain)
-    , STRATEGY_A(make_shared<DummyStrategy>(boost::ref(m_forwarder),
-                                            "/localhost/nfd/strategy/dummy-strategy-a"))
-    , STRATEGY_B(make_shared<DummyStrategy>(boost::ref(m_forwarder),
-                                            "/localhost/nfd/strategy/dummy-strategy-b"))
-    , m_finished(false)
-  {
-    m_strategyChoice.install(STRATEGY_A);
-    m_strategyChoice.install(STRATEGY_B);
-
-    ndn::nfd::StrategyChoice expectedRootEntry;
-    expectedRootEntry.setStrategy(STRATEGY_A->getName());
-    expectedRootEntry.setName("/");
-
-    m_strategyChoice.insert("/", STRATEGY_A->getName());
-    m_expectedEntries["/"] = expectedRootEntry;
-  }
-
-  void
-  validatePublish(const Data& data)
-  {
-    Block payload = data.getContent();
-
-    m_buffer.appendByteArray(payload.value(), payload.value_size());
-
-    BOOST_CHECK_NO_THROW(data.getName()[-1].toSegment());
-    if (data.getFinalBlockId() != data.getName()[-1])
-      {
-        return;
-      }
-
-    // wrap the Strategy Choice entries in a single Content TLV for easy parsing
-    m_buffer.prependVarNumber(m_buffer.size());
-    m_buffer.prependVarNumber(tlv::Content);
-
-    ndn::Block parser(m_buffer.buf(), m_buffer.size());
-    parser.parse();
-
-    BOOST_REQUIRE_EQUAL(parser.elements_size(), m_expectedEntries.size());
-
-    for (Block::element_const_iterator i = parser.elements_begin();
-         i != parser.elements_end();
-         ++i)
-      {
-        if (i->type() != ndn::tlv::nfd::StrategyChoice)
-          {
-            BOOST_FAIL("expected StrategyChoice, got type #" << i->type());
-          }
-
-        ndn::nfd::StrategyChoice entry(*i);
-
-        std::map<std::string, ndn::nfd::StrategyChoice>::const_iterator expectedEntryPos =
-          m_expectedEntries.find(entry.getName().toUri());
-
-        BOOST_REQUIRE(expectedEntryPos != m_expectedEntries.end());
-        const ndn::nfd::StrategyChoice& expectedEntry = expectedEntryPos->second;
-
-        BOOST_CHECK_EQUAL(entry.getStrategy(), expectedEntry.getStrategy());
-        BOOST_CHECK_EQUAL(entry.getName(), expectedEntry.getName());
-
-        m_matchedEntries.insert(entry.getName().toUri());
-      }
-
-    BOOST_CHECK_EQUAL(m_matchedEntries.size(), m_expectedEntries.size());
-
-    m_finished = true;
-  }
-
-protected:
-  Forwarder m_forwarder;
-  StrategyChoice& m_strategyChoice;
-  shared_ptr<InternalFace> m_face;
-  StrategyChoicePublisher m_publisher;
-
-  shared_ptr<DummyStrategy> STRATEGY_A;
-  shared_ptr<DummyStrategy> STRATEGY_B;
-
-  ndn::EncodingBuffer m_buffer;
-
-  std::map<std::string, ndn::nfd::StrategyChoice> m_expectedEntries;
-  std::set<std::string> m_matchedEntries;
-
-  bool m_finished;
-
-  ndn::KeyChain m_keyChain;
-};
-
-
-
-BOOST_FIXTURE_TEST_SUITE(MgmtStrategyChoicePublisher, StrategyChoicePublisherFixture)
-
-BOOST_AUTO_TEST_CASE(Publish)
-{
-  m_strategyChoice.insert("/test/a", STRATEGY_A->getName());
-  m_strategyChoice.insert("/test/b", STRATEGY_B->getName());
-
-  ndn::nfd::StrategyChoice expectedEntryA;
-  expectedEntryA.setStrategy(STRATEGY_A->getName());
-  expectedEntryA.setName("/test/a");
-
-  ndn::nfd::StrategyChoice expectedEntryB;
-  expectedEntryB.setStrategy(STRATEGY_B->getName());
-  expectedEntryB.setName("/test/b");
-
-  m_expectedEntries["/test/a"] = expectedEntryA;
-  m_expectedEntries["/test/b"] = expectedEntryB;
-
-  m_face->onReceiveData.connect(bind(&StrategyChoicePublisherFixture::validatePublish, this, _1));
-
-  m_publisher.publish();
-  BOOST_REQUIRE(m_finished);
-}
-
-
-BOOST_AUTO_TEST_SUITE_END()
-
-} // namespace tests
-} // namespace nfd
-
-#endif // NFD_TESTS_NFD_MGMT_STRATEGY_CHOICE_PUBLISHER_HPP
diff --git a/tests/daemon/mgmt/validation-common.cpp b/tests/daemon/mgmt/validation-common.cpp
deleted file mode 100644
index 6065c27..0000000
--- a/tests/daemon/mgmt/validation-common.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014  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
- *
- * 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 "validation-common.hpp"
-
-#include <boost/test/unit_test.hpp>
-
-namespace nfd {
-namespace tests {
-
-const Name CommandIdentityGlobalFixture::s_identityName("/unit-test/CommandFixture/id");
-shared_ptr<ndn::IdentityCertificate> CommandIdentityGlobalFixture::s_certificate;
-
-CommandIdentityGlobalFixture::CommandIdentityGlobalFixture()
-{
-  BOOST_ASSERT(!static_cast<bool>(s_certificate));
-  s_certificate = m_keys.getCertificate(m_keys.createIdentity(s_identityName));
-}
-
-CommandIdentityGlobalFixture::~CommandIdentityGlobalFixture()
-{
-  s_certificate.reset();
-  m_keys.deleteIdentity(s_identityName);
-}
-
-BOOST_GLOBAL_FIXTURE(CommandIdentityGlobalFixture)
-
-} // namespace tests
-} // namespace nfd
diff --git a/tests/daemon/mgmt/validation-common.hpp b/tests/daemon/mgmt/validation-common.hpp
deleted file mode 100644
index f8b1367..0000000
--- a/tests/daemon/mgmt/validation-common.hpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014  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
- *
- * 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_TESTS_NFD_MGMT_VALIDATION_COMMON_HPP
-#define NFD_TESTS_NFD_MGMT_VALIDATION_COMMON_HPP
-
-#include "common.hpp"
-#include <ndn-cxx/util/command-interest-generator.hpp>
-
-namespace nfd {
-namespace tests {
-
-// class ValidatedManagementFixture
-// {
-// public:
-//   ValidatedManagementFixture()
-//     : m_validator(make_shared<ndn::CommandInterestValidator>())
-//   {
-//   }
-
-//   virtual
-//   ~ValidatedManagementFixture()
-//   {
-//   }
-
-// protected:
-//   shared_ptr<ndn::CommandInterestValidator> m_validator;
-// };
-
-/// a global fixture that holds the identity for CommandFixture
-class CommandIdentityGlobalFixture
-{
-public:
-  CommandIdentityGlobalFixture();
-
-  ~CommandIdentityGlobalFixture();
-
-  static const Name& getIdentityName()
-  {
-    return s_identityName;
-  }
-
-  static shared_ptr<ndn::IdentityCertificate> getCertificate()
-  {
-    BOOST_ASSERT(static_cast<bool>(s_certificate));
-    return s_certificate;
-  }
-
-private:
-  ndn::KeyChain m_keys;
-  static const Name s_identityName;
-  static shared_ptr<ndn::IdentityCertificate> s_certificate;
-};
-
-template<typename T>
-class CommandFixture : public T
-{
-public:
-  virtual
-  ~CommandFixture()
-  {
-  }
-
-  void
-  generateCommand(Interest& interest)
-  {
-    m_generator.generateWithIdentity(interest, getIdentityName());
-  }
-
-  const Name&
-  getIdentityName() const
-  {
-    return CommandIdentityGlobalFixture::getIdentityName();
-  }
-
-protected:
-  CommandFixture()
-    : m_certificate(CommandIdentityGlobalFixture::getCertificate())
-  {
-  }
-
-protected:
-  shared_ptr<ndn::IdentityCertificate> m_certificate;
-  ndn::CommandInterestGenerator m_generator;
-};
-
-template <typename T>
-class UnauthorizedCommandFixture : public CommandFixture<T>
-{
-public:
-  UnauthorizedCommandFixture()
-  {
-  }
-
-  virtual
-  ~UnauthorizedCommandFixture()
-  {
-  }
-};
-
-} // namespace tests
-} // namespace nfd
-
-#endif // NFD_TESTS_NFD_MGMT_VALIDATION_COMMON_HPP