face: use IncomingFaceId, NextHopFaceId, CachePolicy tags

This commit replaces all LocalControlHeader usages with these tags,
and deletes LocalFace.

This commit also does minor improvements in RIB test suites.

refs #3339

Change-Id: I14cbfc296a6723a5860bf8bd95d9804d3bac3da5
diff --git a/daemon/face/face.hpp b/daemon/face/face.hpp
index 25b7fdf..d964ef1 100644
--- a/daemon/face/face.hpp
+++ b/daemon/face/face.hpp
@@ -46,7 +46,7 @@
 
 /// identifies the InternalFace used in management
 const FaceId FACEID_INTERNAL_FACE = 1;
-/// identifies a packet comes from the ContentStore, in LocalControlHeader incomingFaceId
+/// identifies a packet comes from the ContentStore
 const FaceId FACEID_CONTENT_STORE = 254;
 /// identifies the NullFace that drops every packet
 const FaceId FACEID_NULL = 255;
diff --git a/daemon/face/generic-link-service.cpp b/daemon/face/generic-link-service.cpp
index d6d6c02..d1536ce 100644
--- a/daemon/face/generic-link-service.cpp
+++ b/daemon/face/generic-link-service.cpp
@@ -49,29 +49,31 @@
   , m_reassembler(m_options.reassemblerOptions, this)
   , m_lastSeqNo(-2)
 {
-  m_reassembler.beforeTimeout.connect(bind([this] { ++nReassemblyTimeouts; }));
+  m_reassembler.beforeTimeout.connect(bind([this] { ++this->nReassemblyTimeouts; }));
 }
 
 void
 GenericLinkService::doSendInterest(const Interest& interest)
 {
   lp::Packet lpPacket(interest.wireEncode());
+
   if (m_options.allowLocalFields) {
     encodeLocalFields(interest, lpPacket);
   }
 
-  sendNetPacket(std::move(lpPacket));
+  this->sendNetPacket(std::move(lpPacket));
 }
 
 void
 GenericLinkService::doSendData(const Data& data)
 {
   lp::Packet lpPacket(data.wireEncode());
+
   if (m_options.allowLocalFields) {
     encodeLocalFields(data, lpPacket);
   }
 
-  sendNetPacket(std::move(lpPacket));
+  this->sendNetPacket(std::move(lpPacket));
 }
 
 void
@@ -79,50 +81,21 @@
 {
   lp::Packet lpPacket(nack.getInterest().wireEncode());
   lpPacket.add<lp::NackField>(nack.getHeader());
+
   if (m_options.allowLocalFields) {
-    encodeLocalFields(nack.getInterest(), lpPacket);
+    encodeLocalFields(nack, lpPacket);
   }
 
-  sendNetPacket(std::move(lpPacket));
+  this->sendNetPacket(std::move(lpPacket));
 }
 
-bool
-GenericLinkService::encodeLocalFields(const Interest& interest, lp::Packet& lpPacket)
+void
+GenericLinkService::encodeLocalFields(const ndn::TagHost& netPkt, lp::Packet& lpPacket)
 {
-  if (interest.getLocalControlHeader().hasIncomingFaceId()) {
-    lpPacket.add<lp::IncomingFaceIdField>(interest.getIncomingFaceId());
+  shared_ptr<lp::IncomingFaceIdTag> incomingFaceIdTag = netPkt.getTag<lp::IncomingFaceIdTag>();
+  if (incomingFaceIdTag != nullptr) {
+    lpPacket.add<lp::IncomingFaceIdField>(*incomingFaceIdTag);
   }
-
-  if (interest.getLocalControlHeader().hasCachingPolicy()) {
-    // Packet must be dropped
-    return false;
-  }
-
-  return true;
-}
-
-bool
-GenericLinkService::encodeLocalFields(const Data& data, lp::Packet& lpPacket)
-{
-  if (data.getLocalControlHeader().hasIncomingFaceId()) {
-    lpPacket.add<lp::IncomingFaceIdField>(data.getIncomingFaceId());
-  }
-
-  if (data.getLocalControlHeader().hasCachingPolicy()) {
-    switch (data.getCachingPolicy()) {
-      case ndn::nfd::LocalControlHeader::CachingPolicy::NO_CACHE: {
-        lp::CachePolicy cachePolicy;
-        cachePolicy.setPolicy(lp::CachePolicyType::NO_CACHE);
-        lpPacket.add<lp::CachePolicyField>(cachePolicy);
-        break;
-      }
-      default: {
-        break;
-      }
-    }
-  }
-
-  return true;
 }
 
 void
@@ -135,7 +108,7 @@
     std::tie(isOk, frags) = m_fragmenter.fragmentPacket(pkt, mtu);
     if (!isOk) {
       // fragmentation failed (warning is logged by LpFragmenter)
-      ++nFragmentationErrors;
+      ++this->nFragmentationErrors;
       return;
     }
   }
@@ -145,7 +118,7 @@
 
   if (frags.size() > 1) {
     // sequence is needed only if packet is fragmented
-    assignSequences(frags);
+    this->assignSequences(frags);
   }
   else {
     // even if indexed fragmentation is enabled, the fragmenter should not
@@ -158,11 +131,11 @@
   for (const lp::Packet& frag : frags) {
     Transport::Packet tp(frag.wireEncode());
     if (mtu != MTU_UNLIMITED && tp.packet.size() > static_cast<size_t>(mtu)) {
-      ++nOutOverMtu;
+      ++this->nOutOverMtu;
       NFD_LOG_FACE_WARN("attempt to send packet over MTU limit");
       continue;
     }
-    sendPacket(std::move(tp));
+    this->sendPacket(std::move(tp));
   }
 }
 
