mgmt: move management/nfd-* to mgmt/nfd/

refs #3760

Change-Id: Ib4bde3412b5c39b9f4f46113199cebe78704505e
diff --git a/src/mgmt/nfd/channel-status.cpp b/src/mgmt/nfd/channel-status.cpp
new file mode 100644
index 0000000..bbbfdf4
--- /dev/null
+++ b/src/mgmt/nfd/channel-status.cpp
@@ -0,0 +1,109 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "channel-status.hpp"
+#include "encoding/tlv-nfd.hpp"
+#include "encoding/block-helpers.hpp"
+#include "util/concepts.hpp"
+
+namespace ndn {
+namespace nfd {
+
+//BOOST_CONCEPT_ASSERT((boost::EqualityComparable<ChannelStatus>));
+BOOST_CONCEPT_ASSERT((WireEncodable<ChannelStatus>));
+BOOST_CONCEPT_ASSERT((WireDecodable<ChannelStatus>));
+static_assert(std::is_base_of<tlv::Error, ChannelStatus::Error>::value,
+              "ChannelStatus::Error must inherit from tlv::Error");
+
+ChannelStatus::ChannelStatus()
+{
+}
+
+ChannelStatus::ChannelStatus(const Block& payload)
+{
+  this->wireDecode(payload);
+}
+
+template<encoding::Tag TAG>
+size_t
+ChannelStatus::wireEncode(EncodingImpl<TAG>& encoder) const
+{
+  size_t totalLength = 0;
+
+  totalLength += encoder.prependByteArrayBlock(tlv::nfd::LocalUri,
+                 reinterpret_cast<const uint8_t*>(m_localUri.c_str()), m_localUri.size());
+
+  totalLength += encoder.prependVarNumber(totalLength);
+  totalLength += encoder.prependVarNumber(tlv::nfd::ChannelStatus);
+  return totalLength;
+}
+
+template size_t
+ChannelStatus::wireEncode<encoding::EncoderTag>(EncodingImpl<encoding::EncoderTag>&) const;
+
+template size_t
+ChannelStatus::wireEncode<encoding::EstimatorTag>(EncodingImpl<encoding::EstimatorTag>&) const;
+
+const Block&
+ChannelStatus::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+ChannelStatus::wireDecode(const Block& block)
+{
+  if (block.type() != tlv::nfd::ChannelStatus) {
+    BOOST_THROW_EXCEPTION(Error("Expecting ChannelStatus block"));
+  }
+  m_wire = block;
+  m_wire.parse();
+  Block::element_const_iterator val = m_wire.elements_begin();
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::LocalUri) {
+    m_localUri.assign(reinterpret_cast<const char*>(val->value()), val->value_size());
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("Missing required LocalUri field"));
+  }
+}
+
+ChannelStatus&
+ChannelStatus::setLocalUri(const std::string localUri)
+{
+  m_wire.reset();
+  m_localUri = localUri;
+  return *this;
+}
+
+} // namespace nfd
+} // namespace ndn
diff --git a/src/mgmt/nfd/channel-status.hpp b/src/mgmt/nfd/channel-status.hpp
new file mode 100644
index 0000000..5a2aae8
--- /dev/null
+++ b/src/mgmt/nfd/channel-status.hpp
@@ -0,0 +1,82 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MGMT_NFD_CHANNEL_STATUS_HPP
+#define NDN_MGMT_NFD_CHANNEL_STATUS_HPP
+
+#include "../../encoding/block.hpp"
+
+namespace ndn {
+namespace nfd {
+
+/**
+ * @ingroup management
+ * @brief represents NFD Channel Status dataset
+ * @sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Channel-Dataset
+ */
+class ChannelStatus
+{
+public:
+  class Error : public tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : tlv::Error(what)
+    {
+    }
+  };
+
+  ChannelStatus();
+
+  explicit
+  ChannelStatus(const Block& payload);
+
+  template<encoding::Tag TAG>
+  size_t
+  wireEncode(EncodingImpl<TAG>& encoder) const;
+
+  const Block&
+  wireEncode() const;
+
+  void
+  wireDecode(const Block& wire);
+
+public: // getters & setters
+  const std::string&
+  getLocalUri() const
+  {
+    return m_localUri;
+  }
+
+  ChannelStatus&
+  setLocalUri(const std::string localUri);
+
+private:
+  std::string m_localUri;
+
+  mutable Block m_wire;
+};
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MGMT_NFD_CHANNEL_STATUS_HPP
diff --git a/src/mgmt/nfd/command-options.cpp b/src/mgmt/nfd/command-options.cpp
new file mode 100644
index 0000000..557cce3
--- /dev/null
+++ b/src/mgmt/nfd/command-options.cpp
@@ -0,0 +1,141 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "command-options.hpp"
+
+#ifdef NDN_MANAGEMENT_NFD_COMMAND_OPTIONS_KEEP_DEPRECATED_SIGNING_PARAMS
+#include "../../security/v1/identity-certificate.hpp"
+#include "../../security/signing-helpers.hpp"
+#endif // NDN_MANAGEMENT_NFD_COMMAND_OPTIONS_KEEP_DEPRECATED_SIGNING_PARAMS
+
+namespace ndn {
+namespace nfd {
+
+const time::milliseconds CommandOptions::DEFAULT_TIMEOUT(10000);
+const Name CommandOptions::DEFAULT_PREFIX("ndn:/localhost/nfd");
+
+CommandOptions::CommandOptions()
+  : m_timeout(DEFAULT_TIMEOUT)
+  , m_prefix(DEFAULT_PREFIX)
+{
+}
+
+CommandOptions&
+CommandOptions::setTimeout(const time::milliseconds& timeout)
+{
+  if (timeout <= time::milliseconds::zero()) {
+    BOOST_THROW_EXCEPTION(std::out_of_range("Timeout must be positive"));
+  }
+
+  m_timeout = timeout;
+  return *this;
+}
+
+CommandOptions&
+CommandOptions::setPrefix(const Name& prefix)
+{
+  m_prefix = prefix;
+  return *this;
+}
+
+CommandOptions&
+CommandOptions::setSigningInfo(const security::SigningInfo& signingInfo)
+{
+  m_signingInfo = signingInfo;
+  return *this;
+}
+
+#ifdef NDN_MANAGEMENT_NFD_COMMAND_OPTIONS_KEEP_DEPRECATED_SIGNING_PARAMS
+
+CommandOptions::SigningParamsKind
+CommandOptions::getSigningParamsKind() const
+{
+  switch (m_signingInfo.getSignerType()) {
+  case security::SigningInfo::SIGNER_TYPE_NULL:
+    return SIGNING_PARAMS_DEFAULT;
+  case security::SigningInfo::SIGNER_TYPE_ID:
+    return SIGNING_PARAMS_IDENTITY;
+  case security::SigningInfo::SIGNER_TYPE_CERT:
+    return SIGNING_PARAMS_CERTIFICATE;
+  default:
+    BOOST_THROW_EXCEPTION(std::out_of_range("SigningInfo::SignerType is not convertible to "
+                                            "CommandOptions::SigningParamsKind"));
+  }
+}
+
+const Name&
+CommandOptions::getSigningIdentity() const
+{
+  BOOST_ASSERT(m_signingInfo.getSignerType() == security::SigningInfo::SIGNER_TYPE_ID);
+  return m_signingInfo.getSignerName();
+}
+
+const Name&
+CommandOptions::getSigningCertificate() const
+{
+  BOOST_ASSERT(m_signingInfo.getSignerType() == security::SigningInfo::SIGNER_TYPE_CERT);
+  return m_signingInfo.getSignerName();
+}
+
+CommandOptions&
+CommandOptions::setSigningDefault()
+{
+  m_signingInfo = security::SigningInfo();
+  return *this;
+}
+
+CommandOptions&
+CommandOptions::setSigningIdentity(const Name& identityName)
+{
+  m_signingInfo = security::signingByIdentity(identityName);
+  return *this;
+}
+
+static security::SigningInfo
+makeSigningInfoFromIdentityCertificate(const Name& certificateName)
+{
+  // A valid IdentityCertificate has at least 4 name components,
+  // as it follows `<...>/KEY/<...>/<key-id>/ID-CERT/<version>` naming model.
+  if (certificateName.size() < 4) {
+    BOOST_THROW_EXCEPTION(std::invalid_argument("Certificate is invalid"));
+  }
+
+  return security::signingByCertificate(certificateName);
+}
+
+CommandOptions&
+CommandOptions::setSigningCertificate(const Name& certificateName)
+{
+  m_signingInfo = makeSigningInfoFromIdentityCertificate(certificateName);
+  return *this;
+}
+
+CommandOptions&
+CommandOptions::setSigningCertificate(const security::v1::IdentityCertificate& certificate)
+{
+  m_signingInfo = makeSigningInfoFromIdentityCertificate(certificate.getName());
+  return *this;
+}
+
+#endif // NDN_MANAGEMENT_NFD_COMMAND_OPTIONS_KEEP_DEPRECATED_SIGNING_PARAMS
+
+} // namespace nfd
+} // namespace ndn
diff --git a/src/mgmt/nfd/command-options.hpp b/src/mgmt/nfd/command-options.hpp
new file mode 100644
index 0000000..efde928
--- /dev/null
+++ b/src/mgmt/nfd/command-options.hpp
@@ -0,0 +1,196 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MGMT_NFD_COMMAND_OPTIONS_HPP
+#define NDN_MGMT_NFD_COMMAND_OPTIONS_HPP
+
+#include "../../security/signing-info.hpp"
+
+#define NDN_MGMT_NFD_COMMAND_OPTIONS_KEEP_DEPRECATED_SIGNING_PARAMS
+
+namespace ndn {
+
+namespace security {
+namespace v1 {
+class IdentityCertificate;
+} // namespace v1
+} // namespace security
+
+namespace nfd {
+
+/** \ingroup management
+ *  \brief contains options for ControlCommand execution
+ *  \note This type is intentionally copyable
+ */
+class CommandOptions
+{
+public:
+  /** \brief constructs CommandOptions
+   *  \post getTimeout() == DEFAULT_TIMEOUT
+   *  \post getPrefix() == DEFAULT_PREFIX
+   *  \post getSigningInfo().getSignerType() == SIGNER_TYPE_NULL
+   */
+  CommandOptions();
+
+  /** \return command timeout
+   */
+  const time::milliseconds&
+  getTimeout() const
+  {
+    return m_timeout;
+  }
+
+  /** \brief sets command timeout
+   *  \param timeout the new command timeout, must be positive
+   *  \throw std::out_of_range if timeout is non-positive
+   *  \return self
+   */
+  CommandOptions&
+  setTimeout(const time::milliseconds& timeout);
+
+  /** \return command prefix
+   */
+  const Name&
+  getPrefix() const
+  {
+    return m_prefix;
+  }
+
+  /** \brief sets command prefix
+   *  \return self
+   */
+  CommandOptions&
+  setPrefix(const Name& prefix);
+
+  /** \return signing parameters
+   */
+  const security::SigningInfo&
+  getSigningInfo() const
+  {
+    return m_signingInfo;
+  }
+
+  /** \brief sets signing parameters
+   *  \return self
+   */
+  CommandOptions&
+  setSigningInfo(const security::SigningInfo& signingInfo);
+
+#ifdef NDN_MGMT_NFD_COMMAND_OPTIONS_KEEP_DEPRECATED_SIGNING_PARAMS
+public: // signing parameters
+  /** \deprecated use getSigningInfo and setSigningInfo
+   *  \brief indicates the selection of signing parameters
+   */
+  enum SigningParamsKind {
+    /** \brief picks the default signing identity and certificate
+     */
+    SIGNING_PARAMS_DEFAULT,
+    /** \brief picks the default certificate of a specific identity Name
+     */
+    SIGNING_PARAMS_IDENTITY,
+    /** \brief picks a specific identity certificate
+     */
+    SIGNING_PARAMS_CERTIFICATE
+  };
+
+  /** \deprecated use getSigningInfo and setSigningInfo
+   *  \return selection of signing parameters
+   */
+  DEPRECATED(
+  SigningParamsKind
+  getSigningParamsKind() const);
+
+  /** \deprecated use getSigningInfo and setSigningInfo
+   *  \return identity Name
+   *  \pre getSigningParamsKind() == SIGNING_PARAMS_IDENTITY
+   */
+  DEPRECATED(
+  const Name&
+  getSigningIdentity() const);
+
+  /** \deprecated use getSigningInfo and setSigningInfo
+   *  \return certificate Name
+   *  \pre getSigningParamsKind() == SIGNING_PARAMS_CERTIFICATE
+   */
+  DEPRECATED(
+  const Name&
+  getSigningCertificate() const);
+
+  /** \deprecated use getSigningInfo and setSigningInfo
+   *  \brief chooses to use default identity and certificate
+   *  \post getSigningParamsKind() == SIGNING_PARAMS_DEFAULT
+   *  \return self
+   */
+  DEPRECATED(
+  CommandOptions&
+  setSigningDefault());
+
+  /** \deprecated use getSigningInfo and setSigningInfo
+   *  \brief chooses to use a specific identity and its default certificate
+   *  \post getSigningParamsKind() == SIGNING_PARAMS_IDENTITY
+   *  \post getIdentityName() == identityName
+   *  \return self
+   */
+  DEPRECATED(
+  CommandOptions&
+  setSigningIdentity(const Name& identityName));
+
+  /** \deprecated use getSigningInfo and setSigningInfo
+   *  \brief chooses to use a specific identity certificate
+   *  \param certificateName identity certificate Name
+   *  \throw std::invalid_argument if certificateName is invalid
+   *  \post getSigningParamsKind() == SIGNING_PARAMS_CERTIFICATE
+   *  \post getSigningCertificate() == certificateName
+   *  \return self
+   */
+  DEPRECATED(
+  CommandOptions&
+  setSigningCertificate(const Name& certificateName));
+
+  /** \deprecated use getSigningInfo and setSigningInfo
+   *  \brief chooses to use a specific identity certificate
+   *  \details This is equivalent to .setIdentityCertificate(certificate.getName())
+   */
+  DEPRECATED(
+  CommandOptions&
+  setSigningCertificate(const security::v1::IdentityCertificate& certificate));
+
+#endif // NDN_MGMT_NFD_COMMAND_OPTIONS_KEEP_DEPRECATED_SIGNING_PARAMS
+
+public:
+  /** \brief gives the default command timeout: 10000ms
+   */
+  static const time::milliseconds DEFAULT_TIMEOUT;
+
+  /** \brief gives the default command prefix: ndn:/localhost/nfd
+   */
+  static const Name DEFAULT_PREFIX;
+
+private:
+  time::milliseconds m_timeout;
+  Name m_prefix;
+  security::SigningInfo m_signingInfo;
+};
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MGMT_NFD_COMMAND_OPTIONS_HPP
diff --git a/src/mgmt/nfd/control-command.cpp b/src/mgmt/nfd/control-command.cpp
new file mode 100644
index 0000000..2a6eda7
--- /dev/null
+++ b/src/mgmt/nfd/control-command.cpp
@@ -0,0 +1,422 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "control-command.hpp"
+#include "command-options.hpp" // only used in deprecated functions
+
+namespace ndn {
+namespace nfd {
+
+ControlCommand::ControlCommand(const std::string& module, const std::string& verb)
+  : m_module(module)
+  , m_verb(verb)
+{
+}
+
+ControlCommand::~ControlCommand() = default;
+
+void
+ControlCommand::validateRequest(const ControlParameters& parameters) const
+{
+  m_requestValidator.validate(parameters);
+}
+
+void
+ControlCommand::applyDefaultsToRequest(ControlParameters& parameters) const
+{
+}
+
+void
+ControlCommand::validateResponse(const ControlParameters& parameters) const
+{
+  m_responseValidator.validate(parameters);
+}
+
+void
+ControlCommand::applyDefaultsToResponse(ControlParameters& parameters) const
+{
+}
+
+Name
+ControlCommand::getRequestName(const Name& commandPrefix,
+                               const ControlParameters& parameters) const
+{
+  this->validateRequest(parameters);
+
+  Name name = commandPrefix;
+  name.append(m_module).append(m_verb);
+  name.append(parameters.wireEncode());
+  return name;
+}
+
+ControlCommand::FieldValidator::FieldValidator()
+  : m_required(CONTROL_PARAMETER_UBOUND)
+  , m_optional(CONTROL_PARAMETER_UBOUND)
+{
+}
+
+void
+ControlCommand::FieldValidator::validate(const ControlParameters& parameters) const
+{
+  const std::vector<bool>& presentFields = parameters.getPresentFields();
+
+  for (size_t i = 0; i < CONTROL_PARAMETER_UBOUND; ++i) {
+    bool isPresent = presentFields[i];
+    if (m_required[i]) {
+      if (!isPresent) {
+        BOOST_THROW_EXCEPTION(ArgumentError(CONTROL_PARAMETER_FIELD[i] + " is required but "
+                                            "missing"));
+      }
+    }
+    else if (isPresent && !m_optional[i]) {
+      BOOST_THROW_EXCEPTION(ArgumentError(CONTROL_PARAMETER_FIELD[i] + " is forbidden but "
+                                          "present"));
+    }
+  }
+}
+
+FaceCreateCommand::FaceCreateCommand()
+  : ControlCommand("faces", "create")
+{
+  m_requestValidator
+    .required(CONTROL_PARAMETER_URI)
+    .optional(CONTROL_PARAMETER_FACE_PERSISTENCY)
+    .optional(CONTROL_PARAMETER_FLAGS)
+    .optional(CONTROL_PARAMETER_MASK);
+  m_responseValidator
+    .required(CONTROL_PARAMETER_FACE_ID)
+    .required(CONTROL_PARAMETER_FACE_PERSISTENCY)
+    .optional(CONTROL_PARAMETER_FLAGS)
+    .optional(CONTROL_PARAMETER_URI);
+}
+
+void
+FaceCreateCommand::applyDefaultsToRequest(ControlParameters& parameters) const
+{
+  parameters.setFaceId(0);
+
+  if (!parameters.hasFacePersistency()) {
+    parameters.setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+  }
+}
+
+void
+FaceCreateCommand::validateRequest(const ControlParameters& parameters) const
+{
+  this->ControlCommand::validateRequest(parameters);
+
+  if (parameters.hasFlags() != parameters.hasMask()) {
+    BOOST_THROW_EXCEPTION(ArgumentError("Flags must be accompanied by Mask"));
+  }
+}
+
+void
+FaceCreateCommand::validateResponse(const ControlParameters& parameters) const
+{
+  this->ControlCommand::validateResponse(parameters);
+
+  if (parameters.getFaceId() == 0) {
+    BOOST_THROW_EXCEPTION(ArgumentError("FaceId must not be zero"));
+  }
+}
+
+FaceUpdateCommand::FaceUpdateCommand()
+  : ControlCommand("faces", "update")
+{
+  m_requestValidator
+    .optional(CONTROL_PARAMETER_FACE_ID)
+    .optional(CONTROL_PARAMETER_FACE_PERSISTENCY)
+    .optional(CONTROL_PARAMETER_FLAGS)
+    .optional(CONTROL_PARAMETER_MASK);
+  m_responseValidator
+    .required(CONTROL_PARAMETER_FACE_ID)
+    .required(CONTROL_PARAMETER_FACE_PERSISTENCY)
+    .required(CONTROL_PARAMETER_FLAGS);
+}
+
+void
+FaceUpdateCommand::applyDefaultsToRequest(ControlParameters& parameters) const
+{
+  if (!parameters.hasFaceId()) {
+    parameters.setFaceId(0);
+  }
+}
+
+void
+FaceUpdateCommand::validateRequest(const ControlParameters& parameters) const
+{
+  this->ControlCommand::validateRequest(parameters);
+
+  if (parameters.hasFlags() != parameters.hasMask()) {
+    BOOST_THROW_EXCEPTION(ArgumentError("Flags must be accompanied by Mask"));
+  }
+}
+
+void
+FaceUpdateCommand::validateResponse(const ControlParameters& parameters) const
+{
+  this->ControlCommand::validateResponse(parameters);
+
+  if (parameters.getFaceId() == 0) {
+    BOOST_THROW_EXCEPTION(ArgumentError("FaceId must not be zero"));
+  }
+}
+
+FaceDestroyCommand::FaceDestroyCommand()
+  : ControlCommand("faces", "destroy")
+{
+  m_requestValidator
+    .required(CONTROL_PARAMETER_FACE_ID);
+  m_responseValidator = m_requestValidator;
+}
+
+void
+FaceDestroyCommand::validateRequest(const ControlParameters& parameters) const
+{
+  this->ControlCommand::validateRequest(parameters);
+
+  if (parameters.getFaceId() == 0) {
+    BOOST_THROW_EXCEPTION(ArgumentError("FaceId must not be zero"));
+  }
+}
+
+void
+FaceDestroyCommand::validateResponse(const ControlParameters& parameters) const
+{
+  this->validateRequest(parameters);
+}
+
+FaceLocalControlCommand::FaceLocalControlCommand(const std::string& verb)
+  : ControlCommand("faces", verb)
+{
+  m_requestValidator
+    .required(CONTROL_PARAMETER_LOCAL_CONTROL_FEATURE);
+  m_responseValidator = m_requestValidator;
+}
+
+void
+FaceLocalControlCommand::validateRequest(const ControlParameters& parameters) const
+{
+  this->ControlCommand::validateRequest(parameters);
+
+  switch (parameters.getLocalControlFeature()) {
+    case LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID:
+    case LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID:
+      break;
+    default:
+      BOOST_THROW_EXCEPTION(ArgumentError("LocalControlFeature is invalid"));
+  }
+}
+
+void
+FaceLocalControlCommand::validateResponse(const ControlParameters& parameters) const
+{
+  this->validateRequest(parameters);
+}
+
+FaceEnableLocalControlCommand::FaceEnableLocalControlCommand()
+  : FaceLocalControlCommand("enable-local-control")
+{
+}
+
+FaceDisableLocalControlCommand::FaceDisableLocalControlCommand()
+  : FaceLocalControlCommand("disable-local-control")
+{
+}
+
+FibAddNextHopCommand::FibAddNextHopCommand()
+  : ControlCommand("fib", "add-nexthop")
+{
+  m_requestValidator
+    .required(CONTROL_PARAMETER_NAME)
+    .optional(CONTROL_PARAMETER_FACE_ID)
+    .optional(CONTROL_PARAMETER_COST);
+  m_responseValidator
+    .required(CONTROL_PARAMETER_NAME)
+    .required(CONTROL_PARAMETER_FACE_ID)
+    .required(CONTROL_PARAMETER_COST);
+}
+
+void
+FibAddNextHopCommand::applyDefaultsToRequest(ControlParameters& parameters) const
+{
+  if (!parameters.hasFaceId()) {
+    parameters.setFaceId(0);
+  }
+  if (!parameters.hasCost()) {
+    parameters.setCost(0);
+  }
+}
+
+void
+FibAddNextHopCommand::validateResponse(const ControlParameters& parameters) const
+{
+  this->ControlCommand::validateResponse(parameters);
+
+  if (parameters.getFaceId() == 0) {
+    BOOST_THROW_EXCEPTION(ArgumentError("FaceId must not be zero"));
+  }
+}
+
+FibRemoveNextHopCommand::FibRemoveNextHopCommand()
+  : ControlCommand("fib", "remove-nexthop")
+{
+  m_requestValidator
+    .required(CONTROL_PARAMETER_NAME)
+    .optional(CONTROL_PARAMETER_FACE_ID);
+  m_responseValidator
+    .required(CONTROL_PARAMETER_NAME)
+    .required(CONTROL_PARAMETER_FACE_ID);
+}
+
+void
+FibRemoveNextHopCommand::applyDefaultsToRequest(ControlParameters& parameters) const
+{
+  if (!parameters.hasFaceId()) {
+    parameters.setFaceId(0);
+  }
+}
+
+void
+FibRemoveNextHopCommand::validateResponse(const ControlParameters& parameters) const
+{
+  this->ControlCommand::validateResponse(parameters);
+
+  if (parameters.getFaceId() == 0) {
+    BOOST_THROW_EXCEPTION(ArgumentError("FaceId must not be zero"));
+  }
+}
+
+StrategyChoiceSetCommand::StrategyChoiceSetCommand()
+  : ControlCommand("strategy-choice", "set")
+{
+  m_requestValidator
+    .required(CONTROL_PARAMETER_NAME)
+    .required(CONTROL_PARAMETER_STRATEGY);
+  m_responseValidator = m_requestValidator;
+}
+
+StrategyChoiceUnsetCommand::StrategyChoiceUnsetCommand()
+  : ControlCommand("strategy-choice", "unset")
+{
+  m_requestValidator
+    .required(CONTROL_PARAMETER_NAME);
+  m_responseValidator = m_requestValidator;
+}
+
+void
+StrategyChoiceUnsetCommand::validateRequest(const ControlParameters& parameters) const
+{
+  this->ControlCommand::validateRequest(parameters);
+
+  if (parameters.getName().size() == 0) {
+    BOOST_THROW_EXCEPTION(ArgumentError("Name must not be ndn:/"));
+  }
+}
+
+void
+StrategyChoiceUnsetCommand::validateResponse(const ControlParameters& parameters) const
+{
+  this->validateRequest(parameters);
+}
+
+RibRegisterCommand::RibRegisterCommand()
+  : ControlCommand("rib", "register")
+{
+  m_requestValidator
+    .required(CONTROL_PARAMETER_NAME)
+    .optional(CONTROL_PARAMETER_FACE_ID)
+    .optional(CONTROL_PARAMETER_ORIGIN)
+    .optional(CONTROL_PARAMETER_COST)
+    .optional(CONTROL_PARAMETER_FLAGS)
+    .optional(CONTROL_PARAMETER_EXPIRATION_PERIOD);
+  m_responseValidator
+    .required(CONTROL_PARAMETER_NAME)
+    .required(CONTROL_PARAMETER_FACE_ID)
+    .required(CONTROL_PARAMETER_ORIGIN)
+    .required(CONTROL_PARAMETER_COST)
+    .required(CONTROL_PARAMETER_FLAGS)
+    .optional(CONTROL_PARAMETER_EXPIRATION_PERIOD);
+}
+
+void
+RibRegisterCommand::applyDefaultsToRequest(ControlParameters& parameters) const
+{
+  if (!parameters.hasFaceId()) {
+    parameters.setFaceId(0);
+  }
+  if (!parameters.hasOrigin()) {
+    parameters.setOrigin(ROUTE_ORIGIN_APP);
+  }
+  if (!parameters.hasCost()) {
+    parameters.setCost(0);
+  }
+  if (!parameters.hasFlags()) {
+    parameters.setFlags(ROUTE_FLAG_CHILD_INHERIT);
+  }
+}
+
+void
+RibRegisterCommand::validateResponse(const ControlParameters& parameters) const
+{
+  this->ControlCommand::validateResponse(parameters);
+
+  if (parameters.getFaceId() == 0) {
+    BOOST_THROW_EXCEPTION(ArgumentError("FaceId must not be zero"));
+  }
+}
+
+RibUnregisterCommand::RibUnregisterCommand()
+  : ControlCommand("rib", "unregister")
+{
+  m_requestValidator
+    .required(CONTROL_PARAMETER_NAME)
+    .optional(CONTROL_PARAMETER_FACE_ID)
+    .optional(CONTROL_PARAMETER_ORIGIN);
+  m_responseValidator
+    .required(CONTROL_PARAMETER_NAME)
+    .required(CONTROL_PARAMETER_FACE_ID)
+    .required(CONTROL_PARAMETER_ORIGIN);
+}
+
+void
+RibUnregisterCommand::applyDefaultsToRequest(ControlParameters& parameters) const
+{
+  if (!parameters.hasFaceId()) {
+    parameters.setFaceId(0);
+  }
+  if (!parameters.hasOrigin()) {
+    parameters.setOrigin(ROUTE_ORIGIN_APP);
+  }
+}
+
+void
+RibUnregisterCommand::validateResponse(const ControlParameters& parameters) const
+{
+  this->ControlCommand::validateResponse(parameters);
+
+  if (parameters.getFaceId() == 0) {
+    BOOST_THROW_EXCEPTION(ArgumentError("FaceId must not be zero"));
+  }
+}
+
+} // namespace nfd
+} // namespace ndn
diff --git a/src/mgmt/nfd/control-command.hpp b/src/mgmt/nfd/control-command.hpp
new file mode 100644
index 0000000..7544156
--- /dev/null
+++ b/src/mgmt/nfd/control-command.hpp
@@ -0,0 +1,348 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MGMT_NFD_CONTROL_COMMAND_HPP
+#define NDN_MGMT_NFD_CONTROL_COMMAND_HPP
+
+#include "control-parameters.hpp"
+
+namespace ndn {
+namespace nfd {
+
+/**
+ * \ingroup management
+ * \brief base class of NFD ControlCommand
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/ControlCommand
+ */
+class ControlCommand : noncopyable
+{
+public:
+  /** \brief represents an error in ControlParameters
+   */
+  class ArgumentError : public std::invalid_argument
+  {
+  public:
+    explicit
+    ArgumentError(const std::string& what)
+      : std::invalid_argument(what)
+    {
+    }
+  };
+
+  virtual
+  ~ControlCommand();
+
+  /** \brief validate request parameters
+   *  \throw ArgumentError if parameters are invalid
+   */
+  virtual void
+  validateRequest(const ControlParameters& parameters) const;
+
+  /** \brief apply default values to missing fields in request
+   */
+  virtual void
+  applyDefaultsToRequest(ControlParameters& parameters) const;
+
+  /** \brief validate response parameters
+   *  \throw ArgumentError if parameters are invalid
+   */
+  virtual void
+  validateResponse(const ControlParameters& parameters) const;
+
+  /** \brief apply default values to missing fields in response
+   */
+  virtual void
+  applyDefaultsToResponse(ControlParameters& parameters) const;
+
+  /** \brief construct the Name for a request Interest
+   *  \throw ArgumentError if parameters are invalid
+   */
+  Name
+  getRequestName(const Name& commandPrefix, const ControlParameters& parameters) const;
+
+protected:
+  ControlCommand(const std::string& module, const std::string& verb);
+
+  class FieldValidator
+  {
+  public:
+    FieldValidator();
+
+    /** \brief declare a required field
+     */
+    FieldValidator&
+    required(ControlParameterField field)
+    {
+      m_required[field] = true;
+      return *this;
+    }
+
+    /** \brief declare an optional field
+     */
+    FieldValidator&
+    optional(ControlParameterField field)
+    {
+      m_optional[field] = true;
+      return *this;
+    }
+
+    /** \brief verify that all required fields are present,
+     *         and all present fields are either required or optional
+     *  \throw ArgumentError
+     */
+    void
+    validate(const ControlParameters& parameters) const;
+
+  private:
+    std::vector<bool> m_required;
+    std::vector<bool> m_optional;
+  };
+
+protected:
+  /** \brief FieldValidator for request ControlParameters
+   *
+   *  Constructor of subclass should populate this validator.
+   */
+  FieldValidator m_requestValidator;
+  /** \brief FieldValidator for response ControlParameters
+   *
+   *  Constructor of subclass should populate this validator.
+   */
+  FieldValidator m_responseValidator;
+
+private:
+  name::Component m_module;
+  name::Component m_verb;
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a faces/create command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Create-a-face
+ */
+class FaceCreateCommand : public ControlCommand
+{
+public:
+  FaceCreateCommand();
+
+  virtual void
+  applyDefaultsToRequest(ControlParameters& parameters) const override;
+
+  virtual void
+  validateRequest(const ControlParameters& parameters) const override;
+
+  virtual void
+  validateResponse(const ControlParameters& parameters) const override;
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a faces/update command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Update-a-face
+ */
+class FaceUpdateCommand : public ControlCommand
+{
+public:
+  FaceUpdateCommand();
+
+  virtual void
+  applyDefaultsToRequest(ControlParameters& parameters) const override;
+
+  virtual void
+  validateRequest(const ControlParameters& parameters) const override;
+
+  /**
+   * \note This can only validate ControlParameters in a success response.
+   *       Failure responses should be validated with validateRequest.
+   */
+  virtual void
+  validateResponse(const ControlParameters& parameters) const override;
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a faces/destroy command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Destroy-a-face
+ */
+class FaceDestroyCommand : public ControlCommand
+{
+public:
+  FaceDestroyCommand();
+
+  virtual void
+  validateRequest(const ControlParameters& parameters) const override;
+
+  virtual void
+  validateResponse(const ControlParameters& parameters) const override;
+};
+
+
+/**
+ * \ingroup management
+ * \brief Base class for faces/[*]-local-control commands
+ */
+class FaceLocalControlCommand : public ControlCommand
+{
+public:
+  virtual void
+  validateRequest(const ControlParameters& parameters) const override;
+
+  virtual void
+  validateResponse(const ControlParameters& parameters) const override;
+
+protected:
+  explicit
+  FaceLocalControlCommand(const std::string& verb);
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a faces/enable-local-control command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Enable-a-LocalControlHeader-feature
+ */
+class FaceEnableLocalControlCommand : public FaceLocalControlCommand
+{
+public:
+  FaceEnableLocalControlCommand();
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a faces/disable-local-control command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Disable-a-LocalControlHeader-feature
+ */
+class FaceDisableLocalControlCommand : public FaceLocalControlCommand
+{
+public:
+  FaceDisableLocalControlCommand();
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a fib/add-nexthop command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#Add-a-nexthop
+ */
+class FibAddNextHopCommand : public ControlCommand
+{
+public:
+  FibAddNextHopCommand();
+
+  virtual void
+  applyDefaultsToRequest(ControlParameters& parameters) const override;
+
+  virtual void
+  validateResponse(const ControlParameters& parameters) const override;
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a fib/remove-nexthop command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#Remove-a-nexthop
+ */
+class FibRemoveNextHopCommand : public ControlCommand
+{
+public:
+  FibRemoveNextHopCommand();
+
+  virtual void
+  applyDefaultsToRequest(ControlParameters& parameters) const override;
+
+  virtual void
+  validateResponse(const ControlParameters& parameters) const override;
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a strategy-choice/set command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice#Set-the-strategy-for-a-namespace
+ */
+class StrategyChoiceSetCommand : public ControlCommand
+{
+public:
+  StrategyChoiceSetCommand();
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a strategy-choice/set command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice#Unset-the-strategy-for-a-namespace
+ */
+class StrategyChoiceUnsetCommand : public ControlCommand
+{
+public:
+  StrategyChoiceUnsetCommand();
+
+  virtual void
+  validateRequest(const ControlParameters& parameters) const override;
+
+  virtual void
+  validateResponse(const ControlParameters& parameters) const override;
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a rib/register command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#Register-a-route
+ */
+class RibRegisterCommand : public ControlCommand
+{
+public:
+  RibRegisterCommand();
+
+  virtual void
+  applyDefaultsToRequest(ControlParameters& parameters) const override;
+
+  virtual void
+  validateResponse(const ControlParameters& parameters) const override;
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a rib/unregister command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#Unregister-a-route
+ */
+class RibUnregisterCommand : public ControlCommand
+{
+public:
+  RibUnregisterCommand();
+
+  virtual void
+  applyDefaultsToRequest(ControlParameters& parameters) const override;
+
+  virtual void
+  validateResponse(const ControlParameters& parameters) const override;
+};
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MGMT_NFD_CONTROL_COMMAND_HPP
diff --git a/src/mgmt/nfd/control-parameters.cpp b/src/mgmt/nfd/control-parameters.cpp
new file mode 100644
index 0000000..30f8fe9
--- /dev/null
+++ b/src/mgmt/nfd/control-parameters.cpp
@@ -0,0 +1,335 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "control-parameters.hpp"
+#include "encoding/tlv-nfd.hpp"
+#include "encoding/block-helpers.hpp"
+#include "util/concepts.hpp"
+
+namespace ndn {
+namespace nfd {
+
+//BOOST_CONCEPT_ASSERT((boost::EqualityComparable<ControlParameters>));
+BOOST_CONCEPT_ASSERT((WireEncodable<ControlParameters>));
+BOOST_CONCEPT_ASSERT((WireDecodable<ControlParameters>));
+static_assert(std::is_base_of<tlv::Error, ControlParameters::Error>::value,
+              "ControlParameters::Error must inherit from tlv::Error");
+
+ControlParameters::ControlParameters()
+  : m_hasFields(CONTROL_PARAMETER_UBOUND)
+{
+}
+
+ControlParameters::ControlParameters(const Block& block)
+  : m_hasFields(CONTROL_PARAMETER_UBOUND)
+{
+  wireDecode(block);
+}
+
+template<encoding::Tag TAG>
+size_t
+ControlParameters::wireEncode(EncodingImpl<TAG>& encoder) const
+{
+  size_t totalLength = 0;
+
+  if (this->hasFacePersistency()) {
+    totalLength += prependNonNegativeIntegerBlock(encoder,
+                   tlv::nfd::FacePersistency, m_facePersistency);
+  }
+  if (this->hasExpirationPeriod()) {
+    totalLength += prependNonNegativeIntegerBlock(encoder,
+                   tlv::nfd::ExpirationPeriod, m_expirationPeriod.count());
+  }
+  if (this->hasStrategy()) {
+    totalLength += prependNestedBlock(encoder, tlv::nfd::Strategy, m_strategy);
+  }
+  if (this->hasMask()) {
+    totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::Mask, m_mask);
+  }
+  if (this->hasFlags()) {
+    totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::Flags, m_flags);
+  }
+  if (this->hasCost()) {
+    totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::Cost, m_cost);
+  }
+  if (this->hasOrigin()) {
+    totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::Origin, m_origin);
+  }
+  if (this->hasLocalControlFeature()) {
+    totalLength += prependNonNegativeIntegerBlock(encoder,
+                   tlv::nfd::LocalControlFeature, m_localControlFeature);
+  }
+  if (this->hasUri()) {
+    size_t valLength = encoder.prependByteArray(
+                       reinterpret_cast<const uint8_t*>(m_uri.c_str()), m_uri.size());
+    totalLength += valLength;
+    totalLength += encoder.prependVarNumber(valLength);
+    totalLength += encoder.prependVarNumber(tlv::nfd::Uri);
+  }
+  if (this->hasFaceId()) {
+    totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::FaceId, m_faceId);
+  }
+  if (this->hasName()) {
+    totalLength += m_name.wireEncode(encoder);
+  }
+
+  totalLength += encoder.prependVarNumber(totalLength);
+  totalLength += encoder.prependVarNumber(tlv::nfd::ControlParameters);
+  return totalLength;
+}
+
+template size_t
+ControlParameters::wireEncode<encoding::EncoderTag>(EncodingImpl<encoding::EncoderTag>&) const;
+
+template size_t
+ControlParameters::wireEncode<encoding::EstimatorTag>(EncodingImpl<encoding::EstimatorTag>&) const;
+
+Block
+ControlParameters::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+ControlParameters::wireDecode(const Block& block)
+{
+  if (block.type() != tlv::nfd::ControlParameters) {
+    BOOST_THROW_EXCEPTION(Error("Expecting TLV-TYPE ControlParameters"));
+  }
+  m_wire = block;
+  m_wire.parse();
+  Block::element_const_iterator val;
+
+  val = m_wire.find(tlv::Name);
+  m_hasFields[CONTROL_PARAMETER_NAME] = val != m_wire.elements_end();
+  if (this->hasName()) {
+    m_name.wireDecode(*val);
+  }
+
+  val = m_wire.find(tlv::nfd::FaceId);
+  m_hasFields[CONTROL_PARAMETER_FACE_ID] = val != m_wire.elements_end();
+  if (this->hasFaceId()) {
+    m_faceId = static_cast<uint64_t>(readNonNegativeInteger(*val));
+  }
+
+  val = m_wire.find(tlv::nfd::Uri);
+  m_hasFields[CONTROL_PARAMETER_URI] = val != m_wire.elements_end();
+  if (this->hasUri()) {
+    m_uri.assign(reinterpret_cast<const char*>(val->value()), val->value_size());
+  }
+
+  val = m_wire.find(tlv::nfd::LocalControlFeature);
+  m_hasFields[CONTROL_PARAMETER_LOCAL_CONTROL_FEATURE] = val != m_wire.elements_end();
+  if (this->hasLocalControlFeature()) {
+    m_localControlFeature = static_cast<LocalControlFeature>(readNonNegativeInteger(*val));
+  }
+
+  val = m_wire.find(tlv::nfd::Origin);
+  m_hasFields[CONTROL_PARAMETER_ORIGIN] = val != m_wire.elements_end();
+  if (this->hasOrigin()) {
+    m_origin = static_cast<uint64_t>(readNonNegativeInteger(*val));
+  }
+
+  val = m_wire.find(tlv::nfd::Cost);
+  m_hasFields[CONTROL_PARAMETER_COST] = val != m_wire.elements_end();
+  if (this->hasCost()) {
+    m_cost = static_cast<uint64_t>(readNonNegativeInteger(*val));
+  }
+
+  val = m_wire.find(tlv::nfd::Flags);
+  m_hasFields[CONTROL_PARAMETER_FLAGS] = val != m_wire.elements_end();
+  if (this->hasFlags()) {
+    m_flags = static_cast<uint64_t>(readNonNegativeInteger(*val));
+  }
+
+  val = m_wire.find(tlv::nfd::Mask);
+  m_hasFields[CONTROL_PARAMETER_MASK] = val != m_wire.elements_end();
+  if (this->hasMask()) {
+    m_mask = static_cast<uint64_t>(readNonNegativeInteger(*val));
+  }
+
+  val = m_wire.find(tlv::nfd::Strategy);
+  m_hasFields[CONTROL_PARAMETER_STRATEGY] = val != m_wire.elements_end();
+  if (this->hasStrategy()) {
+    val->parse();
+    if (val->elements().empty()) {
+      BOOST_THROW_EXCEPTION(Error("Expecting Strategy/Name"));
+    }
+    else {
+      m_strategy.wireDecode(*val->elements_begin());
+    }
+  }
+
+  val = m_wire.find(tlv::nfd::ExpirationPeriod);
+  m_hasFields[CONTROL_PARAMETER_EXPIRATION_PERIOD] = val != m_wire.elements_end();
+  if (this->hasExpirationPeriod()) {
+    m_expirationPeriod = time::milliseconds(readNonNegativeInteger(*val));
+  }
+
+  val = m_wire.find(tlv::nfd::FacePersistency);
+  m_hasFields[CONTROL_PARAMETER_FACE_PERSISTENCY] = val != m_wire.elements_end();
+  if (this->hasFacePersistency()) {
+    m_facePersistency = static_cast<FacePersistency>(readNonNegativeInteger(*val));
+  }
+}
+
+bool
+ControlParameters::hasFlagBit(size_t bit) const
+{
+  if (bit >= 64) {
+    BOOST_THROW_EXCEPTION(std::out_of_range("bit must be within range [0, 64)"));
+  }
+
+  if (!hasMask()) {
+    return false;
+  }
+
+  return getMask() & (1 << bit);
+}
+
+bool
+ControlParameters::getFlagBit(size_t bit) const
+{
+  if (bit >= 64) {
+    BOOST_THROW_EXCEPTION(std::out_of_range("bit must be within range [0, 64)"));
+  }
+
+  if (!hasFlags()) {
+    return false;
+  }
+
+  return getFlags() & (1 << bit);
+}
+
+ControlParameters&
+ControlParameters::setFlagBit(size_t bit, bool value, bool wantMask/* = true*/)
+{
+  if (bit >= 64) {
+    BOOST_THROW_EXCEPTION(std::out_of_range("bit must be within range [0, 64)"));
+  }
+
+  uint64_t flags = hasFlags() ? getFlags() : 0;
+  if (value) {
+    flags |= (1 << bit);
+  }
+  else {
+    flags &= ~(1 << bit);
+  }
+  setFlags(flags);
+
+  if (wantMask) {
+    uint64_t mask = hasMask() ? getMask() : 0;
+    mask |= (1 << bit);
+    setMask(mask);
+  }
+
+  return *this;
+}
+
+ControlParameters&
+ControlParameters::unsetFlagBit(size_t bit)
+{
+  if (bit >= 64) {
+    BOOST_THROW_EXCEPTION(std::out_of_range("bit must be within range [0, 64)"));
+  }
+
+  uint64_t mask = hasMask() ? getMask() : 0;
+  mask &= ~(1 << bit);
+  if (mask == 0) {
+    unsetMask();
+    unsetFlags();
+  }
+  else {
+    setMask(mask);
+  }
+
+  return *this;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const ControlParameters& parameters)
+{
+  os << "ControlParameters(";
+
+  if (parameters.hasName()) {
+    os << "Name: " << parameters.getName() << ", ";
+  }
+
+  if (parameters.hasFaceId()) {
+    os << "FaceId: " << parameters.getFaceId() << ", ";
+  }
+
+  if (parameters.hasUri()) {
+    os << "Uri: " << parameters.getUri() << ", ";
+  }
+
+  if (parameters.hasLocalControlFeature()) {
+    os << "LocalControlFeature: " << parameters.getLocalControlFeature() << ", ";
+  }
+
+  if (parameters.hasOrigin()) {
+    os << "Origin: " << parameters.getOrigin() << ", ";
+  }
+
+  if (parameters.hasCost()) {
+    os << "Cost: " << parameters.getCost() << ", ";
+  }
+
+  if (parameters.hasFlags()) {
+    std::ios_base::fmtflags osFlags = os.flags();
+    os << "Flags: " << std::showbase << std::hex << parameters.getFlags() << ", ";
+    os.flags(osFlags);
+  }
+
+  if (parameters.hasMask()) {
+    std::ios_base::fmtflags osFlags = os.flags();
+    os << "Mask: " << std::showbase << std::hex << parameters.getMask() << ", ";
+    os.flags(osFlags);
+  }
+
+  if (parameters.hasStrategy()) {
+    os << "Strategy: " << parameters.getStrategy() << ", ";
+  }
+
+  if (parameters.hasExpirationPeriod()) {
+    os << "ExpirationPeriod: " << parameters.getExpirationPeriod() << ", ";
+  }
+
+  if (parameters.hasFacePersistency()) {
+    os << "FacePersistency: " << parameters.getFacePersistency() << ", ";
+  }
+
+  os << ")";
+  return os;
+}
+
+} // namespace nfd
+} // namespace ndn
diff --git a/src/mgmt/nfd/control-parameters.hpp b/src/mgmt/nfd/control-parameters.hpp
new file mode 100644
index 0000000..d57c75d
--- /dev/null
+++ b/src/mgmt/nfd/control-parameters.hpp
@@ -0,0 +1,514 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MGMT_NFD_CONTROL_PARAMETERS_HPP
+#define NDN_MGMT_NFD_CONTROL_PARAMETERS_HPP
+
+#include "../../encoding/nfd-constants.hpp"
+#include "../../name.hpp"
+#include "../../util/time.hpp"
+#include "../control-parameters.hpp"
+
+namespace ndn {
+namespace nfd {
+
+/**
+ * \ingroup management
+ */
+enum ControlParameterField {
+  CONTROL_PARAMETER_NAME,
+  CONTROL_PARAMETER_FACE_ID,
+  CONTROL_PARAMETER_URI,
+  CONTROL_PARAMETER_LOCAL_CONTROL_FEATURE,
+  CONTROL_PARAMETER_ORIGIN,
+  CONTROL_PARAMETER_COST,
+  CONTROL_PARAMETER_FLAGS,
+  CONTROL_PARAMETER_MASK,
+  CONTROL_PARAMETER_STRATEGY,
+  CONTROL_PARAMETER_EXPIRATION_PERIOD,
+  CONTROL_PARAMETER_FACE_PERSISTENCY,
+  CONTROL_PARAMETER_UBOUND
+};
+
+const std::string CONTROL_PARAMETER_FIELD[CONTROL_PARAMETER_UBOUND] = {
+  "Name",
+  "FaceId",
+  "Uri",
+  "LocalControlFeature",
+  "Origin",
+  "Cost",
+  "Flags",
+  "Mask",
+  "Strategy",
+  "ExpirationPeriod",
+  "FacePersistency"
+};
+
+/**
+ * \ingroup management
+ * \deprecated use Flags+Mask fields instead
+ */
+enum LocalControlFeature {
+  LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID = 1,
+  LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID = 2
+};
+
+/**
+ * \ingroup management
+ * \brief represents parameters in a ControlCommand request or response
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/ControlCommand#ControlParameters
+ * \details This type is copyable because it's an abstraction of a TLV type.
+ */
+class ControlParameters : public ndn::mgmt::ControlParameters
+{
+public:
+  class Error : public tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : tlv::Error(what)
+    {
+    }
+  };
+
+  ControlParameters();
+
+  explicit
+  ControlParameters(const Block& block);
+
+  template<encoding::Tag TAG>
+  size_t
+  wireEncode(EncodingImpl<TAG>& encoder) const;
+
+  virtual Block
+  wireEncode() const final;
+
+  virtual void
+  wireDecode(const Block& wire) final;
+
+public: // getters & setters
+  bool
+  hasName() const
+  {
+    return m_hasFields[CONTROL_PARAMETER_NAME];
+  }
+
+  const Name&
+  getName() const
+  {
+    BOOST_ASSERT(this->hasName());
+    return m_name;
+  }
+
+  ControlParameters&
+  setName(const Name& name)
+  {
+    m_wire.reset();
+    m_name = name;
+    m_hasFields[CONTROL_PARAMETER_NAME] = true;
+    return *this;
+  }
+
+  ControlParameters&
+  unsetName()
+  {
+    m_wire.reset();
+    m_hasFields[CONTROL_PARAMETER_NAME] = false;
+    return *this;
+  }
+
+  bool
+  hasFaceId() const
+  {
+    return m_hasFields[CONTROL_PARAMETER_FACE_ID];
+  }
+
+  uint64_t
+  getFaceId() const
+  {
+    BOOST_ASSERT(this->hasFaceId());
+    return m_faceId;
+  }
+
+  ControlParameters&
+  setFaceId(uint64_t faceId)
+  {
+    m_wire.reset();
+    m_faceId = faceId;
+    m_hasFields[CONTROL_PARAMETER_FACE_ID] = true;
+    return *this;
+  }
+
+  ControlParameters&
+  unsetFaceId()
+  {
+    m_wire.reset();
+    m_hasFields[CONTROL_PARAMETER_FACE_ID] = false;
+    return *this;
+  }
+
+  bool
+  hasUri() const
+  {
+    return m_hasFields[CONTROL_PARAMETER_URI];
+  }
+
+  const std::string&
+  getUri() const
+  {
+    BOOST_ASSERT(this->hasUri());
+    return m_uri;
+  }
+
+  ControlParameters&
+  setUri(const std::string& uri)
+  {
+    m_wire.reset();
+    m_uri = uri;
+    m_hasFields[CONTROL_PARAMETER_URI] = true;
+    return *this;
+  }
+
+  ControlParameters&
+  unsetUri()
+  {
+    m_wire.reset();
+    m_hasFields[CONTROL_PARAMETER_URI] = false;
+    return *this;
+  }
+
+  /**
+   * \deprecated use Flags+Mask fields instead
+   */
+  bool
+  hasLocalControlFeature() const
+  {
+    return m_hasFields[CONTROL_PARAMETER_LOCAL_CONTROL_FEATURE];
+  }
+
+  /**
+   * \deprecated use Flags+Mask fields instead
+   */
+  LocalControlFeature
+  getLocalControlFeature() const
+  {
+    BOOST_ASSERT(this->hasLocalControlFeature());
+    return m_localControlFeature;
+  }
+
+  /**
+   * \deprecated use Flags+Mask fields instead
+   */
+  ControlParameters&
+  setLocalControlFeature(LocalControlFeature localControlFeature)
+  {
+    m_wire.reset();
+    m_localControlFeature = localControlFeature;
+    m_hasFields[CONTROL_PARAMETER_LOCAL_CONTROL_FEATURE] = true;
+    return *this;
+  }
+
+  /**
+   * \deprecated use Flags+Mask fields instead
+   */
+  ControlParameters&
+  unsetLocalControlFeature()
+  {
+    m_wire.reset();
+    m_hasFields[CONTROL_PARAMETER_LOCAL_CONTROL_FEATURE] = false;
+    return *this;
+  }
+
+  bool
+  hasOrigin() const
+  {
+    return m_hasFields[CONTROL_PARAMETER_ORIGIN];
+  }
+
+  uint64_t
+  getOrigin() const
+  {
+    BOOST_ASSERT(this->hasOrigin());
+    return m_origin;
+  }
+
+  ControlParameters&
+  setOrigin(uint64_t origin)
+  {
+    m_wire.reset();
+    m_origin = origin;
+    m_hasFields[CONTROL_PARAMETER_ORIGIN] = true;
+    return *this;
+  }
+
+  ControlParameters&
+  unsetOrigin()
+  {
+    m_wire.reset();
+    m_hasFields[CONTROL_PARAMETER_ORIGIN] = false;
+    return *this;
+  }
+
+  bool
+  hasCost() const
+  {
+    return m_hasFields[CONTROL_PARAMETER_COST];
+  }
+
+  uint64_t
+  getCost() const
+  {
+    BOOST_ASSERT(this->hasCost());
+    return m_cost;
+  }
+
+  ControlParameters&
+  setCost(uint64_t cost)
+  {
+    m_wire.reset();
+    m_cost = cost;
+    m_hasFields[CONTROL_PARAMETER_COST] = true;
+    return *this;
+  }
+
+  ControlParameters&
+  unsetCost()
+  {
+    m_wire.reset();
+    m_hasFields[CONTROL_PARAMETER_COST] = false;
+    return *this;
+  }
+
+  bool
+  hasFlags() const
+  {
+    return m_hasFields[CONTROL_PARAMETER_FLAGS];
+  }
+
+  uint64_t
+  getFlags() const
+  {
+    BOOST_ASSERT(this->hasFlags());
+    return m_flags;
+  }
+
+  ControlParameters&
+  setFlags(uint64_t flags)
+  {
+    m_wire.reset();
+    m_flags = flags;
+    m_hasFields[CONTROL_PARAMETER_FLAGS] = true;
+    return *this;
+  }
+
+  ControlParameters&
+  unsetFlags()
+  {
+    m_wire.reset();
+    m_hasFields[CONTROL_PARAMETER_FLAGS] = false;
+    return *this;
+  }
+
+  bool
+  hasMask() const
+  {
+    return m_hasFields[CONTROL_PARAMETER_MASK];
+  }
+
+  uint64_t
+  getMask() const
+  {
+    BOOST_ASSERT(this->hasMask());
+    return m_mask;
+  }
+
+  ControlParameters&
+  setMask(uint64_t mask)
+  {
+    m_wire.reset();
+    m_mask = mask;
+    m_hasFields[CONTROL_PARAMETER_MASK] = true;
+    return *this;
+  }
+
+  ControlParameters&
+  unsetMask()
+  {
+    m_wire.reset();
+    m_hasFields[CONTROL_PARAMETER_MASK] = false;
+    return *this;
+  }
+
+  bool
+  hasStrategy() const
+  {
+    return m_hasFields[CONTROL_PARAMETER_STRATEGY];
+  }
+
+  const Name&
+  getStrategy() const
+  {
+    BOOST_ASSERT(this->hasStrategy());
+    return m_strategy;
+  }
+
+  ControlParameters&
+  setStrategy(const Name& strategy)
+  {
+    m_wire.reset();
+    m_strategy = strategy;
+    m_hasFields[CONTROL_PARAMETER_STRATEGY] = true;
+    return *this;
+  }
+
+  ControlParameters&
+  unsetStrategy()
+  {
+    m_wire.reset();
+    m_hasFields[CONTROL_PARAMETER_STRATEGY] = false;
+    return *this;
+  }
+
+  bool
+  hasExpirationPeriod() const
+  {
+    return m_hasFields[CONTROL_PARAMETER_EXPIRATION_PERIOD];
+  }
+
+  const time::milliseconds&
+  getExpirationPeriod() const
+  {
+    BOOST_ASSERT(this->hasExpirationPeriod());
+    return m_expirationPeriod;
+  }
+
+  ControlParameters&
+  setExpirationPeriod(const time::milliseconds& expirationPeriod)
+  {
+    m_wire.reset();
+    m_expirationPeriod = expirationPeriod;
+    m_hasFields[CONTROL_PARAMETER_EXPIRATION_PERIOD] = true;
+    return *this;
+  }
+
+  ControlParameters&
+  unsetExpirationPeriod()
+  {
+    m_wire.reset();
+    m_hasFields[CONTROL_PARAMETER_EXPIRATION_PERIOD] = false;
+    return *this;
+  }
+
+  bool
+  hasFacePersistency() const
+  {
+    return m_hasFields[CONTROL_PARAMETER_FACE_PERSISTENCY];
+  }
+
+  FacePersistency
+  getFacePersistency() const
+  {
+    BOOST_ASSERT(this->hasFacePersistency());
+    return m_facePersistency;
+  }
+
+  ControlParameters&
+  setFacePersistency(FacePersistency persistency)
+  {
+    m_wire.reset();
+    m_facePersistency = persistency;
+    m_hasFields[CONTROL_PARAMETER_FACE_PERSISTENCY] = true;
+    return *this;
+  }
+
+  ControlParameters&
+  unsetFacePersistency()
+  {
+    m_wire.reset();
+    m_hasFields[CONTROL_PARAMETER_FACE_PERSISTENCY] = false;
+    return *this;
+  }
+
+  const std::vector<bool>&
+  getPresentFields() const
+  {
+    return m_hasFields;
+  }
+
+public: // Flags and Mask helpers
+  /**
+   * \return whether bit is enabled in Mask
+   * \param bit bit position within range [0, 64) (least significant bit is 0)
+   */
+  bool
+  hasFlagBit(size_t bit) const;
+
+  /**
+   * \return bit at a position in Flags
+   * \param bit bit position within range [0, 64) (least significant bit is 0)
+   */
+  bool
+  getFlagBit(size_t bit) const;
+
+  /**
+   * \brief set a bit in Flags
+   * \param bit bit position within range [0, 64) (least significant bit is 0)
+   * \param value new value in Flags
+   * \param wantMask if true, enable the bit in Mask
+   */
+  ControlParameters&
+  setFlagBit(size_t bit, bool value, bool wantMask = true);
+
+  /**
+   * \brief disable a bit in Mask
+   * \param bit bit position within range [0, 64) (least significant bit is 0)
+   * \post If all bits are disabled, Flags and Mask fields are deleted.
+   */
+  ControlParameters&
+  unsetFlagBit(size_t bit);
+
+private: // fields
+  std::vector<bool>   m_hasFields;
+
+  Name                m_name;
+  uint64_t            m_faceId;
+  std::string         m_uri;
+  LocalControlFeature m_localControlFeature;
+  uint64_t            m_origin;
+  uint64_t            m_cost;
+  uint64_t            m_flags;
+  uint64_t            m_mask;
+  Name                m_strategy;
+  time::milliseconds  m_expirationPeriod;
+  FacePersistency     m_facePersistency;
+
+private:
+  mutable Block m_wire;
+};
+
+std::ostream&
+operator<<(std::ostream& os, const ControlParameters& parameters);
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MGMT_NFD_CONTROL_PARAMETERS_HPP
diff --git a/src/mgmt/nfd/control-response.hpp b/src/mgmt/nfd/control-response.hpp
new file mode 100644
index 0000000..8234e2d
--- /dev/null
+++ b/src/mgmt/nfd/control-response.hpp
@@ -0,0 +1,35 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MANAGEMENT_CONTROL_RESPONSE_HPP
+#define NDN_MANAGEMENT_CONTROL_RESPONSE_HPP
+
+#include "../dispatcher.hpp"
+
+namespace ndn {
+namespace nfd {
+
+typedef ndn::mgmt::ControlResponse ControlResponse;
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MANAGEMENT_CONTROL_RESPONSE_HPP
diff --git a/src/mgmt/nfd/controller.cpp b/src/mgmt/nfd/controller.cpp
new file mode 100644
index 0000000..cfb7956
--- /dev/null
+++ b/src/mgmt/nfd/controller.cpp
@@ -0,0 +1,171 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "controller.hpp"
+#include "../../face.hpp"
+#include "../../security/key-chain.hpp"
+#include "../../util/segment-fetcher.hpp"
+
+namespace ndn {
+namespace nfd {
+
+using ndn::util::SegmentFetcher;
+
+const uint32_t Controller::ERROR_TIMEOUT = 10060; // WinSock ESAETIMEDOUT
+const uint32_t Controller::ERROR_NACK = 10800; // 10000 + TLV-TYPE of Nack header
+const uint32_t Controller::ERROR_VALIDATION = 10021; // 10000 + TLS1_ALERT_DECRYPTION_FAILED
+const uint32_t Controller::ERROR_SERVER = 500;
+const uint32_t Controller::ERROR_LBOUND = 400;
+ValidatorNull Controller::s_validatorNull;
+
+Controller::Controller(Face& face, KeyChain& keyChain, Validator& validator)
+  : m_face(face)
+  , m_keyChain(keyChain)
+  , m_validator(validator)
+{
+}
+
+void
+Controller::startCommand(const shared_ptr<ControlCommand>& command,
+                         const ControlParameters& parameters,
+                         const CommandSucceedCallback& onSuccess1,
+                         const CommandFailCallback& onFailure1,
+                         const CommandOptions& options)
+{
+  const CommandSucceedCallback& onSuccess = onSuccess1 ?
+    onSuccess1 : [] (const ControlParameters&) {};
+  const CommandFailCallback& onFailure = onFailure1 ?
+    onFailure1 : [] (const ControlResponse&) {};
+
+  Name requestName = command->getRequestName(options.getPrefix(), parameters);
+  Interest interest(requestName);
+  interest.setInterestLifetime(options.getTimeout());
+  m_keyChain.sign(interest, options.getSigningInfo());
+
+  m_face.expressInterest(interest,
+    [=] (const Interest&, const Data& data) {
+      this->processCommandResponse(data, command, onSuccess, onFailure);
+    },
+    [=] (const Interest&, const lp::Nack&) {
+      onFailure(ControlResponse(Controller::ERROR_NACK, "network Nack received"));
+    },
+    [=] (const Interest&) {
+      onFailure(ControlResponse(Controller::ERROR_TIMEOUT, "request timed out"));
+    });
+}
+
+void
+Controller::processCommandResponse(const Data& data,
+                                   const shared_ptr<ControlCommand>& command,
+                                   const CommandSucceedCallback& onSuccess,
+                                   const CommandFailCallback& onFailure)
+{
+  m_validator.validate(data,
+    [=] (const shared_ptr<const Data>& data) {
+      this->processValidatedCommandResponse(*data, command, onSuccess, onFailure);
+    },
+    [=] (const shared_ptr<const Data>&, const std::string& msg) {
+      onFailure(ControlResponse(ERROR_VALIDATION, msg));
+    }
+  );
+}
+
+void
+Controller::processValidatedCommandResponse(const Data& data,
+                                            const shared_ptr<ControlCommand>& command,
+                                            const CommandSucceedCallback& onSuccess,
+                                            const CommandFailCallback& onFailure)
+{
+  ControlResponse response;
+  try {
+    response.wireDecode(data.getContent().blockFromValue());
+  }
+  catch (const tlv::Error& e) {
+    onFailure(ControlResponse(ERROR_SERVER, e.what()));
+    return;
+  }
+
+  uint32_t code = response.getCode();
+  if (code >= ERROR_LBOUND) {
+    onFailure(response);
+    return;
+  }
+
+  ControlParameters parameters;
+  try {
+    parameters.wireDecode(response.getBody());
+  }
+  catch (const tlv::Error& e) {
+    onFailure(ControlResponse(ERROR_SERVER, e.what()));
+    return;
+  }
+
+  try {
+    command->validateResponse(parameters);
+  }
+  catch (const ControlCommand::ArgumentError& e) {
+    onFailure(ControlResponse(ERROR_SERVER, e.what()));
+    return;
+  }
+
+  onSuccess(parameters);
+}
+
+void
+Controller::fetchDataset(const Name& prefix,
+                         const std::function<void(const ConstBufferPtr&)>& processResponse,
+                         const DatasetFailCallback& onFailure,
+                         const CommandOptions& options)
+{
+  Interest baseInterest(prefix);
+  baseInterest.setInterestLifetime(options.getTimeout());
+
+  SegmentFetcher::fetch(m_face, baseInterest, m_validator, processResponse,
+                        bind(&Controller::processDatasetFetchError, this, onFailure, _1, _2));
+}
+
+void
+Controller::processDatasetFetchError(const DatasetFailCallback& onFailure,
+                                     uint32_t code, std::string msg)
+{
+  switch (static_cast<SegmentFetcher::ErrorCode>(code)) {
+    // It's intentional to cast as SegmentFetcher::ErrorCode, and to not have a 'default' clause.
+    // This forces the switch statement to handle every defined SegmentFetcher::ErrorCode,
+    // and breaks compilation if it does not.
+    case SegmentFetcher::ErrorCode::INTEREST_TIMEOUT:
+      onFailure(ERROR_TIMEOUT, msg);
+      break;
+    case SegmentFetcher::ErrorCode::DATA_HAS_NO_SEGMENT:
+      onFailure(ERROR_SERVER, msg);
+      break;
+    case SegmentFetcher::ErrorCode::SEGMENT_VALIDATION_FAIL:
+      /// \todo When SegmentFetcher exposes validator error code, Controller::ERROR_VALIDATION
+      ///       should be replaced with a range that corresponds to validator error codes.
+      onFailure(ERROR_VALIDATION, msg);
+      break;
+    case SegmentFetcher::ErrorCode::NACK_ERROR:
+      onFailure(ERROR_NACK, msg);
+      break;
+  }
+}
+
+} // namespace nfd
+} // namespace ndn
diff --git a/src/mgmt/nfd/controller.hpp b/src/mgmt/nfd/controller.hpp
new file mode 100644
index 0000000..3d3808c
--- /dev/null
+++ b/src/mgmt/nfd/controller.hpp
@@ -0,0 +1,221 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MGMT_NFD_CONTROLLER_HPP
+#define NDN_MGMT_NFD_CONTROLLER_HPP
+
+#include "control-command.hpp"
+#include "control-response.hpp"
+#include "status-dataset.hpp"
+#include "command-options.hpp"
+#include "../../security/validator-null.hpp"
+
+namespace ndn {
+
+namespace security {
+class KeyChain;
+class Validator;
+} // namespace security
+class Face;
+
+namespace nfd {
+
+/**
+ * \defgroup management Management
+ * \brief Classes and data structures to manage NDN forwarder
+ */
+
+/**
+ * \ingroup management
+ * \brief NFD Management protocol client
+ * \sa https://redmine.named-data.net/projects/nfd/wiki/Management
+ */
+class Controller : noncopyable
+{
+public:
+  /** \brief a callback on command success
+   */
+  typedef function<void(const ControlParameters&)> CommandSucceedCallback;
+
+  /** \brief a callback on command failure
+   */
+  typedef function<void(const ControlResponse&)> CommandFailCallback;
+
+  /** \brief a callback on dataset retrieval failure
+   */
+  typedef function<void(uint32_t code, const std::string& reason)> DatasetFailCallback;
+
+  /** \brief construct a Controller that uses face for transport,
+   *         and uses the passed KeyChain to sign commands
+   */
+  Controller(Face& face, security::KeyChain& keyChain, security::Validator& validator = s_validatorNull);
+
+  /** \brief start command execution
+   */
+  template<typename Command>
+  void
+  start(const ControlParameters& parameters,
+        const CommandSucceedCallback& onSuccess,
+        const CommandFailCallback& onFailure,
+        const CommandOptions& options = CommandOptions())
+  {
+    shared_ptr<ControlCommand> command = make_shared<Command>();
+    this->startCommand(command, parameters, onSuccess, onFailure, options);
+  }
+
+  /** \brief start dataset fetching
+   */
+  template<typename Dataset>
+  typename std::enable_if<std::is_default_constructible<Dataset>::value>::type
+  fetch(const std::function<void(typename Dataset::ResultType)>& onSuccess,
+        const DatasetFailCallback& onFailure,
+        const CommandOptions& options = CommandOptions())
+  {
+    this->fetchDataset(make_shared<Dataset>(), onSuccess, onFailure, options);
+  }
+
+  /** \brief start dataset fetching
+   */
+  template<typename Dataset, typename ParamType = typename Dataset::ParamType>
+  void
+  fetch(const ParamType& param,
+        const std::function<void(typename Dataset::ResultType)>& onSuccess,
+        const DatasetFailCallback& onFailure,
+        const CommandOptions& options = CommandOptions())
+  {
+    this->fetchDataset(make_shared<Dataset>(param), onSuccess, onFailure, options);
+  }
+
+private:
+  void
+  startCommand(const shared_ptr<ControlCommand>& command,
+               const ControlParameters& parameters,
+               const CommandSucceedCallback& onSuccess,
+               const CommandFailCallback& onFailure,
+               const CommandOptions& options);
+
+  void
+  processCommandResponse(const Data& data,
+                         const shared_ptr<ControlCommand>& command,
+                         const CommandSucceedCallback& onSuccess,
+                         const CommandFailCallback& onFailure);
+
+  void
+  processValidatedCommandResponse(const Data& data,
+                                  const shared_ptr<ControlCommand>& command,
+                                  const CommandSucceedCallback& onSuccess,
+                                  const CommandFailCallback& onFailure);
+
+  template<typename Dataset>
+  void
+  fetchDataset(shared_ptr<Dataset> dataset,
+               const std::function<void(typename Dataset::ResultType)>& onSuccess,
+               const DatasetFailCallback& onFailure,
+               const CommandOptions& options);
+
+  void
+  fetchDataset(const Name& prefix,
+               const std::function<void(const ConstBufferPtr&)>& processResponse,
+               const DatasetFailCallback& onFailure,
+               const CommandOptions& options);
+
+  template<typename Dataset>
+  void
+  processDatasetResponse(shared_ptr<Dataset> dataset,
+                         const std::function<void(typename Dataset::ResultType)>& onSuccess,
+                         const DatasetFailCallback& onFailure,
+                         ConstBufferPtr payload);
+
+  void
+  processDatasetFetchError(const DatasetFailCallback& onFailure, uint32_t code, std::string msg);
+
+public:
+  /** \brief error code for timeout
+   */
+  static const uint32_t ERROR_TIMEOUT;
+
+  /** \brief error code for network Nack
+   */
+  static const uint32_t ERROR_NACK;
+
+  /** \brief error code for response validation failure
+   */
+  static const uint32_t ERROR_VALIDATION;
+
+  /** \brief error code for server error
+   */
+  static const uint32_t ERROR_SERVER;
+
+  /** \brief inclusive lower bound of error codes
+   */
+  static const uint32_t ERROR_LBOUND;
+
+protected:
+  Face& m_face;
+  security::KeyChain& m_keyChain;
+  security::Validator& m_validator;
+
+private:
+  static ValidatorNull s_validatorNull;
+};
+
+template<typename Dataset>
+inline void
+Controller::fetchDataset(shared_ptr<Dataset> dataset,
+                         const std::function<void(typename Dataset::ResultType)>& onSuccess1,
+                         const DatasetFailCallback& onFailure1,
+                         const CommandOptions& options)
+{
+  const std::function<void(typename Dataset::ResultType)>& onSuccess = onSuccess1 ?
+    onSuccess1 : [] (const typename Dataset::ResultType&) {};
+  const DatasetFailCallback& onFailure = onFailure1 ?
+    onFailure1 : [] (uint32_t, const std::string&) {};
+
+  Name prefix = dataset->getDatasetPrefix(options.getPrefix());
+  this->fetchDataset(prefix,
+                     bind(&Controller::processDatasetResponse<Dataset>, this, dataset, onSuccess, onFailure, _1),
+                     onFailure,
+                     options);
+}
+
+template<typename Dataset>
+inline void
+Controller::processDatasetResponse(shared_ptr<Dataset> dataset,
+                                   const std::function<void(typename Dataset::ResultType)>& onSuccess,
+                                   const DatasetFailCallback& onFailure,
+                                   ConstBufferPtr payload)
+{
+  typename Dataset::ResultType result;
+  try {
+    result = dataset->parseResult(payload);
+  }
+  catch (const tlv::Error& e) {
+    onFailure(ERROR_SERVER, e.what());
+    return;
+  }
+
+  onSuccess(result);
+}
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MGMT_NFD_CONTROLLER_HPP
diff --git a/src/mgmt/nfd/face-event-notification.cpp b/src/mgmt/nfd/face-event-notification.cpp
new file mode 100644
index 0000000..3422ee8
--- /dev/null
+++ b/src/mgmt/nfd/face-event-notification.cpp
@@ -0,0 +1,195 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "face-event-notification.hpp"
+#include "encoding/tlv-nfd.hpp"
+#include "encoding/block-helpers.hpp"
+#include "util/concepts.hpp"
+
+namespace ndn {
+namespace nfd {
+
+//BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FaceEventNotification>));
+BOOST_CONCEPT_ASSERT((WireEncodable<FaceEventNotification>));
+BOOST_CONCEPT_ASSERT((WireDecodable<FaceEventNotification>));
+static_assert(std::is_base_of<tlv::Error, FaceEventNotification::Error>::value,
+              "FaceEventNotification::Error must inherit from tlv::Error");
+
+FaceEventNotification::FaceEventNotification()
+  : m_kind(static_cast<FaceEventKind>(0))
+{
+}
+
+FaceEventNotification::FaceEventNotification(const Block& block)
+{
+  this->wireDecode(block);
+}
+
+template<encoding::Tag TAG>
+size_t
+FaceEventNotification::wireEncode(EncodingImpl<TAG>& encoder) const
+{
+  size_t totalLength = 0;
+
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::LinkType, m_linkType);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::FacePersistency, m_facePersistency);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::FaceScope, m_faceScope);
+  totalLength += encoder.prependByteArrayBlock(tlv::nfd::LocalUri,
+                 reinterpret_cast<const uint8_t*>(m_localUri.c_str()), m_localUri.size());
+  totalLength += encoder.prependByteArrayBlock(tlv::nfd::Uri,
+                 reinterpret_cast<const uint8_t*>(m_remoteUri.c_str()), m_remoteUri.size());
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::FaceId, m_faceId);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::FaceEventKind, m_kind);
+
+  totalLength += encoder.prependVarNumber(totalLength);
+  totalLength += encoder.prependVarNumber(tlv::nfd::FaceEventNotification);
+  return totalLength;
+}
+
+const Block&
+FaceEventNotification::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+FaceEventNotification::wireDecode(const Block& block)
+{
+  if (block.type() != tlv::nfd::FaceEventNotification) {
+    BOOST_THROW_EXCEPTION(Error("expecting FaceEventNotification block"));
+  }
+  m_wire = block;
+  m_wire.parse();
+  Block::element_const_iterator val = m_wire.elements_begin();
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::FaceEventKind) {
+    m_kind = static_cast<FaceEventKind>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required FaceEventKind field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::FaceId) {
+    m_faceId = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required FaceId field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::Uri) {
+    m_remoteUri.assign(reinterpret_cast<const char*>(val->value()), val->value_size());
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required Uri field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::LocalUri) {
+    m_localUri.assign(reinterpret_cast<const char*>(val->value()), val->value_size());
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required LocalUri field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::FaceScope) {
+    m_faceScope = static_cast<FaceScope>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required FaceScope field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::FacePersistency) {
+    m_facePersistency = static_cast<FacePersistency>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required FacePersistency field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::LinkType) {
+    m_linkType = static_cast<LinkType>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required LinkType field"));
+  }
+}
+
+FaceEventNotification&
+FaceEventNotification::setKind(FaceEventKind kind)
+{
+  m_wire.reset();
+  m_kind = kind;
+  return *this;
+}
+
+void
+FaceEventNotification::wireReset() const
+{
+  m_wire.reset();
+}
+
+std::ostream&
+operator<<(std::ostream& os, const FaceEventNotification& notification)
+{
+  os << "FaceEventNotification(";
+
+  switch (notification.getKind())
+    {
+    case FACE_EVENT_CREATED:
+      os << "Kind: created, ";
+      break;
+    case FACE_EVENT_DESTROYED:
+      os << "Kind: destroyed, ";
+      break;
+    }
+
+  os << "FaceID: " << notification.getFaceId() << ", "
+     << "RemoteUri: " << notification.getRemoteUri() << ", "
+     << "LocalUri: " << notification.getLocalUri() << ", "
+     << "FaceScope: " << notification.getFaceScope() << ", "
+     << "FacePersistency: " << notification.getFacePersistency() << ", "
+     << "LinkType: " << notification.getLinkType()
+     << ")";
+  return os;
+}
+
+} // namespace nfd
+} // namespace ndn
diff --git a/src/mgmt/nfd/face-event-notification.hpp b/src/mgmt/nfd/face-event-notification.hpp
new file mode 100644
index 0000000..a4d1ea7
--- /dev/null
+++ b/src/mgmt/nfd/face-event-notification.hpp
@@ -0,0 +1,94 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MGMT_NFD_FACE_EVENT_NOTIFICATION_HPP
+#define NDN_MGMT_NFD_FACE_EVENT_NOTIFICATION_HPP
+
+#include "face-traits.hpp"
+#include "../../encoding/block.hpp"
+
+namespace ndn {
+namespace nfd {
+
+/**
+ * \ingroup management
+ */
+enum FaceEventKind {
+  FACE_EVENT_CREATED = 1,
+  FACE_EVENT_DESTROYED = 2
+};
+
+/**
+ * \ingroup management
+ * \brief represents a Face status change notification
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Face-Status-Change-Notification
+ */
+class FaceEventNotification : public FaceTraits<FaceEventNotification>
+{
+public:
+  FaceEventNotification();
+
+  explicit
+  FaceEventNotification(const Block& block);
+
+  /** \brief prepend FaceEventNotification to the encoder
+   */
+  template<encoding::Tag TAG>
+  size_t
+  wireEncode(EncodingImpl<TAG>& encoder) const;
+
+  /** \brief encode FaceEventNotification
+   */
+  const Block&
+  wireEncode() const;
+
+  /** \brief decode FaceEventNotification
+   */
+  void
+  wireDecode(const Block& wire);
+
+public: // getters & setters
+  FaceEventKind
+  getKind() const
+  {
+    return m_kind;
+  }
+
+  FaceEventNotification&
+  setKind(FaceEventKind kind);
+
+protected:
+  void
+  wireReset() const;
+
+private:
+  FaceEventKind m_kind;
+
+  mutable Block m_wire;
+};
+
+std::ostream&
+operator<<(std::ostream& os, const FaceEventNotification& notification);
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MGMT_NFD_FACE_EVENT_NOTIFICATION_HPP
diff --git a/src/mgmt/nfd/face-monitor.hpp b/src/mgmt/nfd/face-monitor.hpp
new file mode 100644
index 0000000..5429ea1
--- /dev/null
+++ b/src/mgmt/nfd/face-monitor.hpp
@@ -0,0 +1,72 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+/**
+ * Original copyright notice from NFD:
+ *
+ * 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 NDN_MGMT_NFD_FACE_MONITOR_HPP
+#define NDN_MGMT_NFD_FACE_MONITOR_HPP
+
+#include "../../util/notification-subscriber.hpp"
+#include "face-event-notification.hpp"
+
+namespace ndn {
+namespace nfd {
+
+/** \brief A subscriber for Face status change notification stream
+ *  \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Face-Status-Change-Notification
+ */
+class FaceMonitor : public util::NotificationSubscriber<FaceEventNotification>
+{
+public:
+  FaceMonitor(Face& face)
+    : NotificationSubscriber<nfd::FaceEventNotification>(face, "ndn:/localhost/nfd/faces/events")
+  {
+  }
+};
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MGMT_NFD_FACE_MONITOR_HPP
diff --git a/src/mgmt/nfd/face-query-filter.cpp b/src/mgmt/nfd/face-query-filter.cpp
new file mode 100644
index 0000000..ffaae90
--- /dev/null
+++ b/src/mgmt/nfd/face-query-filter.cpp
@@ -0,0 +1,352 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "face-query-filter.hpp"
+#include "encoding/tlv-nfd.hpp"
+#include "encoding/block-helpers.hpp"
+#include "util/concepts.hpp"
+
+namespace ndn {
+namespace nfd {
+
+//BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FaceQueryFilter>));
+BOOST_CONCEPT_ASSERT((WireEncodable<FaceQueryFilter>));
+BOOST_CONCEPT_ASSERT((WireDecodable<FaceQueryFilter>));
+static_assert(std::is_base_of<tlv::Error, FaceQueryFilter::Error>::value,
+              "FaceQueryFilter::Error must inherit from tlv::Error");
+
+FaceQueryFilter::FaceQueryFilter()
+  : m_hasFaceId(false)
+  , m_hasUriScheme(false)
+  , m_hasRemoteUri(false)
+  , m_hasLocalUri(false)
+  , m_hasFaceScope(false)
+  , m_hasFacePersistency(false)
+  , m_hasLinkType(false)
+{
+}
+
+FaceQueryFilter::FaceQueryFilter(const Block& block)
+{
+  this->wireDecode(block);
+}
+
+template<encoding::Tag TAG>
+size_t
+FaceQueryFilter::wireEncode(EncodingImpl<TAG>& encoder) const
+{
+  size_t totalLength = 0;
+
+  if (m_hasLinkType) {
+    totalLength += prependNonNegativeIntegerBlock(encoder,
+                   tlv::nfd::LinkType, m_linkType);
+  }
+
+  if (m_hasFacePersistency) {
+    totalLength += prependNonNegativeIntegerBlock(encoder,
+                   tlv::nfd::FacePersistency, m_facePersistency);
+  }
+
+  if (m_hasFaceScope) {
+    totalLength += prependNonNegativeIntegerBlock(encoder,
+                   tlv::nfd::FaceScope, m_faceScope);
+  }
+
+  if (m_hasLocalUri) {
+    totalLength += encoder.prependByteArrayBlock(tlv::nfd::LocalUri,
+                   reinterpret_cast<const uint8_t*>(m_localUri.c_str()), m_localUri.size());
+  }
+
+  if (m_hasRemoteUri) {
+    totalLength += encoder.prependByteArrayBlock(tlv::nfd::Uri,
+                   reinterpret_cast<const uint8_t*>(m_remoteUri.c_str()), m_remoteUri.size());
+  }
+
+  if (m_hasUriScheme) {
+    totalLength += encoder.prependByteArrayBlock(tlv::nfd::UriScheme,
+                   reinterpret_cast<const uint8_t*>(m_uriScheme.c_str()), m_uriScheme.size());
+  }
+
+  if (m_hasFaceId) {
+    totalLength += prependNonNegativeIntegerBlock(encoder,
+                   tlv::nfd::FaceId, m_faceId);
+  }
+
+  totalLength += encoder.prependVarNumber(totalLength);
+  totalLength += encoder.prependVarNumber(tlv::nfd::FaceQueryFilter);
+  return totalLength;
+}
+
+template size_t
+FaceQueryFilter::wireEncode<encoding::EncoderTag>(EncodingImpl<encoding::EncoderTag>&) const;
+
+template size_t
+FaceQueryFilter::wireEncode<encoding::EstimatorTag>(EncodingImpl<encoding::EstimatorTag>&) const;
+
+const Block&
+FaceQueryFilter::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+FaceQueryFilter::wireDecode(const Block& block)
+{
+  //all fields are optional
+  if (block.type() != tlv::nfd::FaceQueryFilter) {
+    BOOST_THROW_EXCEPTION(Error("expecting FaceQueryFilter block"));
+  }
+
+  m_wire = block;
+  m_wire.parse();
+  Block::element_const_iterator val = m_wire.elements_begin();
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::FaceId) {
+    m_faceId = readNonNegativeInteger(*val);
+    m_hasFaceId = true;
+    ++val;
+  }
+  else {
+    m_hasFaceId = false;
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::UriScheme) {
+    m_uriScheme.assign(reinterpret_cast<const char*>(val->value()), val->value_size());
+    m_hasUriScheme = true;
+    ++val;
+  }
+  else {
+    m_hasUriScheme = false;
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::Uri) {
+    m_remoteUri.assign(reinterpret_cast<const char*>(val->value()), val->value_size());
+    m_hasRemoteUri = true;
+    ++val;
+  }
+  else {
+    m_hasRemoteUri = false;
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::LocalUri) {
+    m_localUri.assign(reinterpret_cast<const char*>(val->value()), val->value_size());
+    m_hasLocalUri = true;
+    ++val;
+  }
+  else {
+    m_hasLocalUri = false;
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::FaceScope) {
+    m_faceScope = static_cast<FaceScope>(readNonNegativeInteger(*val));
+    m_hasFaceScope = true;
+    ++val;
+  }
+  else {
+    m_hasFaceScope = false;
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::FacePersistency) {
+    m_facePersistency = static_cast<FacePersistency>(readNonNegativeInteger(*val));
+    m_hasFacePersistency = true;
+    ++val;
+  }
+  else {
+    m_hasFacePersistency = false;
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::LinkType) {
+    m_linkType = static_cast<LinkType>(readNonNegativeInteger(*val));
+    m_hasLinkType = true;
+    ++val;
+  }
+  else {
+    m_hasLinkType = false;
+  }
+
+}
+
+FaceQueryFilter&
+FaceQueryFilter::setFaceId(uint64_t faceId)
+{
+  m_wire.reset();
+  m_faceId = faceId;
+  m_hasFaceId = true;
+  return *this;
+}
+
+FaceQueryFilter&
+FaceQueryFilter::unsetFaceId()
+{
+  m_wire.reset();
+  m_hasFaceId = false;
+  return *this;
+}
+
+FaceQueryFilter&
+FaceQueryFilter::setUriScheme(const std::string& uriScheme)
+{
+  m_wire.reset();
+  m_uriScheme = uriScheme;
+  m_hasUriScheme = true;
+  return *this;
+}
+
+FaceQueryFilter&
+FaceQueryFilter::unsetUriScheme()
+{
+  m_wire.reset();
+  m_hasUriScheme = false;
+  return *this;
+}
+
+FaceQueryFilter&
+FaceQueryFilter::setRemoteUri(const std::string& remoteUri)
+{
+  m_wire.reset();
+  m_remoteUri = remoteUri;
+  m_hasRemoteUri = true;
+  return *this;
+}
+
+FaceQueryFilter&
+FaceQueryFilter::unsetRemoteUri()
+{
+  m_wire.reset();
+  m_hasRemoteUri = false;
+  return *this;
+}
+
+FaceQueryFilter&
+FaceQueryFilter::setLocalUri(const std::string& localUri)
+{
+  m_wire.reset();
+  m_localUri = localUri;
+  m_hasLocalUri = true;
+  return *this;
+}
+
+FaceQueryFilter&
+FaceQueryFilter::unsetLocalUri()
+{
+  m_wire.reset();
+  m_hasLocalUri = false;
+  return *this;
+}
+
+FaceQueryFilter&
+FaceQueryFilter::setFaceScope(FaceScope faceScope)
+{
+  m_wire.reset();
+  m_faceScope = faceScope;
+  m_hasFaceScope = true;
+  return *this;
+}
+
+FaceQueryFilter&
+FaceQueryFilter::unsetFaceScope()
+{
+  m_wire.reset();
+  m_hasFaceScope = false;
+  return *this;
+}
+
+FaceQueryFilter&
+FaceQueryFilter::setFacePersistency(FacePersistency facePersistency)
+{
+  m_wire.reset();
+  m_facePersistency = facePersistency;
+  m_hasFacePersistency = true;
+  return *this;
+}
+
+FaceQueryFilter&
+FaceQueryFilter::unsetFacePersistency()
+{
+  m_wire.reset();
+  m_hasFacePersistency = false;
+  return *this;
+}
+
+FaceQueryFilter&
+FaceQueryFilter::setLinkType(LinkType linkType)
+{
+  m_wire.reset();
+  m_linkType = linkType;
+  m_hasLinkType = true;
+  return *this;
+}
+
+FaceQueryFilter&
+FaceQueryFilter::unsetLinkType()
+{
+  m_wire.reset();
+  m_hasLinkType = false;
+  return *this;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const FaceQueryFilter& filter)
+{
+  os << "FaceQueryFilter(";
+  if (filter.hasFaceId()) {
+    os << "FaceID: " << filter.getFaceId() << ",\n";
+  }
+
+  if (filter.hasUriScheme()) {
+    os << "UriScheme: " << filter.getUriScheme() << ",\n";
+  }
+
+  if (filter.hasRemoteUri()) {
+    os << "RemoteUri: " << filter.getRemoteUri() << ",\n";
+  }
+
+  if (filter.hasLocalUri()) {
+    os << "LocalUri: " << filter.getLocalUri() << ",\n";
+  }
+
+  if (filter.hasFaceScope()) {
+    os << "FaceScope: " << filter.getFaceScope() << ",\n";
+  }
+
+  if (filter.hasFacePersistency()) {
+    os << "FacePersistency: " << filter.getFacePersistency() << ",\n";
+  }
+
+  if (filter.hasLinkType()) {
+    os << "LinkType: " << filter.getLinkType() << ",\n";
+  }
+  os << ")";
+  return os;
+}
+
+} // namespace nfd
+} // namespace ndn
diff --git a/src/mgmt/nfd/face-query-filter.hpp b/src/mgmt/nfd/face-query-filter.hpp
new file mode 100644
index 0000000..e0bb393
--- /dev/null
+++ b/src/mgmt/nfd/face-query-filter.hpp
@@ -0,0 +1,231 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MGMT_NFD_FACE_QUERY_FILTER_HPP
+#define NDN_MGMT_NFD_FACE_QUERY_FILTER_HPP
+
+#include "../../encoding/block.hpp"
+#include "../../encoding/nfd-constants.hpp"
+
+namespace ndn {
+namespace nfd {
+
+/**
+ * \ingroup management
+ * \brief represents Face Query Filter
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Query-Operation
+ */
+class FaceQueryFilter
+{
+public:
+  class Error : public tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : tlv::Error(what)
+    {
+    }
+  };
+
+  FaceQueryFilter();
+
+  explicit
+  FaceQueryFilter(const Block& block);
+
+  /** \brief prepend FaceQueryFilter to the encoder
+   */
+  template<encoding::Tag TAG>
+  size_t
+  wireEncode(EncodingImpl<TAG>& encoder) const;
+
+  /** \brief encode FaceQueryFilter
+   */
+  const Block&
+  wireEncode() const;
+
+  /** \brief decode FaceQueryFilter
+   */
+  void
+  wireDecode(const Block& wire);
+
+public: // getters & setters
+
+  bool
+  hasFaceId() const
+  {
+    return m_hasFaceId;
+  }
+
+  uint64_t
+  getFaceId() const
+  {
+    BOOST_ASSERT(this->hasFaceId());
+    return m_faceId;
+  }
+
+  FaceQueryFilter&
+  setFaceId(uint64_t faceId);
+
+  FaceQueryFilter&
+  unsetFaceId();
+
+  bool
+  hasUriScheme() const
+  {
+    return m_hasUriScheme;
+  }
+
+  const std::string&
+  getUriScheme() const
+  {
+    BOOST_ASSERT(this->hasUriScheme());
+    return m_uriScheme;
+  }
+
+  FaceQueryFilter&
+  setUriScheme(const std::string& uriScheme);
+
+  FaceQueryFilter&
+  unsetUriScheme();
+
+  bool
+  hasRemoteUri() const
+  {
+    return m_hasRemoteUri;
+  }
+
+  const std::string&
+  getRemoteUri() const
+  {
+    BOOST_ASSERT(this->hasRemoteUri());
+    return m_remoteUri;
+  }
+
+  FaceQueryFilter&
+  setRemoteUri(const std::string& remoteUri);
+
+  FaceQueryFilter&
+  unsetRemoteUri();
+
+  bool
+  hasLocalUri() const
+  {
+    return m_hasLocalUri;
+  }
+
+  const std::string&
+  getLocalUri() const
+  {
+    BOOST_ASSERT(this->hasLocalUri());
+    return m_localUri;
+  }
+
+  FaceQueryFilter&
+  setLocalUri(const std::string& localUri);
+
+  FaceQueryFilter&
+  unsetLocalUri();
+
+  bool
+  hasFaceScope() const
+  {
+    return m_hasFaceScope;
+  }
+
+  FaceScope
+  getFaceScope() const
+  {
+    BOOST_ASSERT(this->hasFaceScope());
+    return m_faceScope;
+  }
+
+  FaceQueryFilter&
+  setFaceScope(FaceScope faceScope);
+
+  FaceQueryFilter&
+  unsetFaceScope();
+
+  bool
+  hasFacePersistency() const
+  {
+    return m_hasFacePersistency;
+  }
+
+  FacePersistency
+  getFacePersistency() const
+  {
+    BOOST_ASSERT(this->hasFacePersistency());
+    return m_facePersistency;
+  }
+
+  FaceQueryFilter&
+  setFacePersistency(FacePersistency facePersistency);
+
+  FaceQueryFilter&
+  unsetFacePersistency();
+
+  bool
+  hasLinkType() const
+  {
+    return m_hasLinkType;
+  }
+
+  LinkType
+  getLinkType() const
+  {
+    BOOST_ASSERT(this->hasLinkType());
+    return m_linkType;
+  }
+
+  FaceQueryFilter&
+  setLinkType(LinkType linkType);
+
+  FaceQueryFilter&
+  unsetLinkType();
+
+private:
+  uint64_t m_faceId;
+  std::string m_uriScheme;
+  std::string m_remoteUri;
+  std::string m_localUri;
+  FaceScope m_faceScope;
+  FacePersistency m_facePersistency;
+  LinkType m_linkType;
+
+  bool m_hasFaceId;
+  bool m_hasUriScheme;
+  bool m_hasRemoteUri;
+  bool m_hasLocalUri;
+  bool m_hasFaceScope;
+  bool m_hasFacePersistency;
+  bool m_hasLinkType;
+
+  mutable Block m_wire;
+};
+
+std::ostream&
+operator<<(std::ostream& os, const FaceQueryFilter& filter);
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MGMT_NFD_FACE_QUERY_FILTER_HPP
diff --git a/src/mgmt/nfd/face-status.cpp b/src/mgmt/nfd/face-status.cpp
new file mode 100644
index 0000000..bdc7404
--- /dev/null
+++ b/src/mgmt/nfd/face-status.cpp
@@ -0,0 +1,363 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "face-status.hpp"
+#include "encoding/tlv-nfd.hpp"
+#include "encoding/block-helpers.hpp"
+#include "util/concepts.hpp"
+
+namespace ndn {
+namespace nfd {
+
+//BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FaceStatus>));
+BOOST_CONCEPT_ASSERT((WireEncodable<FaceStatus>));
+BOOST_CONCEPT_ASSERT((WireDecodable<FaceStatus>));
+static_assert(std::is_base_of<tlv::Error, FaceStatus::Error>::value,
+              "FaceStatus::Error must inherit from tlv::Error");
+
+FaceStatus::FaceStatus()
+  : m_hasExpirationPeriod(false)
+  , m_nInInterests(0)
+  , m_nInDatas(0)
+  , m_nInNacks(0)
+  , m_nOutInterests(0)
+  , m_nOutDatas(0)
+  , m_nOutNacks(0)
+  , m_nInBytes(0)
+  , m_nOutBytes(0)
+{
+}
+
+FaceStatus::FaceStatus(const Block& block)
+{
+  this->wireDecode(block);
+}
+
+template<encoding::Tag TAG>
+size_t
+FaceStatus::wireEncode(EncodingImpl<TAG>& encoder) const
+{
+  size_t totalLength = 0;
+
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::NOutBytes, m_nOutBytes);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::NInBytes, m_nInBytes);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::NOutNacks, m_nOutNacks);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::NOutDatas, m_nOutDatas);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::NOutInterests, m_nOutInterests);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::NInNacks, m_nInNacks);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::NInDatas, m_nInDatas);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::NInInterests, m_nInInterests);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::LinkType, m_linkType);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::FacePersistency, m_facePersistency);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::FaceScope, m_faceScope);
+  if (m_hasExpirationPeriod) {
+    totalLength += prependNonNegativeIntegerBlock(encoder,
+                   tlv::nfd::ExpirationPeriod, m_expirationPeriod.count());
+  }
+  totalLength += encoder.prependByteArrayBlock(tlv::nfd::LocalUri,
+                 reinterpret_cast<const uint8_t*>(m_localUri.c_str()), m_localUri.size());
+  totalLength += encoder.prependByteArrayBlock(tlv::nfd::Uri,
+                 reinterpret_cast<const uint8_t*>(m_remoteUri.c_str()), m_remoteUri.size());
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::FaceId, m_faceId);
+
+  totalLength += encoder.prependVarNumber(totalLength);
+  totalLength += encoder.prependVarNumber(tlv::nfd::FaceStatus);
+  return totalLength;
+}
+
+template size_t
+FaceStatus::wireEncode<encoding::EncoderTag>(EncodingImpl<encoding::EncoderTag>& block) const;
+
+template size_t
+FaceStatus::wireEncode<encoding::EstimatorTag>(EncodingImpl<encoding::EstimatorTag>& block) const;
+
+const Block&
+FaceStatus::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+FaceStatus::wireDecode(const Block& block)
+{
+  if (block.type() != tlv::nfd::FaceStatus) {
+    BOOST_THROW_EXCEPTION(Error("expecting FaceStatus block"));
+  }
+  m_wire = block;
+  m_wire.parse();
+  Block::element_const_iterator val = m_wire.elements_begin();
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::FaceId) {
+    m_faceId = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required FaceId field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::Uri) {
+    m_remoteUri.assign(reinterpret_cast<const char*>(val->value()), val->value_size());
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required Uri field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::LocalUri) {
+    m_localUri.assign(reinterpret_cast<const char*>(val->value()), val->value_size());
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required LocalUri field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::ExpirationPeriod) {
+    m_expirationPeriod = time::milliseconds(readNonNegativeInteger(*val));
+    m_hasExpirationPeriod = true;
+    ++val;
+  }
+  else {
+    m_hasExpirationPeriod = false;
+    // ExpirationPeriod is optional
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::FaceScope) {
+    m_faceScope = static_cast<FaceScope>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required FaceScope field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::FacePersistency) {
+    m_facePersistency = static_cast<FacePersistency>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required FacePersistency field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::LinkType) {
+    m_linkType = static_cast<LinkType>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required LinkType field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NInInterests) {
+    m_nInInterests = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NInInterests field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NInDatas) {
+    m_nInDatas = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NInDatas field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NInNacks) {
+    m_nInNacks = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NInNacks field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NOutInterests) {
+    m_nOutInterests = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NOutInterests field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NOutDatas) {
+    m_nOutDatas = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NOutDatas field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NOutNacks) {
+    m_nOutNacks = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NOutNacks field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NInBytes) {
+    m_nInBytes = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NInBytes field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NOutBytes) {
+    m_nOutBytes = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NOutBytes field"));
+  }
+}
+
+FaceStatus&
+FaceStatus::setExpirationPeriod(const time::milliseconds& expirationPeriod)
+{
+  m_wire.reset();
+  m_expirationPeriod = expirationPeriod;
+  m_hasExpirationPeriod = true;
+  return *this;
+}
+
+FaceStatus&
+FaceStatus::setNInInterests(uint64_t nInInterests)
+{
+  m_wire.reset();
+  m_nInInterests = nInInterests;
+  return *this;
+}
+
+FaceStatus&
+FaceStatus::setNInDatas(uint64_t nInDatas)
+{
+  m_wire.reset();
+  m_nInDatas = nInDatas;
+  return *this;
+}
+
+FaceStatus&
+FaceStatus::setNInNacks(uint64_t nInNacks)
+{
+  m_wire.reset();
+  m_nInNacks = nInNacks;
+  return *this;
+}
+
+FaceStatus&
+FaceStatus::setNOutInterests(uint64_t nOutInterests)
+{
+  m_wire.reset();
+  m_nOutInterests = nOutInterests;
+  return *this;
+}
+
+FaceStatus&
+FaceStatus::setNOutDatas(uint64_t nOutDatas)
+{
+  m_wire.reset();
+  m_nOutDatas = nOutDatas;
+  return *this;
+}
+
+FaceStatus&
+FaceStatus::setNOutNacks(uint64_t nOutNacks)
+{
+  m_wire.reset();
+  m_nOutNacks = nOutNacks;
+  return *this;
+}
+
+FaceStatus&
+FaceStatus::setNInBytes(uint64_t nInBytes)
+{
+  m_wire.reset();
+  m_nInBytes = nInBytes;
+  return *this;
+}
+
+FaceStatus&
+FaceStatus::setNOutBytes(uint64_t nOutBytes)
+{
+  m_wire.reset();
+  m_nOutBytes = nOutBytes;
+  return *this;
+}
+
+void
+FaceStatus::wireReset() const
+{
+  m_wire.reset();
+}
+
+std::ostream&
+operator<<(std::ostream& os, const FaceStatus& status)
+{
+  os << "FaceStatus("
+     << "FaceID: " << status.getFaceId() << ",\n"
+     << "RemoteUri: " << status.getRemoteUri() << ",\n"
+     << "LocalUri: " << status.getLocalUri() << ",\n";
+
+  if (status.hasExpirationPeriod()) {
+    os << "ExpirationPeriod: " << status.getExpirationPeriod() << ",\n";
+  }
+  else {
+    os << "ExpirationPeriod: infinite,\n";
+  }
+
+  os << "FaceScope: " << status.getFaceScope() << ",\n"
+     << "FacePersistency: " << status.getFacePersistency() << ",\n"
+     << "LinkType: " << status.getLinkType() << ",\n"
+     << "Counters: { Interests: {in: " << status.getNInInterests() << ", "
+     << "out: " << status.getNOutInterests() << "},\n"
+     << "            Data: {in: " << status.getNInDatas() << ", "
+     << "out: " << status.getNOutDatas() << "},\n"
+     << "            Nack: {in: " << status.getNInNacks() << ", "
+     << "out: " << status.getNOutNacks() << "},\n"
+     << "            bytes: {in: " << status.getNInBytes() << ", "
+     << "out: " << status.getNOutBytes() << "} }\n"
+     << ")";
+  return os;
+}
+
+} // namespace nfd
+} // namespace ndn
diff --git a/src/mgmt/nfd/face-status.hpp b/src/mgmt/nfd/face-status.hpp
new file mode 100644
index 0000000..4f20e53
--- /dev/null
+++ b/src/mgmt/nfd/face-status.hpp
@@ -0,0 +1,175 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MGMT_NFD_FACE_STATUS_HPP
+#define NDN_MGMT_NFD_FACE_STATUS_HPP
+
+#include "face-traits.hpp" // include this first, to ensure it compiles on its own.
+#include "../../encoding/block.hpp"
+#include "../../util/time.hpp"
+
+namespace ndn {
+namespace nfd {
+
+/**
+ * \ingroup management
+ * \brief represents Face status
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Face-Dataset
+ */
+class FaceStatus : public FaceTraits<FaceStatus>
+{
+public:
+  FaceStatus();
+
+  explicit
+  FaceStatus(const Block& block);
+
+  /** \brief prepend FaceStatus to the encoder
+   */
+  template<encoding::Tag TAG>
+  size_t
+  wireEncode(EncodingImpl<TAG>& encoder) const;
+
+  /** \brief encode FaceStatus
+   */
+  const Block&
+  wireEncode() const;
+
+  /** \brief decode FaceStatus
+   */
+  void
+  wireDecode(const Block& wire);
+
+public: // getters & setters
+  bool
+  hasExpirationPeriod() const
+  {
+    return m_hasExpirationPeriod;
+  }
+
+  const time::milliseconds&
+  getExpirationPeriod() const
+  {
+    BOOST_ASSERT(m_hasExpirationPeriod);
+    return m_expirationPeriod;
+  }
+
+  FaceStatus&
+  setExpirationPeriod(const time::milliseconds& expirationPeriod);
+
+  uint64_t
+  getNInInterests() const
+  {
+    return m_nInInterests;
+  }
+
+  FaceStatus&
+  setNInInterests(uint64_t nInInterests);
+
+  uint64_t
+  getNInDatas() const
+  {
+    return m_nInDatas;
+  }
+
+  FaceStatus&
+  setNInDatas(uint64_t nInDatas);
+
+  uint64_t
+  getNInNacks() const
+  {
+    return m_nInNacks;
+  }
+
+  FaceStatus&
+  setNInNacks(uint64_t nInNacks);
+
+  uint64_t
+  getNOutInterests() const
+  {
+    return m_nOutInterests;
+  }
+
+  FaceStatus&
+  setNOutInterests(uint64_t nOutInterests);
+
+  uint64_t
+  getNOutDatas() const
+  {
+    return m_nOutDatas;
+  }
+
+  FaceStatus&
+  setNOutDatas(uint64_t nOutDatas);
+
+  uint64_t
+  getNOutNacks() const
+  {
+    return m_nOutNacks;
+  }
+
+  FaceStatus&
+  setNOutNacks(uint64_t nOutNacks);
+
+  uint64_t
+  getNInBytes() const
+  {
+    return m_nInBytes;
+  }
+
+  FaceStatus&
+  setNInBytes(uint64_t nInBytes);
+
+  uint64_t
+  getNOutBytes() const
+  {
+    return m_nOutBytes;
+  }
+
+  FaceStatus&
+  setNOutBytes(uint64_t nOutBytes);
+
+protected:
+  void
+  wireReset() const;
+
+private:
+  time::milliseconds m_expirationPeriod;
+  bool m_hasExpirationPeriod;
+  uint64_t m_nInInterests;
+  uint64_t m_nInDatas;
+  uint64_t m_nInNacks;
+  uint64_t m_nOutInterests;
+  uint64_t m_nOutDatas;
+  uint64_t m_nOutNacks;
+  uint64_t m_nInBytes;
+  uint64_t m_nOutBytes;
+
+  mutable Block m_wire;
+};
+
+std::ostream&
+operator<<(std::ostream& os, const FaceStatus& status);
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MGMT_NFD_FACE_STATUS_HPP
diff --git a/src/mgmt/nfd/face-traits.hpp b/src/mgmt/nfd/face-traits.hpp
new file mode 100644
index 0000000..d9422c3
--- /dev/null
+++ b/src/mgmt/nfd/face-traits.hpp
@@ -0,0 +1,157 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MGMT_NFD_FACE_TRAITS_HPP
+#define NDN_MGMT_NFD_FACE_TRAITS_HPP
+
+#include "../../encoding/tlv-nfd.hpp"
+
+namespace ndn {
+namespace nfd {
+
+/** \ingroup management
+ *  \brief providers getters and setters of face information fields
+ *  \tparam C the concrete class; it must provide .wireReset() method
+            to clear wire encoding when a field changes
+ */
+template<class C>
+class FaceTraits
+{
+public:
+  class Error : public tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : tlv::Error(what)
+    {
+    }
+  };
+
+  FaceTraits()
+    : m_faceId(0)
+    , m_faceScope(FACE_SCOPE_NON_LOCAL)
+    , m_facePersistency(FACE_PERSISTENCY_PERSISTENT)
+    , m_linkType(LINK_TYPE_POINT_TO_POINT)
+  {
+  }
+
+  uint64_t
+  getFaceId() const
+  {
+    return m_faceId;
+  }
+
+  C&
+  setFaceId(uint64_t faceId)
+  {
+    wireReset();
+    m_faceId = faceId;
+    return static_cast<C&>(*this);
+  }
+
+  const std::string&
+  getRemoteUri() const
+  {
+    return m_remoteUri;
+  }
+
+  C&
+  setRemoteUri(const std::string& remoteUri)
+  {
+    wireReset();
+    m_remoteUri = remoteUri;
+    return static_cast<C&>(*this);
+  }
+
+  const std::string&
+  getLocalUri() const
+  {
+    return m_localUri;
+  }
+
+  C&
+  setLocalUri(const std::string& localUri)
+  {
+    wireReset();
+    m_localUri = localUri;
+    return static_cast<C&>(*this);
+  }
+
+  FaceScope
+  getFaceScope() const
+  {
+    return m_faceScope;
+  }
+
+  C&
+  setFaceScope(FaceScope faceScope)
+  {
+    wireReset();
+    m_faceScope = faceScope;
+    return static_cast<C&>(*this);
+  }
+
+  FacePersistency
+  getFacePersistency() const
+  {
+    return m_facePersistency;
+  }
+
+  C&
+  setFacePersistency(FacePersistency facePersistency)
+  {
+    wireReset();
+    m_facePersistency = facePersistency;
+    return static_cast<C&>(*this);
+  }
+
+  LinkType
+  getLinkType() const
+  {
+    return m_linkType;
+  }
+
+  C&
+  setLinkType(LinkType linkType)
+  {
+    wireReset();
+    m_linkType = linkType;
+    return static_cast<C&>(*this);
+  }
+
+protected:
+  virtual void
+  wireReset() const = 0;
+
+protected:
+  uint64_t m_faceId;
+  std::string m_remoteUri;
+  std::string m_localUri;
+  FaceScope m_faceScope;
+  FacePersistency  m_facePersistency;
+  LinkType m_linkType;
+};
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MGMT_NFD_FACE_TRAITS_HPP
diff --git a/src/mgmt/nfd/fib-entry.cpp b/src/mgmt/nfd/fib-entry.cpp
new file mode 100644
index 0000000..dd1092f
--- /dev/null
+++ b/src/mgmt/nfd/fib-entry.cpp
@@ -0,0 +1,268 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "fib-entry.hpp"
+#include <sstream>
+#include "encoding/tlv-nfd.hpp"
+#include "encoding/block-helpers.hpp"
+#include "util/concepts.hpp"
+
+namespace ndn {
+namespace nfd {
+
+//BOOST_CONCEPT_ASSERT((boost::EqualityComparable<NextHopRecord>));
+BOOST_CONCEPT_ASSERT((WireEncodable<NextHopRecord>));
+BOOST_CONCEPT_ASSERT((WireDecodable<NextHopRecord>));
+static_assert(std::is_base_of<tlv::Error, NextHopRecord::Error>::value,
+              "NextHopRecord::Error must inherit from tlv::Error");
+
+//BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FibEntry>));
+BOOST_CONCEPT_ASSERT((WireEncodable<FibEntry>));
+BOOST_CONCEPT_ASSERT((WireDecodable<FibEntry>));
+static_assert(std::is_base_of<tlv::Error, FibEntry::Error>::value,
+              "FibEntry::Error must inherit from tlv::Error");
+
+// NextHopRecord := NEXT-HOP-RECORD TLV-LENGTH
+//                    FaceId
+//                    Cost
+
+NextHopRecord::NextHopRecord()
+  : m_faceId(std::numeric_limits<uint64_t>::max())
+  , m_cost(0)
+{
+}
+
+NextHopRecord::NextHopRecord(const Block& block)
+{
+  this->wireDecode(block);
+}
+
+NextHopRecord&
+NextHopRecord::setFaceId(uint64_t faceId)
+{
+  m_faceId = faceId;
+  m_wire.reset();
+  return *this;
+}
+
+NextHopRecord&
+NextHopRecord::setCost(uint64_t cost)
+{
+  m_cost = cost;
+  m_wire.reset();
+  return *this;
+}
+
+template<encoding::Tag TAG>
+size_t
+NextHopRecord::wireEncode(EncodingImpl<TAG>& block) const
+{
+  size_t totalLength = 0;
+  totalLength += prependNonNegativeIntegerBlock(block,
+                                                ndn::tlv::nfd::Cost,
+                                                m_cost);
+
+  totalLength += prependNonNegativeIntegerBlock(block,
+                                                ndn::tlv::nfd::FaceId,
+                                                m_faceId);
+
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(ndn::tlv::nfd::NextHopRecord);
+  return totalLength;
+}
+
+template size_t
+NextHopRecord::wireEncode<encoding::EncoderTag>(EncodingImpl<encoding::EncoderTag>& block) const;
+
+template size_t
+NextHopRecord::wireEncode<encoding::EstimatorTag>(EncodingImpl<encoding::EstimatorTag>& block) const;
+
+const Block&
+NextHopRecord::wireEncode() const
+{
+  if (m_wire.hasWire()) {
+    return m_wire;
+  }
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+NextHopRecord::wireDecode(const Block& wire)
+{
+  m_faceId = std::numeric_limits<uint64_t>::max();
+  m_cost = 0;
+
+  m_wire = wire;
+
+  if (m_wire.type() != tlv::nfd::NextHopRecord) {
+    std::stringstream error;
+    error << "Requested decoding of NextHopRecord, but Block is of a different type: #"
+          << m_wire.type();
+    BOOST_THROW_EXCEPTION(Error(error.str()));
+  }
+  m_wire.parse();
+
+  Block::element_const_iterator val = m_wire.elements_begin();
+  if (val == m_wire.elements_end()) {
+    BOOST_THROW_EXCEPTION(Error("Unexpected end of NextHopRecord"));
+  }
+  else if (val->type() != tlv::nfd::FaceId) {
+    std::stringstream error;
+    error << "Expected FaceId, but Block is of a different type: #"
+          << val->type();
+    BOOST_THROW_EXCEPTION(Error(error.str()));
+  }
+  m_faceId = readNonNegativeInteger(*val);
+  ++val;
+
+  if (val == m_wire.elements_end()) {
+    BOOST_THROW_EXCEPTION(Error("Unexpected end of NextHopRecord"));
+  }
+  else if (val->type() != tlv::nfd::Cost) {
+    std::stringstream error;
+    error << "Expected Cost, but Block is of a different type: #"
+          << m_wire.type();
+    BOOST_THROW_EXCEPTION(Error(error.str()));
+  }
+  m_cost = readNonNegativeInteger(*val);
+}
+
+// FibEntry      := FIB-ENTRY-TYPE TLV-LENGTH
+//                    Name
+//                    NextHopRecord*
+
+FibEntry::FibEntry()
+{
+}
+
+FibEntry::FibEntry(const Block& block)
+{
+  this->wireDecode(block);
+}
+
+FibEntry&
+FibEntry::setPrefix(const Name& prefix)
+{
+  m_prefix = prefix;
+  m_wire.reset();
+  return *this;
+}
+
+FibEntry&
+FibEntry::addNextHopRecord(const NextHopRecord& nextHopRecord)
+{
+  m_nextHopRecords.push_back(nextHopRecord);
+  m_wire.reset();
+  return *this;
+}
+
+template<encoding::Tag TAG>
+size_t
+FibEntry::wireEncode(EncodingImpl<TAG>& block) const
+{
+  size_t totalLength = 0;
+
+  for (auto i = m_nextHopRecords.rbegin(); i != m_nextHopRecords.rend(); ++i) {
+    totalLength += i->wireEncode(block);
+  }
+
+  totalLength += m_prefix.wireEncode(block);
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::nfd::FibEntry);
+
+  return totalLength;
+}
+
+template size_t
+FibEntry::wireEncode<encoding::EncoderTag>(EncodingImpl<encoding::EncoderTag>& block) const;
+
+template size_t
+FibEntry::wireEncode<encoding::EstimatorTag>(EncodingImpl<encoding::EstimatorTag>& block) const;
+
+const Block&
+FibEntry::wireEncode() const
+{
+  if (m_wire.hasWire()) {
+    return m_wire;
+  }
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+
+  return m_wire;
+}
+
+void
+FibEntry::wireDecode(const Block& wire)
+{
+  m_prefix.clear();
+  m_nextHopRecords.clear();
+
+  m_wire = wire;
+
+  if (m_wire.type() != tlv::nfd::FibEntry) {
+    std::stringstream error;
+    error << "Requested decoding of FibEntry, but Block is of a different type: #"
+          << m_wire.type();
+    BOOST_THROW_EXCEPTION(Error(error.str()));
+  }
+
+  m_wire.parse();
+
+  Block::element_const_iterator val = m_wire.elements_begin();
+  if (val == m_wire.elements_end()) {
+    BOOST_THROW_EXCEPTION(Error("Unexpected end of FibEntry"));
+  }
+  else if (val->type() != tlv::Name) {
+    std::stringstream error;
+    error << "Expected Name, but Block is of a different type: #"
+          << val->type();
+    BOOST_THROW_EXCEPTION(Error(error.str()));
+  }
+  m_prefix.wireDecode(*val);
+  ++val;
+
+  for (; val != m_wire.elements_end(); ++val) {
+    if (val->type() != tlv::nfd::NextHopRecord) {
+      std::stringstream error;
+      error << "Expected NextHopRecords, but Block is of a different type: #"
+            << val->type();
+      BOOST_THROW_EXCEPTION(Error(error.str()));
+    }
+    m_nextHopRecords.push_back(NextHopRecord(*val));
+  }
+}
+
+} // namespace nfd
+} // namespace ndn
diff --git a/src/mgmt/nfd/fib-entry.hpp b/src/mgmt/nfd/fib-entry.hpp
new file mode 100644
index 0000000..6a7304c
--- /dev/null
+++ b/src/mgmt/nfd/fib-entry.hpp
@@ -0,0 +1,155 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MGMT_NFD_FIB_ENTRY_HPP
+#define NDN_MGMT_NFD_FIB_ENTRY_HPP
+
+#include "../../encoding/block.hpp"
+#include "../../name.hpp"
+#include <list>
+
+namespace ndn {
+namespace nfd {
+
+/** @ingroup management
+ */
+class NextHopRecord
+{
+public:
+  class Error : public tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : tlv::Error(what)
+    {
+    }
+  };
+
+  NextHopRecord();
+
+  explicit
+  NextHopRecord(const Block& block);
+
+  uint64_t
+  getFaceId() const
+  {
+    return m_faceId;
+  }
+
+  NextHopRecord&
+  setFaceId(uint64_t faceId);
+
+  uint64_t
+  getCost() const
+  {
+    return m_cost;
+  }
+
+  NextHopRecord&
+  setCost(uint64_t cost);
+
+  template<encoding::Tag TAG>
+  size_t
+  wireEncode(EncodingImpl<TAG>& block) const;
+
+  const Block&
+  wireEncode() const;
+
+  void
+  wireDecode(const Block& wire);
+
+private:
+  uint64_t m_faceId;
+  uint64_t m_cost;
+
+  mutable Block m_wire;
+};
+
+/** @ingroup management
+ */
+class FibEntry
+{
+public:
+  class Error : public tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : tlv::Error(what)
+    {
+    }
+  };
+
+  FibEntry();
+
+  explicit
+  FibEntry(const Block& block);
+
+  const Name&
+  getPrefix() const
+  {
+    return m_prefix;
+  }
+
+  FibEntry&
+  setPrefix(const Name& prefix);
+
+  const std::list<NextHopRecord>&
+  getNextHopRecords() const
+  {
+    return m_nextHopRecords;
+  }
+
+  FibEntry&
+  addNextHopRecord(const NextHopRecord& nextHopRecord);
+
+  template<typename T>
+  FibEntry&
+  setNextHopRecords(const T& begin, const T& end)
+  {
+    m_nextHopRecords.clear();
+    m_nextHopRecords.assign(begin, end);
+    m_wire.reset();
+    return *this;
+  }
+
+  template<encoding::Tag TAG>
+  size_t
+  wireEncode(EncodingImpl<TAG>& block) const;
+
+  const Block&
+  wireEncode() const;
+
+  void
+  wireDecode(const Block& wire);
+
+private:
+  Name m_prefix;
+  std::list<NextHopRecord> m_nextHopRecords;
+
+  mutable Block m_wire;
+};
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MGMT_NFD_FIB_ENTRY_HPP
diff --git a/src/mgmt/nfd/forwarder-status.cpp b/src/mgmt/nfd/forwarder-status.cpp
new file mode 100644
index 0000000..21fee56
--- /dev/null
+++ b/src/mgmt/nfd/forwarder-status.cpp
@@ -0,0 +1,357 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "forwarder-status.hpp"
+#include "encoding/tlv-nfd.hpp"
+#include "encoding/block-helpers.hpp"
+#include "util/concepts.hpp"
+
+namespace ndn {
+namespace nfd {
+
+//BOOST_CONCEPT_ASSERT((boost::EqualityComparable<ForwarderStatus>));
+BOOST_CONCEPT_ASSERT((WireEncodable<ForwarderStatus>));
+BOOST_CONCEPT_ASSERT((WireDecodable<ForwarderStatus>));
+static_assert(std::is_base_of<tlv::Error, ForwarderStatus::Error>::value,
+              "ForwarderStatus::Error must inherit from tlv::Error");
+
+ForwarderStatus::ForwarderStatus()
+  : m_startTimestamp(time::system_clock::TimePoint::min())
+  , m_currentTimestamp(time::system_clock::TimePoint::min())
+  , m_nNameTreeEntries(0)
+  , m_nFibEntries(0)
+  , m_nPitEntries(0)
+  , m_nMeasurementsEntries(0)
+  , m_nCsEntries(0)
+  , m_nInInterests(0)
+  , m_nInDatas(0)
+  , m_nInNacks(0)
+  , m_nOutInterests(0)
+  , m_nOutDatas(0)
+  , m_nOutNacks(0)
+{
+}
+
+ForwarderStatus::ForwarderStatus(const Block& payload)
+{
+  this->wireDecode(payload);
+}
+
+template<encoding::Tag TAG>
+size_t
+ForwarderStatus::wireEncode(EncodingImpl<TAG>& encoder) const
+{
+  size_t totalLength = 0;
+
+  totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::NOutNacks,
+                                                m_nOutNacks);
+  totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::NOutDatas,
+                                                m_nOutDatas);
+  totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::NOutInterests,
+                                                m_nOutInterests);
+  totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::NInNacks,
+                                                m_nInNacks);
+  totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::NInDatas,
+                                                m_nInDatas);
+  totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::NInInterests,
+                                                m_nInInterests);
+  totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::NCsEntries,
+                                                m_nCsEntries);
+  totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::NMeasurementsEntries,
+                                                m_nMeasurementsEntries);
+  totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::NPitEntries,
+                                                m_nPitEntries);
+  totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::NFibEntries,
+                                                m_nFibEntries);
+  totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::NNameTreeEntries,
+                                                m_nNameTreeEntries);
+  totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::CurrentTimestamp,
+                                                time::toUnixTimestamp(m_currentTimestamp).count());
+  totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::StartTimestamp,
+                                                time::toUnixTimestamp(m_startTimestamp).count());
+  totalLength += encoder.prependByteArrayBlock(tlv::nfd::NfdVersion,
+                                       reinterpret_cast<const uint8_t*>(m_nfdVersion.c_str()),
+                                       m_nfdVersion.size());
+
+  totalLength += encoder.prependVarNumber(totalLength);
+  totalLength += encoder.prependVarNumber(tlv::Content);
+  return totalLength;
+}
+
+template size_t
+ForwarderStatus::wireEncode<encoding::EncoderTag>(EncodingImpl<encoding::EncoderTag>&) const;
+
+template size_t
+ForwarderStatus::wireEncode<encoding::EstimatorTag>(EncodingImpl<encoding::EstimatorTag>&) const;
+
+const Block&
+ForwarderStatus::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+ForwarderStatus::wireDecode(const Block& block)
+{
+  if (block.type() != tlv::Content) {
+    BOOST_THROW_EXCEPTION(Error("expecting Content block for Status payload"));
+  }
+  m_wire = block;
+  m_wire.parse();
+  Block::element_const_iterator val = m_wire.elements_begin();
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NfdVersion) {
+    m_nfdVersion.assign(reinterpret_cast<const char*>(val->value()), val->value_size());
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NfdVersion field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::StartTimestamp) {
+    m_startTimestamp = time::fromUnixTimestamp(time::milliseconds(readNonNegativeInteger(*val)));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required StartTimestamp field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::CurrentTimestamp) {
+    m_currentTimestamp = time::fromUnixTimestamp(time::milliseconds(readNonNegativeInteger(*val)));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required CurrentTimestamp field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NNameTreeEntries) {
+    m_nNameTreeEntries = static_cast<size_t>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NNameTreeEntries field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NFibEntries) {
+    m_nFibEntries = static_cast<size_t>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NFibEntries field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NPitEntries) {
+    m_nPitEntries = static_cast<size_t>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NPitEntries field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NMeasurementsEntries) {
+    m_nMeasurementsEntries = static_cast<size_t>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NMeasurementsEntries field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NCsEntries) {
+    m_nCsEntries = static_cast<size_t>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NCsEntries field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NInInterests) {
+    m_nInInterests = static_cast<uint64_t>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NInInterests field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NInDatas) {
+    m_nInDatas = static_cast<uint64_t>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NInDatas field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NInNacks) {
+    m_nInNacks = static_cast<uint64_t>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NInNacks field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NOutInterests) {
+    m_nOutInterests = static_cast<uint64_t>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NOutInterests field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NOutDatas) {
+    m_nOutDatas = static_cast<uint64_t>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NOutDatas field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NOutNacks) {
+    m_nOutNacks = static_cast<uint64_t>(readNonNegativeInteger(*val));
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required NInNacks field"));
+  }
+}
+
+ForwarderStatus&
+ForwarderStatus::setNfdVersion(const std::string& nfdVersion)
+{
+  m_wire.reset();
+  m_nfdVersion = nfdVersion;
+  return *this;
+}
+
+ForwarderStatus&
+ForwarderStatus::setStartTimestamp(const time::system_clock::TimePoint& startTimestamp)
+{
+  m_wire.reset();
+  m_startTimestamp = startTimestamp;
+  return *this;
+}
+
+ForwarderStatus&
+ForwarderStatus::setCurrentTimestamp(const time::system_clock::TimePoint& currentTimestamp)
+{
+  m_wire.reset();
+  m_currentTimestamp = currentTimestamp;
+  return *this;
+}
+
+ForwarderStatus&
+ForwarderStatus::setNNameTreeEntries(size_t nNameTreeEntries)
+{
+  m_wire.reset();
+  m_nNameTreeEntries = nNameTreeEntries;
+  return *this;
+}
+
+ForwarderStatus&
+ForwarderStatus::setNFibEntries(size_t nFibEntries)
+{
+  m_wire.reset();
+  m_nFibEntries = nFibEntries;
+  return *this;
+}
+
+ForwarderStatus&
+ForwarderStatus::setNPitEntries(size_t nPitEntries)
+{
+  m_wire.reset();
+  m_nPitEntries = nPitEntries;
+  return *this;
+}
+
+ForwarderStatus&
+ForwarderStatus::setNMeasurementsEntries(size_t nMeasurementsEntries)
+{
+  m_wire.reset();
+  m_nMeasurementsEntries = nMeasurementsEntries;
+  return *this;
+}
+
+ForwarderStatus&
+ForwarderStatus::setNCsEntries(size_t nCsEntries)
+{
+  m_wire.reset();
+  m_nCsEntries = nCsEntries;
+  return *this;
+}
+
+ForwarderStatus&
+ForwarderStatus::setNInInterests(uint64_t nInInterests)
+{
+  m_wire.reset();
+  m_nInInterests = nInInterests;
+  return *this;
+}
+
+ForwarderStatus&
+ForwarderStatus::setNInDatas(uint64_t nInDatas)
+{
+  m_wire.reset();
+  m_nInDatas = nInDatas;
+  return *this;
+}
+
+ForwarderStatus&
+ForwarderStatus::setNInNacks(uint64_t nInNacks)
+{
+  m_wire.reset();
+  m_nInNacks = nInNacks;
+  return *this;
+}
+
+ForwarderStatus&
+ForwarderStatus::setNOutInterests(uint64_t nOutInterests)
+{
+  m_wire.reset();
+  m_nOutInterests = nOutInterests;
+  return *this;
+}
+
+ForwarderStatus&
+ForwarderStatus::setNOutDatas(uint64_t nOutDatas)
+{
+  m_wire.reset();
+  m_nOutDatas = nOutDatas;
+  return *this;
+}
+
+ForwarderStatus&
+ForwarderStatus::setNOutNacks(uint64_t nOutNacks)
+{
+  m_wire.reset();
+  m_nOutNacks = nOutNacks;
+  return *this;
+}
+
+} // namespace nfd
+} // namespace ndn
diff --git a/src/mgmt/nfd/forwarder-status.hpp b/src/mgmt/nfd/forwarder-status.hpp
new file mode 100644
index 0000000..4601610
--- /dev/null
+++ b/src/mgmt/nfd/forwarder-status.hpp
@@ -0,0 +1,225 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MGMT_NFD_FORWARDER_STATUS_HPP
+#define NDN_MGMT_NFD_FORWARDER_STATUS_HPP
+
+#include "../../encoding/block.hpp"
+#include "../../util/time.hpp"
+
+namespace ndn {
+namespace nfd {
+
+/**
+ * \ingroup management
+ * \brief represents NFD Forwarder Status
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/ForwarderStatus
+ */
+class ForwarderStatus
+{
+public:
+  class Error : public tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : tlv::Error(what)
+    {
+    }
+  };
+
+  ForwarderStatus();
+
+  explicit
+  ForwarderStatus(const Block& payload);
+
+  /** \brief prepend ForwarderStatus as a Content block to the encoder
+   *
+   *  The outermost Content element isn't part of ForwardStatus structure.
+   */
+  template<encoding::Tag TAG>
+  size_t
+  wireEncode(EncodingImpl<TAG>& encoder) const;
+
+  /** \brief encode ForwarderStatus as a Content block
+   *
+   *  The outermost Content element isn't part of ForwardStatus structure.
+   */
+  const Block&
+  wireEncode() const;
+
+  /** \brief decode ForwarderStatus from a Content block
+   *
+   *  The outermost Content element isn't part of ForwardStatus structure.
+   */
+  void
+  wireDecode(const Block& wire);
+
+public: // getters & setters
+  const std::string&
+  getNfdVersion() const
+  {
+    return m_nfdVersion;
+  }
+
+  ForwarderStatus&
+  setNfdVersion(const std::string& nfdVersion);
+
+  const time::system_clock::TimePoint&
+  getStartTimestamp() const
+  {
+    return m_startTimestamp;
+  }
+
+  ForwarderStatus&
+  setStartTimestamp(const time::system_clock::TimePoint& startTimestamp);
+
+  const time::system_clock::TimePoint&
+  getCurrentTimestamp() const
+  {
+    return m_currentTimestamp;
+  }
+
+  ForwarderStatus&
+  setCurrentTimestamp(const time::system_clock::TimePoint& currentTimestamp);
+
+  size_t
+  getNNameTreeEntries() const
+  {
+    return m_nNameTreeEntries;
+  }
+
+  ForwarderStatus&
+  setNNameTreeEntries(size_t nNameTreeEntries);
+
+  size_t
+  getNFibEntries() const
+  {
+    return m_nFibEntries;
+  }
+
+  ForwarderStatus&
+  setNFibEntries(size_t nFibEntries);
+
+  size_t
+  getNPitEntries() const
+  {
+    return m_nPitEntries;
+  }
+
+  ForwarderStatus&
+  setNPitEntries(size_t nPitEntries);
+
+  size_t
+  getNMeasurementsEntries() const
+  {
+    return m_nMeasurementsEntries;
+  }
+
+  ForwarderStatus&
+  setNMeasurementsEntries(size_t nMeasurementsEntries);
+
+  size_t
+  getNCsEntries() const
+  {
+    return m_nCsEntries;
+  }
+
+  ForwarderStatus&
+  setNCsEntries(size_t nCsEntries);
+
+  uint64_t
+  getNInInterests() const
+  {
+    return m_nInInterests;
+  }
+
+  ForwarderStatus&
+  setNInInterests(uint64_t nInInterests);
+
+  uint64_t
+  getNInDatas() const
+  {
+    return m_nInDatas;
+  }
+
+  ForwarderStatus&
+  setNInDatas(uint64_t nInDatas);
+
+  uint64_t
+  getNInNacks() const
+  {
+    return m_nInNacks;
+  }
+
+  ForwarderStatus&
+  setNInNacks(uint64_t nInNacks);
+
+  uint64_t
+  getNOutInterests() const
+  {
+    return m_nOutInterests;
+  }
+
+  ForwarderStatus&
+  setNOutInterests(uint64_t nOutInterests);
+
+  uint64_t
+  getNOutDatas() const
+  {
+    return m_nOutDatas;
+  }
+
+  ForwarderStatus&
+  setNOutDatas(uint64_t nOutDatas);
+
+  uint64_t
+  getNOutNacks() const
+  {
+    return m_nOutNacks;
+  }
+
+  ForwarderStatus&
+  setNOutNacks(uint64_t nOutNacks);
+
+private:
+  std::string m_nfdVersion;
+  time::system_clock::TimePoint m_startTimestamp;
+  time::system_clock::TimePoint m_currentTimestamp;
+  size_t m_nNameTreeEntries;
+  size_t m_nFibEntries;
+  size_t m_nPitEntries;
+  size_t m_nMeasurementsEntries;
+  size_t m_nCsEntries;
+  uint64_t m_nInInterests;
+  uint64_t m_nInDatas;
+  uint64_t m_nInNacks;
+  uint64_t m_nOutInterests;
+  uint64_t m_nOutDatas;
+  uint64_t m_nOutNacks;
+
+  mutable Block m_wire;
+};
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MGMT_NFD_FORWARDER_STATUS_HPP
diff --git a/src/mgmt/nfd/rib-entry.cpp b/src/mgmt/nfd/rib-entry.cpp
new file mode 100644
index 0000000..d47d316
--- /dev/null
+++ b/src/mgmt/nfd/rib-entry.cpp
@@ -0,0 +1,320 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "rib-entry.hpp"
+#include "encoding/tlv-nfd.hpp"
+#include "encoding/block-helpers.hpp"
+#include "util/concepts.hpp"
+
+namespace ndn {
+namespace nfd {
+
+//BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Route>));
+BOOST_CONCEPT_ASSERT((WireEncodable<Route>));
+BOOST_CONCEPT_ASSERT((WireDecodable<Route>));
+static_assert(std::is_base_of<tlv::Error, Route::Error>::value,
+              "Route::Error must inherit from tlv::Error");
+
+//BOOST_CONCEPT_ASSERT((boost::EqualityComparable<RibEntry>));
+BOOST_CONCEPT_ASSERT((WireEncodable<RibEntry>));
+BOOST_CONCEPT_ASSERT((WireDecodable<RibEntry>));
+static_assert(std::is_base_of<tlv::Error, RibEntry::Error>::value,
+              "RibEntry::Error must inherit from tlv::Error");
+
+const time::milliseconds Route::INFINITE_EXPIRATION_PERIOD(time::milliseconds::max());
+
+Route::Route()
+  : m_faceId(0)
+  , m_origin(0)
+  , m_cost(0)
+  , m_flags(ROUTE_FLAG_CHILD_INHERIT)
+  , m_expirationPeriod(INFINITE_EXPIRATION_PERIOD)
+  , m_hasInfiniteExpirationPeriod(true)
+{
+}
+
+Route::Route(const Block& block)
+{
+  wireDecode(block);
+}
+
+template<encoding::Tag TAG>
+size_t
+Route::wireEncode(EncodingImpl<TAG>& block) const
+{
+  size_t totalLength = 0;
+
+  // Absence of an ExpirationPeriod signifies non-expiration
+  if (!m_hasInfiniteExpirationPeriod) {
+    totalLength += prependNonNegativeIntegerBlock(block,
+                                                  ndn::tlv::nfd::ExpirationPeriod,
+                                                  m_expirationPeriod.count());
+  }
+
+  totalLength += prependNonNegativeIntegerBlock(block,
+                                                ndn::tlv::nfd::Flags,
+                                                m_flags);
+
+  totalLength += prependNonNegativeIntegerBlock(block,
+                                                ndn::tlv::nfd::Cost,
+                                                m_cost);
+
+  totalLength += prependNonNegativeIntegerBlock(block,
+                                                ndn::tlv::nfd::Origin,
+                                                m_origin);
+
+  totalLength += prependNonNegativeIntegerBlock(block,
+                                                ndn::tlv::nfd::FaceId,
+                                                m_faceId);
+
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(ndn::tlv::nfd::Route);
+
+  return totalLength;
+}
+
+template size_t
+Route::wireEncode<encoding::EncoderTag>(EncodingImpl<encoding::EncoderTag>& block) const;
+
+template size_t
+Route::wireEncode<encoding::EstimatorTag>(EncodingImpl<encoding::EstimatorTag>& block) const;
+
+const Block&
+Route::wireEncode() const
+{
+  if (m_wire.hasWire()) {
+    return m_wire;
+  }
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+
+  return m_wire;
+}
+
+void
+Route::wireDecode(const Block& wire)
+{
+  m_faceId = 0;
+  m_origin = 0;
+  m_cost = 0;
+  m_flags = 0;
+  m_expirationPeriod = time::milliseconds::min();
+
+  m_wire = wire;
+
+  if (m_wire.type() != tlv::nfd::Route) {
+    std::stringstream error;
+    error << "Expected Route Block, but Block is of a different type: #"
+          << m_wire.type();
+    BOOST_THROW_EXCEPTION(Error(error.str()));
+  }
+
+  m_wire.parse();
+
+  Block::element_const_iterator val = m_wire.elements_begin();
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::FaceId) {
+    m_faceId = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("Missing required FaceId field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::Origin) {
+    m_origin = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("Missing required Origin field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::Cost) {
+    m_cost = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("Missing required Cost field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::Flags) {
+    m_flags = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("Missing required Flags field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::ExpirationPeriod) {
+    m_expirationPeriod = time::milliseconds(readNonNegativeInteger(*val));
+    m_hasInfiniteExpirationPeriod = false;
+  }
+  else {
+    m_expirationPeriod = INFINITE_EXPIRATION_PERIOD;
+    m_hasInfiniteExpirationPeriod = true;
+  }
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Route& route)
+{
+  os << "Route("
+     << "FaceId: " << route.getFaceId() << ", "
+     << "Origin: " << route.getOrigin() << ", "
+     << "Cost: " << route.getCost() << ", "
+     << "Flags: " << route.getFlags() << ", ";
+
+  if (!route.hasInfiniteExpirationPeriod()) {
+    os << "ExpirationPeriod: " << route.getExpirationPeriod();
+  }
+  else {
+    os << "ExpirationPeriod: Infinity";
+  }
+
+  os << ")";
+
+  return os;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+
+
+RibEntry::RibEntry()
+{
+}
+
+RibEntry::RibEntry(const Block& block)
+{
+  wireDecode(block);
+}
+
+
+template<encoding::Tag TAG>
+size_t
+RibEntry::wireEncode(EncodingImpl<TAG>& block) const
+{
+  size_t totalLength = 0;
+
+  for (std::list<Route>::const_reverse_iterator it = m_routes.rbegin();
+       it != m_routes.rend(); ++it)
+    {
+      totalLength += it->wireEncode(block);
+    }
+
+  totalLength += m_prefix.wireEncode(block);
+
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::nfd::RibEntry);
+
+  return totalLength;
+}
+
+template size_t
+RibEntry::wireEncode<encoding::EncoderTag>(EncodingImpl<encoding::EncoderTag>& block) const;
+
+template size_t
+RibEntry::wireEncode<encoding::EstimatorTag>(EncodingImpl<encoding::EstimatorTag>& block) const;
+
+const Block&
+RibEntry::wireEncode() const
+{
+  if (m_wire.hasWire()) {
+    return m_wire;
+  }
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+
+  return m_wire;
+}
+
+void
+RibEntry::wireDecode(const Block& wire)
+{
+  m_prefix.clear();
+  m_routes.clear();
+
+  m_wire = wire;
+
+  if (m_wire.type() != tlv::nfd::RibEntry) {
+    std::stringstream error;
+    error << "Expected RibEntry Block, but Block is of a different type: #"
+          << m_wire.type();
+    BOOST_THROW_EXCEPTION(Error(error.str()));
+  }
+
+  m_wire.parse();
+
+  Block::element_const_iterator val = m_wire.elements_begin();
+
+  if (val != m_wire.elements_end() && val->type() == tlv::Name) {
+    m_prefix.wireDecode(*val);
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("Missing required Name field"));
+  }
+
+  for (; val != m_wire.elements_end(); ++val) {
+
+    if (val->type() == tlv::nfd::Route) {
+      m_routes.push_back(Route(*val));
+    }
+    else {
+      std::stringstream error;
+      error << "Expected Route Block, but Block is of a different type: #"
+            << m_wire.type();
+      BOOST_THROW_EXCEPTION(Error(error.str()));
+    }
+  }
+}
+
+std::ostream&
+operator<<(std::ostream& os, const RibEntry& entry)
+{
+  os << "RibEntry{\n"
+     << "  Name: " << entry.getName() << "\n";
+
+  for (RibEntry::iterator it = entry.begin(); it != entry.end(); ++it) {
+    os << "  " << *it << "\n";
+  }
+
+  os << "}";
+
+  return os;
+}
+
+} // namespace nfd
+} // namespace ndn
diff --git a/src/mgmt/nfd/rib-entry.hpp b/src/mgmt/nfd/rib-entry.hpp
new file mode 100644
index 0000000..6c1c5a2
--- /dev/null
+++ b/src/mgmt/nfd/rib-entry.hpp
@@ -0,0 +1,287 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MGMT_NFD_RIB_ENTRY_HPP
+#define NDN_MGMT_NFD_RIB_ENTRY_HPP
+
+#include "rib-flags.hpp" // include this first, to ensure it compiles on its own.
+#include "../../name.hpp"
+#include "../../util/time.hpp"
+
+#include <list>
+
+namespace ndn {
+namespace nfd {
+
+/**
+ * @ingroup management
+ *
+ * @brief Data abstraction for Route
+ *
+ * A route indicates the availability of content via a certain face and
+ * provides meta-information about the face.
+ *
+ *     Route := ROUTE-TYPE TLV-LENGTH
+ *                FaceId
+ *                Origin
+ *                Cost
+ *                Flags
+ *                ExpirationPeriod?
+ *
+ * @sa http://redmine.named-data.net/projects/nfd/wiki/RibMgmt
+ */
+class Route : public RibFlagsTraits<Route>
+{
+public:
+  class Error : public tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what) : tlv::Error(what)
+    {
+    }
+  };
+
+  Route();
+
+  explicit
+  Route(const Block& block);
+
+  uint64_t
+  getFaceId() const
+  {
+    return m_faceId;
+  }
+
+  Route&
+  setFaceId(uint64_t faceId)
+  {
+    m_faceId = faceId;
+    m_wire.reset();
+    return *this;
+  }
+
+  uint64_t
+  getOrigin() const
+  {
+    return m_origin;
+  }
+
+  /** @brief set Origin
+   *  @param origin a code defined in ndn::nfd::RouteOrigin
+   */
+  Route&
+  setOrigin(uint64_t origin)
+  {
+    m_origin = origin;
+    m_wire.reset();
+    return *this;
+  }
+
+  uint64_t
+  getCost() const
+  {
+    return m_cost;
+  }
+
+  Route&
+  setCost(uint64_t cost)
+  {
+    m_cost = cost;
+    m_wire.reset();
+    return *this;
+  }
+
+  uint64_t
+  getFlags() const
+  {
+    return m_flags;
+  }
+
+  /** @brief set route inheritance flags
+   *  @param flags a bitwise OR'ed code from ndn::nfd::RouteFlags
+   */
+  Route&
+  setFlags(uint64_t flags)
+  {
+    m_flags = flags;
+    m_wire.reset();
+    return *this;
+  }
+
+  static const time::milliseconds INFINITE_EXPIRATION_PERIOD;
+
+  const time::milliseconds&
+  getExpirationPeriod() const
+  {
+    return m_expirationPeriod;
+  }
+
+  Route&
+  setExpirationPeriod(const time::milliseconds& expirationPeriod)
+  {
+    m_expirationPeriod = expirationPeriod;
+
+    m_hasInfiniteExpirationPeriod = m_expirationPeriod == INFINITE_EXPIRATION_PERIOD;
+
+    m_wire.reset();
+    return *this;
+  }
+
+  bool
+  hasInfiniteExpirationPeriod() const
+  {
+    return m_hasInfiniteExpirationPeriod;
+  }
+
+  template<encoding::Tag TAG>
+  size_t
+  wireEncode(EncodingImpl<TAG>& block) const;
+
+  const Block&
+  wireEncode() const;
+
+  void
+  wireDecode(const Block& wire);
+
+private:
+  uint64_t m_faceId;
+  uint64_t m_origin;
+  uint64_t m_cost;
+  uint64_t m_flags;
+  time::milliseconds m_expirationPeriod;
+  bool m_hasInfiniteExpirationPeriod;
+
+  mutable Block m_wire;
+};
+
+std::ostream&
+operator<<(std::ostream& os, const Route& route);
+
+/**
+ * @ingroup management
+ *
+ * @brief Data abstraction for RIB entry
+ *
+ * A RIB entry contains one or more routes for the name prefix
+ *
+ *     RibEntry := RIB-ENTRY-TYPE TLV-LENGTH
+ *                Name
+ *                Route+
+ *
+ * @sa http://redmine.named-data.net/projects/nfd/wiki/RibMgmt
+ */
+class RibEntry
+{
+public:
+  class Error : public tlv::Error
+  {
+  public:
+    Error(const std::string& what) : tlv::Error(what)
+    {
+    }
+  };
+
+  typedef std::list<Route> RouteList;
+  typedef RouteList::const_iterator iterator;
+
+  RibEntry();
+
+  explicit
+  RibEntry(const Block& block);
+
+  const Name&
+  getName() const
+  {
+    return m_prefix;
+  }
+
+  RibEntry&
+  setName(const Name& prefix)
+  {
+    m_prefix = prefix;
+    m_wire.reset();
+    return *this;
+  }
+
+  const std::list<Route>&
+  getRoutes() const
+  {
+    return m_routes;
+  }
+
+  RibEntry&
+  addRoute(const Route& route)
+  {
+    m_routes.push_back(route);
+    m_wire.reset();
+    return *this;
+  }
+
+  RibEntry&
+  clearRoutes()
+  {
+    m_routes.clear();
+    return *this;
+  }
+
+  template<encoding::Tag TAG>
+  size_t
+  wireEncode(EncodingImpl<TAG>& block) const;
+
+  const Block&
+  wireEncode() const;
+
+  void
+  wireDecode(const Block& wire);
+
+  iterator
+  begin() const;
+
+  iterator
+  end() const;
+
+private:
+  Name m_prefix;
+  RouteList m_routes;
+
+  mutable Block m_wire;
+};
+
+inline RibEntry::iterator
+RibEntry::begin() const
+{
+  return m_routes.begin();
+}
+
+inline RibEntry::iterator
+RibEntry::end() const
+{
+  return m_routes.end();
+}
+
+std::ostream&
+operator<<(std::ostream& os, const RibEntry& entry);
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MGMT_NFD_RIB_ENTRY_HPP
diff --git a/src/mgmt/nfd/rib-flags.hpp b/src/mgmt/nfd/rib-flags.hpp
new file mode 100644
index 0000000..158048f
--- /dev/null
+++ b/src/mgmt/nfd/rib-flags.hpp
@@ -0,0 +1,57 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MGMT_NFD_RIB_FLAGS_HPP
+#define NDN_MGMT_NFD_RIB_FLAGS_HPP
+
+#include "../../encoding/nfd-constants.hpp"
+
+namespace ndn {
+namespace nfd {
+
+/**
+ * \ingroup management
+ * \brief implements getters to each RIB flag
+ *
+ * \tparam T class containing a RibFlags field and implements
+ *           `RibFlags getFlags() const` method
+ */
+template<typename T>
+class RibFlagsTraits
+{
+public:
+  bool
+  isChildInherit() const
+  {
+    return static_cast<const T*>(this)->getFlags() & ROUTE_FLAG_CHILD_INHERIT;
+  }
+
+  bool
+  isRibCapture() const
+  {
+    return static_cast<const T*>(this)->getFlags() & ROUTE_FLAG_CAPTURE;
+  }
+};
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MGMT_NFD_RIB_FLAGS_HPP
diff --git a/src/mgmt/nfd/status-dataset.cpp b/src/mgmt/nfd/status-dataset.cpp
new file mode 100644
index 0000000..ca5f5df
--- /dev/null
+++ b/src/mgmt/nfd/status-dataset.cpp
@@ -0,0 +1,162 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "status-dataset.hpp"
+#include "../../util/concepts.hpp"
+
+namespace ndn {
+namespace nfd {
+
+StatusDataset::StatusDataset(const PartialName& datasetName)
+  : m_datasetName(datasetName)
+{
+}
+
+Name
+StatusDataset::getDatasetPrefix(const Name& prefix) const
+{
+  Name name;
+  name.append(prefix).append(m_datasetName);
+  this->addParameters(name);
+  return name;
+}
+
+void
+StatusDataset::addParameters(Name& name) const
+{
+}
+
+/**
+ * \brief parses elements into a vector of T
+ * \tparam T element type
+ * \param payload pointer to a buffer of zero or more blocks of decodable by T
+ * \return a vector of T
+ * \throw tlv::Error cannot parse payload
+ */
+template<typename T>
+static std::vector<T>
+parseDatasetVector(ConstBufferPtr payload)
+{
+  BOOST_CONCEPT_ASSERT((WireDecodable<T>));
+
+  std::vector<T> result;
+
+  size_t offset = 0;
+  while (offset < payload->size()) {
+    bool isOk = false;
+    Block block;
+    std::tie(isOk, block) = Block::fromBuffer(payload, offset);
+    if (!isOk) {
+      BOOST_THROW_EXCEPTION(StatusDataset::ParseResultError("cannot decode Block"));
+    }
+
+    offset += block.size();
+    result.emplace_back(block);
+  }
+
+  return result;
+}
+
+ForwarderGeneralStatusDataset::ForwarderGeneralStatusDataset()
+  : StatusDataset("status/general")
+{
+}
+
+ForwarderGeneralStatusDataset::ResultType
+ForwarderGeneralStatusDataset::parseResult(ConstBufferPtr payload) const
+{
+  return ForwarderStatus(Block(tlv::Content, payload));
+}
+
+FaceDatasetBase::FaceDatasetBase(const PartialName& datasetName)
+  : StatusDataset(datasetName)
+{
+}
+
+FaceDatasetBase::ResultType
+FaceDatasetBase::parseResult(ConstBufferPtr payload) const
+{
+  return parseDatasetVector<FaceStatus>(payload);
+}
+
+FaceDataset::FaceDataset()
+  : FaceDatasetBase("faces/list")
+{
+}
+
+FaceQueryDataset::FaceQueryDataset(const FaceQueryFilter& filter)
+  : FaceDatasetBase("faces/query")
+  , m_filter(filter)
+{
+}
+
+void
+FaceQueryDataset::addParameters(Name& name) const
+{
+  name.append(m_filter.wireEncode());
+}
+
+ChannelDataset::ChannelDataset()
+  : StatusDataset("faces/channels")
+{
+}
+
+ChannelDataset::ResultType
+ChannelDataset::parseResult(ConstBufferPtr payload) const
+{
+  return parseDatasetVector<ChannelStatus>(payload);
+}
+
+FibDataset::FibDataset()
+  : StatusDataset("fib/list")
+{
+}
+
+FibDataset::ResultType
+FibDataset::parseResult(ConstBufferPtr payload) const
+{
+  return parseDatasetVector<FibEntry>(payload);
+}
+
+StrategyChoiceDataset::StrategyChoiceDataset()
+  : StatusDataset("strategy-choice/list")
+{
+}
+
+StrategyChoiceDataset::ResultType
+StrategyChoiceDataset::parseResult(ConstBufferPtr payload) const
+{
+  return parseDatasetVector<StrategyChoice>(payload);
+}
+
+RibDataset::RibDataset()
+  : StatusDataset("rib/list")
+{
+}
+
+RibDataset::ResultType
+RibDataset::parseResult(ConstBufferPtr payload) const
+{
+  return parseDatasetVector<RibEntry>(payload);
+}
+
+} // namespace nfd
+} // namespace ndn
diff --git a/src/mgmt/nfd/status-dataset.hpp b/src/mgmt/nfd/status-dataset.hpp
new file mode 100644
index 0000000..9eb43d4
--- /dev/null
+++ b/src/mgmt/nfd/status-dataset.hpp
@@ -0,0 +1,252 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MGMT_NFD_STATUS_DATASET_HPP
+#define NDN_MGMT_NFD_STATUS_DATASET_HPP
+
+#include "../../name.hpp"
+#include "forwarder-status.hpp"
+#include "face-status.hpp"
+#include "face-query-filter.hpp"
+#include "channel-status.hpp"
+#include "fib-entry.hpp"
+#include "strategy-choice.hpp"
+#include "rib-entry.hpp"
+
+namespace ndn {
+namespace nfd {
+
+/**
+ * \ingroup management
+ * \brief base class of NFD StatusDataset
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/StatusDataset
+ */
+class StatusDataset : noncopyable
+{
+public:
+#ifdef DOXYGEN
+  /**
+   * \brief if defined, specifies constructor argument type;
+   *        otherwise, constructor has no argument
+   */
+  typedef int ParamType;
+#endif
+
+  /**
+   * \brief constructs a name prefix for the dataset
+   * \param prefix top-level prefix, such as ndn:/localhost/nfd
+   * \return name prefix without version and segment components
+   */
+  Name
+  getDatasetPrefix(const Name& prefix) const;
+
+#ifdef DOXYGEN
+  /**
+   * \brief provides the result type, usually a vector
+   */
+  typedef std::vector<int> ResultType;
+#endif
+
+  /**
+   * \brief indicates reassembled payload cannot be parsed as ResultType
+   */
+  class ParseResultError : public tlv::Error
+  {
+  public:
+    explicit
+    ParseResultError(const std::string& what)
+      : tlv::Error(what)
+    {
+    }
+  };
+
+#ifdef DOXYGEN
+  /**
+   * \brief parses a result from reassembled payload
+   * \param payload reassembled payload
+   * \throw tlv::Error cannot parse payload
+   */
+  ResultType
+  parseResult(ConstBufferPtr payload) const;
+#endif
+
+protected:
+  /**
+   * \brief constructs a StatusDataset instance with given sub-prefix
+   * \param datasetName dataset name after top-level prefix, such as faces/list
+   */
+  explicit
+  StatusDataset(const PartialName& datasetName);
+
+private:
+  /**
+   * \brief appends parameters to the dataset name prefix
+   * \param[in,out] the dataset name prefix onto which parameter components can be appended
+   */
+  virtual void
+  addParameters(Name& name) const;
+
+private:
+  PartialName m_datasetName;
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a status/general dataset
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/ForwarderStatus#General-Status-Dataset
+ */
+class ForwarderGeneralStatusDataset : public StatusDataset
+{
+public:
+  ForwarderGeneralStatusDataset();
+
+  typedef ForwarderStatus ResultType;
+
+  ResultType
+  parseResult(ConstBufferPtr payload) const;
+};
+
+
+/**
+ * \ingroup management
+ * \brief provides common functionality among FaceDataset and FaceQueryDataset
+ */
+class FaceDatasetBase : public StatusDataset
+{
+public:
+  typedef std::vector<FaceStatus> ResultType;
+
+  ResultType
+  parseResult(ConstBufferPtr payload) const;
+
+protected:
+  explicit
+  FaceDatasetBase(const PartialName& datasetName);
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a faces/list dataset
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Face-Dataset
+ */
+class FaceDataset : public FaceDatasetBase
+{
+public:
+  FaceDataset();
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a faces/query dataset
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Query-Operation
+ */
+class FaceQueryDataset : public FaceDatasetBase
+{
+public:
+  typedef FaceQueryFilter ParamType;
+
+  explicit
+  FaceQueryDataset(const FaceQueryFilter& filter);
+
+private:
+  virtual void
+  addParameters(Name& name) const override;
+
+private:
+  FaceQueryFilter m_filter;
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a faces/channels dataset
+ * \sa https://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Channel-Dataset
+ */
+class ChannelDataset : public StatusDataset
+{
+public:
+  ChannelDataset();
+
+  typedef std::vector<ChannelStatus> ResultType;
+
+  ResultType
+  parseResult(ConstBufferPtr payload) const;
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a fib/list dataset
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#FIB-Dataset
+ */
+class FibDataset : public StatusDataset
+{
+public:
+  FibDataset();
+
+  typedef std::vector<FibEntry> ResultType;
+
+  ResultType
+  parseResult(ConstBufferPtr payload) const;
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a strategy-choice/list dataset
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice#Strategy-Choice-Dataset
+ */
+class StrategyChoiceDataset : public StatusDataset
+{
+public:
+  StrategyChoiceDataset();
+
+  typedef std::vector<StrategyChoice> ResultType;
+
+  ResultType
+  parseResult(ConstBufferPtr payload) const;
+};
+
+
+/**
+ * \ingroup management
+ * \brief represents a rib/list dataset
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#RIB-Dataset
+ */
+class RibDataset : public StatusDataset
+{
+public:
+  RibDataset();
+
+  typedef std::vector<RibEntry> ResultType;
+
+  ResultType
+  parseResult(ConstBufferPtr payload) const;
+};
+
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MGMT_NFD_STATUS_DATASET_HPP
diff --git a/src/mgmt/nfd/strategy-choice.cpp b/src/mgmt/nfd/strategy-choice.cpp
new file mode 100644
index 0000000..2e294d7
--- /dev/null
+++ b/src/mgmt/nfd/strategy-choice.cpp
@@ -0,0 +1,131 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "strategy-choice.hpp"
+#include "encoding/tlv-nfd.hpp"
+#include "encoding/block-helpers.hpp"
+#include "util/concepts.hpp"
+
+namespace ndn {
+namespace nfd {
+
+//BOOST_CONCEPT_ASSERT((boost::EqualityComparable<StrategyChoice>));
+BOOST_CONCEPT_ASSERT((WireEncodable<StrategyChoice>));
+BOOST_CONCEPT_ASSERT((WireDecodable<StrategyChoice>));
+static_assert(std::is_base_of<tlv::Error, StrategyChoice::Error>::value,
+              "StrategyChoice::Error must inherit from tlv::Error");
+
+StrategyChoice::StrategyChoice()
+{
+}
+
+StrategyChoice::StrategyChoice(const Block& payload)
+{
+  this->wireDecode(payload);
+}
+
+template<encoding::Tag TAG>
+size_t
+StrategyChoice::wireEncode(EncodingImpl<TAG>& encoder) const
+{
+  size_t totalLength = 0;
+
+  totalLength += prependNestedBlock(encoder, tlv::nfd::Strategy, m_strategy);
+  totalLength += m_name.wireEncode(encoder);
+
+  totalLength += encoder.prependVarNumber(totalLength);
+  totalLength += encoder.prependVarNumber(tlv::nfd::StrategyChoice);
+  return totalLength;
+}
+
+template size_t
+StrategyChoice::wireEncode<encoding::EncoderTag>(EncodingImpl<encoding::EncoderTag>&) const;
+
+template size_t
+StrategyChoice::wireEncode<encoding::EstimatorTag>(EncodingImpl<encoding::EstimatorTag>&) const;
+
+const Block&
+StrategyChoice::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+StrategyChoice::wireDecode(const Block& block)
+{
+  if (block.type() != tlv::nfd::StrategyChoice) {
+    BOOST_THROW_EXCEPTION(Error("expecting StrategyChoice block"));
+  }
+  m_wire = block;
+  m_wire.parse();
+  Block::element_const_iterator val = m_wire.elements_begin();
+
+  if (val != m_wire.elements_end() && val->type() == tlv::Name) {
+    m_name.wireDecode(*val);
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required Name field"));
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::Strategy) {
+    val->parse();
+    if (val->elements().empty()) {
+      BOOST_THROW_EXCEPTION(Error("expecting Strategy/Name"));
+    }
+    else {
+      m_strategy.wireDecode(*val->elements_begin());
+    }
+    ++val;
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("missing required Strategy field"));
+  }
+}
+
+StrategyChoice&
+StrategyChoice::setName(const Name& name)
+{
+  m_wire.reset();
+  m_name = name;
+  return *this;
+}
+
+StrategyChoice&
+StrategyChoice::setStrategy(const Name& strategy)
+{
+  m_wire.reset();
+  m_strategy = strategy;
+  return *this;
+}
+
+} // namespace nfd
+} // namespace ndn
diff --git a/src/mgmt/nfd/strategy-choice.hpp b/src/mgmt/nfd/strategy-choice.hpp
new file mode 100644
index 0000000..8cdaa59
--- /dev/null
+++ b/src/mgmt/nfd/strategy-choice.hpp
@@ -0,0 +1,93 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MGMT_NFD_STRATEGY_CHOICE_HPP
+#define NDN_MGMT_NFD_STRATEGY_CHOICE_HPP
+
+#include "../../encoding/block.hpp"
+#include "../../name.hpp"
+
+namespace ndn {
+namespace nfd {
+
+/**
+ * @ingroup management
+ * @brief represents NFD StrategyChoice dataset
+ * @sa http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice#Strategy-Choice-Dataset
+ */
+class StrategyChoice
+{
+public:
+  class Error : public tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : tlv::Error(what)
+    {
+    }
+  };
+
+  StrategyChoice();
+
+  explicit
+  StrategyChoice(const Block& payload);
+
+  template<encoding::Tag TAG>
+  size_t
+  wireEncode(EncodingImpl<TAG>& encoder) const;
+
+  const Block&
+  wireEncode() const;
+
+  void
+  wireDecode(const Block& wire);
+
+public: // getters & setters
+  const Name&
+  getName() const
+  {
+    return m_name;
+  }
+
+  StrategyChoice&
+  setName(const Name& name);
+
+  const Name&
+  getStrategy() const
+  {
+    return m_strategy;
+  }
+
+  StrategyChoice&
+  setStrategy(const Name& strategy);
+
+private:
+  Name m_name; // namespace
+  Name m_strategy; // strategy for the namespace
+
+  mutable Block m_wire;
+};
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MGMT_NFD_STRATEGY_CHOICE_HPP