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