@@ -249,7 +222,7 @@
 
   if (firstPkt.has<lp::NextHopFaceIdField>()) {
     if (m_options.allowLocalFields) {
-      interest->setNextHopFaceId(firstPkt.get<lp::NextHopFaceIdField>());
+      interest->setTag(make_shared<lp::NextHopFaceIdTag>(firstPkt.get<lp::NextHopFaceIdField>()));
     }
     else {
       NFD_LOG_FACE_WARN("received NextHopFaceId, but local fields disabled: DROP");
@@ -292,16 +265,9 @@
 
   if (firstPkt.has<lp::CachePolicyField>()) {
     if (m_options.allowLocalFields) {
-      lp::CachePolicyType policy = firstPkt.get<lp::CachePolicyField>().getPolicy();
-      switch (policy) {
-        case lp::CachePolicyType::NO_CACHE:
-          data->setCachingPolicy(ndn::nfd::LocalControlHeader::CachingPolicy::NO_CACHE);
-          break;
-        default:
-          ++this->nInNetInvalid;
-          NFD_LOG_FACE_WARN("unrecognized CachePolicyType " << policy << ": DROP");
-          return;
-      }
+      // In case of an invalid CachePolicyType, get<lp::CachePolicyField> will throw,
+      // so it's unnecessary to check here.
+      data->setTag(make_shared<lp::CachePolicyTag>(firstPkt.get<lp::CachePolicyField>()));
     }
     else {
       NFD_LOG_FACE_WARN("received CachePolicy, but local fields disabled: IGNORE");
diff --git a/daemon/face/generic-link-service.hpp b/daemon/face/generic-link-service.hpp
index d7cc22f..8afc693 100644
--- a/daemon/face/generic-link-service.hpp
+++ b/daemon/face/generic-link-service.hpp
@@ -130,33 +130,28 @@
   getCounters() const DECL_OVERRIDE;
 
 private: // send path
-  /** \brief sends Interest
+  /** \brief send Interest
    */
   void
   doSendInterest(const Interest& interest) DECL_OVERRIDE;
 
-  /** \brief sends Data
+  /** \brief send Data
    */
   void
   doSendData(const Data& data) DECL_OVERRIDE;
 
-  /** \brief sends Nack
-   *  This class does not send out a Nack.
+  /** \brief send Nack
    */
   void
   doSendNack(const ndn::lp::Nack& nack) DECL_OVERRIDE;
 
-  /** \brief encode IncomingFaceId into LpPacket and verify local fields
+  /** \brief encode local fields from tags onto outgoing LpPacket
+   *  \param pkt LpPacket containing a complete network layer packet
    */
-  static bool
-  encodeLocalFields(const Interest& interest, lp::Packet& lpPacket);
+  static void
+  encodeLocalFields(const ndn::TagHost& netPkt, lp::Packet& lpPacket);
 
-  /** \brief encode CachingPolicy and IncomingFaceId into LpPacket and verify local fields
-   */
-  static bool
-  encodeLocalFields(const Data& data, lp::Packet& lpPacket);
-
-  /** \brief encode and send a complete network layer packet
+  /** \brief send a complete network layer packet
    *  \param pkt LpPacket containing a complete network layer packet
    */
   void
diff --git a/daemon/face/local-face.hpp b/daemon/face/local-face.hpp
deleted file mode 100644
index f13cf9b..0000000
--- a/daemon/face/local-face.hpp
+++ /dev/null
@@ -1,219 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2015,  Regents of the University of California,
- *                           Arizona Board of Regents,
- *                           Colorado State University,
- *                           University Pierre & Marie Curie, Sorbonne University,
- *                           Washington University in St. Louis,
- *                           Beijing Institute of Technology,
- *                           The University of Memphis.
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon).
- * See AUTHORS.md for complete list of NFD authors and contributors.
- *
- * NFD is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef NFD_DAEMON_FACE_LOCAL_FACE_HPP
-#define NFD_DAEMON_FACE_LOCAL_FACE_HPP
-
-#include "face.hpp"
-#include <ndn-cxx/management/nfd-control-parameters.hpp>
-
-namespace nfd {
-
-using ndn::nfd::LocalControlFeature;
-using ndn::nfd::LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID;
-using ndn::nfd::LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID;
-
-/** \brief represents a face
- */
-class LocalFace : public Face
-{
-public:
-  LocalFace(const FaceUri& remoteUri, const FaceUri& localUri);
-
-  /** \brief get whether any LocalControlHeader feature is enabled
-   *
-   * \returns true if any feature is enabled.
-   */
-  bool
-  isLocalControlHeaderEnabled() const;
-
-  /** \brief get whether a specific LocalControlHeader feature is enabled
-   *
-   *  \param feature The feature.
-   *  \returns true if the specified feature is enabled.
-   */
-  bool
-  isLocalControlHeaderEnabled(LocalControlFeature feature) const;
-
-  /** \brief enable or disable a LocalControlHeader feature
-   *
-   *  \param feature The feature. Cannot be LOCAL_CONTROL_FEATURE_ANY
-   *                                     or LOCAL_CONTROL_FEATURE_MAX
-   *  \param enabled true/false enable/disable the feature
-   */
-  void
-  setLocalControlHeaderFeature(LocalControlFeature feature, bool enabled = true);
-
-public:
-
-  static const size_t LOCAL_CONTROL_FEATURE_MAX = 3; /// upper bound of LocalControlFeature enum
-  static const size_t LOCAL_CONTROL_FEATURE_ANY = 0; /// any feature
-
-protected:
-  // statically overridden from Face
-
-  /** \brief Decode block into Interest/Data, considering potential LocalControlHeader
-   *
-   *  If LocalControlHeader is present, the encoded data is filtered out, based
-   *  on enabled features on the face.
-   */
-  bool
-  decodeAndDispatchInput(const Block& element);
-
-  // LocalFace-specific methods
-
-  /** \brief Check if LocalControlHeader needs to be included, taking into account
-   *         both set parameters in supplied LocalControlHeader and features
-   *         enabled on the local face.
-   */
-  bool
-  isEmptyFilteredLocalControlHeader(const ndn::nfd::LocalControlHeader& header) const;
-
-  /** \brief Create LocalControlHeader, considering enabled features
-   */
-  template<class Packet>
-  Block
-  filterAndEncodeLocalControlHeader(const Packet& packet);
-
-private:
-  std::vector<bool> m_localControlHeaderFeatures;
-};
-
-inline
-LocalFace::LocalFace(const FaceUri& remoteUri, const FaceUri& localUri)
-  : Face(remoteUri, localUri, true)
-  , m_localControlHeaderFeatures(LocalFace::LOCAL_CONTROL_FEATURE_MAX)
-{
-}
-
-inline bool
-LocalFace::isLocalControlHeaderEnabled() const
-{
-  return m_localControlHeaderFeatures[LOCAL_CONTROL_FEATURE_ANY];
-}
-
-inline bool
-LocalFace::isLocalControlHeaderEnabled(LocalControlFeature feature) const
-{
-  BOOST_ASSERT(0 < feature &&
-               static_cast<size_t>(feature) < m_localControlHeaderFeatures.size());
-  return m_localControlHeaderFeatures[feature];
-}
-
-inline void
-LocalFace::setLocalControlHeaderFeature(LocalControlFeature feature, bool enabled/* = true*/)
-{
-  BOOST_ASSERT(0 < feature &&
-               static_cast<size_t>(feature) < m_localControlHeaderFeatures.size());
-
-  m_localControlHeaderFeatures[feature] = enabled;
-
-  m_localControlHeaderFeatures[LOCAL_CONTROL_FEATURE_ANY] =
-    std::find(m_localControlHeaderFeatures.begin() + 1,
-              m_localControlHeaderFeatures.end(), true) <
-              m_localControlHeaderFeatures.end();
-  // 'find(..) < .end()' instead of 'find(..) != .end()' due to LLVM Bug 16816
-}
-
-inline bool
-LocalFace::decodeAndDispatchInput(const Block& element)
-{
-  try {
-    const Block& payload = ndn::nfd::LocalControlHeader::getPayload(element);
-
-    // If received LocalControlHeader, but it is not enabled on the face
-    if ((&payload != &element) && !this->isLocalControlHeaderEnabled())
-      return false;
-
-    if (payload.type() == tlv::Interest)
-      {
-        shared_ptr<Interest> i = make_shared<Interest>();
-        i->wireDecode(payload);
-        if (&payload != &element)
-          {
-            uint8_t mask = 0;
-            if (this->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID)) {
-              mask |= ndn::nfd::LocalControlHeader::ENCODE_NEXT_HOP;
-            }
-            i->getLocalControlHeader().wireDecode(element, mask);
-          }
-
-        this->emitSignal(onReceiveInterest, *i);
-      }
-    else if (payload.type() == tlv::Data)
-      {
-        shared_ptr<Data> d = make_shared<Data>();
-        d->wireDecode(payload);
-
-        /// \todo Uncomment and correct the following when we have more
-        ///       options in LocalControlHeader that apply for incoming
-        ///       Data packets (if ever)
-        // if (&payload != &element)
-        //   {
-        //
-        //     d->getLocalControlHeader().wireDecode(element,
-        //       false,
-        //       false);
-        //   }
-
-        this->emitSignal(onReceiveData, *d);
-      }
-    else
-      return false;
-
-    return true;
-  }
-  catch (const tlv::Error&) {
-    return false;
-  }
-}
-
-inline bool
-LocalFace::isEmptyFilteredLocalControlHeader(const ndn::nfd::LocalControlHeader& header) const
-{
-  if (!this->isLocalControlHeaderEnabled())
-    return true;
-
-  uint8_t mask = 0;
-  if (this->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID)) {
-    mask |= ndn::nfd::LocalControlHeader::ENCODE_INCOMING_FACE_ID;
-  }
-  return header.empty(mask);
-}
-
-template<class Packet>
-inline Block
-LocalFace::filterAndEncodeLocalControlHeader(const Packet& packet)
-{
-  uint8_t mask = 0;
-  if (this->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID)) {
-    mask |= ndn::nfd::LocalControlHeader::ENCODE_INCOMING_FACE_ID;
-  }
-  return packet.getLocalControlHeader().wireEncode(packet, mask);
-}
-
-} // namespace nfd
-
-#endif // NFD_DAEMON_FACE_LOCAL_FACE_HPP
diff --git a/daemon/face/lp-face.hpp b/daemon/face/lp-face.hpp
index 17746f2..130fe00 100644
--- a/daemon/face/lp-face.hpp
+++ b/daemon/face/lp-face.hpp
@@ -42,7 +42,7 @@
 const FaceId INVALID_FACEID = 0;
 /// identifies the InternalFace used in management
 const FaceId FACEID_INTERNAL_FACE = 1;
-/// identifies a packet comes from the ContentStore, in LocalControlHeader incomingFaceId
+/// identifies a packet comes from the ContentStore
 const FaceId FACEID_CONTENT_STORE = 254;
 /// identifies the NullFace that drops every packet
 const FaceId FACEID_NULL = 255;
diff --git a/daemon/face/protocol-factory.hpp b/daemon/face/protocol-factory.hpp
index 31efe8b..6af5bb8 100644
--- a/daemon/face/protocol-factory.hpp
+++ b/daemon/face/protocol-factory.hpp
@@ -27,6 +27,7 @@
 #define NFD_DAEMON_FACE_PROTOCOL_FACTORY_HPP
 
 #include "channel.hpp"
