rib: redesign of remote prefix registration

Change-Id: I8418de6d5bb9615af898df5dbf9ed4cc2cb6a43a
Refs: #3211, #2413
diff --git a/rib/auto-prefix-propagator.cpp b/rib/auto-prefix-propagator.cpp
new file mode 100644
index 0000000..12344e2
--- /dev/null
+++ b/rib/auto-prefix-propagator.cpp
@@ -0,0 +1,472 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "auto-prefix-propagator.hpp"
+#include "core/logger.hpp"
+#include "core/scheduler.hpp"
+#include <ndn-cxx/security/signing-helpers.hpp>
+#include <vector>
+
+namespace nfd {
+namespace rib {
+
+NFD_LOG_INIT("AutoPrefixPropagator");
+
+using ndn::nfd::ControlParameters;
+using ndn::nfd::CommandOptions;
+
+const Name LOCAL_REGISTRATION_PREFIX("/localhost");
+const Name LINK_LOCAL_NFD_PREFIX("/localhop/nfd");
+const name::Component IGNORE_COMMPONENT("nrd");
+const time::seconds PREFIX_PROPAGATION_DEFAULT_REFRESH_INTERVAL = time::seconds(25);
+const time::seconds PREFIX_PROPAGATION_MAX_REFRESH_INTERVAL = time::seconds(600);
+const time::seconds PREFIX_PROPAGATION_DEFAULT_BASE_RETRY_WAIT = time::seconds(50);
+const time::seconds PREFIX_PROPAGATION_DEFAULT_MAX_RETRY_WAIT = time::seconds(3600);
+const uint64_t PREFIX_PROPAGATION_DEFAULT_COST = 15;
+const time::milliseconds PREFIX_PROPAGATION_DEFAULT_TIMEOUT = time::milliseconds(10000);
+
+AutoPrefixPropagator::AutoPrefixPropagator(ndn::nfd::Controller& controller,
+                                           ndn::KeyChain& keyChain,
+                                           Rib& rib)
+  : m_nfdController(controller)
+  , m_keyChain(keyChain)
+  , m_rib(rib)
+  , m_refreshInterval(PREFIX_PROPAGATION_DEFAULT_REFRESH_INTERVAL)
+  , m_baseRetryWait(PREFIX_PROPAGATION_DEFAULT_BASE_RETRY_WAIT)
+  , m_maxRetryWait(PREFIX_PROPAGATION_DEFAULT_MAX_RETRY_WAIT)
+  , m_hasConnectedHub(false)
+{
+}
+
+void
+AutoPrefixPropagator::loadConfig(const ConfigSection& configSection)
+{
+  m_refreshInterval = PREFIX_PROPAGATION_DEFAULT_REFRESH_INTERVAL;
+  m_baseRetryWait = PREFIX_PROPAGATION_DEFAULT_BASE_RETRY_WAIT;
+  m_maxRetryWait = PREFIX_PROPAGATION_DEFAULT_MAX_RETRY_WAIT;
+
+  m_controlParameters
+     .setCost(PREFIX_PROPAGATION_DEFAULT_COST)
+     .setOrigin(ndn::nfd::ROUTE_ORIGIN_CLIENT)// set origin to client.
+     .setFaceId(0);// the remote hub will take the input face as the faceId.
+
+   m_commandOptions
+     .setPrefix(LINK_LOCAL_NFD_PREFIX)
+     .setTimeout(PREFIX_PROPAGATION_DEFAULT_TIMEOUT);
+
+  NFD_LOG_INFO("Load auto_prefix_propagate section in rib section");
+
+  for (auto&& i : configSection) {
+    if (i.first == "cost") {
+      m_controlParameters.setCost(i.second.get_value<uint64_t>());
+    }
+    else if (i.first == "timeout") {
+      m_commandOptions.setTimeout(time::milliseconds(i.second.get_value<size_t>()));
+    }
+    else if (i.first == "refresh_interval") {
+      m_refreshInterval = std::min(PREFIX_PROPAGATION_MAX_REFRESH_INTERVAL,
+                                   time::seconds(i.second.get_value<size_t>()));
+    }
+    else if (i.first == "base_retry_wait") {
+      m_baseRetryWait = time::seconds(i.second.get_value<size_t>());
+    }
+    else if (i.first == "max_retry_wait") {
+      m_maxRetryWait = time::seconds(i.second.get_value<size_t>());
+    }
+    else {
+      BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" + i.first +
+                                              "\" in \"auto_prefix_propagate\" section"));
+    }
+  }
+}
+
+void
+AutoPrefixPropagator::enable()
+{
+  m_afterInsertConnection =
+    m_rib.afterInsertEntry.connect(bind(&AutoPrefixPropagator::afterInsertRibEntry, this, _1));
+  m_afterEraseConnection =
+    m_rib.afterEraseEntry.connect(bind(&AutoPrefixPropagator::afterEraseRibEntry, this, _1));
+}
+
+void
+AutoPrefixPropagator::disable()
+{
+  m_afterInsertConnection.disconnect();
+  m_afterEraseConnection.disconnect();
+}
+
+AutoPrefixPropagator::PrefixPropagationParameters
+AutoPrefixPropagator::getPrefixPropagationParameters(const Name& localRibPrefix)
+{
+  // get all identities from the KeyChain
+  std::vector<Name> identities;
+  m_keyChain.getAllIdentities(identities, false); // get all except the default
+  identities.push_back(m_keyChain.getDefaultIdentity()); // get the default
+
+  // shortest prefix matching to all identies.
+  Name propagatedPrefix, signingIdentity;
+  bool isFound = false;
+  for (auto&& i : identities) {
+    Name prefix = !i.empty() && IGNORE_COMMPONENT == i.at(-1) ? i.getPrefix(-1) : i;
+    if (prefix.isPrefixOf(localRibPrefix) && (!isFound || i.size() < signingIdentity.size())) {
+      isFound = true;
+      propagatedPrefix = prefix;
+      signingIdentity = i;
+    }
+  }
+
+  PrefixPropagationParameters propagateParameters;
+  if (!isFound) {
+    propagateParameters.isValid = false;
+  }
+  else {
+    propagateParameters.isValid = true;
+    propagateParameters.parameters = m_controlParameters;
+    propagateParameters.options = m_commandOptions;
+    propagateParameters.parameters.setName(propagatedPrefix);
+    propagateParameters.options.setSigningInfo(signingByIdentity(signingIdentity));
+  }
+
+  return propagateParameters;
+}
+
+void
+AutoPrefixPropagator::afterInsertRibEntry(const Name& prefix)
+{
+  if (LOCAL_REGISTRATION_PREFIX.isPrefixOf(prefix)) {
+    NFD_LOG_INFO("local registration only for " << prefix);
+    return;
+  }
+
+  if (prefix == LINK_LOCAL_NFD_PREFIX) {
+    NFD_LOG_INFO("this is a prefix registered by some hub: " << prefix);
+
+    m_hasConnectedHub = true;
+    return afterHubConnect();
+  }
+
+  auto propagateParameters = getPrefixPropagationParameters(prefix);
+  if (!propagateParameters.isValid) {
+    NFD_LOG_INFO("no signing identity available for: " << prefix);
+    return;
+  }
+
+  auto entryIt = m_propagatedEntries.find(propagateParameters.parameters.getName());
+  if (entryIt != m_propagatedEntries.end()) {
+    BOOST_ASSERT(!entryIt->second.isNew());
+    NFD_LOG_INFO("prefix has already been propagated: "
+                 << propagateParameters.parameters.getName());
+    return;
+  }
+
+  afterRibInsert(propagateParameters.parameters, propagateParameters.options);
+}
+
+void
+AutoPrefixPropagator::afterEraseRibEntry(const Name& prefix)
+{
+  if (LOCAL_REGISTRATION_PREFIX.isPrefixOf(prefix)) {
+    NFD_LOG_INFO("local unregistration only for " << prefix);
+    return;
+  }
+
+  if (prefix == LINK_LOCAL_NFD_PREFIX) {
+    NFD_LOG_INFO("disconnected to hub with prefix: " << prefix);
+
+    m_hasConnectedHub = false;
+    return afterHubDisconnect();
+  }
+
+  auto propagateParameters = getPrefixPropagationParameters(prefix);
+  if (!propagateParameters.isValid) {
+    NFD_LOG_INFO("no signing identity available for: " << prefix);
+    return;
+  }
+
+  auto entryIt = m_propagatedEntries.find(propagateParameters.parameters.getName());
+  if (entryIt == m_propagatedEntries.end()) {
+    NFD_LOG_INFO("prefix has not been propagated yet: "
+                 << propagateParameters.parameters.getName());
+    return;
+  }
+
+  for (auto&& ribTableEntry : m_rib) {
+    if (propagateParameters.parameters.getName().isPrefixOf(ribTableEntry.first) &&
+        propagateParameters.options.getSigningInfo().getSignerName() ==
+        getPrefixPropagationParameters(ribTableEntry.first)
+          .options.getSigningInfo().getSignerName()) {
+      NFD_LOG_INFO("should be kept for another RIB entry: " << ribTableEntry.first);
+      return;
+    }
+  }
+
+  afterRibErase(propagateParameters.parameters.unsetCost(), propagateParameters.options);
+}
+
+bool
+AutoPrefixPropagator::doesCurrentPropagatedPrefixWork(const Name& prefix)
+{
+  auto propagateParameters = getPrefixPropagationParameters(prefix);
+  if (!propagateParameters.isValid) {
+    // no identity can sign the input prefix
+    return false;
+  }
+
+  // there is at least one identity can sign the input prefix, so the prefix selected for
+  // propagation (i.e., propagateParameters.parameters.getName()) must be a prefix of the input
+  // prefix. Namely it's either equal to the input prefix or a better choice.
+  return propagateParameters.parameters.getName().size() == prefix.size();
+}
+
+void
+AutoPrefixPropagator::redoPropagation(PropagatedEntryIt entryIt,
+                                      const ControlParameters& parameters,
+                                      const CommandOptions& options,
+                                      time::seconds retryWaitTime)
+{
+  if (doesCurrentPropagatedPrefixWork(parameters.getName())) {
+    // PROPAGATED / PROPAGATE_FAIL --> PROPAGATING
+    entryIt->second.startPropagation();
+    return startPropagation(parameters, options, retryWaitTime);
+  }
+
+  NFD_LOG_INFO("current propagated prefix does not work any more");
+  m_propagatedEntries.erase(entryIt);
+
+  // re-handle all locally RIB entries that can be covered by this propagated prefix
+  for (auto&& ribTableEntry : m_rib) {
+    if (parameters.getName().isPrefixOf(ribTableEntry.first)) {
+      afterInsertRibEntry(ribTableEntry.first);
+    }
+  }
+}
+
+void
+AutoPrefixPropagator::startPropagation(const ControlParameters& parameters,
+                                       const CommandOptions& options,
+                                       time::seconds retryWaitTime)
+{
+  NFD_LOG_TRACE("start propagate " << parameters.getName());
+
+  ndn::Scheduler::Event refreshEvent =
+    bind(&AutoPrefixPropagator::onRefreshTimer, this, parameters, options);
+  ndn::Scheduler::Event retryEvent =
+    bind(&AutoPrefixPropagator::onRetryTimer, this, parameters, options,
+         std::min(m_maxRetryWait, retryWaitTime * 2));
+
+  m_nfdController.start<ndn::nfd::RibRegisterCommand>(
+     parameters,
+     bind(&AutoPrefixPropagator::afterPropagateSucceed, this, parameters, options, refreshEvent),
+     bind(&AutoPrefixPropagator::afterPropagateFail,
+          this, _1, _2, parameters, options, retryWaitTime, retryEvent),
+     options);
+}
+
+void
+AutoPrefixPropagator::startRevocation(const ControlParameters& parameters,
+                                      const CommandOptions& options,
+                                      time::seconds retryWaitTime)
+{
+  NFD_LOG_INFO("start revoke propagation of " << parameters.getName());
+
+  m_nfdController.start<ndn::nfd::RibUnregisterCommand>(
+     parameters,
+     bind(&AutoPrefixPropagator::afterRevokeSucceed, this, parameters, options, retryWaitTime),
+     bind(&AutoPrefixPropagator::afterRevokeFail, this, _1, _2, parameters, options),
+     options);
+}
+
+void
+AutoPrefixPropagator::afterRibInsert(const ControlParameters& parameters,
+                                     const CommandOptions& options)
+{
+  BOOST_ASSERT(m_propagatedEntries.find(parameters.getName()) == m_propagatedEntries.end());
+
+  // keep valid entries although there is no connectivity to hub
+  auto& entry = m_propagatedEntries[parameters.getName()]
+    .setSigningIdentity(options.getSigningInfo().getSignerName());
+
+  if (!m_hasConnectedHub) {
+    NFD_LOG_INFO("no hub connected to propagate " << parameters.getName());
+    return;
+  }
+
+  // NEW --> PROPAGATING
+  entry.startPropagation();
+  startPropagation(parameters, options, m_baseRetryWait);
+}
+
+void
+AutoPrefixPropagator::afterRibErase(const ControlParameters& parameters,
+                                    const CommandOptions& options)
+{
+  auto entryIt = m_propagatedEntries.find(parameters.getName());
+  BOOST_ASSERT(entryIt != m_propagatedEntries.end());
+
+  bool hasPropagationSucceeded = entryIt->second.isPropagated();
+
+  // --> "RELEASED"
+  m_propagatedEntries.erase(entryIt);
+
+  if (!m_hasConnectedHub) {
+    NFD_LOG_INFO("no hub connected to revoke propagation of " << parameters.getName());
+    return;
+  }
+
+  if (!hasPropagationSucceeded) {
+    NFD_LOG_INFO("propagation has not succeeded: " << parameters.getName());
+    return;
+  }
+
+  startRevocation(parameters, options, m_baseRetryWait);
+}
+
+void
+AutoPrefixPropagator::afterHubConnect()
+{
+  NFD_LOG_INFO("redo " << m_propagatedEntries.size()
+                       << " propagations when new Hub connectivity is built.");
+
+  std::vector<PropagatedEntryIt> regEntryIterators;
+  for (auto it = m_propagatedEntries.begin() ; it != m_propagatedEntries.end() ; it ++) {
+    BOOST_ASSERT(it->second.isNew());
+    regEntryIterators.push_back(it);
+  }
+
+  for (auto&& it : regEntryIterators) {
+    auto parameters = m_controlParameters;
+    auto options = m_commandOptions;
+
+    redoPropagation(it,
+                     parameters.setName(it->first),
+                     options.setSigningInfo(signingByIdentity(it->second.getSigningIdentity())),
+                     m_baseRetryWait);
+  }
+}
+
+void
+AutoPrefixPropagator::afterHubDisconnect()
+{
+  for (auto&& entry : m_propagatedEntries) {
+    // --> NEW
+    BOOST_ASSERT(!entry.second.isNew());
+    entry.second.initialize();
+  }
+}
+
+void
+AutoPrefixPropagator::afterPropagateSucceed(const ControlParameters& parameters,
+                                            const CommandOptions& options,
+                                            const ndn::Scheduler::Event& refreshEvent)
+{
+  NFD_LOG_TRACE("success to propagate " << parameters.getName());
+
+  auto entryIt = m_propagatedEntries.find(parameters.getName());
+  if (entryIt == m_propagatedEntries.end()) {
+    // propagation should be revoked if this entry has been erased (i.e., be in RELEASED state)
+    NFD_LOG_DEBUG("Already erased!");
+    ControlParameters newParameters = parameters;
+    return startRevocation(newParameters.unsetCost(), options, m_baseRetryWait);
+  }
+
+  // PROPAGATING --> PROPAGATED
+  BOOST_ASSERT(entryIt->second.isPropagating());
+  entryIt->second.succeed(scheduler::schedule(m_refreshInterval, refreshEvent));
+}
+
+void
+AutoPrefixPropagator::afterPropagateFail(uint32_t code, const std::string& reason,
+                                         const ControlParameters& parameters,
+                                         const CommandOptions& options,
+                                         time::seconds retryWaitTime,
+                                         const ndn::Scheduler::Event& retryEvent)
+{
+  NFD_LOG_TRACE("fail to propagate " << parameters.getName()
+                                     << "\n\t reason:" << reason
+                                     << "\n\t retry wait time: " << retryWaitTime);
+
+  auto entryIt = m_propagatedEntries.find(parameters.getName());
+  if (entryIt == m_propagatedEntries.end()) {
+    // current state is RELEASED
+    return;
+  }
+
+  // PROPAGATING --> PROPAGATE_FAIL
+  BOOST_ASSERT(entryIt->second.isPropagating());
+  entryIt->second.fail(scheduler::schedule(retryWaitTime, retryEvent));
+}
+
+void
+AutoPrefixPropagator::afterRevokeSucceed(const ControlParameters& parameters,
+                                         const CommandOptions& options,
+                                         time::seconds retryWaitTime)
+{
+  NFD_LOG_TRACE("success to revoke propagation of " << parameters.getName());
+
+  auto entryIt = m_propagatedEntries.find(parameters.getName());
+  if (m_propagatedEntries.end() != entryIt && !entryIt->second.isPropagateFail()) {
+    // if is not RELEASED or PROPAGATE_FAIL
+    NFD_LOG_DEBUG("propagated entry still exists");
+
+    // PROPAGATING / PROPAGATED --> PROPAGATING
+    BOOST_ASSERT(!entryIt->second.isNew());
+    entryIt->second.startPropagation();
+
+    ControlParameters newParameters = parameters;
+    startPropagation(newParameters.setCost(m_controlParameters.getCost()), options, retryWaitTime);
+  }
+}
+
+void
+AutoPrefixPropagator::afterRevokeFail(uint32_t code, const std::string& reason,
+                                       const ControlParameters& parameters,
+                                       const CommandOptions& options)
+{
+  NFD_LOG_INFO("fail to revoke the propagation of  " << parameters.getName()
+                                                     << "\n\t reason:" << reason);
+}
+
+void
+AutoPrefixPropagator::onRefreshTimer(const ControlParameters& parameters,
+                                     const CommandOptions& options)
+{
+  auto entryIt = m_propagatedEntries.find(parameters.getName());
+  BOOST_ASSERT(entryIt != m_propagatedEntries.end() && entryIt->second.isPropagated());
+  redoPropagation(entryIt, parameters, options, m_baseRetryWait);
+}
+
+void
+AutoPrefixPropagator::onRetryTimer(const ControlParameters& parameters,
+                                   const CommandOptions& options,
+                                   time::seconds retryWaitTime)
+{
+  auto entryIt = m_propagatedEntries.find(parameters.getName());
+  BOOST_ASSERT(entryIt != m_propagatedEntries.end() && entryIt->second.isPropagateFail());
+  redoPropagation(entryIt, parameters, options, retryWaitTime);
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/rib/auto-prefix-propagator.hpp b/rib/auto-prefix-propagator.hpp
new file mode 100644
index 0000000..11ad67f
--- /dev/null
+++ b/rib/auto-prefix-propagator.hpp
@@ -0,0 +1,418 @@
+/* -*- 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_RIB_AUTO_PREFIX_PROPAGATOR_HPP
+#define NFD_RIB_AUTO_PREFIX_PROPAGATOR_HPP
+
+#include "rib.hpp"
+#include "core/config-file.hpp"
+#include "rib-status-publisher.hpp"
+#include "propagated-entry.hpp"
+
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/management/nfd-controller.hpp>
+#include <ndn-cxx/management/nfd-control-command.hpp>
+#include <ndn-cxx/management/nfd-control-parameters.hpp>
+#include <ndn-cxx/management/nfd-command-options.hpp>
+#include <ndn-cxx/util/signal.hpp>
+
+namespace nfd {
+namespace rib {
+
+/** @brief provides automatic prefix propagation feature
+ *
+ * The AutoPrefixPropagator monitors changes to local RIB, and registers prefixes onto a
+ * connected gateway router (HUB), so that Interests under propagated prefixes will be forwarded
+ * toward the local host by the HUB.
+ *
+ * The route origin of propagated prefix is CLIENT, as shown on the HUB.
+ *
+ * Connectivity to a HUB is indicated with a special RIB entry "ndn:/localhop/nfd".
+ * Currently, the AutoPrefixPropagator can process the connection to at most one HUB.
+ *
+ * For every RIB entry except "ndn:/localhop/nfd" and those starting with "ndn:/localhost", the
+ * AutoPrefixPropagator queries the local KeyChain for signing identities that is authorized
+ * to sign a prefix registration command of a prefix of the RIB prefix.
+ *
+ * If one or more signing identities are found, the identity that can sign a prefix registration
+ * command of the shortest prefix is chosen, and the AutoPrefixPropagator will attempt to
+ * propagte a prefix to the HUB using the shortest prefix. It's noteworthy that no route flags will
+ * be inherited from the local registration.
+ * If no signing identity is available, no prefix of the RIB entry is propagated to the HUB.
+ *
+ * When a RIB entry is erased, the corresponding propagated entry would be revoked,
+ * unless another local RIB entry is causing the propagation of that prefix.
+ *
+ * After a successful propagation, the AutoPrefixPropagator will refresh the propagation
+ * periodically by resending the registration command.
+ *
+ * In case of a failure or timeout in a registration command, the command will be retried with an
+ * exponential back-off strategy.
+ *
+ * The AutoPrefixPropagator can be configured in NFD configuration file, at the
+ * rib.auto_prefix_propagate section.
+ */
+class AutoPrefixPropagator : noncopyable
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  AutoPrefixPropagator(ndn::nfd::Controller& controller,
+                       ndn::KeyChain& keyChain,
+                       Rib& rib);
+  /**
+   * @brief load the "auto_prefix_propagate" section from config file
+   *
+   * @param configSection the sub section in "rib" section.
+   */
+  void
+  loadConfig(const ConfigSection& configSection);
+
+  /**
+   * @brief enable automatic prefix propagation
+   */
+  void
+  enable();
+
+  /**
+   * @brief disable automatic prefix propagation
+   */
+  void
+  disable();
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE: // helpers
+  /**
+   * When a local RIB prefix triggers prefix propagation, we propagate the shortest identity that
+   * can sign this prefix to the HUB.
+   *
+   * Thus, the propagated prefix does not equal to local RIB prefix.
+   * So it needs separate storage instead of storing within the RIB.
+   *
+   * the propagated prefix is used as the key to retrive the corresponding entry.
+   */
+  typedef std::unordered_map<Name, PropagatedEntry> PropagatedEntryList;
+  typedef PropagatedEntryList::iterator PropagatedEntryIt;
+
+  /**
+   * @brief parameters used by the registration commands for prefix propagation
+   *
+   * consists of a ControlParameters and a CommandOptions, as well as a bool variable indicates
+   * whether this set of parameters is valid (i.e., the signing identity exists)
+   */
+  struct PrefixPropagationParameters
+  {
+    bool isValid;
+    ndn::nfd::ControlParameters parameters;
+    ndn::nfd::CommandOptions options;
+  };
+
+  /**
+   * @brief get the required parameters for prefix propagation.
+   *
+   * given a local RIB prefix @p localRibPrefix, find out, in local KeyChain, a proper identity
+   * whose namespace covers the input prefix. If there is no desired identity, return a invalid
+   * PrefixPropagationParameters.
+   *
+   * Otherwise, set the selected identity as the signing identity in CommandOptions. Meanwhile,
+   * set this identity (or its prefix with the last component eliminated if it ends with "nrd")
+   * as the name of ControlParameters.
+   *
+   * @return the PrefixPropagationParameters.
+   */
+  PrefixPropagationParameters
+  getPrefixPropagationParameters(const Name& localRibPrefix);
+
+  /**
+   * @brief check whether current propagated prefix still works
+   *
+   * the current propagated prefix @p prefix will still work if and only if its corresponding
+   * signing identity still exists and there is no better signing identity covering it.
+   *
+   * @return true if current propagated prefix still works, otherwise false
+   */
+  bool
+  doesCurrentPropagatedPrefixWork(const Name& prefix);
+
+  /**
+   * @brief process re-propagation for the given propagated entry
+   *
+   * This function may be invoked in 3 cases:
+   * 1. After the hub is connected to redo some propagations.
+   * 2. On refresh timer to handle refresh requests.
+   * 3. On retry timer to handle retry requests.
+   *
+   * @param entryIt the propagated entry need to do re-propagation
+   * @param parameters the ControlParameters used by registration commands for propagation
+   * @param options the CommandOptions used for registration commands for propagation
+   * @param retryWaitTime the current wait time before retrying propagation
+   */
+  void
+  redoPropagation(PropagatedEntryIt entryIt,
+                  const ndn::nfd::ControlParameters& parameters,
+                  const ndn::nfd::CommandOptions& options,
+                  time::seconds retryWaitTime);
+
+private:
+  /**
+   * @brief send out the registration command for propagation
+   *
+   * Before sending out the command, two events, for refresh and retry respectively, are
+   * established (but not scheduled) and assigned to two callbacks, afterPropagateSucceed and
+   * afterPropagateFail respectively.
+   *
+   * The retry event requires an argument to define the retry wait time, which is calculated
+   * according to the back-off policy based on current retry wait time @p retryWaitTime and the
+   * maxRetryWait.
+   *
+   * The baseRetryWait and maxRetryWait used in back-off strategy are all configured at
+   * rib.auto_prefix_propagate.base_retry_wait and
+   * rib.auto_prefix_propagate.max_retry_wait respectively
+
+   * @param parameters the ControlParameters used by the registration command for propagation
+   * @param options the CommandOptions used by the registration command for propagation
+   * @param retryWaitTime the current wait time before retrying propagation
+   */
+  void
+  startPropagation(const ndn::nfd::ControlParameters& parameters,
+                   const ndn::nfd::CommandOptions& options,
+                   time::seconds retryWaitTime);
+
+  /**
+   * @brief send out the unregistration command to revoke the corresponding propagation.
+   *
+   * @param parameters the ControlParameters used by the unregistration command for revocation
+   * @param options the CommandOptions used by the unregistration command for revocation
+   * @param retryWaitTime the current wait time before retrying propagation
+   */
+  void
+  startRevocation(const ndn::nfd::ControlParameters& parameters,
+                  const ndn::nfd::CommandOptions& options,
+                  time::seconds retryWaitTime);
+
+  /**
+   * @brief invoked when Rib::afterInsertEntry signal is emitted
+   *
+   * step1: if the local RIB prefix @param prefix is for local use only, return
+   * step2: if the local RIB prefix @param prefix is a hub prefix, invoke afterHubConnect
+   * step3: if no valid PrefixPropagationParameters can be found for the local RIB prefix
+   *        @param prefix, or the propagated prefix has already been processed, return
+   * step4: invoke afterRibInsert
+   */
+  void
+  afterInsertRibEntry(const Name& prefix);
+
+  /**
+   * @brief invoked when Rib::afterEraseEntry signal is emitted
+   *
+   * step1: if local RIB prefix @param prefix is for local use only, return
+   * step2: if local RIB prefix @param prefix is a hub prefix, invoke afterHubDisconnect
+   * step3: if no valid PrefixPropagationParameters can be found for local RIB prefix
+   *        @param prefix, or there are other local RIB prefixes can be covered by the propagated
+   *        prefix, return
+   * step4: invoke afterRibErase
+   */
+  void
+  afterEraseRibEntry(const Name& prefix);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE: // PropagatedEntry state changes
+  /**
+   * @brief invoked after rib insertion for non-hub prefixes
+   * @pre the PropagatedEntry is in RELEASED state
+   * @post the PropagatedEntry will be in NEW state
+   *
+   * @param parameters the ControlParameters used by registration commands for propagation
+   * @param options the CommandOptions used by registration commands for propagation
+   */
+  void
+  afterRibInsert(const ndn::nfd::ControlParameters& parameters,
+                 const ndn::nfd::CommandOptions& options);
+
+  /**
+   * @brief invoked after rib deletion for non-hub prefixes
+   * @pre the PropagatedEntry is not in RELEASED state
+   * @post the PropagatedEntry will be in RELEASED state
+   *
+   * @param parameters the ControlParameters used by registration commands for propagation
+   * @param options the CommandOptions used by registration commands for propagation
+   */
+  void
+  afterRibErase(const ndn::nfd::ControlParameters& parameters,
+                const ndn::nfd::CommandOptions& options);
+
+  /**
+   * @brief invoked after the hub is connected
+   * @pre the PropagatedEntry is in NEW state or RELEASED state
+   * @post the PropagatedEntry will be in PROPAGATING state or keep in RELEASED state
+   *
+   * call redoPropagation for each propagated entry.
+   */
+  void
+  afterHubConnect();
+
+  /**
+   * @brief invoked after the hub is disconnected
+   * @pre the PropagatedEntry is not in NEW state
+   * @post the PropagatedEntry will be in NEW state or keep in RELEASED state
+   *
+   * for each propagated entry, switch its state to NEW and cancel its event.
+   */
+  void
+  afterHubDisconnect();
+
+  /**
+   * @brief invoked after propagation succeeds
+   * @pre the PropagatedEntry is in PROPAGATING state or RELEASED state
+   * @post the PropagatedEntry will be in PROPAGATED state or keep in RELEASED state
+   *
+   * If the PropagatedEntry does not exist (in RELEASED state), an unregistration command is
+   * sent immediately for revocation.
+   *
+   * If the PropagatedEntry is in PROPAGATING state, switch it to PROPAGATED, and schedule a
+   * refresh timer to redo propagation after a duration, which is configured at
+   * rib.auto_prefix_propagate.refresh_interval.
+   *
+   * Otherwise, make a copy of the ControlParameters @p parameters, unset its Cost field, and then
+   * invoke startRevocation with this new ControlParameters.
+   *
+   * @param parameters the ControlParameters used by the registration command for propagation
+   * @param options the CommandOptions used by the registration command for propagation
+   * @param refreshEvent the event of refreshing propagation
+   */
+  void
+  afterPropagateSucceed(const ndn::nfd::ControlParameters& parameters,
+                        const ndn::nfd::CommandOptions& options,
+                        const ndn::Scheduler::Event& refreshEvent);
+
+  /**
+   * @brief invoked after propagation fails.
+   * @pre the PropagatedEntry is in PROPAGATING state or RELEASED state
+   * @post the PropagatedEntry will be in PROPAGATE_FAIL state or keep in RELEASED state
+   *
+   * If the PropagatedEntry still exists, schedule a retry timer to redo propagation
+   * after a duration defined by current retry time @p retryWaitTime
+   *
+   * @param code error code.
+   * @param reason error reason in string.
+   * @param parameters the ControlParameters used by the registration command for propagation
+   * @param options the CommandOptions used by registration command for propagation
+   * @param retryWaitTime the current wait time before retrying propagation
+   * @param retryEvent the event of retrying propagation
+   */
+  void
+  afterPropagateFail(uint32_t code, const std::string& reason,
+                     const ndn::nfd::ControlParameters& parameters,
+                     const ndn::nfd::CommandOptions& options,
+                     time::seconds retryWaitTime,
+                     const ndn::Scheduler::Event& retryEvent);
+
+  /**
+   * @brief invoked after revocation succeeds
+   * @pre the PropagatedEntry is not in NEW state
+   * @post the PropagatedEntry will be in PROPAGATING state, or keep in PROPAGATE_FAIL state,
+   * or keep in RELEASED state
+   *
+   * If the PropagatedEntry still exists and is not in PROPAGATE_FAIL state, switch it to
+   * PROPAGATING. Then make a copy of the ControlParameters @p parameters, reset its Cost, and
+   * invoke startPropagation with this new ControlParameters.
+   *
+   * @param parameters the ControlParameters used by the unregistration command for revocation
+   * @param options the CommandOptions used by the unregistration command for revocation
+   * @param retryWaitTime the current wait time before retrying propagation
+   */
+  void
+  afterRevokeSucceed(const ndn::nfd::ControlParameters& parameters,
+                     const ndn::nfd::CommandOptions& options,
+                     time::seconds retryWaitTime);
+
+  /**
+   * @brief invoked after revocation fails.
+   *
+   * @param code error code.
+   * @param reason error reason in string.
+   * @param parameters the ControlParameters used by the unregistration command for revocation
+   * @param options the CommandOptions used by the unregistration command for revocation
+   */
+  void
+  afterRevokeFail(uint32_t code, const std::string& reason,
+                  const ndn::nfd::ControlParameters& parameters,
+                  const ndn::nfd::CommandOptions& options);
+
+  /**
+   * @brief invoked when the refresh timer is triggered.
+   * @pre the PropagatedEntry is in PROPAGATED state
+   * @post the PropagatedEntry will be in PROPAGATING state
+   *
+   * call redoPropagation to handle this refresh request
+   *
+   * @param parameters the ControlParameters used by registration commands for propagation
+   * @param options the CommandOptions used by registration commands for propagation
+   */
+  void
+  onRefreshTimer(const ndn::nfd::ControlParameters& parameters,
+                 const ndn::nfd::CommandOptions& options);
+
+  /**
+   * @brief invoked when the retry timer is triggered.
+   * @pre the PropagatedEntry is in PROPAGATE_FAIL state
+   * @post the PropagatedEntry will be in PROPAGATING state
+   *
+   * call redoPropagation to handle this retry request
+   *
+   * @param parameters the ControlParameters used by registration commands for propagation
+   * @param options the CommandOptions used by registration commands for propagation
+   * @param retryWaitTime the current wait time before retrying propagation
+   */
+  void
+  onRetryTimer(const ndn::nfd::ControlParameters& parameters,
+               const ndn::nfd::CommandOptions& options,
+               time::seconds retryWaitTime);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  ndn::nfd::Controller& m_nfdController;
+  ndn::KeyChain& m_keyChain;
+  Rib& m_rib;
+  ndn::util::signal::ScopedConnection m_afterInsertConnection;
+  ndn::util::signal::ScopedConnection m_afterEraseConnection;
+  ndn::nfd::ControlParameters m_controlParameters;
+  ndn::nfd::CommandOptions m_commandOptions;
+  time::seconds m_refreshInterval;
+  time::seconds m_baseRetryWait;
+  time::seconds m_maxRetryWait;
+  bool m_hasConnectedHub;
+  PropagatedEntryList m_propagatedEntries;
+};
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_RIB_AUTO_PREFIX_PROPAGATOR_HPP
diff --git a/rib/propagated-entry.cpp b/rib/propagated-entry.cpp
new file mode 100644
index 0000000..8dff54a
--- /dev/null
+++ b/rib/propagated-entry.cpp
@@ -0,0 +1,131 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "core/logger.hpp"
+#include "propagated-entry.hpp"
+
+namespace nfd {
+namespace rib {
+
+void
+operator<<(std::ostream& out, PropagationStatus status)
+{
+  switch (status) {
+  case PropagationStatus::NEW:
+    out << "NEW";
+    break;
+  case PropagationStatus::PROPAGATING:
+    out << "PROPAGATING";
+    break;
+  case PropagationStatus::PROPAGATED:
+    out << "PROPAGATED";
+    break;
+  case PropagationStatus::PROPAGATE_FAIL:
+    out << "PROPAGATE_FAIL";
+    break;
+  default:
+    out << "undefined status";
+    break;
+  }
+}
+
+PropagatedEntry::PropagatedEntry()
+  : m_propagationStatus(PropagationStatus::NEW)
+{
+}
+
+PropagatedEntry::PropagatedEntry(const PropagatedEntry& other)
+  : m_signingIdentity(other.m_signingIdentity)
+  , m_propagationStatus(other.m_propagationStatus)
+{
+  BOOST_ASSERT(!other.isPropagated() && !other.isPropagateFail());
+}
+
+PropagatedEntry&
+PropagatedEntry::setSigningIdentity(const Name& identity)
+{
+  m_signingIdentity = identity;
+  return *this;
+}
+
+const Name&
+PropagatedEntry::getSigningIdentity() const
+{
+  return m_signingIdentity;
+}
+
+void
+PropagatedEntry::startPropagation()
+{
+  m_propagationStatus = PropagationStatus::PROPAGATING;
+}
+
+void
+PropagatedEntry::succeed(const scheduler::EventId& event)
+{
+  m_propagationStatus = PropagationStatus::PROPAGATED;
+  m_rePropagateEvent = event;
+}
+
+void
+PropagatedEntry::fail(const scheduler::EventId& event)
+{
+  m_propagationStatus = PropagationStatus::PROPAGATE_FAIL;
+  m_rePropagateEvent = event;
+}
+
+void
+PropagatedEntry::initialize()
+{
+  m_propagationStatus = PropagationStatus::NEW;
+  m_rePropagateEvent.cancel();
+}
+
+bool
+PropagatedEntry::isNew() const
+{
+  return PropagationStatus::NEW == m_propagationStatus;
+}
+
+bool
+PropagatedEntry::isPropagating() const
+{
+  return PropagationStatus::PROPAGATING == m_propagationStatus;
+}
+
+bool
+PropagatedEntry::isPropagated() const
+{
+  return PropagationStatus::PROPAGATED == m_propagationStatus;
+}
+
+bool
+PropagatedEntry::isPropagateFail() const
+{
+  return PropagationStatus::PROPAGATE_FAIL == m_propagationStatus;
+}
+
+} // namespace rib
+} // namespace nfd
diff --git a/rib/propagated-entry.hpp b/rib/propagated-entry.hpp
new file mode 100644
index 0000000..f4e61dd
--- /dev/null
+++ b/rib/propagated-entry.hpp
@@ -0,0 +1,158 @@
+/* -*- 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_RIB_PROPAGATED_ENTRY_HPP
+#define NFD_RIB_PROPAGATED_ENTRY_HPP
+
+#include "core/scheduler.hpp"
+
+namespace nfd {
+namespace rib {
+
+enum class PropagationStatus {
+  /// initial status
+  NEW,
+  /// is being propagated
+  PROPAGATING,
+  /// has been propagated successfully
+  PROPAGATED,
+  /// has failed in propagation
+  PROPAGATE_FAIL
+};
+
+void
+operator<<(std::ostream& out, PropagationStatus status);
+
+/**
+ * @brief represents an entry for prefix propagation.
+ * @sa http://redmine.named-data.net/issues/3211
+ *
+ * it consists of a PropagationStatus indicates current state of the state machine, as
+ * well as an event scheduled for refresh or retry (i.e., the RefreshTimer and the RetryTimer of
+ * the state machine respectively). Besides, it stores a copy of signing identity for this entry.
+ */
+class PropagatedEntry
+{
+public:
+  PropagatedEntry();
+
+  /**
+   * @pre other is not in PROPAGATED or PROPAGATE_FAIL state
+   */
+  PropagatedEntry(const PropagatedEntry& other);
+
+  PropagatedEntry&
+  operator=(const PropagatedEntry& other) = delete;
+
+  /**
+   * @brief set the signing identity
+   */
+  PropagatedEntry&
+  setSigningIdentity(const Name& identity);
+
+  /**
+   * @brief get the signing identity
+   *
+   * @return the signing identity
+   */
+  const Name&
+  getSigningIdentity() const;
+
+  /**
+   * @brief switch the propagation status to PROPAGATING.
+   *
+   * this is called before start the propagation process of this entry.
+   */
+  void
+  startPropagation();
+
+  /**
+   * @brief switch the propagation status to PROPAGATED, and set the
+   *        rePropagateEvent to @p event for refresh.
+   *
+   * this is called just after this entry is successfully propagated.
+   */
+  void
+  succeed(const scheduler::EventId& event);
+
+  /**
+   * @brief switch the propagation status to PROPAGATE_FAIL, and then set the
+   *        rePropagateEvent to @p event for retry.
+   *
+   * this is called just after propagation for this entry fails.
+   */
+  void
+  fail(const scheduler::EventId& event);
+
+  /**
+   * @brief cancel the events of re-sending propagation commands.
+   *
+   * switch the propagation status to NEW.
+   */
+  void
+  initialize();
+
+  /**
+   * @brief check whether this entry is a new entry.
+   *
+   * @return true if current status is NEW.
+   */
+  bool
+  isNew() const;
+
+  /**
+   * @brief check whether this entry is being propagated.
+   *
+   * @return true if current status is PROPAGATING.
+   */
+  bool
+  isPropagating() const;
+
+  /**
+   * @brief check whether this entry has been successfully propagated.
+   *
+   * @return true if current status is PROPAGATED.
+   */
+  bool
+  isPropagated() const;
+
+  /**
+   * @brief check whether this entry has failed in propagating.
+   *
+   * @return true if current status is PROPAGATE_FAIL.
+   */
+  bool
+  isPropagateFail() const;
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  Name m_signingIdentity;
+  scheduler::ScopedEventId m_rePropagateEvent;
+  PropagationStatus m_propagationStatus;
+};
+
+} // namespace rib
+} // namespace nfd
+
+#endif // NFD_RIB_PROPAGATED_ENTRY_HPP
diff --git a/rib/remote-registrator.cpp b/rib/remote-registrator.cpp
deleted file mode 100644
index d33ba1b..0000000
--- a/rib/remote-registrator.cpp
+++ /dev/null
@@ -1,446 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2015,  Regents of the University of California,
- *                           Arizona Board of Regents,
- *                           Colorado State University,
- *                           University Pierre & Marie Curie, Sorbonne University,
- *                           Washington University in St. Louis,
- *                           Beijing Institute of Technology,
- *                           The University of Memphis.
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon).
- * See AUTHORS.md for complete list of NFD authors and contributors.
- *
- * NFD is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "remote-registrator.hpp"
-#include "core/logger.hpp"
-#include "core/scheduler.hpp"
-#include <ndn-cxx/security/signing-helpers.hpp>
-
-namespace nfd {
-namespace rib {
-
-NFD_LOG_INIT("RemoteRegistrator");
-
-using ndn::nfd::ControlParameters;
-using ndn::nfd::CommandOptions;
-
-const Name RemoteRegistrator::LOCAL_REGISTRATION_PREFIX = "/localhost";
-const Name RemoteRegistrator::REMOTE_HUB_PREFIX = "/localhop/nfd";
-const name::Component RemoteRegistrator::IGNORE_COMMPONENT("rib");
-
-RemoteRegistrator::RemoteRegistrator(ndn::nfd::Controller& controller,
-                                     ndn::KeyChain& keyChain,
-                                     Rib& rib)
-  : m_nfdController(controller)
-  , m_keyChain(keyChain)
-  , m_rib(rib)
-  , m_refreshInterval(time::seconds(25))
-  , m_hasConnectedHub(false)
-  , m_nRetries(0)
-{
-}
-
-RemoteRegistrator::~RemoteRegistrator()
-{
-  // cancel all periodically refresh events.
-  for (auto&& entry : m_regEntries)
-    {
-      scheduler::cancel(entry.second);
-    }
-}
-
-void
-RemoteRegistrator::loadConfig(const ConfigSection& configSection)
-{
-  size_t cost = 15, timeout = 10000;
-  size_t retry = 0;
-  size_t interval = 0;
-  const size_t intervalDef = 25, intervalMax = 600;
-
-  NFD_LOG_INFO("Load remote_register section in rib section");
-  for (auto&& i : configSection)
-    {
-      if (i.first == "cost")
-        {
-          cost = i.second.get_value<size_t>();
-        }
-      else if (i.first == "timeout")
-        {
-          timeout = i.second.get_value<size_t>();
-        }
-      else if (i.first == "retry")
-        {
-          retry = i.second.get_value<size_t>();
-        }
-      else if (i.first == "refresh_interval")
-        {
-          interval = i.second.get_value<size_t>();
-        }
-      else
-        {
-          BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" + i.first +
-                                                  "\" in \"remote-registrator\" section"));
-        }
-    }
-
-   m_controlParameters
-     .setCost(cost)
-     .setOrigin(ndn::nfd::ROUTE_ORIGIN_CLIENT)// set origin to client.
-     .setFaceId(0);// the remote hub will take the input face as the faceId.
-
-   m_commandOptions
-     .setPrefix(REMOTE_HUB_PREFIX)
-     .setTimeout(time::milliseconds(timeout));
-
-   m_nRetries = retry;
-
-   if (interval == 0)
-     {
-       interval = intervalDef;
-     }
-
-   interval = std::min(interval, intervalMax);
-
-   m_refreshInterval = time::seconds(interval);
-}
-
-void
-RemoteRegistrator::enable()
-{
-  // do remote registration after an entry is inserted into the RIB.
-  m_afterInsertConnection =
-    m_rib.afterInsertEntry.connect([this] (const Name& prefix) {
-        registerPrefix(prefix);
-      });
-
-  // do remote unregistration after an entry is erased from the RIB.
-  m_afterEraseConnection =
-    m_rib.afterEraseEntry.connect([this] (const Name& prefix) {
-        unregisterPrefix(prefix);
-      });
-}
-
-void
-RemoteRegistrator::disable()
-{
-  m_afterInsertConnection.disconnect();
-  m_afterEraseConnection.disconnect();
-}
-
-void
-RemoteRegistrator::registerPrefix(const Name& prefix)
-{
-  if (LOCAL_REGISTRATION_PREFIX.isPrefixOf(prefix))
-    {
-      NFD_LOG_INFO("local registration only for " << prefix);
-      return;
-    }
-
-  bool isHubPrefix = prefix == REMOTE_HUB_PREFIX;
-
-  if (isHubPrefix)
-    {
-      NFD_LOG_INFO("this is a prefix registered by some hub: " << prefix);
-
-      m_hasConnectedHub = true;
-
-      redoRegistration();
-      return;
-    }
-
-  if (!m_hasConnectedHub)
-    {
-      NFD_LOG_INFO("no hub connected when registering " << prefix);
-      return;
-    }
-
-  std::pair<Name, size_t> identity = findIdentityForRegistration(prefix);
-
-  if (0 == identity.second)
-    {
-      NFD_LOG_INFO("no proper identity found for registering " << prefix);
-      return;
-    }
-
-  Name prefixForRegistration;
-  if (identity.first.size() == identity.second)
-    {
-      prefixForRegistration = identity.first;
-    }
-  else
-    {
-      prefixForRegistration = identity.first.getPrefix(-1);
-    }
-
-  if (m_regEntries.find(prefixForRegistration) != m_regEntries.end())
-    {
-      NFD_LOG_INFO("registration already in process for " << prefix);
-      return;
-    }
-
-  // make copies of m_controlParameters and m_commandOptions to
-  // avoid unreasonable overwriting during concurrent registration
-  // and unregistration.
-  ControlParameters parameters = m_controlParameters;
-  CommandOptions    options    = m_commandOptions;
-
-  startRegistration(parameters.setName(prefixForRegistration),
-                    options.setSigningInfo(signingByIdentity(identity.first)),
-                    m_nRetries);
-}
-
-void
-RemoteRegistrator::unregisterPrefix(const Name& prefix)
-{
-  if (prefix == REMOTE_HUB_PREFIX)
-    {
-      NFD_LOG_INFO("disconnected to hub with prefix: " << prefix);
-
-      // for phase 1: suppose there is at most one hub connected.
-      // if the hub prefix has been unregistered locally, there may
-      // be no connected hub.
-      m_hasConnectedHub = false;
-
-      clearRefreshEvents();
-      return;
-    }
-
-  if (!m_hasConnectedHub)
-    {
-      NFD_LOG_INFO("no hub connected when unregistering " << prefix);
-      return;
-    }
-
-  std::pair<Name, size_t> identity = findIdentityForRegistration(prefix);
-
-  if (0 == identity.second)
-    {
-      NFD_LOG_INFO("no proper identity found for unregistering " << prefix);
-      return;
-    }
-
-  Name prefixForRegistration;
-  if (identity.first.size() == identity.second)
-    {
-      prefixForRegistration = identity.first;
-    }
-  else
-    {
-      prefixForRegistration = identity.first.getPrefix(-1);
-    }
-
-  RegisteredEntryIt iRegEntry = m_regEntries.find(prefixForRegistration);
-  if (m_regEntries.end() == iRegEntry)
-    {
-      NFD_LOG_INFO("no existing entry found when unregistering " << prefix);
-      return;
-    }
-
-  for (auto&& entry : m_rib)
-    {
-      if (prefixForRegistration.isPrefixOf(entry.first) &&
-          findIdentityForRegistration(entry.first) == identity)
-        {
-          NFD_LOG_INFO("this identity should be kept for other rib entry: "
-                       << entry.first);
-          return;
-        }
-    }
-
-  scheduler::cancel(iRegEntry->second);
-  m_regEntries.erase(iRegEntry);
-
-  // make copies of m_controlParameters and m_commandOptions to
-  // avoid unreasonable overwriting during concurrent registration
-  // and unregistration.
-  ControlParameters parameters = m_controlParameters;
-  CommandOptions    options    = m_commandOptions;
-
-  startUnregistration(parameters.setName(prefixForRegistration).unsetCost(),
-                      options.setSigningInfo(signingByIdentity(identity.first)),
-                      m_nRetries);
-}
-
-std::pair<Name, size_t>
-RemoteRegistrator::findIdentityForRegistration(const Name& prefix)
-{
-  std::pair<Name, size_t> candidateIdentity;
-  std::vector<Name> identities;
-  bool isPrefix = false;
-  size_t maxLength = 0, curLength = 0;
-
-  // get all identies from the key-cahin except the default one.
-  m_keyChain.getAllIdentities(identities, false);
-
-  // get the default identity.
-  identities.push_back(m_keyChain.getDefaultIdentity());
-
-  // longest prefix matching to all indenties.
-  for (auto&& i : identities)
-    {
-      if (!i.empty() && IGNORE_COMMPONENT == i.at(-1))
-        {
-          isPrefix = i.getPrefix(-1).isPrefixOf(prefix);
-          curLength = i.size() - 1;
-        }
-      else
-        {
-          isPrefix = i.isPrefixOf(prefix);
-          curLength = i.size();
-        }
-
-      if (isPrefix && curLength > maxLength)
-        {
-          candidateIdentity.first = i;
-          maxLength = curLength;
-        }
-    }
-
-  candidateIdentity.second = maxLength;
-
-  return candidateIdentity;
-}
-
-void
-RemoteRegistrator::startRegistration(const ControlParameters& parameters,
-                                     const CommandOptions& options,
-                                     int nRetries)
-{
-  NFD_LOG_INFO("start register " << parameters.getName());
-
-  m_nfdController.start<ndn::nfd::RibRegisterCommand>(
-     parameters,
-     bind(&RemoteRegistrator::onRegSuccess,
-          this, parameters, options),
-     bind(&RemoteRegistrator::onRegFailure,
-          this, _1, _2, parameters, options, nRetries),
-     options);
-}
-
-void
-RemoteRegistrator::startUnregistration(const ControlParameters& parameters,
-                                       const CommandOptions& options,
-                                       int nRetries)
-{
-  NFD_LOG_INFO("start unregister " << parameters.getName());
-
-  m_nfdController.start<ndn::nfd::RibUnregisterCommand>(
-     parameters,
-     bind(&RemoteRegistrator::onUnregSuccess,
-          this, parameters, options),
-     bind(&RemoteRegistrator::onUnregFailure,
-          this, _1, _2, parameters, options, nRetries),
-     options);
-}
-
-void
-RemoteRegistrator::onRegSuccess(const ControlParameters& parameters,
-                                const CommandOptions& options)
-{
-  NFD_LOG_INFO("success to register " << parameters.getName());
-
-  RegisteredEntryIt iRegEntry = m_regEntries.find(parameters.getName());
-
-  if (m_regEntries.end() != iRegEntry)
-    {
-      NFD_LOG_DEBUG("Existing Entry: (" << iRegEntry->first
-                                        << ", " << iRegEntry->second
-                                        << ")");
-
-      scheduler::cancel(iRegEntry->second);
-      iRegEntry->second = scheduler::schedule(
-                            m_refreshInterval,
-                            bind(&RemoteRegistrator::startRegistration,
-                                 this, parameters, options, m_nRetries));
-    }
-  else
-    {
-      NFD_LOG_DEBUG("New Entry");
-      m_regEntries.insert(RegisteredEntry(
-                              parameters.getName(),
-                              scheduler::schedule(
-                                m_refreshInterval,
-                                bind(&RemoteRegistrator::startRegistration,
-                                     this, parameters, options, m_nRetries))));
-    }
-}
-
-void
-RemoteRegistrator::onRegFailure(uint32_t code, const std::string& reason,
-                                const ControlParameters& parameters,
-                                const CommandOptions& options,
-                                int nRetries)
-{
-  NFD_LOG_INFO("fail to register " << parameters.getName()
-                                   << "\n\t reason:" << reason
-                                   << "\n\t remain retries:" << nRetries);
-
-  if (nRetries > 0)
-    {
-      startRegistration(parameters, options, nRetries - 1);
-    }
-}
-
-void
-RemoteRegistrator::onUnregSuccess(const ControlParameters& parameters,
-                                  const CommandOptions& options)
-{
-  NFD_LOG_INFO("success to unregister " << parameters.getName());
-}
-
-void
-RemoteRegistrator::onUnregFailure(uint32_t code, const std::string& reason,
-                                  const ControlParameters& parameters,
-                                  const CommandOptions& options,
-                                  int nRetries)
-{
-  NFD_LOG_INFO("fail to unregister " << parameters.getName()
-                                     << "\n\t reason:" << reason
-                                     << "\n\t remain retries:" << nRetries);
-
-  if (nRetries > 0)
-    {
-      startUnregistration(parameters, options, nRetries - 1);
-    }
-}
-
-void
-RemoteRegistrator::redoRegistration()
-{
-  NFD_LOG_INFO("redo " << m_regEntries.size()
-                       << " registration when new Hub connection is built.");
-
-  for (auto&& entry : m_regEntries)
-    {
-      // make copies to avoid unreasonable overwrite.
-      ControlParameters parameters = m_controlParameters;
-      CommandOptions    options    = m_commandOptions;
-      startRegistration(parameters.setName(entry.first),
-                        options.setSigningInfo(signingByIdentity(entry.first)),
-                        m_nRetries);
-    }
-}
-
-void
-RemoteRegistrator::clearRefreshEvents()
-{
-  for (auto&& entry : m_regEntries)
-    {
-      scheduler::cancel(entry.second);
-    }
-}
-
-} // namespace rib
-} // namespace nfd
diff --git a/rib/remote-registrator.hpp b/rib/remote-registrator.hpp
deleted file mode 100644
index 2eea754..0000000
--- a/rib/remote-registrator.hpp
+++ /dev/null
@@ -1,250 +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_RIB_REMOTE_REGISTRATOR_HPP
-#define NFD_RIB_REMOTE_REGISTRATOR_HPP
-
-#include "rib.hpp"
-#include "core/config-file.hpp"
-#include "rib-status-publisher.hpp"
-
-#include <unordered_map>
-#include <ndn-cxx/security/key-chain.hpp>
-#include <ndn-cxx/management/nfd-controller.hpp>
-#include <ndn-cxx/management/nfd-control-command.hpp>
-#include <ndn-cxx/management/nfd-control-parameters.hpp>
-#include <ndn-cxx/management/nfd-command-options.hpp>
-#include <ndn-cxx/util/signal.hpp>
-
-namespace nfd {
-namespace rib {
-
-/**
- * @brief define the RemoteRegistrator class, which handles
- *        the registration/unregistration to remote hub(s).
- */
-class RemoteRegistrator : noncopyable
-{
-public:
-  class Error : public std::runtime_error
-  {
-  public:
-    explicit
-    Error(const std::string& what)
-      : std::runtime_error(what)
-    {
-    }
-  };
-
-  RemoteRegistrator(ndn::nfd::Controller& controller,
-                    ndn::KeyChain& keyChain,
-                    Rib& rib);
-
-  ~RemoteRegistrator();
-
-  /**
-   * @brief load the "remote_register" section from config file
-   *
-   * @param configSection the sub section in "rib" section.
-   */
-  void
-  loadConfig(const ConfigSection& configSection);
-
-  /**
-   * @brief enable remote registration/unregistration.
-   *
-   */
-  void
-  enable();
-
-  /**
-   * @brief disable remote registration/unregistration.
-   *
-   */
-  void
-  disable();
-
-  /**
-   * @brief register a prefix to remote hub(s).
-   *
-   * For the input prefix, we find the longest identity
-   * in the key-chain that can sign it, and then
-   * register this identity to remote hub(s).
-   *
-   * @param prefix the prefix being registered in local RIB.
-   */
-  void
-  registerPrefix(const Name& prefix);
-
-  /**
-   * @brief unregister a prefix from remote hub(s).
-   *
-   * For the input prefix, if the longest identity can sign it
-   * is already registered remotely, that identity should be
-   * unregistered from remote hub(s).
-   *
-   * @param prefix the prefix being unregistered in local RIB.
-   */
-  void
-  unregisterPrefix(const Name& prefix);
-
-private:
-  /**
-   * @brief find the most proper identity that can sign the
-   *        registration/unregistration command for the input prefix.
-   *
-   * @return the identity and the length of the longest match to the
-   *         input prefix.
-   *
-   * @retval { ignored, 0 } no matching identity
-   */
-  std::pair<Name, size_t>
-  findIdentityForRegistration(const Name& prefix);
-
-  /**
-   * @brief make and send the remote registration command.
-   *
-   * @param nRetries remaining number of retries.
-   */
-  void
-  startRegistration(const ndn::nfd::ControlParameters& parameters,
-                    const ndn::nfd::CommandOptions& options,
-                    int nRetries);
-
-  /**
-   * @brief make and send the remote unregistration command.
-   *
-   * @param nRetries remaining number of retries.
-   */
-  void
-  startUnregistration(const ndn::nfd::ControlParameters& parameters,
-                      const ndn::nfd::CommandOptions& options,
-                      int nRetries);
-  /**
-   * @brief refresh the remotely registered entry if registration
-   *        successes by re-sending the registration command.
-   *
-   * The interval of sending refresh command is defined in the
-   * "remote_register" section of the config file.
-   *
-   * @param parameters the same paremeters from startRegistration.
-   * @param options the same options as in startRegistration.
-   */
-  void
-  onRegSuccess(const ndn::nfd::ControlParameters& parameters,
-               const ndn::nfd::CommandOptions& options);
-
-  /**
-   * @brief retry to send registration command if registration fails.
-   *
-   * the number of retries is defined in the "remote_register"
-   * section of the config file.
-   *
-   * @param code error code.
-   * @param reason error reason in string.
-   * @param parameters the same paremeters from startRegistration.
-   * @param options the same options from startRegistration.
-   * @param nRetries remaining number of retries.
-   */
-  void
-  onRegFailure(uint32_t code, const std::string& reason,
-               const ndn::nfd::ControlParameters& parameters,
-               const ndn::nfd::CommandOptions& options,
-               int nRetries);
-
-  void
-  onUnregSuccess(const ndn::nfd::ControlParameters& parameters,
-                 const ndn::nfd::CommandOptions& options);
-
-  /**
-   * @brief retry to send unregistration command if registration fails.
-   *
-   * the number of retries is defined in the "remote_register"
-   * section of the config file.
-   *
-   * @param code error code.
-   * @param reason error reason in string.
-   * @param parameters the same paremeters as in startRegistration.
-   * @param options the same options as in startRegistration.
-   * @param nRetries remaining number of retries.
-   */
-  void
-  onUnregFailure(uint32_t code, const std::string& reason,
-                 const ndn::nfd::ControlParameters& parameters,
-                 const ndn::nfd::CommandOptions& options,
-                 int nRetries);
-
-  /**
-   * @brief re-register all prefixes
-   *
-   * This is called when a HUB connection is established.
-   */
-  void
-  redoRegistration();
-
-  /**
-   * @brief clear all refresh events
-   *
-   * This is called when all HUB connections are lost.
-   */
-  void
-  clearRefreshEvents();
-
-PUBLIC_WITH_TESTS_ELSE_PRIVATE:
-  /**
-   * When a locally registered prefix triggles remote
-   * registration, we actually register the longest
-   * identity that can sign this prefix to remote hub(s).
-   *
-   * Thus, the remotely reigstered prefix does not equal
-   * to Route Name. So it needs seperate sotrage instead
-   * of storing within the RIB.
-   */
-  typedef std::unordered_map<Name, scheduler::EventId> RegisteredList;
-  typedef RegisteredList::iterator RegisteredEntryIt;
-  typedef RegisteredList::value_type RegisteredEntry;
-  RegisteredList m_regEntries;
-
-private:
-  ndn::nfd::Controller& m_nfdController;
-  ndn::KeyChain& m_keyChain;
-  Rib& m_rib;
-  ndn::util::signal::ScopedConnection m_afterInsertConnection;
-  ndn::util::signal::ScopedConnection m_afterEraseConnection;
-  ndn::nfd::ControlParameters m_controlParameters;
-  ndn::nfd::CommandOptions m_commandOptions;
-  time::seconds m_refreshInterval;
-  bool m_hasConnectedHub;
-  int m_nRetries;
-
-  static const Name LOCAL_REGISTRATION_PREFIX; // /localhost
-  static const Name REMOTE_HUB_PREFIX; // /localhop/nfd
-  static const name::Component IGNORE_COMMPONENT; // rib
-};
-
-} // namespace rib
-} // namespace nfd
-
-#endif // NFD_RIB_REMOTE_REGISTRATOR_HPP
diff --git a/rib/rib-manager.cpp b/rib/rib-manager.cpp
index 91f0eda..33d1622 100644
--- a/rib/rib-manager.cpp
+++ b/rib/rib-manager.cpp
@@ -81,7 +81,7 @@
   , m_localhopValidator(m_face)
   , m_faceMonitor(m_face)
   , m_isLocalhopEnabled(false)