+#include <ndn-cxx/encoding/nfd-constants.hpp>
 
 namespace nfd {
 
diff --git a/daemon/face/transport.hpp b/daemon/face/transport.hpp
index 8735de6..626809c 100644
--- a/daemon/face/transport.hpp
+++ b/daemon/face/transport.hpp
@@ -28,6 +28,7 @@
 
 #include "core/counter.hpp"
 #include "face-log.hpp"
+#include <ndn-cxx/encoding/nfd-constants.hpp>
 
 namespace nfd {
 namespace face {
diff --git a/daemon/fw/client-control-strategy.cpp b/daemon/fw/client-control-strategy.cpp
index f65cf69..7cc07fd 100644
--- a/daemon/fw/client-control-strategy.cpp
+++ b/daemon/fw/client-control-strategy.cpp
@@ -50,14 +50,13 @@
                                             shared_ptr<fib::Entry> fibEntry,
                                             shared_ptr<pit::Entry> pitEntry)
 {
-  // Strategy needn't check whether LocalControlHeader-NextHopFaceId is enabled.
-  // LocalFace does this check.
-  if (!interest.getLocalControlHeader().hasNextHopFaceId()) {
+  shared_ptr<lp::NextHopFaceIdTag> tag = interest.getTag<lp::NextHopFaceIdTag>();
+  if (tag == nullptr) {
     this->BestRouteStrategy::afterReceiveInterest(inFace, interest, fibEntry, pitEntry);
     return;
   }
 
-  FaceId outFaceId = static_cast<FaceId>(interest.getNextHopFaceId());
+  FaceId outFaceId = static_cast<FaceId>(*tag);
   shared_ptr<Face> outFace = this->getFace(outFaceId);
   if (!static_cast<bool>(outFace)) {
     // If outFace doesn't exist, it's better to reject the Interest
diff --git a/daemon/fw/client-control-strategy.hpp b/daemon/fw/client-control-strategy.hpp
index a0ddbf2..85a6e3b 100644
--- a/daemon/fw/client-control-strategy.hpp
+++ b/daemon/fw/client-control-strategy.hpp
@@ -31,8 +31,11 @@
 namespace nfd {
 namespace fw {
 
-/** \brief a forwarding strategy that forwards Interests
- *         according to NextHopFaceId field in LocalControlHeader
+/** \brief a forwarding strategy controlled by client application
+ *
+ *  The consumer may specify the nexthop for an Interest in NDNLPv2 NextHopFaceId field.
+ *  If NextHopFaceId field is omitted, the Interest is forwarded to
+ *  the FIB nexthop with lowest routing cost.
  */
 class ClientControlStrategy : public BestRouteStrategy
 {
diff --git a/daemon/fw/forwarder.cpp b/daemon/fw/forwarder.cpp
index b2dc6bb..e96b87b 100644
--- a/daemon/fw/forwarder.cpp
+++ b/daemon/fw/forwarder.cpp
@@ -104,7 +104,7 @@
   // receive Interest
   NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId() <<
                 " interest=" << interest.getName());
-  const_cast<Interest&>(interest).setIncomingFaceId(inFace.getId());
+  interest.setTag(make_shared<lp::IncomingFaceIdTag>(inFace.getId()));
   ++m_counters.nInInterests;
 
   // /localhost scope control
@@ -244,7 +244,7 @@
 {
   NFD_LOG_DEBUG("onContentStoreHit interest=" << interest.getName());
 
-  const_pointer_cast<Data>(data.shared_from_this())->setIncomingFaceId(FACEID_CONTENT_STORE);
+  data.setTag(make_shared<lp::IncomingFaceIdTag>(FACEID_CONTENT_STORE));
   // XXX should we lookup PIT for other Interests that also match csMatch?
 
   // set PIT straggler timer
@@ -370,7 +370,7 @@
 {
   // receive Data
   NFD_LOG_DEBUG("onIncomingData face=" << inFace.getId() << " data=" << data.getName());
-  const_cast<Data&>(data).setIncomingFaceId(inFace.getId());
+  data.setTag(make_shared<lp::IncomingFaceIdTag>(inFace.getId()));
   ++m_counters.nInData;
 
   // /localhost scope control
@@ -479,6 +479,8 @@
 void
 Forwarder::onIncomingNack(Face& inFace, const lp::Nack& nack)
 {
+  // receive Nack
+  nack.setTag(make_shared<lp::IncomingFaceIdTag>(inFace.getId()));
   ++m_counters.nInNacks;
 
   // if multi-access face, drop
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index 1bc1701..d773206 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -178,11 +178,6 @@
     return;
   }
 
-  if (result.face) {
-    result.face->setLocalControlHeaderFeature(result.feature, true);
-    return done(ControlResponse(200, "OK").setBody(parameters.wireEncode()));
-  }
-
   // TODO#3226 redesign enable-local-control
   // For now, enable-local-control will enable all local fields in GenericLinkService.
   BOOST_ASSERT(result.lpFace != nullptr);
@@ -209,11 +204,6 @@
     return;
   }
 
-  if (result.face) {
-    result.face->setLocalControlHeaderFeature(result.feature, false);
-    return done(ControlResponse(200, "OK").setBody(parameters.wireEncode()));
-  }
-
   // TODO#3226 redesign disable-local-control
   // For now, disable-local-control will disable all local fields in GenericLinkService.
   BOOST_ASSERT(result.lpFace != nullptr);
@@ -239,9 +229,15 @@
   result.isValid = false;
   result.lpFace = nullptr;
 
-  auto face = m_faceTable.get(request.getIncomingFaceId());
+  shared_ptr<lp::IncomingFaceIdTag> incomingFaceIdTag = request.getTag<lp::IncomingFaceIdTag>();
+  // NDNLPv2 says "application MUST be prepared to receive a packet without IncomingFaceId field",
+  // but it's fine to assert IncomingFaceId is available, because InternalFace lives inside NFD
+  // and is initialized synchronously with IncomingFaceId field enabled.
+  BOOST_ASSERT(incomingFaceIdTag != nullptr);
+
+  auto face = m_faceTable.get(*incomingFaceIdTag);
   if (face == nullptr) {
-    NFD_LOG_DEBUG("FaceId " << request.getIncomingFaceId() << " not found");
+    NFD_LOG_DEBUG("FaceId " << *incomingFaceIdTag << " not found");
     done(ControlResponse(410, "Face not found"));
     return result;
   }
@@ -253,13 +249,10 @@
   }
 
   result.isValid = true;
-  result.face = dynamic_pointer_cast<LocalFace>(face);
-  if (result.face == nullptr) {
-    auto lpFaceW = dynamic_pointer_cast<face::LpFaceWrapper>(face);
-    BOOST_ASSERT(lpFaceW != nullptr);
-    result.lpFace = lpFaceW->getLpFace();
-  }
-  result.feature = parameters.getLocalControlFeature();
+  auto lpFaceW = dynamic_pointer_cast<face::LpFaceWrapper>(face);
+  // LocalFace is gone, so a face that is local must be an LpFace.
+  BOOST_ASSERT(lpFaceW != nullptr);
+  result.lpFace = lpFaceW->getLpFace();
 
   return result;
 }
diff --git a/daemon/mgmt/face-manager.hpp b/daemon/mgmt/face-manager.hpp
index da217b2..8e90a73 100644
--- a/daemon/mgmt/face-manager.hpp
+++ b/daemon/mgmt/face-manager.hpp
@@ -27,11 +27,12 @@
 #define NFD_DAEMON_MGMT_FACE_MANAGER_HPP
 
 #include "manager-base.hpp"
-#include "face/local-face.hpp"
+#include <ndn-cxx/management/nfd-face-status.hpp>
 #include <ndn-cxx/management/nfd-face-query-filter.hpp>
 
 namespace nfd {
 
+class Face;
 class FaceTable;
 class NetworkInterfaceInfo;
 class ProtocolFactory;
@@ -91,9 +92,7 @@
   struct ExtractLocalControlParametersResult
   {
     bool isValid;
-    shared_ptr<LocalFace> face;
     face::LpFace* lpFace;
-    LocalControlFeature feature;
   };
 
   ExtractLocalControlParametersResult
diff --git a/daemon/mgmt/fib-manager.cpp b/daemon/mgmt/fib-manager.cpp
index 12374f1..9882033 100644
--- a/daemon/mgmt/fib-manager.cpp
+++ b/daemon/mgmt/fib-manager.cpp
@@ -142,7 +142,12 @@
 {
   bool isSelfRegistration = (parameters.getFaceId() == 0);
   if (isSelfRegistration) {
-    parameters.setFaceId(request.getIncomingFaceId());
+    shared_ptr<lp::IncomingFaceIdTag> incomingFaceIdTag = request.getTag<lp::IncomingFaceIdTag>();
+    // NDNLPv2 says "application MUST be prepared to receive a packet without IncomingFaceId field",
+    // but it's fine to assert IncomingFaceId is available, because InternalFace lives inside NFD
+    // and is initialized synchronously with IncomingFaceId field enabled.
+    BOOST_ASSERT(incomingFaceIdTag != nullptr);
+    parameters.setFaceId(*incomingFaceIdTag);
   }
 }
 
diff --git a/daemon/table/cs.cpp b/daemon/table/cs.cpp
index 674d333..4550361 100644
--- a/daemon/table/cs.cpp
+++ b/daemon/table/cs.cpp
@@ -83,12 +83,11 @@
 {
   NFD_LOG_DEBUG("insert " << data.getName());
 
-  // recognize CachingPolicy
-  using ndn::nfd::LocalControlHeader;
-  const LocalControlHeader& lch = data.getLocalControlHeader();
-  if (lch.hasCachingPolicy()) {
-    LocalControlHeader::CachingPolicy policy = lch.getCachingPolicy();
-    if (policy == LocalControlHeader::CachingPolicy::NO_CACHE) {
+  // recognize CachePolicy
+  shared_ptr<lp::CachePolicyTag> tag = data.getTag<lp::CachePolicyTag>();
+  if (tag != nullptr) {
+    lp::CachePolicyType policy = tag->get().getPolicy();
+    if (policy == lp::CachePolicyType::NO_CACHE) {
       return false;
     }
   }
diff --git a/rib/rib-manager.cpp b/rib/rib-manager.cpp
index 33d1622..03faf21 100644
--- a/rib/rib-manager.cpp
+++ b/rib/rib-manager.cpp
@@ -280,7 +280,13 @@
 
   bool isSelfRegistration = (!parameters.hasFaceId() || parameters.getFaceId() == 0);
   if (isSelfRegistration) {
-    parameters.setFaceId(request->getIncomingFaceId());
+    shared_ptr<lp::IncomingFaceIdTag> incomingFaceIdTag = request->getTag<lp::IncomingFaceIdTag>();
+    if (incomingFaceIdTag == nullptr) {
+      sendResponse(request->getName(), 503,
+                   "requested self-registration, but IncomingFaceId is unavailable");
+      return;
+    }
+    parameters.setFaceId(*incomingFaceIdTag);
   }
 
   // Respond since command is valid and authorized
@@ -358,7 +364,13 @@
 
   bool isSelfRegistration = (!parameters.hasFaceId() || parameters.getFaceId() == 0);
   if (isSelfRegistration) {
-    parameters.setFaceId(request->getIncomingFaceId());
+    shared_ptr<lp::IncomingFaceIdTag> incomingFaceIdTag = request->getTag<lp::IncomingFaceIdTag>();
+    if (incomingFaceIdTag == nullptr) {
+      sendResponse(request->getName(), 503,
+                   "requested self-registration, but IncomingFaceId is unavailable");
+      return;
+    }
+    parameters.setFaceId(*incomingFaceIdTag);
   }
 
   // Respond since command is valid and authorized
diff --git a/rib/route.hpp b/rib/route.hpp
index d6a8b6e..9e0935a 100644
--- a/rib/route.hpp
+++ b/rib/route.hpp
@@ -28,6 +28,7 @@
 
 #include "common.hpp"
 #include "core/scheduler.hpp"
+#include <ndn-cxx/encoding/nfd-constants.hpp>
 
 namespace nfd {
 namespace rib {
diff --git a/tests/daemon/face/dummy-face.hpp b/tests/daemon/face/dummy-face.hpp
index 2991afb..6ed7d46 100644
--- a/tests/daemon/face/dummy-face.hpp
+++ b/tests/daemon/face/dummy-face.hpp
@@ -27,25 +27,19 @@
 #define NFD_TESTS_DAEMON_FACE_DUMMY_FACE_HPP
 
 #include "face/face.hpp"
-#include "face/local-face.hpp"
 
 namespace nfd {
 namespace tests {
 
-/** \class DummyFace
- *  \brief a Face for unit testing
+/** \brief a Face for unit testing
  */
-template<class FaceBase>
-class DummyFaceImpl : public FaceBase
+class DummyFace : public Face
 {
 public:
-  DummyFaceImpl()
-    : FaceBase(FaceUri("dummy://"), FaceUri("dummy://"))
-  {
-  }
-
-  DummyFaceImpl(const std::string& remoteUri, const std::string& localUri)
-    : FaceBase(FaceUri(remoteUri), FaceUri(localUri))
+  explicit
+  DummyFace(const std::string& remoteUri = "dummy://", const std::string& localUri = "dummy://",
+            bool isLocal = false)
+    : Face(FaceUri(remoteUri), FaceUri(localUri), isLocal)
   {
   }
 
@@ -83,15 +77,22 @@
     this->emitSignal(onReceiveData, data);
   }
 
-  signal::Signal<DummyFaceImpl<FaceBase>> afterSend;
+  signal::Signal<DummyFace> afterSend;
 
 public:
   std::vector<Interest> m_sentInterests;
   std::vector<Data> m_sentDatas;
 };
 
-typedef DummyFaceImpl<Face> DummyFace;
-typedef DummyFaceImpl<LocalFace> DummyLocalFace;
+class DummyLocalFace : public DummyFace
+{
+public:
+  explicit
+  DummyLocalFace(const std::string& remoteUri = "dummy://", const std::string& localUri = "dummy://")
+    : DummyFace(remoteUri, localUri, true)
+  {
+  }
+};
 
 } // namespace tests
 } // namespace nfd
diff --git a/tests/daemon/face/face.t.cpp b/tests/daemon/face/face.t.cpp
index 6ed17bf..4a94a58 100644
--- a/tests/daemon/face/face.t.cpp
+++ b/tests/daemon/face/face.t.cpp
@@ -24,7 +24,6 @@
  */
 
 #include "face/face.hpp"
-#include "face/local-face.hpp"
 #include "dummy-face.hpp"
 
 #include "tests/test-common.hpp"
@@ -41,24 +40,6 @@
   BOOST_CHECK_EQUAL(face.getDescription(), "3pFsKrvWr");
 }
 
-BOOST_AUTO_TEST_CASE(LocalControlHeaderEnabled)
-{
-  DummyLocalFace face;
-
-  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(), false);
-
-  face.setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID, true);
-  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(), true);
-  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID), true);
-  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(
-                         LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID), false);
-
-  face.setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID, false);
-  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(), false);
-  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(
-                         LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID), false);
-}
-
 class FaceFailTestFace : public DummyFace
 {
 public:
diff --git a/tests/daemon/face/generic-link-service.t.cpp b/tests/daemon/face/generic-link-service.t.cpp
index f3c3c7b..70e078d 100644
--- a/tests/daemon/face/generic-link-service.t.cpp
+++ b/tests/daemon/face/generic-link-service.t.cpp
@@ -407,8 +407,9 @@
   transport->receivePacket(packet.wireEncode());
 
   BOOST_REQUIRE_EQUAL(receivedInterests.size(), 1);
-  BOOST_REQUIRE(receivedInterests.back().getLocalControlHeader().hasNextHopFaceId());
-  BOOST_CHECK_EQUAL(receivedInterests.back().getNextHopFaceId(), 1000);
+  shared_ptr<lp::NextHopFaceIdTag> tag = receivedInterests.back().getTag<lp::NextHopFaceIdTag>();
+  BOOST_REQUIRE(tag != nullptr);
+  BOOST_CHECK_EQUAL(*tag, 1000);
 }
 
 BOOST_AUTO_TEST_CASE(ReceiveNextHopFaceIdDisabled)
@@ -474,16 +475,14 @@
 
   shared_ptr<Data> data = makeData("/12345678");
   lp::Packet packet(data->wireEncode());
-  lp::CachePolicy policy;
-  policy.setPolicy(lp::CachePolicyType::NO_CACHE);
-  packet.set<lp::CachePolicyField>(policy);
+  packet.set<lp::CachePolicyField>(lp::CachePolicy().setPolicy(lp::CachePolicyType::NO_CACHE));
 
   transport->receivePacket(packet.wireEncode());
 
   BOOST_REQUIRE_EQUAL(receivedData.size(), 1);
-  BOOST_REQUIRE(receivedData.back().getLocalControlHeader().hasCachingPolicy());
-  BOOST_CHECK_EQUAL(receivedData.back().getCachingPolicy(),
-                    ndn::nfd::LocalControlHeader::CachingPolicy::NO_CACHE);
+  shared_ptr<lp::CachePolicyTag> tag = receivedData.back().getTag<lp::CachePolicyTag>();
+  BOOST_REQUIRE(tag != nullptr);
+  BOOST_CHECK_EQUAL(tag->get().getPolicy(), lp::CachePolicyType::NO_CACHE);
 }
 
 BOOST_AUTO_TEST_CASE(ReceiveCacheControlDisabled)
@@ -503,7 +502,7 @@
 
   BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 0); // not an error
   BOOST_REQUIRE_EQUAL(receivedData.size(), 1);
-  BOOST_CHECK(!receivedData.back().getLocalControlHeader().hasCachingPolicy());
+  BOOST_CHECK(receivedData.back().getTag<lp::CachePolicyTag>() == nullptr);
 }
 
 BOOST_AUTO_TEST_CASE(ReceiveCacheControlDropInterest)
@@ -553,7 +552,7 @@
   initialize(options);
 
   shared_ptr<Interest> interest = makeInterest("/12345678");
-  interest->setIncomingFaceId(1000);
+  interest->setTag(make_shared<lp::IncomingFaceIdTag>(1000));
 
   face->sendInterest(*interest);
 
@@ -571,7 +570,7 @@
   initialize(options);
 
   shared_ptr<Interest> interest = makeInterest("/12345678");
-  interest->setIncomingFaceId(1000);
+  interest->setTag(make_shared<lp::IncomingFaceIdTag>(1000));
 
   face->sendInterest(*interest);
 
@@ -595,7 +594,7 @@
 
   BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 0); // not an error
   BOOST_REQUIRE_EQUAL(receivedInterests.size(), 1);
-  BOOST_CHECK(!receivedInterests.back().getLocalControlHeader().hasIncomingFaceId());
+  BOOST_CHECK(receivedInterests.back().getTag<lp::IncomingFaceIdTag>() == nullptr);
 }
 
 BOOST_AUTO_TEST_CASE(ReceiveIncomingFaceIdIgnoreData)