-  , m_remoteRegistrator(m_nfdController, m_keyChain, m_managedRib)
+  , m_prefixPropagator(m_nfdController, m_keyChain, m_managedRib)
   , m_ribStatusPublisher(m_managedRib, face, LIST_COMMAND_PREFIX, m_keyChain)
   , m_fibUpdater(m_managedRib, m_nfdController)
   , m_signedVerbDispatch(SIGNED_COMMAND_VERBS,
@@ -145,7 +145,7 @@
                      bool isDryRun,
                      const std::string& filename)
 {
-  bool isRemoteRegisterEnabled = false;
+  bool isAutoPrefixPropagatorEnabled = false;
 
   for (const auto& item : configSection) {
     if (item.first == "localhost_security") {
@@ -155,24 +155,24 @@
       m_localhopValidator.load(item.second, filename);
       m_isLocalhopEnabled = true;
     }
-    else if (item.first == "remote_register") {
-      m_remoteRegistrator.loadConfig(item.second);
-      isRemoteRegisterEnabled = true;
+    else if (item.first == "auto_prefix_propagate") {
+      m_prefixPropagator.loadConfig(item.second);
+      isAutoPrefixPropagatorEnabled = true;
 
       // Avoid other actions when isDryRun == true
       if (isDryRun) {
         continue;
       }
 
-      m_remoteRegistrator.enable();
+      m_prefixPropagator.enable();
     }
     else {
       BOOST_THROW_EXCEPTION(Error("Unrecognized rib property: " + item.first));
     }
   }
 
-  if (!isRemoteRegisterEnabled) {
-    m_remoteRegistrator.disable();
+  if (!isAutoPrefixPropagatorEnabled) {
+    m_prefixPropagator.disable();
   }
 }
 
diff --git a/rib/rib-manager.hpp b/rib/rib-manager.hpp
index e38ee3c..d7c6819 100644
--- a/rib/rib-manager.hpp
+++ b/rib/rib-manager.hpp
@@ -29,7 +29,7 @@
 #include "rib.hpp"
 #include "core/config-file.hpp"
 #include "rib-status-publisher.hpp"
-#include "remote-registrator.hpp"
+#include "auto-prefix-propagator.hpp"
 #include "fib-updater.hpp"
 
 #include <ndn-cxx/security/validator-config.hpp>
@@ -207,7 +207,7 @@
   ndn::ValidatorConfig m_localhopValidator;
   ndn::nfd::FaceMonitor m_faceMonitor;
   bool m_isLocalhopEnabled;
-  RemoteRegistrator m_remoteRegistrator;
+  AutoPrefixPropagator m_prefixPropagator;
 
   RibStatusPublisher m_ribStatusPublisher;