@@ -613,7 +612,7 @@
 
   BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 0); // not an error
   BOOST_REQUIRE_EQUAL(receivedData.size(), 1);
-  BOOST_CHECK(!receivedData.back().getLocalControlHeader().hasIncomingFaceId());
+  BOOST_CHECK(receivedData.back().getTag<lp::IncomingFaceIdTag>() == nullptr);
 }
 
 BOOST_AUTO_TEST_CASE(ReceiveIncomingFaceIdIgnoreNack)
@@ -632,7 +631,7 @@
 
   BOOST_CHECK_EQUAL(service->getCounters().nInNetInvalid, 0); // not an error
   BOOST_REQUIRE_EQUAL(receivedNacks.size(), 1);
-  BOOST_CHECK(!receivedNacks.back().getLocalControlHeader().hasIncomingFaceId());
+  BOOST_CHECK(receivedNacks.back().getTag<lp::IncomingFaceIdTag>() == nullptr);
 }
 
 BOOST_AUTO_TEST_SUITE_END() // LocalFields
diff --git a/tests/daemon/face/unix-stream.t.cpp b/tests/daemon/face/unix-stream.t.cpp
index 47610af..fc528c2 100644
--- a/tests/daemon/face/unix-stream.t.cpp
+++ b/tests/daemon/face/unix-stream.t.cpp
@@ -340,187 +340,6 @@
   BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1->getName());
 }
 
-//BOOST_FIXTURE_TEST_CASE(UnixStreamTransportLocalControlHeader, EndToEndFixture)
-//{
-//  UnixStreamFactory factory;
-//
-//  shared_ptr<UnixStreamChannel> channel1 = factory.createChannel(CHANNEL_PATH1);
-//  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
-//                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
-//
-//  UnixStreamTransport::protocol::socket client(g_io);
-//  client.async_connect(UnixStreamTransport::protocol::endpoint(CHANNEL_PATH1),
-//                       bind(&EndToEndFixture::client_onConnect, this, _1));
-//
-//  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS, "Connect");
-//
-//  BOOST_REQUIRE(static_cast<bool>(face1));
-//
-//  face2 = makeFace(std::move(client));
-//  face2->onReceiveInterest.connect(bind(&EndToEndFixture::face2_onReceiveInterest, this, _1));
-//  face2->onReceiveData.connect(bind(&EndToEndFixture::face2_onReceiveData, this, _1));
-//
-//  shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
-//  shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
-
-//  face1->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
-//  face1->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
-//
-//  BOOST_CHECK(face1->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
-//  BOOST_CHECK(face1->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
-
-//  face2->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
-//  face2->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
-
-//  BOOST_CHECK(face2->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
-//  BOOST_CHECK(face2->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
-
-  ////////////////////////////////////////////////////////
-
-//  interest1->setIncomingFaceId(11);
-//  interest1->setNextHopFaceId(111);
-//  face1->sendInterest(*interest1);
-
-//  data1->setIncomingFaceId(22);
-//  data1->getLocalControlHeader().setNextHopFaceId(222);
-//  face1->sendData(*data1);
-
-//  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
-//                      "Regular send/receive");
-
-//  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
-//  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
-
-//  sending allows only IncomingFaceId, receiving allows only NextHopFaceId
-//  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getLocalControlHeader().hasIncomingFaceId(), false);
-//  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getLocalControlHeader().hasNextHopFaceId(), false);
-
-//  BOOST_CHECK_EQUAL(face2_receivedDatas[0].getLocalControlHeader().hasIncomingFaceId(), false);
-//  BOOST_CHECK_EQUAL(face2_receivedDatas[0].getLocalControlHeader().hasNextHopFaceId(), false);
-
-//  face1->close();
-//  face1.reset();
-
-  ////////////////////////////////////////////////////////
-
-//  client.async_connect(UnixStreamTransport::protocol::endpoint(CHANNEL_PATH1),
-//                       bind(&EndToEndFixture::client_onConnect, this, _1));
-
-//  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS, "Connect");
-
-//  BOOST_REQUIRE(static_cast<bool>(face1));
-//  face1->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
-//  face1->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
-
-//  Block iHeader = interest1->getLocalControlHeader()
-//                  .wireEncode(*interest1, ndn::nfd::LocalControlHeader::ENCODE_INCOMING_FACE_ID |
-//                                          ndn::nfd::LocalControlHeader::ENCODE_NEXT_HOP);
-//  Block iPayload = interest1->wireEncode();
-
-//  Block dHeader = data1->getLocalControlHeader()
-//                  .wireEncode(*data1, ndn::nfd::LocalControlHeader::ENCODE_INCOMING_FACE_ID |
-//                                      ndn::nfd::LocalControlHeader::ENCODE_NEXT_HOP);
-//  Block dPayload = data1->wireEncode();
-
-//  client.async_send(std::vector<boost::asio::const_buffer>{iHeader, iPayload},
-//                    [] (const boost::system::error_code& error, size_t nBytesSent) {
-//                      BOOST_CHECK_MESSAGE(!error, error.message());
-//                    });
-//  client.async_send(std::vector<boost::asio::const_buffer>{dHeader, dPayload},
-//                    [] (const boost::system::error_code& error, size_t nBytesSent) {
-//                      BOOST_CHECK_MESSAGE(!error, error.message());
-//                    });
-
-//  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
-//                      "Send/receive with LocalControlHeader");
-
-//  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
-//  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 1);
-
-//  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getLocalControlHeader().hasIncomingFaceId(), false);
-//  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getLocalControlHeader().hasNextHopFaceId(), true);
-//  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getNextHopFaceId(), 111);
-
-//  BOOST_CHECK_EQUAL(face1_receivedDatas[0].getLocalControlHeader().hasIncomingFaceId(), false);
-//  BOOST_CHECK_EQUAL(face1_receivedDatas[0].getLocalControlHeader().hasNextHopFaceId(), false);
-//}
-
-
-//class SimpleEndToEndFixture : protected BaseFixture
-//{
-//public:
-//  void
-//  onFaceCreated(const shared_ptr<Face>& face)
-//  {
-//    face->onReceiveInterest.connect(bind(&SimpleEndToEndFixture::onReceiveInterest, this, _1));
-//    face->onReceiveData.connect(bind(&SimpleEndToEndFixture::onReceiveData, this, _1));
-//    face->onFail.connect(bind(&SimpleEndToEndFixture::onFail, this, face));
-
-//    if (static_cast<bool>(dynamic_pointer_cast<LocalFace>(face))) {
-//    static_pointer_cast<LocalFace>(face)->setLocalControlHeaderFeature(
-//     LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
-
-//    static_pointer_cast<LocalFace>(face)->setLocalControlHeaderFeature(
-//      LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
-//  }
-
-//    limitedIo.afterOp();
-//  }
-
-//  void
-//  onConnectFailed(const std::string& reason)
-//  {
-//    BOOST_CHECK_MESSAGE(false, reason);
-
-//    limitedIo.afterOp();
-//  }
-
-//  void
-//  onReceiveInterest(const Interest& interest)
-//  {
-//    receivedInterests.push_back(interest);
-
-//    limitedIo.afterOp();
-//  }
-
-//  void
-//  onReceiveData(const Data& data)
-//  {
-//    receivedDatas.push_back(data);
-
-//    limitedIo.afterOp();
-//  }
-
-//  void
-//  onFail(const shared_ptr<Face>& face)
-//  {
-//    limitedIo.afterOp();
-//  }
-
-//public:
-//  LimitedIo limitedIo;
-
-//  std::vector<Interest> receivedInterests;
-//  std::vector<Data> receivedDatas;
-//};
-
-
-//BOOST_FIXTURE_TEST_CASE_TEMPLATE(CorruptedInput, Dataset,
-//                                 CorruptedPackets, SimpleEndToEndFixture)
-//{
-//  UnixStreamFactory factory;
-
-//  shared_ptr<UnixStreamChannel> channel = factory.createChannel(CHANNEL_PATH1);
-//  channel->listen(bind(&SimpleEndToEndFixture::onFaceCreated,   this, _1),
-//                  bind(&SimpleEndToEndFixture::onConnectFailed, this, _1));
-
-//  DummyStreamSender<UnixStreamTransport::protocol, Dataset> sender;
-//  sender.start(UnixStreamTransport::protocol::endpoint(CHANNEL_PATH1));
-
-//  BOOST_CHECK_MESSAGE(limitedIo.run(LimitedIo::UNLIMITED_OPS, time::seconds(1)) == LimitedIo::EXCEED_TIME,
-//                      "Exception thrown for " + Dataset::getName());
-//}
-
 BOOST_AUTO_TEST_SUITE_END() // TestUnixStream
 BOOST_AUTO_TEST_SUITE_END() // Face
 
diff --git a/tests/daemon/fw/client-control-strategy.t.cpp b/tests/daemon/fw/client-control-strategy.t.cpp
index 189d42f..8f0870b 100644
--- a/tests/daemon/fw/client-control-strategy.t.cpp
+++ b/tests/daemon/fw/client-control-strategy.t.cpp
@@ -48,7 +48,6 @@
   shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
   shared_ptr<DummyFace> face3 = make_shared<DummyFace>();
   shared_ptr<DummyLocalFace> face4 = make_shared<DummyLocalFace>();
-  face4->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
   forwarder.addFace(face1);
   forwarder.addFace(face2);
   forwarder.addFace(face3);
@@ -62,7 +61,7 @@
 
   // Interest with valid NextHopFaceId
   shared_ptr<Interest> interest1 = makeInterest("ndn:/0z8r6yDDe");
-  interest1->setNextHopFaceId(face1->getId());
+  interest1->setTag(make_shared<lp::NextHopFaceIdTag>(face1->getId()));
   shared_ptr<pit::Entry> pitEntry1 = pit.insert(*interest1).first;
   pitEntry1->insertOrUpdateInRecord(face4, *interest1);
 
@@ -83,7 +82,7 @@
 
   // Interest with invalid NextHopFaceId
   shared_ptr<Interest> interest3 = makeInterest("ndn:/0z8r6yDDe");
-  interest3->setNextHopFaceId(face3->getId());
+  interest3->setTag(make_shared<lp::NextHopFaceIdTag>(face3->getId()));
   shared_ptr<pit::Entry> pitEntry3 = pit.insert(*interest3).first;
   pitEntry3->insertOrUpdateInRecord(face4, *interest3);
 
diff --git a/tests/daemon/fw/forwarder.t.cpp b/tests/daemon/fw/forwarder.t.cpp
index e40b5ad..67bce49 100644
--- a/tests/daemon/fw/forwarder.t.cpp
+++ b/tests/daemon/fw/forwarder.t.cpp
@@ -68,7 +68,8 @@
   BOOST_CHECK_EQUAL(limitedIo.run(1, time::seconds(1)), LimitedIo::EXCEED_OPS);
   BOOST_REQUIRE_EQUAL(face2->m_sentInterests.size(), 1);
   BOOST_CHECK_EQUAL(face2->m_sentInterests[0].getName(), nameAB);
-  BOOST_CHECK_EQUAL(face2->m_sentInterests[0].getIncomingFaceId(), face1->getId());
+  BOOST_REQUIRE(face2->m_sentInterests[0].getTag<lp::IncomingFaceIdTag>() != nullptr);
+  BOOST_CHECK_EQUAL(*face2->m_sentInterests[0].getTag<lp::IncomingFaceIdTag>(), face1->getId());
   BOOST_CHECK_EQUAL(forwarder.getCounters().nInInterests, 1);
   BOOST_CHECK_EQUAL(forwarder.getCounters().nOutInterests, 1);
 
@@ -78,7 +79,8 @@
   BOOST_CHECK_EQUAL(limitedIo.run(1, time::seconds(1)), LimitedIo::EXCEED_OPS);
   BOOST_REQUIRE_EQUAL(face1->m_sentDatas.size(), 1);
   BOOST_CHECK_EQUAL(face1->m_sentDatas[0].getName(), nameABC);
-  BOOST_CHECK_EQUAL(face1->m_sentDatas[0].getIncomingFaceId(), face2->getId());
+  BOOST_REQUIRE(face1->m_sentDatas[0].getTag<lp::IncomingFaceIdTag>() != nullptr);
+  BOOST_CHECK_EQUAL(*face1->m_sentDatas[0].getTag<lp::IncomingFaceIdTag>(), face2->getId());
   BOOST_CHECK_EQUAL(forwarder.getCounters().nInData, 1);
   BOOST_CHECK_EQUAL(forwarder.getCounters().nOutData, 1);
 }
@@ -98,7 +100,7 @@
   shared_ptr<Interest> interestA = makeInterest("ndn:/A");
   interestA->setInterestLifetime(time::seconds(4));
   shared_ptr<Data> dataA = makeData("ndn:/A");
-  dataA->setIncomingFaceId(face3->getId());
+  dataA->setTag(make_shared<lp::IncomingFaceIdTag>(face3->getId()));
 
   Fib& fib = forwarder.getFib();
   shared_ptr<fib::Entry> fibEntry = fib.insert(Name("ndn:/A")).first;
@@ -117,7 +119,8 @@
 
   BOOST_REQUIRE_EQUAL(face1->m_sentDatas.size(), 1);
   // IncomingFaceId field should be reset to represent CS
-  BOOST_CHECK_EQUAL(face1->m_sentDatas[0].getIncomingFaceId(), FACEID_CONTENT_STORE);
+  BOOST_REQUIRE(face1->m_sentDatas[0].getTag<lp::IncomingFaceIdTag>() != nullptr);
+  BOOST_CHECK_EQUAL(*face1->m_sentDatas[0].getTag<lp::IncomingFaceIdTag>(), FACEID_CONTENT_STORE);
 
   limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(500));
   // PIT entry should not be left behind
diff --git a/tests/daemon/mgmt/face-manager.t.cpp b/tests/daemon/mgmt/face-manager.t.cpp
index 8e4da1f..02bcd78 100644
--- a/tests/daemon/mgmt/face-manager.t.cpp
+++ b/tests/daemon/mgmt/face-manager.t.cpp
@@ -149,86 +149,6 @@
   BOOST_CHECK_EQUAL(addedFace->getId(), -1);
 }
 
-BOOST_AUTO_TEST_CASE(EnableDisableLocalControl)
-{
-  auto nonLocalFace = addFace<DummyFace>(true); // clear notification
-  auto localFace = addFace<DummyLocalFace>(true); // clear notification
-  BOOST_CHECK(localFace->isLocal());
-  BOOST_CHECK(!nonLocalFace->isLocal());
-
-  std::vector<Name> commandNames;
-  auto testLocalControl = [&] (const Name& name, const ControlParameters& params, uint64_t faceId) {
-    auto command = makeControlCommandRequest(name, params,
-                                             [faceId] (shared_ptr<Interest> interest) {
-                                               interest->setIncomingFaceId(faceId);
-                                             });
-    receiveInterest(command);
-    commandNames.push_back(command->getName());
-  };
-
-  auto paramsF = ControlParameters().setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
-  auto paramsN = ControlParameters().setLocalControlFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
-
-  // non-existing face: 0~3
-  testLocalControl("/localhost/nfd/faces/enable-local-control", paramsF, FACEID_NULL);
-  testLocalControl("/localhost/nfd/faces/disable-local-control", paramsF, FACEID_NULL);
-  testLocalControl("/localhost/nfd/faces/enable-local-control", paramsN, FACEID_NULL);
-  testLocalControl("/localhost/nfd/faces/disable-local-control", paramsN, FACEID_NULL);
-
-  // non-local face: 4~7
-  testLocalControl("/localhost/nfd/faces/enable-local-control", paramsF, nonLocalFace->getId());
-  testLocalControl("/localhost/nfd/faces/disable-local-control", paramsF, nonLocalFace->getId());
-  testLocalControl("/localhost/nfd/faces/enable-local-control", paramsN, nonLocalFace->getId());
-  testLocalControl("/localhost/nfd/faces/disable-local-control", paramsN, nonLocalFace->getId());
-
-  // enableLocalControl for Incoming FaceId on existing local face:
-  testLocalControl("/localhost/nfd/faces/enable-local-control", paramsF, localFace->getId()); // 8
-  BOOST_CHECK(localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
-  BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
-
-  // disableLocalControl for Incoming FaceId on existing local face
-  testLocalControl("/localhost/nfd/faces/disable-local-control", paramsF, localFace->getId()); // 9
-  BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
-  BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
-
-  // enableLocalControl for NextHop ID on existing local face
-  testLocalControl("/localhost/nfd/faces/enable-local-control", paramsN, localFace->getId()); // 10
-  BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
-  BOOST_CHECK(localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
-
-  // disableLocalControl for NextHop ID on existing local face
-  testLocalControl("/localhost/nfd/faces/disable-local-control", paramsN, localFace->getId()); // 11
-  BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
-  BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
-
-  // check responses
-  BOOST_REQUIRE_EQUAL(m_responses.size(), 12);
-  BOOST_CHECK_EQUAL(checkResponse(0,  commandNames[0],  ControlResponse(410, "Face not found")),
-                    CheckResponseResult::OK);
-  BOOST_CHECK_EQUAL(checkResponse(1,  commandNames[1],  ControlResponse(410, "Face not found")),
-                    CheckResponseResult::OK);
-  BOOST_CHECK_EQUAL(checkResponse(2,  commandNames[2],  ControlResponse(410, "Face not found")),
-                    CheckResponseResult::OK);
-  BOOST_CHECK_EQUAL(checkResponse(3,  commandNames[3],  ControlResponse(410, "Face not found")),
-                    CheckResponseResult::OK);
-  BOOST_CHECK_EQUAL(checkResponse(4,  commandNames[4],  ControlResponse(412, "Face is non-local")),
-                    CheckResponseResult::OK);
-  BOOST_CHECK_EQUAL(checkResponse(5,  commandNames[5],  ControlResponse(412, "Face is non-local")),
-                    CheckResponseResult::OK);
-  BOOST_CHECK_EQUAL(checkResponse(6,  commandNames[6],  ControlResponse(412, "Face is non-local")),
-                    CheckResponseResult::OK);
-  BOOST_CHECK_EQUAL(checkResponse(7,  commandNames[7],  ControlResponse(412, "Face is non-local")),
-                    CheckResponseResult::OK);
-  BOOST_CHECK_EQUAL(checkResponse(8,  commandNames[8],  makeResponse(200, "OK", paramsF)),
-                    CheckResponseResult::OK);
-  BOOST_CHECK_EQUAL(checkResponse(9,  commandNames[9],  makeResponse(200, "OK", paramsF)),
-                    CheckResponseResult::OK);
-  BOOST_CHECK_EQUAL(checkResponse(10, commandNames[10], makeResponse(200, "OK", paramsN)),
-                    CheckResponseResult::OK);
-  BOOST_CHECK_EQUAL(checkResponse(11, commandNames[11], makeResponse(200, "OK", paramsN)),
-                    CheckResponseResult::OK);
-}
-
 class TestFace : public DummyFace
 {
 public:
diff --git a/tests/daemon/mgmt/fib-manager.t.cpp b/tests/daemon/mgmt/fib-manager.t.cpp
index 513536c..945a07e 100644
--- a/tests/daemon/mgmt/fib-manager.t.cpp
+++ b/tests/daemon/mgmt/fib-manager.t.cpp
@@ -180,9 +180,9 @@
   ControlResponse expectedResponse;
   auto testAddNextHop = [&] (ControlParameters parameters, const FaceId& faceId) {
     auto command = makeControlCommandRequest("/localhost/nfd/fib/add-nexthop", parameters,
-                                             [&faceId] (shared_ptr<Interest> interest) {
-                                               interest->setIncomingFaceId(faceId);
-                                             });
+                   [&faceId] (shared_ptr<Interest> interest) {
+                     interest->setTag(make_shared<lp::IncomingFaceIdTag>(faceId));
+                   });
     m_responses.clear();
     expectedName = command->getName();
     expectedResponse = makeResponse(200, "Success", parameters.setFaceId(faceId));
@@ -328,9 +328,9 @@
   auto testWithImplicitFaceId = [&] (ControlParameters parameters, FaceId face) {
     m_responses.clear();
     auto command = makeControlCommandRequest("/localhost/nfd/fib/remove-nexthop", parameters,
-                                             [face] (shared_ptr<Interest> interest) {
-                                               interest->setIncomingFaceId(face);
-                                             });
+                   [face] (shared_ptr<Interest> interest) {
+                     interest->setTag(make_shared<lp::IncomingFaceIdTag>(face));
+                   });
     expectedName = command->getName();
     expectedResponse = makeResponse(200, "Success", parameters.setFaceId(face));
     receiveInterest(command);
diff --git a/tests/daemon/table/cs-policy-lru.t.cpp b/tests/daemon/table/cs-policy-lru.t.cpp
index 02ce3c0..a4e76a8 100644
--- a/tests/daemon/table/cs-policy-lru.t.cpp
+++ b/tests/daemon/table/cs-policy-lru.t.cpp
@@ -23,8 +23,8 @@
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "table/cs.hpp"
 #include "table/cs-policy-lru.hpp"
+#include "table/cs.hpp"
 #include <ndn-cxx/util/crypto.hpp>
 
 #include "tests/test-common.hpp"
@@ -34,7 +34,6 @@
 namespace tests {
 
 using namespace nfd::tests;
-using ndn::nfd::LocalControlHeader;
 
 BOOST_AUTO_TEST_SUITE(CsLru)
 
diff --git a/tests/daemon/table/cs-policy-priority-fifo.t.cpp b/tests/daemon/table/cs-policy-priority-fifo.t.cpp
index 8fd1746..8f365d4 100644
--- a/tests/daemon/table/cs-policy-priority-fifo.t.cpp
+++ b/tests/daemon/table/cs-policy-priority-fifo.t.cpp
@@ -23,8 +23,8 @@
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "table/cs.hpp"
 #include "table/cs-policy-priority-fifo.hpp"
+#include "table/cs.hpp"
 #include <ndn-cxx/util/crypto.hpp>
 
 #include "tests/test-common.hpp"
@@ -34,7 +34,6 @@
 namespace tests {
 
 using namespace nfd::tests;
-using ndn::nfd::LocalControlHeader;
 
 BOOST_AUTO_TEST_SUITE(CsPriorityFifo)
 
diff --git a/tests/daemon/table/cs.t.cpp b/tests/daemon/table/cs.t.cpp
index 0b3bebb..63caa44 100644
--- a/tests/daemon/table/cs.t.cpp
+++ b/tests/daemon/table/cs.t.cpp
@@ -35,7 +35,6 @@
 namespace tests {
 
 using namespace nfd::tests;
-using ndn::nfd::LocalControlHeader;
 
 BOOST_FIXTURE_TEST_SUITE(TableCs, BaseFixture)
 
@@ -316,13 +315,16 @@
 
 BOOST_AUTO_TEST_SUITE_END()
 
-BOOST_AUTO_TEST_CASE(CachingPolicyNoCache)
+BOOST_AUTO_TEST_CASE(CachePolicyNoCache)
 {
   Cs cs(3);
 
   shared_ptr<Data> dataA = makeData("ndn:/A");
-  dataA->getLocalControlHeader().setCachingPolicy(LocalControlHeader::CachingPolicy::NO_CACHE);
   dataA->wireEncode();
+
+  dataA->setTag(make_shared<lp::CachePolicyTag>(
+                lp::CachePolicy().setPolicy(lp::CachePolicyType::NO_CACHE)));
+
   BOOST_CHECK_EQUAL(cs.insert(*dataA), false);
 
   cs.find(Interest("ndn:/A"),
diff --git a/tests/rib/auto-prefix-propagator.t.cpp b/tests/rib/auto-prefix-propagator.t.cpp
index 67e7b1b..3699815 100644
--- a/tests/rib/auto-prefix-propagator.t.cpp
+++ b/tests/rib/auto-prefix-propagator.t.cpp
@@ -457,15 +457,15 @@
   BOOST_REQUIRE_EQUAL(m_requests.size(), 1);
   BOOST_CHECK_EQUAL(checkRequest(0, "register", "/test/A"), CheckRequestResult::OK);
 
-  advanceClocks(time::milliseconds(1), 1050); // wait for the 1st retry
+  advanceClocks(time::milliseconds(10), time::milliseconds(1050)); // wait for the 1st retry
   BOOST_REQUIRE_EQUAL(m_requests.size(), 2);
   BOOST_CHECK_EQUAL(checkRequest(1, "register", "/test/A"), CheckRequestResult::OK);
 
-  advanceClocks(time::milliseconds(1), 2050); // wait for the 2nd retry, 2 times
+  advanceClocks(time::milliseconds(10), time::milliseconds(2050)); // wait for the 2nd retry, 2 times
   BOOST_REQUIRE_EQUAL(m_requests.size(), 3);
   BOOST_CHECK_EQUAL(checkRequest(2, "register", "/test/A"), CheckRequestResult::OK);
 
-  advanceClocks(time::milliseconds(1), 2050); // wait for the 3rd retry, reach the upper bound
+  advanceClocks(time::milliseconds(10), time::milliseconds(2050)); // wait for the 3rd retry, reach the upper bound
   BOOST_REQUIRE_EQUAL(m_requests.size(), 4);
   BOOST_CHECK_EQUAL(checkRequest(3, "register", "/test/A"), CheckRequestResult::OK);
 }
diff --git a/tests/rib/rib-manager.t.cpp b/tests/rib/rib-manager.t.cpp
index 94a37af..1287284 100644
--- a/tests/rib/rib-manager.t.cpp
+++ b/tests/rib/rib-manager.t.cpp
@@ -28,29 +28,25 @@
 #include "rib/rib-status-publisher-common.hpp"
 
 #include "tests/test-common.hpp"
-#include "tests/limited-io.hpp"
 #include <ndn-cxx/util/dummy-client-face.hpp>
 
 namespace nfd {
 namespace rib {
 namespace tests {
 
-class RibManagerFixture : public nfd::tests::BaseFixture
+using namespace nfd::tests;
+
+class RibManagerFixture : public UnitTestTimeFixture
 {
 public:
   RibManagerFixture()
-    : COMMAND_PREFIX("/localhost/nfd/rib")
-    , ADD_NEXTHOP_VERB("add-nexthop")
-    , REMOVE_NEXTHOP_VERB("remove-nexthop")
-    , REGISTER_COMMAND("/localhost/nfd/rib/register")
-    , UNREGISTER_COMMAND("/localhost/nfd/rib/unregister")
   {
-    face = ndn::util::makeDummyClientFace();
+    face = ndn::util::makeDummyClientFace(g_io);
 
     manager = make_shared<RibManager>(*face, keyChain);
     manager->registerWithNfd();
 
-    face->processEvents(time::milliseconds(1));
+    advanceClocks(time::milliseconds(1));
     face->sentInterests.clear();
   }
 
@@ -73,18 +69,20 @@
   }
 
   void
-  receiveCommandInterest(const Name& name, ControlParameters& parameters)
+  receiveCommandInterest(const Name& name, ControlParameters& parameters,
+                         uint64_t incomingFaceId = DEFAULT_INCOMING_FACE_ID)
   {
     Name commandName = name;
     commandName.append(parameters.wireEncode());
 
     Interest commandInterest(commandName);
+    commandInterest.setTag(make_shared<lp::IncomingFaceIdTag>(incomingFaceId));
 
     manager->m_managedRib.m_onSendBatchFromQueue = bind(&RibManagerFixture::onSendBatchFromQueue,
                                                         this, _1, parameters);
 
     face->receive(commandInterest);
-    face->processEvents(time::milliseconds(1));
+    advanceClocks(time::milliseconds(1));
   }
 
   void
@@ -109,14 +107,23 @@
   shared_ptr<ndn::util::DummyClientFace> face;
   ndn::KeyChain keyChain;
 
-  const Name COMMAND_PREFIX;
-  const Name::Component ADD_NEXTHOP_VERB;
-  const Name::Component REMOVE_NEXTHOP_VERB;
+  static const uint64_t DEFAULT_INCOMING_FACE_ID;
 
-  const Name REGISTER_COMMAND;
-  const Name UNREGISTER_COMMAND;
+  static const Name COMMAND_PREFIX;
+  static const name::Component ADD_NEXTHOP_VERB;
+  static const name::Component REMOVE_NEXTHOP_VERB;
+
+  static const Name REGISTER_COMMAND;
+  static const Name UNREGISTER_COMMAND;
 };
 
+const uint64_t RibManagerFixture::DEFAULT_INCOMING_FACE_ID = 25122;
+const Name RibManagerFixture::COMMAND_PREFIX("/localhost/nfd/rib");
+const name::Component RibManagerFixture::ADD_NEXTHOP_VERB("add-nexthop");
+const name::Component RibManagerFixture::REMOVE_NEXTHOP_VERB("remove-nexthop");
+const Name RibManagerFixture::REGISTER_COMMAND("/localhost/nfd/rib/register");
+const Name RibManagerFixture::UNREGISTER_COMMAND("/localhost/nfd/rib/unregister");
+
 class AuthorizedRibManager : public RibManagerFixture
 {
 public:
@@ -232,6 +239,55 @@
   BOOST_CHECK_EQUAL(extractedParameters.getFaceId(), removeParameters.getFaceId());
 }
 
+BOOST_FIXTURE_TEST_CASE(SelfRegister, AuthorizedRibManager)
+{
+  ControlParameters parameters;
+  parameters
+    .setName("/hello");
+
+  receiveCommandInterest(REGISTER_COMMAND, parameters, 10129);
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 1);
+
+  Interest& request = face->sentInterests[0];
+
+  ControlParameters extractedParameters;
+  Name::Component verb;
+  extractParameters(request, verb, extractedParameters);
+
+  BOOST_CHECK_EQUAL(verb, ADD_NEXTHOP_VERB);
+  BOOST_CHECK_EQUAL(extractedParameters.getName(), parameters.getName());
+  BOOST_CHECK_EQUAL(extractedParameters.getFaceId(), 10129);
+}
+
+BOOST_FIXTURE_TEST_CASE(SelfUnregister, AuthorizedRibManager)
+{
+  ControlParameters addParameters;
+  addParameters
+    .setName("/hello")
+    .setFaceId(10129);
+
+  receiveCommandInterest(REGISTER_COMMAND, addParameters);
+  face->sentInterests.clear();
+
+  ControlParameters removeParameters;
+  removeParameters
+    .setName("/hello");
+
+  receiveCommandInterest(UNREGISTER_COMMAND, removeParameters, 10129);
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 1);
+
+  Interest& request = face->sentInterests[0];
+
+  ControlParameters extractedParameters;
+  Name::Component verb;
+  extractParameters(request, verb, extractedParameters);
+
+  BOOST_CHECK_EQUAL(verb, REMOVE_NEXTHOP_VERB);
+  BOOST_CHECK_EQUAL(extractedParameters.getName(), removeParameters.getName());
+  BOOST_CHECK_EQUAL(extractedParameters.getFaceId(), 10129);
+}
 
 BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand, UnauthorizedRibManager)
 {
@@ -264,7 +320,7 @@
   manager->m_managedRib.insert(prefix, route);
 
   face->receive(Interest("/localhost/nfd/rib/list"));
-  face->processEvents(time::milliseconds(1));
+  advanceClocks(time::milliseconds(1));
 
   BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
   RibStatusPublisherFixture::decodeRibEntryBlock(face->sentDatas[0], prefix, route);
@@ -298,8 +354,7 @@
   addParameters.setExpirationPeriod(ndn::time::milliseconds::max());
   receiveCommandInterest(REGISTER_COMMAND, addParameters);
 
-  nfd::tests::LimitedIo limitedIo;
-  limitedIo.run(nfd::tests::LimitedIo::UNLIMITED_OPS, time::seconds(1));
+  advanceClocks(time::milliseconds(100), time::seconds(1));
 
   BOOST_REQUIRE_EQUAL(manager->m_managedRib.size(), 1);
 }
@@ -338,8 +393,7 @@
   manager->removeInvalidFaces(buffer);
 
   // Run scheduler
-  nfd::tests::LimitedIo limitedIo;
-  limitedIo.run(nfd::tests::LimitedIo::UNLIMITED_OPS, time::seconds(1));
+  advanceClocks(time::milliseconds(100), time::seconds(1));
 
   BOOST_REQUIRE_EQUAL(manager->m_managedRib.size(), 1);
 
@@ -399,8 +453,7 @@
   BOOST_REQUIRE_EQUAL(manager->m_managedRib.size(), 1);
 
   // Route should expire
-  nfd::tests::LimitedIo limitedIo;
-  limitedIo.run(nfd::tests::LimitedIo::UNLIMITED_OPS, time::seconds(1));
+  advanceClocks(time::milliseconds(100), time::seconds(1));
 
   BOOST_CHECK_EQUAL(manager->m_managedRib.size(), 0);
 }
diff --git a/tests/rib/rib-status-publisher-common.hpp b/tests/rib/rib-status-publisher-common.hpp
index c4055d5..eee9a5e 100644
--- a/tests/rib/rib-status-publisher-common.hpp
+++ b/tests/rib/rib-status-publisher-common.hpp
@@ -34,6 +34,7 @@
 #include <ndn-cxx/management/nfd-control-parameters.hpp>
 #include <ndn-cxx/management/nfd-rib-entry.hpp>
 #include <ndn-cxx/encoding/tlv.hpp>
+#include <ndn-cxx/encoding/tlv-nfd.hpp>
 
 namespace nfd {
 namespace rib {
diff --git a/tests/rib/rib.t.cpp b/tests/rib/rib.t.cpp
index 25440dd..91445c4 100644
--- a/tests/rib/rib.t.cpp
+++ b/tests/rib/rib.t.cpp
@@ -24,6 +24,7 @@
  */
 
 #include "rib/rib.hpp"
+#include <ndn-cxx/encoding/tlv-nfd.hpp>
 
 #include "tests/test-common.hpp"
 
diff --git a/tools/ndn-autoconfig-server.cpp b/tools/ndn-autoconfig-server.cpp
index fa4b2e7..ae576f3 100644
--- a/tools/ndn-autoconfig-server.cpp
+++ b/tools/ndn-autoconfig-server.cpp
@@ -23,6 +23,7 @@
  **/
 
 #include "version.hpp"
+#include <ndn-cxx/encoding/tlv-nfd.hpp>
 #include <ndn-cxx/face.hpp>
 #include <ndn-cxx/security/key-chain.hpp>