Merge branch 'feature-cs' into master

refs #2254

Change-Id: Iafd4b8d51477fc11340156cc65c2732923e16354
diff --git a/.jenkins b/.jenkins
index afc4699..674d751 100755
--- a/.jenkins
+++ b/.jenkins
@@ -3,7 +3,8 @@
 
 DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
 
-for i in $( find "$DIR/.jenkins.d" -type f -perm +111 | sort ); do
-    echo "Run: $i"
-    "$i"
+for file in "$DIR"/.jenkins.d/*; do
+    [[ -f $file && -x $file ]] || continue
+    echo "Run: $file"
+    "$file"
 done
diff --git a/.waf-tools/boost-kqueue.py b/.waf-tools/boost-kqueue.py
index a2ec732..d4d5139 100644
--- a/.waf-tools/boost-kqueue.py
+++ b/.waf-tools/boost-kqueue.py
@@ -8,13 +8,14 @@
 
 BOOST_ASIO_HAS_KQUEUE_CHECK = '''
 #include <boost/asio.hpp>
-#if defined(BOOST_ASIO_HAS_KQUEUE) and BOOST_VERSION >= 105600
-#error "Ethernet face support cannot be enabled on this platform"
+#if defined(BOOST_ASIO_HAS_KQUEUE) and BOOST_VERSION == 105600
+#error "Ethernet face support cannot be enabled on this platform with boost 1.56"
 #endif
 '''
 
 @Configure.conf
 def check_asio_pcap_support(conf):
+    # https://svn.boost.org/trac/boost/ticket/10367
     if conf.check_cxx(msg='Checking if Ethernet face support can be enabled',
                       fragment=BOOST_ASIO_HAS_KQUEUE_CHECK,
                       features='cxx', use='BOOST', mandatory=False):
diff --git a/.waf-tools/default-compiler-flags.py b/.waf-tools/default-compiler-flags.py
index 31f070e..1bb2563 100644
--- a/.waf-tools/default-compiler-flags.py
+++ b/.waf-tools/default-compiler-flags.py
@@ -27,6 +27,7 @@
                          '-Werror',
                          '-Wno-error=maybe-uninitialized', # Bug #1560
                          '-Wno-error=unneeded-internal-declaration', # Bug #1588
+                         '-Wno-error=deprecated-register', # Bug #2288
                         ]
         if areCustomCxxflagsPresent:
             missingFlags = [x for x in defaultFlags if x not in conf.env.CXXFLAGS]
diff --git a/README-dev.md b/README-dev.md
index 17af2ee..2c454ad 100644
--- a/README-dev.md
+++ b/README-dev.md
@@ -4,17 +4,15 @@
 Requirements
 ------------
 
+Contributions to NFD must be licensed under GPL 3.0 or compatible license.  If you are
+choosing GPL 3.0, please use the following license boilerplate in all `.hpp` and `.cpp`
+files:
+
 Include the following license boilerplate into all `.hpp` and `.cpp` files:
 
     /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
     /**
-     * Copyright (c) 2014,  Regents of the University of California,
-     *                      Arizona Board of Regents,
-     *                      Colorado State University,
-     *                      University Pierre & Marie Curie, Sorbonne University,
-     *                      Washington University in St. Louis,
-     *                      Beijing Institute of Technology,
-     *                      The University of Memphis
+     * Copyright (c) [Year(s)],  [Copyright Holder(s)].
      *
      * This file is part of NFD (Named Data Networking Forwarding Daemon).
      * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -29,13 +27,11 @@
      *
      * 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/>.
-     ////// [optional part] //////
-     *
-     * \author Author's Name <email@domain>
-     * \author Other Author's Name <another.email@domain>
-     ////// [end of optional part] //////
      */
 
+If you are affiliated to an NSF-supported NDN project institution, please use the [NDN Team License
+Boilerplate](http://redmine.named-data.net/projects/nfd/wiki/NDN_Team_License_Boilerplate_(NFD)).
+
 Recommendations
 ---------------
 
diff --git a/common.hpp b/common.hpp
index 94171c7..8316184 100644
--- a/common.hpp
+++ b/common.hpp
@@ -58,7 +58,8 @@
 #include <ndn-cxx/common.hpp>
 #include <ndn-cxx/interest.hpp>
 #include <ndn-cxx/data.hpp>
-#include <ndn-cxx/util/event-emitter.hpp>
+#include <ndn-cxx/util/event-emitter.hpp> // deprecated
+#include <ndn-cxx/util/signal.hpp>
 
 #include <boost/algorithm/string.hpp>
 #include <boost/asio.hpp>
@@ -94,7 +95,7 @@
 using ndn::Name;
 using ndn::Exclude;
 using ndn::Block;
-using ndn::util::EventEmitter;
+using ndn::util::EventEmitter; // deprecated
 
 namespace tlv {
 // Don't write "namespace tlv = ndn::tlv", because NFD can add other members into this namespace.
@@ -103,6 +104,7 @@
 
 namespace name = ndn::name;
 namespace time = ndn::time;
+namespace signal = ndn::util::signal;
 
 } // namespace nfd
 
diff --git a/core/network-interface.cpp b/core/network-interface.cpp
index d7eebbd..1aad63e 100644
--- a/core/network-interface.cpp
+++ b/core/network-interface.cpp
@@ -54,9 +54,25 @@
               "NetworkInterfaceInfo must provide a default constructor");
 #endif
 
+#ifdef WITH_TESTS
+static shared_ptr<std::vector<NetworkInterfaceInfo>> s_debugNetworkInterfaces = nullptr;
+
+void
+setDebugNetworkInterfaces(shared_ptr<std::vector<NetworkInterfaceInfo>> interfaces)
+{
+  s_debugNetworkInterfaces = interfaces;
+}
+#endif
+
 std::vector<NetworkInterfaceInfo>
 listNetworkInterfaces()
 {
+#ifdef WITH_TESTS
+  if (s_debugNetworkInterfaces != nullptr) {
+    return *s_debugNetworkInterfaces;
+  }
+#endif
+
   using namespace boost::asio::ip;
   using std::strerror;
 
diff --git a/core/network-interface.hpp b/core/network-interface.hpp
index 8519db1..0034052 100644
--- a/core/network-interface.hpp
+++ b/core/network-interface.hpp
@@ -88,9 +88,20 @@
   return (flags & IFF_UP) != 0;
 }
 
+/** \brief List configured network interfaces on the system and their info
+ *  \warning invalid IP addresses (e.g., 0.0.0.0) may be returned in some environments
+ */
 std::vector<NetworkInterfaceInfo>
 listNetworkInterfaces();
 
+#ifdef WITH_TESTS
+/** \brief Set a list of network interfaces to be returned by subsequent listNetworkInterfaces call
+ *  \note To reset to normal behavior, use `setDebugNetworkInterfaces(nullptr);`
+ */
+void
+setDebugNetworkInterfaces(shared_ptr<std::vector<NetworkInterfaceInfo>> interfaces);
+#endif
+
 } // namespace nfd
 
 #endif // NFD_CORE_NETWORK_INTERFACE_HPP
diff --git a/core/resolver.hpp b/core/resolver.hpp
deleted file mode 100644
index d4dc31c..0000000
--- a/core/resolver.hpp
+++ /dev/null
@@ -1,214 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon).
- * See AUTHORS.md for complete list of NFD authors and contributors.
- *
- * NFD is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- **/
-
-#ifndef NFD_CORE_RESOLVER_H
-#define NFD_CORE_RESOLVER_H
-
-#include "common.hpp"
-#include "core/global-io.hpp"
-#include "core/scheduler.hpp"
-
-namespace nfd {
-namespace resolver {
-
-typedef function<bool (const boost::asio::ip::address& address)> AddressSelector;
-
-struct AnyAddress {
-  bool
-  operator()(const boost::asio::ip::address& address)
-  {
-    return true;
-  }
-};
-
-struct Ipv4Address {
-  bool
-  operator()(const boost::asio::ip::address& address)
-  {
-    return address.is_v4();
-  }
-};
-
-struct Ipv6Address {
-  bool
-  operator()(const boost::asio::ip::address& address)
-  {
-    return address.is_v6();
-  }
-};
-
-} // namespace resolver
-
-template<class Protocol>
-class Resolver
-{
-public:
-  struct Error : public std::runtime_error
-  {
-    Error(const std::string& what) : std::runtime_error(what) {}
-  };
-
-  typedef function<void (const typename Protocol::endpoint& endpoint)> SuccessCallback;
-  typedef function<void (const std::string& reason)> ErrorCallback;
-
-  typedef boost::asio::ip::basic_resolver< Protocol > resolver;
-
-  /** \brief Asynchronously resolve host and port
-   *
-   * If an address selector predicate is specified, then each resolved IP address
-   * is checked against the predicate.
-   *
-   * Available address selector predicates:
-   *
-   * - resolver::AnyAddress()
-   * - resolver::Ipv4Address()
-   * - resolver::Ipv6Address()
-   */
-  static void
-  asyncResolve(const std::string& host, const std::string& port,
-               const SuccessCallback& onSuccess,
-               const ErrorCallback& onError,
-               const nfd::resolver::AddressSelector& addressSelector = nfd::resolver::AnyAddress(),
-               const time::seconds& timeout = time::seconds(4))
-  {
-    shared_ptr<Resolver> resolver =
-      shared_ptr<Resolver>(new Resolver(onSuccess, onError,
-                                        addressSelector));
-
-    resolver->asyncResolve(host, port, timeout, resolver);
-    // resolver will be destroyed when async operation finishes or global IO service stops
-  }
-
-  /** \brief Synchronously resolve host and port
-   *
-   * If an address selector predicate is specified, then each resolved IP address
-   * is checked against the predicate.
-   *
-   * Available address selector predicates:
-   *
-   * - resolver::AnyAddress()
-   * - resolver::Ipv4Address()
-   * - resolver::Ipv6Address()
-   */
-  static typename Protocol::endpoint
-  syncResolve(const std::string& host, const std::string& port,
-              const nfd::resolver::AddressSelector& addressSelector = nfd::resolver::AnyAddress())
-  {
-    Resolver resolver(SuccessCallback(), ErrorCallback(), addressSelector);
-
-    typename resolver::query query(host, port
-#if not defined(__FreeBSD__)
-                                   , resolver::query::all_matching
-#endif
-                                   );
-
-    typename resolver::iterator remoteEndpoint = resolver.m_resolver.resolve(query);
-    typename resolver::iterator end;
-    for (; remoteEndpoint != end; ++remoteEndpoint)
-      {
-        if (addressSelector(typename Protocol::endpoint(*remoteEndpoint).address()))
-          return *remoteEndpoint;
-      }
-    throw Error("No endpoint matching the specified address selector found");
-  }
-
-private:
-  Resolver(const SuccessCallback& onSuccess,
-           const ErrorCallback& onError,
-           const nfd::resolver::AddressSelector& addressSelector)
-    : m_resolver(getGlobalIoService())
-    , m_addressSelector(addressSelector)
-    , m_onSuccess(onSuccess)
-    , m_onError(onError)
-  {
-  }
-
-  void
-  asyncResolve(const std::string& host, const std::string& port,
-               const time::seconds& timeout,
-               const shared_ptr<Resolver>& self)
-  {
-    typename resolver::query query(host, port
-#if not defined(__FreeBSD__)
-                                   , resolver::query::all_matching
-#endif
-                                   );
-
-    m_resolver.async_resolve(query,
-                             bind(&Resolver::onResolveSuccess, this, _1, _2, self));
-
-    m_resolveTimeout = scheduler::schedule(timeout,
-                                           bind(&Resolver::onResolveError, this,
-                                                "Timeout", self));
-  }
-
-  void
-  onResolveSuccess(const boost::system::error_code& error,
-                   typename resolver::iterator remoteEndpoint,
-                   const shared_ptr<Resolver>& self)
-  {
-    scheduler::cancel(m_resolveTimeout);
-
-    if (error)
-      {
-        if (error == boost::system::errc::operation_canceled)
-          return;
-
-        return m_onError("Remote endpoint hostname or port cannot be resolved: " +
-                         error.category().message(error.value()));
-      }
-
-    typename resolver::iterator end;
-    for (; remoteEndpoint != end; ++remoteEndpoint)
-      {
-        if (m_addressSelector(typename Protocol::endpoint(*remoteEndpoint).address()))
-          return m_onSuccess(*remoteEndpoint);
-      }
-
-    m_onError("No endpoint matching the specified address selector found");
-  }
-
-  void
-  onResolveError(const std::string& errorInfo,
-                 const shared_ptr<Resolver>& self)
-  {
-    m_resolver.cancel();
-    m_onError(errorInfo);
-  }
-
-private:
-  resolver m_resolver;
-  EventId m_resolveTimeout;
-
-  nfd::resolver::AddressSelector m_addressSelector;
-  SuccessCallback m_onSuccess;
-  ErrorCallback m_onError;
-};
-
-typedef Resolver<boost::asio::ip::tcp> TcpResolver;
-typedef Resolver<boost::asio::ip::udp> UdpResolver;
-
-} // namespace nfd
-
-#endif // NFD_CORE_RESOLVER_H
diff --git a/core/scheduler.cpp b/core/scheduler.cpp
index 5f9e707..0979f97 100644
--- a/core/scheduler.cpp
+++ b/core/scheduler.cpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * Copyright (c) 2014,  Regents of the University of California,
+ *                      Arizona Board of Regents,
+ *                      Colorado State University,
+ *                      University Pierre & Marie Curie, Sorbonne University,
+ *                      Washington University in St. Louis,
+ *                      Beijing Institute of Technology,
+ *                      The University of Memphis
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -20,7 +21,7 @@
  *
  * 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 "scheduler.hpp"
 #include "global-io.hpp"
@@ -57,5 +58,47 @@
   g_scheduler.reset();
 }
 
+ScopedEventId::ScopedEventId()
+{
+}
+
+ScopedEventId::ScopedEventId(const EventId& event)
+  : m_event(event)
+{
+}
+
+ScopedEventId::ScopedEventId(ScopedEventId&& other)
+  : m_event(other.m_event)
+{
+  other.release();
+}
+
+ScopedEventId&
+ScopedEventId::operator=(const EventId& event)
+{
+  if (m_event != event) {
+    scheduler::cancel(m_event);
+    m_event = event;
+  }
+  return *this;
+}
+
+ScopedEventId::~ScopedEventId()
+{
+  scheduler::cancel(m_event);
+}
+
+void
+ScopedEventId::cancel()
+{
+  scheduler::cancel(m_event);
+}
+
+void
+ScopedEventId::release()
+{
+  m_event.reset();
+}
+
 } // namespace scheduler
 } // namespace nfd
diff --git a/core/scheduler.hpp b/core/scheduler.hpp
index c674429..b846c64 100644
--- a/core/scheduler.hpp
+++ b/core/scheduler.hpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * 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.
@@ -20,7 +21,7 @@
  *
  * 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_CORE_SCHEDULER_HPP
 #define NFD_CORE_SCHEDULER_HPP
@@ -48,9 +49,50 @@
 void
 cancel(const EventId& eventId);
 
-} // namespace scheduler
+/** \brief cancels an event automatically upon destruction
+ */
+class ScopedEventId : noncopyable
+{
+public:
+  ScopedEventId();
 
-using scheduler::EventId;
+  /** \brief implicit constructor from EventId
+   *  \param event the event to be cancelled upon destruction
+   */
+  ScopedEventId(const EventId& event);
+
+  /** \brief move constructor
+   */
+  ScopedEventId(ScopedEventId&& other);
+
+  /** \brief assigns an event
+   *
+   *  If a different event has been assigned to this instance previously,
+   *  that event will be cancelled immediately.
+   */
+  ScopedEventId&
+  operator=(const EventId& event);
+
+  /** \brief cancels the event
+   */
+  ~ScopedEventId();
+
+  /** \brief cancels the event manually
+   */
+  void
+  cancel();
+
+  /** \brief releases the event so that it won't be disconnected
+   *         when this ScopedEventId is destructed
+   */
+  void
+  release();
+
+private:
+  EventId m_event;
+};
+
+} // namespace scheduler
 
 } // namespace nfd
 
diff --git a/daemon/face/datagram-face.hpp b/daemon/face/datagram-face.hpp
index 5b1b3d9..1b534ca 100644
--- a/daemon/face/datagram-face.hpp
+++ b/daemon/face/datagram-face.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -129,7 +129,7 @@
 inline void
 DatagramFace<T, U>::sendInterest(const Interest& interest)
 {
-  this->onSendInterest(interest);
+  this->emitSignal(onSendInterest, interest);
   const Block& payload = interest.wireEncode();
   m_socket->async_send(boost::asio::buffer(payload.wire(), payload.size()),
                        bind(&DatagramFace<T, U>::handleSend, this, _1, _2, payload));
@@ -141,7 +141,7 @@
 inline void
 DatagramFace<T, U>::sendData(const Data& data)
 {
-  this->onSendData(data);
+  this->emitSignal(onSendData, data);
   const Block& payload = data.wireEncode();
   m_socket->async_send(boost::asio::buffer(payload.wire(), payload.size()),
                        bind(&DatagramFace<T, U>::handleSend, this, _1, _2, payload));
@@ -176,8 +176,7 @@
       fail("Tunnel closed");
     }
     else {
-      fail("Send operation failed, closing socket: " +
-             error.category().message(error.value()));
+      fail("Send operation failed, closing socket: " + error.message());
     }
     return;
   }
diff --git a/daemon/face/ethernet-face.cpp b/daemon/face/ethernet-face.cpp
index 21a12a4..d623aa2 100644
--- a/daemon/face/ethernet-face.cpp
+++ b/daemon/face/ethernet-face.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -24,6 +24,7 @@
  */
 
 #include "ethernet-face.hpp"
+#include "core/global-io.hpp"
 #include "core/logger.hpp"
 
 #include <pcap/pcap.h>
@@ -81,7 +82,7 @@
 
   int fd = pcap_get_selectable_fd(m_pcap.get());
   if (fd < 0)
-    throw Error("pcap_get_selectable_fd() failed");
+    throw Error("pcap_get_selectable_fd failed");
 
   // need to duplicate the fd, otherwise both pcap_close()
   // and stream_descriptor::close() will try to close the
@@ -102,7 +103,12 @@
                 m_srcAddress.toString().c_str());
   setPacketFilter(filter);
 
-  joinMulticastGroup();
+  if (!m_destAddress.isBroadcast() && !joinMulticastGroup())
+    {
+      NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
+                   << "] Falling back to promiscuous mode");
+      pcap_set_promisc(m_pcap.get(), 1);
+    }
 
   m_socket->async_read_some(boost::asio::null_buffers(),
                             bind(&EthernetFace::handleRead, this,
@@ -117,7 +123,7 @@
 void
 EthernetFace::sendInterest(const Interest& interest)
 {
-  onSendInterest(interest);
+  this->emitSignal(onSendInterest, interest);
   ndnlp::PacketArray pa = m_slicer->slice(interest.wireEncode());
   for (const auto& packet : *pa) {
     sendPacket(packet);
@@ -127,7 +133,7 @@
 void
 EthernetFace::sendData(const Data& data)
 {
-  onSendData(data);
+  this->emitSignal(onSendData, data);
   ndnlp::PacketArray pa = m_slicer->slice(data.wireEncode());
   for (const auto& packet : *pa) {
     sendPacket(packet);
@@ -157,7 +163,7 @@
   char errbuf[PCAP_ERRBUF_SIZE] = {};
   m_pcap.reset(pcap_create(m_interfaceName.c_str(), errbuf));
   if (!m_pcap)
-    throw Error("pcap_create(): " + std::string(errbuf));
+    throw Error("pcap_create: " + std::string(errbuf));
 
 #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
   // Enable "immediate mode", effectively disabling any read buffering in the kernel.
@@ -168,14 +174,14 @@
 #endif
 
   if (pcap_activate(m_pcap.get()) < 0)
-    throw Error("pcap_activate() failed");
+    throw Error("pcap_activate failed");
 
   if (pcap_set_datalink(m_pcap.get(), DLT_EN10MB) < 0)
-    throw Error("pcap_set_datalink(): " + std::string(pcap_geterr(m_pcap.get())));
+    throw Error("pcap_set_datalink: " + std::string(pcap_geterr(m_pcap.get())));
 
   if (pcap_setdirection(m_pcap.get(), PCAP_D_IN) < 0)
     // no need to throw on failure, BPF will filter unwanted packets anyway
-    NFD_LOG_WARN("pcap_setdirection(): " << pcap_geterr(m_pcap.get()));
+    NFD_LOG_WARN("pcap_setdirection: " << pcap_geterr(m_pcap.get()));
 }
 
 void
@@ -183,15 +189,15 @@
 {
   bpf_program filter;
   if (pcap_compile(m_pcap.get(), &filter, filterString, 1, PCAP_NETMASK_UNKNOWN) < 0)
-    throw Error("pcap_compile(): " + std::string(pcap_geterr(m_pcap.get())));
+    throw Error("pcap_compile: " + std::string(pcap_geterr(m_pcap.get())));
 
   int ret = pcap_setfilter(m_pcap.get(), &filter);
   pcap_freecode(&filter);
   if (ret < 0)
-    throw Error("pcap_setfilter(): " + std::string(pcap_geterr(m_pcap.get())));
+    throw Error("pcap_setfilter: " + std::string(pcap_geterr(m_pcap.get())));
 }
 
-void
+bool
 EthernetFace::joinMulticastGroup()
 {
 #if defined(__linux__)
@@ -203,7 +209,7 @@
 
   if (::setsockopt(m_socket->native_handle(), SOL_PACKET,
                    PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) == 0)
-    return; // success
+    return true; // success
 
   NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
                << "] setsockopt(PACKET_ADD_MEMBERSHIP) failed: " << std::strerror(errno));
@@ -214,6 +220,11 @@
   std::strncpy(ifr.ifr_name, m_interfaceName.c_str(), sizeof(ifr.ifr_name) - 1);
 
 #if defined(__APPLE__) || defined(__FreeBSD__)
+  // see bug #2327
+  using boost::asio::ip::udp;
+  udp::socket sock(ref(getGlobalIoService()), udp::v4());
+  int fd = sock.native_handle();
+
   /*
    * Differences between Linux and the BSDs (including OS X):
    *   o BSD does not have ifr_hwaddr; use ifr_addr instead.
@@ -235,6 +246,8 @@
   static_assert(sizeof(ifr.ifr_addr) >= offsetof(sockaddr_dl, sdl_data) + ethernet::ADDR_LEN,
                 "ifr_addr in struct ifreq is too small on this platform");
 #else
+  int fd = m_socket->native_handle();
+
   ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
   std::copy(m_destAddress.begin(), m_destAddress.end(), ifr.ifr_hwaddr.sa_data);
 
@@ -242,19 +255,14 @@
                 "ifr_hwaddr in struct ifreq is too small on this platform");
 #endif
 
-  if (::ioctl(m_socket->native_handle(), SIOCADDMULTI, &ifr) >= 0)
-    return; // success
+  if (::ioctl(fd, SIOCADDMULTI, &ifr) == 0)
+    return true; // success
 
   NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
                << "] ioctl(SIOCADDMULTI) failed: " << std::strerror(errno));
 #endif
 
-  if (!m_destAddress.isBroadcast())
-    {
-      NFD_LOG_DEBUG("[id:" << getId() << ",endpoint:" << m_interfaceName
-                    << "] Falling back to promiscuous mode");
-      pcap_set_promisc(m_pcap.get(), 1);
-    }
+  return false;
 }
 
 void
@@ -276,7 +284,7 @@
   // pad with zeroes if the payload is too short
   if (block.size() < ethernet::MIN_DATA_LEN)
     {
-      static const uint8_t padding[ethernet::MIN_DATA_LEN] = {0};
+      static const uint8_t padding[ethernet::MIN_DATA_LEN] = {};
       buffer.appendByteArray(padding, ethernet::MIN_DATA_LEN - block.size());
     }
 
@@ -290,7 +298,7 @@
   int sent = pcap_inject(m_pcap.get(), buffer.buf(), buffer.size());
   if (sent < 0)
     {
-      return fail("pcap_inject(): " + std::string(pcap_geterr(m_pcap.get())));
+      return fail("pcap_inject: " + std::string(pcap_geterr(m_pcap.get())));
     }
   else if (static_cast<size_t>(sent) < buffer.size())
     {
@@ -316,7 +324,7 @@
   int ret = pcap_next_ex(m_pcap.get(), &header, &packet);
   if (ret < 0)
     {
-      return fail("pcap_next_ex(): " + std::string(pcap_geterr(m_pcap.get())));
+      return fail("pcap_next_ex: " + std::string(pcap_geterr(m_pcap.get())));
     }
   else if (ret == 0)
     {
@@ -379,7 +387,7 @@
     {
       // new sender, setup a PartialMessageStore for it
       reassembler.pms.reset(new ndnlp::PartialMessageStore);
-      reassembler.pms->onReceive +=
+      reassembler.pms->onReceive.connect(
         [this, sourceAddress] (const Block& block) {
           NFD_LOG_TRACE("[id:" << getId() << ",endpoint:" << m_interfaceName
                         << "] All fragments received from " << sourceAddress.toString());
@@ -387,7 +395,7 @@
             NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
                          << "] Received unrecognized TLV block of type " << block.type()
                          << " from " << sourceAddress.toString());
-        };
+        });
     }
 
   scheduler::cancel(reassembler.expireEvent);
@@ -417,7 +425,7 @@
   if (error == boost::asio::error::eof)
     {
       msg = "Face closed";
-      NFD_LOG_INFO("[id:" << getId() << ",endpoint:" << m_interfaceName << "] " << msg);
+      NFD_LOG_DEBUG("[id:" << getId() << ",endpoint:" << m_interfaceName << "] " << msg);
     }
   else
     {
@@ -430,24 +438,27 @@
 size_t
 EthernetFace::getInterfaceMtu() const
 {
-  size_t mtu = ethernet::MAX_DATA_LEN;
-
 #ifdef SIOCGIFMTU
+#if defined(__APPLE__) || defined(__FreeBSD__)
+  // see bug #2328
+  using boost::asio::ip::udp;
+  udp::socket sock(ref(getGlobalIoService()), udp::v4());
+  int fd = sock.native_handle();
+#else
+  int fd = m_socket->native_handle();
+#endif
+
   ifreq ifr{};
   std::strncpy(ifr.ifr_name, m_interfaceName.c_str(), sizeof(ifr.ifr_name) - 1);
 
-  if (::ioctl(m_socket->native_handle(), SIOCGIFMTU, &ifr) < 0)
-    {
-      NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
-                   << "] Failed to get interface MTU: " << std::strerror(errno));
-    }
-  else
-    {
-      mtu = std::min(mtu, static_cast<size_t>(ifr.ifr_mtu));
-    }
+  if (::ioctl(fd, SIOCGIFMTU, &ifr) == 0)
+    return static_cast<size_t>(ifr.ifr_mtu);
+
+  NFD_LOG_WARN("[id:" << getId() << ",endpoint:" << m_interfaceName
+               << "] Failed to get interface MTU: " << std::strerror(errno));
 #endif
 
-  return mtu;
+  return ethernet::MAX_DATA_LEN;
 }
 
 } // namespace nfd
diff --git a/daemon/face/ethernet-face.hpp b/daemon/face/ethernet-face.hpp
index 87e2238..7a3d881 100644
--- a/daemon/face/ethernet-face.hpp
+++ b/daemon/face/ethernet-face.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -100,8 +100,10 @@
 
   /**
    * @brief Enables receiving frames addressed to our MAC multicast group
+   *
+   * @return true if successful, false otherwise
    */
-  void
+  bool
   joinMulticastGroup();
 
   /**
@@ -143,7 +145,7 @@
   struct Reassembler
   {
     unique_ptr<ndnlp::PartialMessageStore> pms;
-    EventId expireEvent;
+    scheduler::EventId expireEvent;
   };
 
   unique_ptr<pcap_t, void(*)(pcap_t*)> m_pcap;
diff --git a/daemon/face/ethernet-factory.cpp b/daemon/face/ethernet-factory.cpp
index d9f1612..0126b48 100644
--- a/daemon/face/ethernet-factory.cpp
+++ b/daemon/face/ethernet-factory.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -24,6 +24,8 @@
  */
 
 #include "ethernet-factory.hpp"
+#include "face/ethernet-face.hpp"
+
 #include "core/logger.hpp"
 #include "core/global-io.hpp"
 
@@ -46,9 +48,9 @@
   face = make_shared<EthernetFace>(socket, interface, address);
 
   auto key = std::make_pair(interface.name, address);
-  face->onFail += [this, key] (const std::string& reason) {
+  face->onFail.connectSingleShot([this, key] (const std::string& reason) {
     m_multicastFaces.erase(key);
-  };
+  });
   m_multicastFaces.insert({key, face});
 
   return face;
diff --git a/daemon/face/ethernet-factory.hpp b/daemon/face/ethernet-factory.hpp
index cc8516a..aa7861f 100644
--- a/daemon/face/ethernet-factory.hpp
+++ b/daemon/face/ethernet-factory.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -26,11 +26,13 @@
 #ifndef NFD_DAEMON_FACE_ETHERNET_FACTORY_HPP
 #define NFD_DAEMON_FACE_ETHERNET_FACTORY_HPP
 
-#include "ethernet-face.hpp"
 #include "protocol-factory.hpp"
+#include "core/network-interface.hpp"
 
 namespace nfd {
 
+class EthernetFace;
+
 class EthernetFactory : public ProtocolFactory
 {
 public:
diff --git a/daemon/face/face.cpp b/daemon/face/face.cpp
index 7d3c1b7..ef17d29 100644
--- a/daemon/face/face.cpp
+++ b/daemon/face/face.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -36,10 +36,10 @@
   , m_isOnDemand(false)
   , m_isFailed(false)
 {
-  onReceiveInterest += [this](const ndn::Interest&) { ++m_counters.getNInInterests(); };
-  onReceiveData     += [this](const ndn::Data&) {     ++m_counters.getNInDatas(); };
-  onSendInterest    += [this](const ndn::Interest&) { ++m_counters.getNOutInterests(); };
-  onSendData        += [this](const ndn::Data&) {     ++m_counters.getNOutDatas(); };
+  onReceiveInterest.connect([this] (const ndn::Interest&) { ++m_counters.getNInInterests(); });
+  onReceiveData    .connect([this] (const ndn::Data&)     { ++m_counters.getNInDatas(); });
+  onSendInterest   .connect([this] (const ndn::Interest&) { ++m_counters.getNOutInterests(); });
+  onSendData       .connect([this] (const ndn::Data&)     { ++m_counters.getNOutDatas(); });
 }
 
 Face::~Face()
@@ -120,8 +120,6 @@
 
   m_isFailed = true;
   this->onFail(reason);
-
-  this->onFail.clear();
 }
 
 template<typename FaceTraits>
diff --git a/daemon/face/face.hpp b/daemon/face/face.hpp
index 40a77a1..2883770 100644
--- a/daemon/face/face.hpp
+++ b/daemon/face/face.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -78,19 +78,19 @@
   ~Face();
 
   /// fires when an Interest is received
-  EventEmitter<Interest> onReceiveInterest;
+  signal::Signal<Face, Interest> onReceiveInterest;
 
   /// fires when a Data is received
-  EventEmitter<Data> onReceiveData;
+  signal::Signal<Face, Data> onReceiveData;
 
   /// fires when an Interest is sent out
-  EventEmitter<Interest> onSendInterest;
+  signal::Signal<Face, Interest> onSendInterest;
 
   /// fires when a Data is sent out
-  EventEmitter<Data> onSendData;
+  signal::Signal<Face, Data> onSendData;
 
   /// fires when face disconnects or fails to perform properly
-  EventEmitter<std::string/*reason*/> onFail;
+  signal::Signal<Face, std::string/*reason*/> onFail;
 
   /// send an Interest
   virtual void
@@ -187,6 +187,11 @@
   void
   fail(const std::string& reason);
 
+  DECLARE_SIGNAL_EMIT(onReceiveInterest)
+  DECLARE_SIGNAL_EMIT(onReceiveData)
+  DECLARE_SIGNAL_EMIT(onSendInterest)
+  DECLARE_SIGNAL_EMIT(onSendData)
+
 private:
   void
   setId(FaceId faceId);
diff --git a/daemon/face/local-face.hpp b/daemon/face/local-face.hpp
index e1c34c3..3a0c39f 100644
--- a/daemon/face/local-face.hpp
+++ b/daemon/face/local-face.hpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * 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.
@@ -20,7 +21,7 @@
  *
  * 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
@@ -156,7 +157,7 @@
               this->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
           }
 
-        this->onReceiveInterest(*i);
+        this->emitSignal(onReceiveInterest, *i);
       }
     else if (payload.type() == tlv::Data)
       {
@@ -174,7 +175,7 @@
         //       false);
         //   }
 
-        this->onReceiveData(*d);
+        this->emitSignal(onReceiveData, *d);
       }
     else
       return false;
diff --git a/daemon/face/multicast-udp-face.cpp b/daemon/face/multicast-udp-face.cpp
index 34c6cd2..aa1005e 100644
--- a/daemon/face/multicast-udp-face.cpp
+++ b/daemon/face/multicast-udp-face.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -61,7 +61,7 @@
 void
 MulticastUdpFace::sendInterest(const Interest& interest)
 {
-  onSendInterest(interest);
+  this->emitSignal(onSendInterest, interest);
 
   NFD_LOG_DEBUG("Sending interest");
   sendBlock(interest.wireEncode());
@@ -70,9 +70,9 @@
 void
 MulticastUdpFace::sendData(const Data& data)
 {
-  /// \todo After this method implements duplicate suppression, onSendData event should
-  ///       be triggered only when data is actually sent out
-  onSendData(data);
+  /// \todo After this method implements duplicate suppression, onSendData signal should
+  ///       be emitted only when data is actually sent out
+  this->emitSignal(onSendData, data);
 
   NFD_LOG_DEBUG("Sending data");
   sendBlock(data.wireEncode());
diff --git a/daemon/face/ndnlp-partial-message-store.hpp b/daemon/face/ndnlp-partial-message-store.hpp
index c707d92..4143107 100644
--- a/daemon/face/ndnlp-partial-message-store.hpp
+++ b/daemon/face/ndnlp-partial-message-store.hpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * 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.
@@ -20,7 +21,7 @@
  *
  * 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_NDNLP_PARTIAL_MESSAGE_STORE_HPP
 #define NFD_DAEMON_FACE_NDNLP_PARTIAL_MESSAGE_STORE_HPP
@@ -55,7 +56,7 @@
   reassemble();
 
 public:
-  EventId m_expiry;
+  scheduler::EventId m_expiry;
 
 private:
   size_t m_fragCount;
@@ -84,7 +85,7 @@
   receiveNdnlpData(const Block& pkt);
 
   /// fires when network layer packet is received
-  EventEmitter<Block> onReceive;
+  signal::Signal<PartialMessageStore, Block> onReceive;
 
 private:
   void
diff --git a/daemon/face/ndnlp-slicer.cpp b/daemon/face/ndnlp-slicer.cpp
index 9efe7d6..03b1454 100644
--- a/daemon/face/ndnlp-slicer.cpp
+++ b/daemon/face/ndnlp-slicer.cpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * 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.
@@ -20,7 +21,7 @@
  *
  * 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 "ndnlp-slicer.hpp"
 
@@ -40,10 +41,10 @@
 }
 
 template<bool T>
-static inline size_t
-Slicer_encodeFragment(ndn::EncodingImpl<T>& blk,
-                      uint64_t seq, uint16_t fragIndex, uint16_t fragCount,
-                      const uint8_t* payload, size_t payloadSize)
+size_t
+Slicer::encodeFragment(ndn::EncodingImpl<T>& blk,
+                       uint64_t seq, uint16_t fragIndex, uint16_t fragCount,
+                       const uint8_t* payload, size_t payloadSize)
 {
   size_t totalLength = 0;
 
@@ -86,11 +87,15 @@
 void
 Slicer::estimateOverhead()
 {
+  // estimate fragment size with all header fields at largest possible size
   ndn::EncodingEstimator estimator;
-  size_t estimatedSize = Slicer_encodeFragment(estimator,
-                         0, 0, 2, 0, m_mtu);
+  size_t estimatedSize = this->encodeFragment(estimator,
+                                              std::numeric_limits<uint64_t>::max(),
+                                              std::numeric_limits<uint16_t>::max() - 1,
+                                              std::numeric_limits<uint16_t>::max(),
+                                              nullptr, m_mtu);
 
-  size_t overhead = estimatedSize - m_mtu;
+  size_t overhead = estimatedSize - m_mtu; // minus payload length in estimation
   m_maxPayload = m_mtu - overhead;
 }
 
@@ -105,7 +110,7 @@
                          (networkPacketSize / m_maxPayload) +
                          (networkPacketSize % m_maxPayload == 0 ? 0 : 1)
                        );
-  PacketArray pa = make_shared<std::vector<Block> >();
+  PacketArray pa = make_shared<std::vector<Block>>();
   pa->reserve(fragCount);
   SequenceBlock seqBlock = m_seqgen.nextBlock(fragCount);
 
@@ -115,10 +120,10 @@
     size_t payloadSize = std::min(m_maxPayload, networkPacketSize - payloadOffset);
 
     ndn::EncodingBuffer buffer(m_mtu, 0);
-    size_t pktSize = Slicer_encodeFragment(buffer,
+    size_t pktSize = this->encodeFragment(buffer,
       seqBlock[fragIndex], fragIndex, fragCount, payload, payloadSize);
 
-    BOOST_ASSERT(pktSize <= m_mtu);
+    BOOST_VERIFY(pktSize <= m_mtu);
 
     pa->push_back(buffer.block());
   }
diff --git a/daemon/face/ndnlp-slicer.hpp b/daemon/face/ndnlp-slicer.hpp
index 9442a07..1e2985a 100644
--- a/daemon/face/ndnlp-slicer.hpp
+++ b/daemon/face/ndnlp-slicer.hpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * 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.
@@ -20,7 +21,7 @@
  *
  * 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_NDNLP_SLICER_HPP
 #define NFD_DAEMON_FACE_NDNLP_SLICER_HPP
@@ -31,13 +32,17 @@
 namespace nfd {
 namespace ndnlp {
 
-typedef shared_ptr<std::vector<Block> > PacketArray;
+typedef shared_ptr<std::vector<Block>> PacketArray;
 
 /** \brief provides fragmentation feature at sender
  */
 class Slicer : noncopyable
 {
 public:
+  /** \param mtu maximum size of NDNLP header and payload
+   *  \note If NDNLP packets are to be encapsulated in an additional header
+   *        (eg. in UDP packets), the caller must deduct such overhead.
+   */
   explicit
   Slicer(size_t mtu);
 
@@ -48,6 +53,12 @@
   slice(const Block& block);
 
 private:
+  template<bool T>
+  size_t
+  encodeFragment(ndn::EncodingImpl<T>& blk,
+                 uint64_t seq, uint16_t fragIndex, uint16_t fragCount,
+                 const uint8_t* payload, size_t payloadSize);
+
   /// estimate the size of NDNLP header and maximum payload size per packet
   void
   estimateOverhead();
diff --git a/daemon/face/stream-face.hpp b/daemon/face/stream-face.hpp
index e719da3..64c1d57 100644
--- a/daemon/face/stream-face.hpp
+++ b/daemon/face/stream-face.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -178,7 +178,7 @@
 inline void
 StreamFace<T, U>::sendInterest(const Interest& interest)
 {
-  this->onSendInterest(interest);
+  this->emitSignal(onSendInterest, interest);
   StreamFaceSenderImpl<T, U, Interest>::send(*this, interest);
 }
 
@@ -186,7 +186,7 @@
 inline void
 StreamFace<T, U>::sendData(const Data& data)
 {
-  this->onSendData(data);
+  this->emitSignal(onSendData, data);
   StreamFaceSenderImpl<T, U, Data>::send(*this, data);
 }
 
diff --git a/daemon/face/tcp-channel.cpp b/daemon/face/tcp-channel.cpp
index cd2744d..69a5241 100644
--- a/daemon/face/tcp-channel.cpp
+++ b/daemon/face/tcp-channel.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -83,9 +83,8 @@
   shared_ptr<ip::tcp::socket> clientSocket =
     make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
 
-  EventId connectTimeoutEvent = scheduler::schedule(timeout,
-                                                    bind(&TcpChannel::handleFailedConnect, this,
-                                                         clientSocket, onConnectFailed));
+  scheduler::EventId connectTimeoutEvent = scheduler::schedule(timeout,
+      bind(&TcpChannel::handleFailedConnect, this, clientSocket, onConnectFailed));
 
   clientSocket->async_connect(remoteEndpoint,
                               bind(&TcpChannel::handleSuccessfulConnect, this, _1,
@@ -106,9 +105,8 @@
   shared_ptr<ip::tcp::resolver> resolver =
     make_shared<ip::tcp::resolver>(ref(getGlobalIoService()));
 
-  EventId connectTimeoutEvent = scheduler::schedule(timeout,
-                                                    bind(&TcpChannel::handleFailedConnect, this,
-                                                         clientSocket, onConnectFailed));
+  scheduler::EventId connectTimeoutEvent = scheduler::schedule(timeout,
+      bind(&TcpChannel::handleFailedConnect, this, clientSocket, onConnectFailed));
 
   resolver->async_resolve(query,
                           bind(&TcpChannel::handleEndpointResolution, this, _1, _2,
@@ -140,7 +138,7 @@
       else
         face = make_shared<TcpFace>(socket, isOnDemand);
 
-      face->onFail += bind(&TcpChannel::afterFaceFailed, this, remoteEndpoint);
+      face->onFail.connectSingleShot(bind(&TcpChannel::afterFaceFailed, this, remoteEndpoint));
 
       m_channelFaces[remoteEndpoint] = face;
     }
@@ -201,7 +199,7 @@
 void
 TcpChannel::handleSuccessfulConnect(const boost::system::error_code& error,
                                     const shared_ptr<ip::tcp::socket>& socket,
-                                    const EventId& connectTimeoutEvent,
+                                    const scheduler::EventId& connectTimeoutEvent,
                                     const FaceCreatedCallback& onFaceCreated,
                                     const ConnectFailedCallback& onConnectFailed)
 {
@@ -249,7 +247,7 @@
 TcpChannel::handleEndpointResolution(const boost::system::error_code& error,
                                      ip::tcp::resolver::iterator remoteEndpoint,
                                      const shared_ptr<boost::asio::ip::tcp::socket>& socket,
-                                     const EventId& connectTimeoutEvent,
+                                     const scheduler::EventId& connectTimeoutEvent,
                                      const FaceCreatedCallback& onFaceCreated,
                                      const ConnectFailedCallback& onConnectFailed,
                                      const shared_ptr<ip::tcp::resolver>& resolver)
diff --git a/daemon/face/tcp-channel.hpp b/daemon/face/tcp-channel.hpp
index f126a47..f83ca55 100644
--- a/daemon/face/tcp-channel.hpp
+++ b/daemon/face/tcp-channel.hpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * 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.
@@ -20,7 +21,7 @@
  *
  * 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_TCP_CHANNEL_HPP
 #define NFD_DAEMON_FACE_TCP_CHANNEL_HPP
@@ -124,7 +125,7 @@
   void
   handleSuccessfulConnect(const boost::system::error_code& error,
                           const shared_ptr<boost::asio::ip::tcp::socket>& socket,
-                          const EventId& connectTimeoutEvent,
+                          const scheduler::EventId& connectTimeoutEvent,
                           const FaceCreatedCallback& onFaceCreated,
                           const ConnectFailedCallback& onConnectFailed);
 
@@ -136,7 +137,7 @@
   handleEndpointResolution(const boost::system::error_code& error,
                            boost::asio::ip::tcp::resolver::iterator remoteEndpoint,
                            const shared_ptr<boost::asio::ip::tcp::socket>& socket,
-                           const EventId& connectTimeoutEvent,
+                           const scheduler::EventId& connectTimeoutEvent,
                            const FaceCreatedCallback& onFaceCreated,
                            const ConnectFailedCallback& onConnectFailed,
                            const shared_ptr<boost::asio::ip::tcp::resolver>& resolver);
diff --git a/daemon/face/tcp-factory.cpp b/daemon/face/tcp-factory.cpp
index bdc8c24..0a152b1 100644
--- a/daemon/face/tcp-factory.cpp
+++ b/daemon/face/tcp-factory.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -24,14 +24,21 @@
  */
 
 #include "tcp-factory.hpp"
-#include "core/resolver.hpp"
 #include "core/logger.hpp"
 #include "core/network-interface.hpp"
+#include "core/global-io.hpp"
+#include <ndn-cxx/util/dns.hpp>
 
 NFD_LOG_INIT("TcpFactory");
 
 namespace nfd {
 
+static const boost::asio::ip::address_v4 ALL_V4_ENDPOINT(
+  boost::asio::ip::address_v4::from_string("0.0.0.0"));
+
+static const boost::asio::ip::address_v6 ALL_V6_ENDPOINT(
+  boost::asio::ip::address_v6::from_string("::"));
+
 TcpFactory::TcpFactory(const std::string& defaultPort/* = "6363"*/)
   : m_defaultPort(defaultPort)
 {
@@ -42,9 +49,6 @@
 {
   using namespace boost::asio::ip;
 
-  static const address_v4 ALL_V4_ENDPOINT(address_v4::from_string("0.0.0.0"));
-  static const address_v6 ALL_V6_ENDPOINT(address_v6::from_string("::"));
-
   const address& address = endpoint.address();
 
   if (address.is_v4() && address == ALL_V4_ENDPOINT)
@@ -56,8 +60,7 @@
       prohibitAllIpv6Endpoints(endpoint.port());
     }
 
-  NFD_LOG_TRACE("prohibiting TCP " <<
-                endpoint.address().to_string() << ":" << endpoint.port());
+  NFD_LOG_TRACE("prohibiting TCP " << endpoint);
 
   m_prohibitedEndpoints.insert(endpoint);
 }
@@ -69,7 +72,9 @@
 
   for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
     for (const address_v4& addr : nic.ipv4Addresses) {
-      prohibitEndpoint(tcp::Endpoint(addr, port));
+      if (addr != ALL_V4_ENDPOINT) {
+        prohibitEndpoint(tcp::Endpoint(addr, port));
+      }
     }
   }
 }
@@ -81,7 +86,9 @@
 
   for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
     for (const address_v6& addr : nic.ipv6Addresses) {
-      prohibitEndpoint(tcp::Endpoint(addr, port));
+      if (addr != ALL_V6_ENDPOINT) {
+        prohibitEndpoint(tcp::Endpoint(addr, port));
+      }
     }
   }
 }
@@ -104,7 +111,9 @@
 shared_ptr<TcpChannel>
 TcpFactory::createChannel(const std::string& localHost, const std::string& localPort)
 {
-  return createChannel(TcpResolver::syncResolve(localHost, localPort));
+  tcp::Endpoint endpoint(ndn::dns::syncResolve(localHost, getGlobalIoService()),
+                         boost::lexical_cast<uint16_t>(localPort));
+  return createChannel(endpoint);
 }
 
 shared_ptr<TcpChannel>
@@ -122,30 +131,10 @@
                        const FaceCreatedCallback& onCreated,
                        const FaceConnectFailedCallback& onConnectFailed)
 {
-  resolver::AddressSelector addressSelector = resolver::AnyAddress();
-  if (uri.getScheme() == "tcp4")
-    addressSelector = resolver::Ipv4Address();
-  else if (uri.getScheme() == "tcp6")
-    addressSelector = resolver::Ipv6Address();
+  BOOST_ASSERT(uri.isCanonical());
+  boost::asio::ip::address ipAddress = boost::asio::ip::address::from_string(uri.getHost());
+  tcp::Endpoint endpoint(ipAddress, boost::lexical_cast<uint16_t>(uri.getPort()));
 
-  if (!uri.getPath().empty() && uri.getPath() != "/")
-    {
-      onConnectFailed("Invalid URI");
-    }
-
-  TcpResolver::asyncResolve(uri.getHost(),
-                            uri.getPort().empty() ? m_defaultPort : uri.getPort(),
-                            bind(&TcpFactory::continueCreateFaceAfterResolve, this, _1,
-                                 onCreated, onConnectFailed),
-                            onConnectFailed,
-                            addressSelector);
-}
-
-void
-TcpFactory::continueCreateFaceAfterResolve(const tcp::Endpoint& endpoint,
-                                           const FaceCreatedCallback& onCreated,
-                                           const FaceConnectFailedCallback& onConnectFailed)
-{
   if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end())
     {
       onConnectFailed("Requested endpoint is prohibited "
@@ -154,7 +143,6 @@
     }
 
   // very simple logic for now
-
   for (ChannelMap::iterator channel = m_channels.begin();
        channel != m_channels.end();
        ++channel)
diff --git a/daemon/face/tcp-factory.hpp b/daemon/face/tcp-factory.hpp
index fd57fe5..e50bf66 100644
--- a/daemon/face/tcp-factory.hpp
+++ b/daemon/face/tcp-factory.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -29,6 +29,7 @@
 #include "protocol-factory.hpp"
 #include "tcp-channel.hpp"
 
+
 namespace nfd {
 
 class TcpFactory : public ProtocolFactory
@@ -88,7 +89,7 @@
   virtual std::list<shared_ptr<const Channel> >
   getChannels() const;
 
-private:
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
 
   void
   prohibitEndpoint(const tcp::Endpoint& endpoint);
@@ -110,12 +111,7 @@
   shared_ptr<TcpChannel>
   findChannel(const tcp::Endpoint& localEndpoint);
 
-  void
-  continueCreateFaceAfterResolve(const tcp::Endpoint& endpoint,
-                                 const FaceCreatedCallback& onCreated,
-                                 const FaceConnectFailedCallback& onConnectFailed);
-
-private:
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   typedef std::map< tcp::Endpoint, shared_ptr<TcpChannel> > ChannelMap;
   ChannelMap m_channels;
 
diff --git a/daemon/face/udp-channel.cpp b/daemon/face/udp-channel.cpp
index c4bb0e8..8e151b3 100644
--- a/daemon/face/udp-channel.cpp
+++ b/daemon/face/udp-channel.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -177,7 +177,8 @@
   if (faceMapPos == m_channelFaces.end())
     {
       face = make_shared<UdpFace>(socket, isOnDemand, m_idleFaceTimeout);
-      face->onFail += bind(&UdpChannel::afterFaceFailed, this, remoteEndpoint);
+
+      face->onFail.connectSingleShot(bind(&UdpChannel::afterFaceFailed, this, remoteEndpoint));
 
       m_channelFaces[remoteEndpoint] = face;
     }
@@ -229,7 +230,13 @@
     clientSocket->open(m_localEndpoint.protocol());
     clientSocket->set_option(ip::udp::socket::reuse_address(true));
     clientSocket->bind(m_localEndpoint);
-    clientSocket->connect(m_newRemoteEndpoint);
+    boost::system::error_code ec;
+    clientSocket->connect(m_newRemoteEndpoint, ec);
+    if (ec) {
+      NFD_LOG_WARN("Error while creating on-demand UDP face from " << m_newRemoteEndpoint << ": "
+                   << boost::system::system_error(ec).what());
+      return;
+    }
 
     face = createFace(clientSocket,
                       onFaceCreatedNewPeerCallback,
diff --git a/daemon/face/udp-face.cpp b/daemon/face/udp-face.cpp
index 81e915c..17214c5 100644
--- a/daemon/face/udp-face.cpp
+++ b/daemon/face/udp-face.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -24,6 +24,7 @@
  */
 
 #include "udp-face.hpp"
+// #include "core/global-io.hpp" // for #1718 manual test below
 
 #ifdef __linux__
 #include <netinet/in.h> // for IP_MTU_DISCOVER and IP_PMTUDISC_DONT
@@ -112,6 +113,12 @@
                    << ",uri:" << this->getRemoteUri()
                    << "] Idle for more than " << m_idleTimeout << ", closing");
       close();
+
+      // #1718 manual test: uncomment, run NFD in valgrind, send in a UDP packet
+      //                    expect read-after-free error and crash
+      // getGlobalIoService().post([this] {
+      //   NFD_LOG_ERROR("Remaining references: " << this->shared_from_this().use_count());
+      // });
     }
     else {
       resetRecentUsage();
diff --git a/daemon/face/udp-face.hpp b/daemon/face/udp-face.hpp
index 49c374b..b56b81d 100644
--- a/daemon/face/udp-face.hpp
+++ b/daemon/face/udp-face.hpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * 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.
@@ -20,7 +21,7 @@
  *
  * 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_UDP_FACE_HPP
 #define NFD_DAEMON_FACE_UDP_FACE_HPP
@@ -52,7 +53,7 @@
   closeIfIdle();
 
 private:
-  EventId m_closeIfIdleEvent;
+  scheduler::EventId m_closeIfIdleEvent;
   time::seconds m_idleTimeout;
   time::steady_clock::TimePoint m_lastIdleCheck;
 };
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
index dcb819d..4ef4e17 100644
--- a/daemon/face/udp-factory.cpp
+++ b/daemon/face/udp-factory.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -25,8 +25,8 @@
 
 #include "udp-factory.hpp"
 #include "core/global-io.hpp"
-#include "core/resolver.hpp"
 #include "core/network-interface.hpp"
+#include <ndn-cxx/util/dns.hpp>
 
 #if defined(__linux__)
 #include <sys/socket.h>
@@ -38,6 +38,12 @@
 
 NFD_LOG_INIT("UdpFactory");
 
+static const boost::asio::ip::address_v4 ALL_V4_ENDPOINT(
+  boost::asio::ip::address_v4::from_string("0.0.0.0"));
+
+static const boost::asio::ip::address_v6 ALL_V6_ENDPOINT(
+  boost::asio::ip::address_v6::from_string("::"));
+
 UdpFactory::UdpFactory(const std::string& defaultPort/* = "6363"*/)
   : m_defaultPort(defaultPort)
 {
@@ -48,9 +54,6 @@
 {
   using namespace boost::asio::ip;
 
-  static const address_v4 ALL_V4_ENDPOINT(address_v4::from_string("0.0.0.0"));
-  static const address_v6 ALL_V6_ENDPOINT(address_v6::from_string("::"));
-
   const address& address = endpoint.address();
 
   if (address.is_v4() && address == ALL_V4_ENDPOINT)
@@ -62,8 +65,7 @@
       prohibitAllIpv6Endpoints(endpoint.port());
     }
 
-  NFD_LOG_TRACE("prohibiting UDP " <<
-                endpoint.address().to_string() << ":" << endpoint.port());
+  NFD_LOG_TRACE("prohibiting UDP " << endpoint);
 
   m_prohibitedEndpoints.insert(endpoint);
 }
@@ -73,14 +75,14 @@
 {
   using namespace boost::asio::ip;
 
-  static const address_v4 INVALID_BROADCAST(address_v4::from_string("0.0.0.0"));
-
   for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
     for (const address_v4& addr : nic.ipv4Addresses) {
-      prohibitEndpoint(udp::Endpoint(addr, port));
+      if (addr != ALL_V4_ENDPOINT) {
+        prohibitEndpoint(udp::Endpoint(addr, port));
+      }
     }
 
-    if (nic.isBroadcastCapable() && nic.broadcastAddress != INVALID_BROADCAST)
+    if (nic.isBroadcastCapable() && nic.broadcastAddress != ALL_V4_ENDPOINT)
     {
       NFD_LOG_TRACE("prohibiting broadcast address: " << nic.broadcastAddress.to_string());
       prohibitEndpoint(udp::Endpoint(nic.broadcastAddress, port));
@@ -97,7 +99,9 @@
 
   for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
     for (const address_v6& addr : nic.ipv6Addresses) {
-      prohibitEndpoint(udp::Endpoint(addr, port));
+      if (addr != ALL_V6_ENDPOINT) {
+        prohibitEndpoint(udp::Endpoint(addr, port));
+      }
     }
   }
 }
@@ -136,7 +140,9 @@
                           const std::string& localPort,
                           const time::seconds& timeout)
 {
-  return createChannel(UdpResolver::syncResolve(localHost, localPort), timeout);
+  udp::Endpoint endPoint(ndn::dns::syncResolve(localHost, getGlobalIoService()),
+                         boost::lexical_cast<uint16_t>(localPort));
+  return createChannel(endPoint, timeout);
 }
 
 shared_ptr<MulticastUdpFace>
@@ -233,7 +239,8 @@
 
   multicastFace = make_shared<MulticastUdpFace>(receiveSocket, sendSocket,
                                                 localEndpoint, multicastEndpoint);
-  multicastFace->onFail += bind(&UdpFactory::afterFaceFailed, this, localEndpoint);
+
+  multicastFace->onFail.connectSingleShot(bind(&UdpFactory::afterFaceFailed, this, localEndpoint));
 
   m_multicastFaces[localEndpoint] = multicastFace;
 
@@ -246,12 +253,13 @@
                                 const std::string& multicastPort,
                                 const std::string& networkInterfaceName /* "" */)
 {
+  udp::Endpoint localEndpoint(ndn::dns::syncResolve(localIp, getGlobalIoService()),
+                              boost::lexical_cast<uint16_t>(multicastPort));
 
-  return createMulticastFace(UdpResolver::syncResolve(localIp,
-                                                      multicastPort),
-                             UdpResolver::syncResolve(multicastIp,
-                                                      multicastPort),
-                             networkInterfaceName);
+  udp::Endpoint multicastEndpoint(ndn::dns::syncResolve(multicastIp, getGlobalIoService()),
+                                  boost::lexical_cast<uint16_t>(multicastPort));
+
+  return createMulticastFace(localEndpoint, multicastEndpoint, networkInterfaceName);
 }
 
 void
@@ -259,31 +267,10 @@
                        const FaceCreatedCallback& onCreated,
                        const FaceConnectFailedCallback& onConnectFailed)
 {
-  resolver::AddressSelector addressSelector = resolver::AnyAddress();
-  if (uri.getScheme() == "udp4")
-    addressSelector = resolver::Ipv4Address();
-  else if (uri.getScheme() == "udp6")
-    addressSelector = resolver::Ipv6Address();
+  BOOST_ASSERT(uri.isCanonical());
+  boost::asio::ip::address ipAddress = boost::asio::ip::address::from_string(uri.getHost());
+  udp::Endpoint endpoint(ipAddress, boost::lexical_cast<uint16_t>(uri.getPort()));
 
-  if (!uri.getPath().empty() && uri.getPath() != "/")
-    {
-      onConnectFailed("Invalid URI");
-    }
-
-  UdpResolver::asyncResolve(uri.getHost(),
-                            uri.getPort().empty() ? m_defaultPort : uri.getPort(),
-                            bind(&UdpFactory::continueCreateFaceAfterResolve, this, _1,
-                                 onCreated, onConnectFailed),
-                            onConnectFailed,
-                            addressSelector);
-
-}
-
-void
-UdpFactory::continueCreateFaceAfterResolve(const udp::Endpoint& endpoint,
-                                           const FaceCreatedCallback& onCreated,
-                                           const FaceConnectFailedCallback& onConnectFailed)
-{
   if (endpoint.address().is_multicast()) {
     onConnectFailed("The provided address is multicast. Please use createMulticastFace method");
     return;
diff --git a/daemon/face/udp-factory.hpp b/daemon/face/udp-factory.hpp
index 5d83541..db4760b 100644
--- a/daemon/face/udp-factory.hpp
+++ b/daemon/face/udp-factory.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -160,7 +160,7 @@
   const MulticastFaceMap&
   getMulticastFaces() const;
 
-private:
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
 
   void
   prohibitEndpoint(const udp::Endpoint& endpoint);
@@ -197,12 +197,7 @@
   shared_ptr<MulticastUdpFace>
   findMulticastFace(const udp::Endpoint& localEndpoint);
 
-  void
-  continueCreateFaceAfterResolve(const udp::Endpoint& endpoint,
-                                 const FaceCreatedCallback& onCreated,
-                                 const FaceConnectFailedCallback& onConnectFailed);
-
-private:
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   typedef std::map< udp::Endpoint, shared_ptr<UdpChannel> > ChannelMap;
 
   ChannelMap m_channels;
diff --git a/daemon/face/websocket-channel.cpp b/daemon/face/websocket-channel.cpp
index 8abdbac..016dcac 100644
--- a/daemon/face/websocket-channel.cpp
+++ b/daemon/face/websocket-channel.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -120,8 +120,8 @@
   m_channelFaces[hdl] = face;
 
   // Schedule PING message
-  EventId pingEvent = scheduler::schedule(m_pingInterval,
-                                          bind(&WebSocketChannel::sendPing, this, hdl));
+  scheduler::EventId pingEvent = scheduler::schedule(m_pingInterval,
+                                                     bind(&WebSocketChannel::sendPing, this, hdl));
   face->setPingEventId(pingEvent);
 }
 
@@ -145,8 +145,8 @@
       NFD_LOG_TRACE("sendPing: to " << it->second->getRemoteUri());
 
       // Schedule next PING message
-      EventId pingEvent = scheduler::schedule(m_pingInterval,
-                                              bind(&WebSocketChannel::sendPing, this, hdl));
+      scheduler::EventId pingEvent = scheduler::schedule(m_pingInterval,
+                                         bind(&WebSocketChannel::sendPing, this, hdl));
       it->second->setPingEventId(pingEvent);
     }
 }
diff --git a/daemon/face/websocket-face.cpp b/daemon/face/websocket-face.cpp
index abd8b3c..26c1a25 100644
--- a/daemon/face/websocket-face.cpp
+++ b/daemon/face/websocket-face.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -47,7 +47,7 @@
   if (m_closed)
     return;
 
-  this->onSendInterest(interest);
+  this->emitSignal(onSendInterest, interest);
   const Block& payload = interest.wireEncode();
   this->getMutableCounters().getNOutBytes() += payload.size();
 
@@ -67,7 +67,7 @@
   if (m_closed)
     return;
 
-  this->onSendData(data);
+  this->emitSignal(onSendData, data);
   const Block& payload = data.wireEncode();
   this->getMutableCounters().getNOutBytes() += payload.size();
 
diff --git a/daemon/face/websocket-face.hpp b/daemon/face/websocket-face.hpp
index 81a1111..56dd015 100644
--- a/daemon/face/websocket-face.hpp
+++ b/daemon/face/websocket-face.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology,
- *                     The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -21,7 +21,7 @@
  *
  * 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_WEBSOCKET_FACE_HPP
 #define NFD_DAEMON_FACE_WEBSOCKET_FACE_HPP
@@ -65,7 +65,7 @@
   close();
 
   void
-  setPingEventId(EventId& id)
+  setPingEventId(scheduler::EventId& id)
   {
     m_pingEventId = id;
   }
@@ -76,7 +76,7 @@
 private:
   websocketpp::connection_hdl m_handle;
   websocket::Server& m_server;
-  EventId m_pingEventId;
+  scheduler::EventId m_pingEventId;
   bool m_closed;
 };
 
diff --git a/daemon/face/websocket-factory.cpp b/daemon/face/websocket-factory.cpp
index 30f3afd..49f28b0 100644
--- a/daemon/face/websocket-factory.cpp
+++ b/daemon/face/websocket-factory.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -24,7 +24,7 @@
  */
 
 #include "websocket-factory.hpp"
-#include "core/resolver.hpp"
+#include <ndn-cxx/util/dns.hpp>
 
 namespace nfd {
 
@@ -53,7 +53,8 @@
 shared_ptr<WebSocketChannel>
 WebSocketFactory::createChannel(const std::string& host, const std::string& port)
 {
-  ip::tcp::endpoint tcpEndpoint = TcpResolver::syncResolve(host, port);
+  ip::tcp::endpoint tcpEndpoint(ndn::dns::syncResolve(host, getGlobalIoService()),
+                                boost::lexical_cast<uint16_t>(port));
   websocket::Endpoint endpoint(tcpEndpoint.address(), tcpEndpoint.port());
   return createChannel(endpoint);
 }
diff --git a/daemon/fw/best-route-strategy2.cpp b/daemon/fw/best-route-strategy2.cpp
index 044f5e9..bd58590 100644
--- a/daemon/fw/best-route-strategy2.cpp
+++ b/daemon/fw/best-route-strategy2.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -32,8 +32,6 @@
 NFD_LOG_INIT("BestRouteStrategy2");
 
 const Name BestRouteStrategy2::STRATEGY_NAME("ndn:/localhost/nfd/strategy/best-route/%FD%02");
-/// \todo don't use fixed interval; make it adaptive or use exponential back-off #1913
-const time::milliseconds BestRouteStrategy2::MIN_RETRANSMISSION_INTERVAL(100);
 
 BestRouteStrategy2::BestRouteStrategy2(Forwarder& forwarder, const Name& name)
   : Strategy(forwarder, name)
@@ -73,12 +71,6 @@
   return true;
 }
 
-static inline bool
-compare_OutRecord_lastRenewed(const pit::OutRecord& a, const pit::OutRecord& b)
-{
-  return a.getLastRenewed() < b.getLastRenewed();
-}
-
 /** \brief pick an eligible NextHop with earliest OutRecord
  *  \note It is assumed that every nexthop has an OutRecord
  */
@@ -111,8 +103,9 @@
   const fib::NextHopList& nexthops = fibEntry->getNextHops();
   fib::NextHopList::const_iterator it = nexthops.end();
 
-  bool isNewPitEntry = !pitEntry->hasUnexpiredOutRecords();
-  if (isNewPitEntry) {
+  RetransmissionSuppression::Result suppression =
+      m_retransmissionSuppression.decide(inFace, interest, *pitEntry);
+  if (suppression == RetransmissionSuppression::NEW) {
     // forward to nexthop with lowest cost except downstream
     it = std::find_if(nexthops.begin(), nexthops.end(),
       bind(&predicate_NextHop_eligible, pitEntry, _1, inFace.getId(),
@@ -131,24 +124,16 @@
     return;
   }
 
-  // when was the last outgoing Interest?
-  const pit::OutRecordCollection& outRecords = pitEntry->getOutRecords();
-  pit::OutRecordCollection::const_iterator lastOutgoing = std::max_element(
-    outRecords.begin(), outRecords.end(), &compare_OutRecord_lastRenewed);
-  BOOST_ASSERT(lastOutgoing != outRecords.end()); // otherwise it's new PIT entry
-
-  time::steady_clock::TimePoint now = time::steady_clock::now();
-  time::steady_clock::Duration sinceLastOutgoing = now - lastOutgoing->getLastRenewed();
-  bool shouldRetransmit = sinceLastOutgoing >= MIN_RETRANSMISSION_INTERVAL;
-  if (!shouldRetransmit) {
+  if (suppression == RetransmissionSuppression::SUPPRESS) {
     NFD_LOG_DEBUG(interest << " from=" << inFace.getId()
-                           << " dontRetransmit sinceLastOutgoing=" << sinceLastOutgoing.count());
+                           << " suppressed");
     return;
   }
 
   // find an unused upstream with lowest cost except downstream
   it = std::find_if(nexthops.begin(), nexthops.end(),
-    bind(&predicate_NextHop_eligible, pitEntry, _1, inFace.getId(), true, now));
+                    bind(&predicate_NextHop_eligible, pitEntry, _1, inFace.getId(),
+                         true, time::steady_clock::now()));
   if (it != nexthops.end()) {
     shared_ptr<Face> outFace = it->getFace();
     this->sendInterest(pitEntry, outFace);
diff --git a/daemon/fw/best-route-strategy2.hpp b/daemon/fw/best-route-strategy2.hpp
index bb259d0..5a0f3be 100644
--- a/daemon/fw/best-route-strategy2.hpp
+++ b/daemon/fw/best-route-strategy2.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -27,6 +27,7 @@
 #define NFD_DAEMON_FW_BEST_ROUTE_STRATEGY2_HPP
 
 #include "strategy.hpp"
+#include "retransmission-suppression.hpp"
 
 namespace nfd {
 namespace fw {
@@ -52,7 +53,9 @@
 
 public:
   static const Name STRATEGY_NAME;
-  static const time::milliseconds MIN_RETRANSMISSION_INTERVAL;
+
+private:
+  RetransmissionSuppression m_retransmissionSuppression;
 };
 
 } // namespace fw
diff --git a/daemon/fw/face-table.cpp b/daemon/fw/face-table.cpp
index f1db53b..1848380 100644
--- a/daemon/fw/face-table.cpp
+++ b/daemon/fw/face-table.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -85,12 +85,9 @@
   NFD_LOG_INFO("Added face id=" << faceId << " remote=" << face->getRemoteUri()
                                           << " local=" << face->getLocalUri());
 
-  face->onReceiveInterest += bind(&Forwarder::onInterest,
-                                  &m_forwarder, ref(*face), _1);
-  face->onReceiveData     += bind(&Forwarder::onData,
-                                  &m_forwarder, ref(*face), _1);
-  face->onFail            += bind(&FaceTable::remove,
-                                  this, face);
+  face->onReceiveInterest.connect(bind(&Forwarder::onInterest, &m_forwarder, ref(*face), _1));
+  face->onReceiveData.connect(bind(&Forwarder::onData, &m_forwarder, ref(*face), _1));
+  face->onFail.connectSingleShot(bind(&FaceTable::remove, this, face));
 
   this->onAdd(face);
 }
@@ -104,15 +101,7 @@
   m_faces.erase(faceId);
   face->setId(INVALID_FACEID);
   NFD_LOG_INFO("Removed face id=" << faceId << " remote=" << face->getRemoteUri() <<
-                                                 " local=" << face->getLocalUri());
-
-  // XXX This clears all subscriptions, because EventEmitter
-  //     does not support only removing Forwarder's subscription
-  face->onReceiveInterest.clear();
-  face->onReceiveData    .clear();
-  face->onSendInterest   .clear();
-  face->onSendData       .clear();
-  // don't clear onFail because other functions may need to execute
+                                                " local=" << face->getLocalUri());
 
   m_forwarder.getFib().removeNextHopFromAllEntries(face);
 }
diff --git a/daemon/fw/face-table.hpp b/daemon/fw/face-table.hpp
index 70ceb11..435999e 100644
--- a/daemon/fw/face-table.hpp
+++ b/daemon/fw/face-table.hpp
@@ -29,8 +29,7 @@
 #include "face/face.hpp"
 #include <boost/range/adaptor/map.hpp>
 
-namespace nfd
-{
+namespace nfd {
 
 class Forwarder;
 
@@ -73,16 +72,16 @@
   const_iterator
   end() const;
 
-public: // events
+public: // signals
   /** \brief fires after a Face is added
    */
-  EventEmitter<shared_ptr<Face> > onAdd;
+  signal::Signal<FaceTable, shared_ptr<Face>> onAdd;
 
   /** \brief fires before a Face is removed
    *
    *  FaceId is valid when this event is fired
    */
-  EventEmitter<shared_ptr<Face> > onRemove;
+  signal::Signal<FaceTable, shared_ptr<Face>> onRemove;
 
 private:
   void
diff --git a/daemon/fw/ncc-strategy.cpp b/daemon/fw/ncc-strategy.cpp
index e5aaab9..b9a0ce4 100644
--- a/daemon/fw/ncc-strategy.cpp
+++ b/daemon/fw/ncc-strategy.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -84,9 +84,14 @@
       bind(&NccStrategy::timeoutOnBestFace, this, weak_ptr<pit::Entry>(pitEntry)));
   }
   else {
-    // use first nexthop
-    this->sendInterest(pitEntry, nexthops.begin()->getFace());
-    // TODO avoid sending to inFace
+    // use first eligible nexthop
+    auto firstEligibleNexthop = std::find_if(nexthops.begin(), nexthops.end(),
+        [&pitEntry] (const fib::NextHop& nexthop) {
+          return pitEntry->canForwardTo(*nexthop.getFace());
+        });
+    if (firstEligibleNexthop != nexthops.end()) {
+      this->sendInterest(pitEntry, firstEligibleNexthop->getFace());
+    }
   }
 
   shared_ptr<Face> previousFace = measurementsEntryInfo->previousFace.lock();
diff --git a/daemon/fw/ncc-strategy.hpp b/daemon/fw/ncc-strategy.hpp
index 72f39e4..0c0991f 100644
--- a/daemon/fw/ncc-strategy.hpp
+++ b/daemon/fw/ncc-strategy.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -56,6 +56,12 @@
   class MeasurementsEntryInfo : public StrategyInfo
   {
   public:
+    static constexpr int
+    getTypeId()
+    {
+      return 1000;
+    }
+
     MeasurementsEntryInfo();
 
     void
@@ -93,14 +99,20 @@
   class PitEntryInfo : public StrategyInfo
   {
   public:
+    static constexpr int
+    getTypeId()
+    {
+      return 1001;
+    }
+
     virtual
     ~PitEntryInfo();
 
   public:
     /// timer that expires when best face does not respond within predicted time
-    EventId bestFaceTimeout;
+    scheduler::EventId bestFaceTimeout;
     /// timer for propagating to another face
-    EventId propagateTimer;
+    scheduler::EventId propagateTimer;
     /// maximum interval between forwarding to two nexthops except best and previous
     time::microseconds maxInterval;
   };
diff --git a/daemon/fw/retransmission-suppression.cpp b/daemon/fw/retransmission-suppression.cpp
new file mode 100644
index 0000000..be1ff0b
--- /dev/null
+++ b/daemon/fw/retransmission-suppression.cpp
@@ -0,0 +1,59 @@
+/* -*- 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 "retransmission-suppression.hpp"
+
+namespace nfd {
+namespace fw {
+
+/// \todo don't use fixed interval; make it adaptive or use exponential back-off #1913
+const time::milliseconds RetransmissionSuppression::MIN_RETRANSMISSION_INTERVAL(100);
+
+RetransmissionSuppression::Result
+RetransmissionSuppression::decide(const Face& inFace, const Interest& interest,
+                                  const pit::Entry& pitEntry)
+{
+  bool isNewPitEntry = !pitEntry.hasUnexpiredOutRecords();
+  if (isNewPitEntry) {
+    return NEW;
+  }
+
+  // when was the last outgoing Interest?
+  const pit::OutRecordCollection& outRecords = pitEntry.getOutRecords();
+  pit::OutRecordCollection::const_iterator lastOutgoing = std::max_element(
+      outRecords.begin(), outRecords.end(),
+      [] (const pit::OutRecord& a, const pit::OutRecord& b) {
+        return a.getLastRenewed() < b.getLastRenewed();
+      });
+  BOOST_ASSERT(lastOutgoing != outRecords.end()); // otherwise it's new PIT entry
+
+  time::steady_clock::TimePoint now = time::steady_clock::now();
+  time::steady_clock::Duration sinceLastOutgoing = now - lastOutgoing->getLastRenewed();
+  bool shouldSuppress = sinceLastOutgoing < MIN_RETRANSMISSION_INTERVAL;
+  return shouldSuppress ? SUPPRESS : FORWARD;
+}
+
+} // namespace fw
+} // namespace nfd
diff --git a/daemon/fw/retransmission-suppression.hpp b/daemon/fw/retransmission-suppression.hpp
new file mode 100644
index 0000000..4553433
--- /dev/null
+++ b/daemon/fw/retransmission-suppression.hpp
@@ -0,0 +1,66 @@
+/* -*- 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_FW_RETRANSMISSION_SUPPRESSION_HPP
+#define NFD_DAEMON_FW_RETRANSMISSION_SUPPRESSION_HPP
+
+#include "strategy.hpp"
+
+namespace nfd {
+namespace fw {
+
+/** \brief helper for consumer retransmission suppression
+ */
+class RetransmissionSuppression : noncopyable
+{
+public:
+  enum Result {
+    /** \brief Interest is new (not a retransmission)
+     */
+    NEW,
+
+    /** \brief Interest is retransmission and should be forwarded
+     */
+    FORWARD,
+
+    /** \brief Interest is retransmission and should be suppressed
+     */
+    SUPPRESS
+  };
+
+  /** \brief determines whether Interest is a retransmission,
+   *         and if so, whether it shall be forwarded or suppressed
+   */
+  Result
+  decide(const Face& inFace, const Interest& interest, const pit::Entry& pitEntry);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  static const time::milliseconds MIN_RETRANSMISSION_INTERVAL;
+};
+
+} // namespace fw
+} // namespace nfd
+
+#endif // NFD_DAEMON_FW_RETRANSMISSION_SUPPRESSION_HPP
diff --git a/daemon/fw/strategy-info.hpp b/daemon/fw/strategy-info.hpp
index d670f10..b701fc5 100644
--- a/daemon/fw/strategy-info.hpp
+++ b/daemon/fw/strategy-info.hpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * Copyright (c) 2014,  Regents of the University of California,
+ *                      Arizona Board of Regents,
+ *                      Colorado State University,
+ *                      University Pierre & Marie Curie, Sorbonne University,
+ *                      Washington University in St. Louis,
+ *                      Beijing Institute of Technology,
+ *                      The University of Memphis
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -20,7 +21,7 @@
  *
  * 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_FW_STRATEGY_INFO_HPP
 #define NFD_DAEMON_FW_STRATEGY_INFO_HPP
@@ -30,12 +31,21 @@
 namespace nfd {
 namespace fw {
 
-/** \class StrategyInfo
- *  \brief contains arbitrary information forwarding strategy places on table entries
+/** \brief contains arbitrary information forwarding strategy places on table entries
  */
 class StrategyInfo
 {
 public:
+  /** \fn static constexpr int getTypeId()
+   *  \return an integer that uniquely identifies this StrategyInfo type
+   *  \sa http://redmine.named-data.net/projects/nfd/wiki/StrategyInfoType
+   */
+  // static constexpr int
+  // getTypeId()
+  // {
+  //   return <type-identifier>;
+  // }
+
   virtual
   ~StrategyInfo();
 };
diff --git a/daemon/fw/strategy.cpp b/daemon/fw/strategy.cpp
index 96f132f..4f439db 100644
--- a/daemon/fw/strategy.cpp
+++ b/daemon/fw/strategy.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -33,10 +33,12 @@
 NFD_LOG_INIT("Strategy");
 
 Strategy::Strategy(Forwarder& forwarder, const Name& name)
-  : m_name(name)
+  : afterAddFace(forwarder.getFaceTable().onAdd)
+  , beforeRemoveFace(forwarder.getFaceTable().onRemove)
+  , m_name(name)
   , m_forwarder(forwarder)
   , m_measurements(m_forwarder.getMeasurements(),
-                   m_forwarder.getStrategyChoice(), this)
+                   m_forwarder.getStrategyChoice(), *this)
 {
 }
 
diff --git a/daemon/fw/strategy.hpp b/daemon/fw/strategy.hpp
index e1ff23c..813cce0 100644
--- a/daemon/fw/strategy.hpp
+++ b/daemon/fw/strategy.hpp
@@ -131,6 +131,13 @@
   shared_ptr<Face>
   getFace(FaceId id);
 
+  const FaceTable&
+  getFaceTable();
+
+protected: // accessors
+  signal::Signal<FaceTable, shared_ptr<Face>>& afterAddFace;
+  signal::Signal<FaceTable, shared_ptr<Face>>& beforeRemoveFace;
+
 private:
   Name m_name;
 
@@ -175,6 +182,12 @@
   return m_forwarder.getFace(id);
 }
 
+inline const FaceTable&
+Strategy::getFaceTable()
+{
+  return m_forwarder.getFaceTable();
+}
+
 } // namespace fw
 } // namespace nfd
 
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index c88329c..5256c92 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -38,6 +38,7 @@
 
 #ifdef HAVE_LIBPCAP
 #include "face/ethernet-factory.hpp"
+#include "face/ethernet-face.hpp"
 #endif // HAVE_LIBPCAP
 
 #ifdef HAVE_WEBSOCKET
@@ -124,6 +125,8 @@
                          ndn::KeyChain& keyChain)
   : ManagerBase(face, FACE_MANAGER_PRIVILEGE, keyChain)
   , m_faceTable(faceTable)
+  , m_faceAddConn(m_faceTable.onAdd.connect(bind(&FaceManager::onAddFace, this, _1)))
+  , m_faceRemoveConn(m_faceTable.onRemove.connect(bind(&FaceManager::onRemoveFace, this, _1)))
   , m_faceStatusPublisher(m_faceTable, *m_face, FACES_LIST_DATASET_PREFIX, keyChain)
   , m_channelStatusPublisher(m_factories, *m_face, CHANNELS_LIST_DATASET_PREFIX, keyChain)
   , m_notificationStream(*m_face, FACE_EVENTS_PREFIX, keyChain)
@@ -137,9 +140,6 @@
 {
   face->setInterestFilter("/localhost/nfd/faces",
                           bind(&FaceManager::onFaceRequest, this, _2));
-
-  m_faceTable.onAdd    += bind(&FaceManager::onAddFace, this, _1);
-  m_faceTable.onRemove += bind(&FaceManager::onRemoveFace, this, _1);
 }
 
 FaceManager::~FaceManager()
@@ -844,10 +844,18 @@
 {
   const Name& command = request.getName();
   const size_t commandNComps = command.size();
-  const Name::Component& verb = command.get(COMMAND_PREFIX.size());
 
-  UnsignedVerbDispatchTable::const_iterator unsignedVerbProcessor =
-    m_unsignedVerbDispatch.find(verb);
+  if (commandNComps <= COMMAND_PREFIX.size())
+    {
+      // command is too short to have a verb
+      NFD_LOG_DEBUG("command result: malformed");
+      sendResponse(command, 400, "Malformed command");
+      return;
+    }
+
+  const Name::Component& verb = command.at(COMMAND_PREFIX.size());
+
+  const auto unsignedVerbProcessor = m_unsignedVerbDispatch.find(verb);
   if (unsignedVerbProcessor != m_unsignedVerbDispatch.end())
     {
       NFD_LOG_DEBUG("command result: processing verb: " << verb);
@@ -950,6 +958,13 @@
       return;
     }
 
+  if (!uri.isCanonical())
+    {
+      sendResponse(requestName, 400, "Non-canonical URI");
+      NFD_LOG_TRACE("received non-canonical URI");
+      return;
+    }
+
   FactoryMap::iterator factory = m_factories.find(uri.getScheme());
   if (factory == m_factories.end())
     {
diff --git a/daemon/mgmt/face-manager.hpp b/daemon/mgmt/face-manager.hpp
index d4d979b..75477bf 100644
--- a/daemon/mgmt/face-manager.hpp
+++ b/daemon/mgmt/face-manager.hpp
@@ -173,6 +173,8 @@
 
 private:
   FaceTable& m_faceTable;
+  signal::ScopedConnection m_faceAddConn;
+  signal::ScopedConnection m_faceRemoveConn;
   FaceStatusPublisher m_faceStatusPublisher;
   ChannelStatusPublisher m_channelStatusPublisher;
   NotificationStream<AppFace> m_notificationStream;
diff --git a/daemon/mgmt/fib-manager.cpp b/daemon/mgmt/fib-manager.cpp
index 5c91547..3db26e7 100644
--- a/daemon/mgmt/fib-manager.cpp
+++ b/daemon/mgmt/fib-manager.cpp
@@ -104,9 +104,18 @@
 {
   const Name& command = request.getName();
   const size_t commandNComps = command.size();
-  const Name::Component& verb = command.get(COMMAND_PREFIX.size());
 
-  UnsignedVerbDispatchTable::const_iterator unsignedVerbProcessor = m_unsignedVerbDispatch.find(verb);
+  if (commandNComps <= COMMAND_PREFIX.size())
+    {
+      // command is too short to have a verb
+      NFD_LOG_DEBUG("command result: malformed");
+      sendResponse(command, 400, "Malformed command");
+      return;
+    }
+
+  const Name::Component& verb = command.at(COMMAND_PREFIX.size());
+
+  const auto unsignedVerbProcessor = m_unsignedVerbDispatch.find(verb);
   if (unsignedVerbProcessor != m_unsignedVerbDispatch.end())
     {
       NFD_LOG_DEBUG("command result: processing verb: " << verb);
diff --git a/daemon/mgmt/internal-face.cpp b/daemon/mgmt/internal-face.cpp
index 9fad328..ec142ef 100644
--- a/daemon/mgmt/internal-face.cpp
+++ b/daemon/mgmt/internal-face.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -39,7 +39,7 @@
 void
 InternalFace::sendInterest(const Interest& interest)
 {
-  onSendInterest(interest);
+  this->emitSignal(onSendInterest, interest);
 
   // Invoke .processInterest a bit later,
   // to avoid potential problems in forwarding pipelines.
@@ -122,7 +122,7 @@
 void
 InternalFace::sendData(const Data& data)
 {
-  onSendData(data);
+  this->emitSignal(onSendData, data);
 }
 
 void
@@ -142,7 +142,7 @@
 void
 InternalFace::put(const Data& data)
 {
-  onReceiveData(data);
+  this->emitSignal(onReceiveData, data);
 }
 
 InternalFace::~InternalFace()
diff --git a/daemon/mgmt/strategy-choice-manager.cpp b/daemon/mgmt/strategy-choice-manager.cpp
index e93353b..4f2728c 100644
--- a/daemon/mgmt/strategy-choice-manager.cpp
+++ b/daemon/mgmt/strategy-choice-manager.cpp
@@ -72,6 +72,13 @@
       listStrategies(request);
       return;
     }
+  else if (commandNComps <= COMMAND_PREFIX.size())
+    {
+      // command is too short to have a verb
+      NFD_LOG_DEBUG("command result: malformed");
+      sendResponse(command, 400, "Malformed command");
+      return;
+    }
 
   if (COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
       commandNComps < COMMAND_SIGNED_NCOMPS)
@@ -116,7 +123,7 @@
       return;
     }
 
-  const Name::Component& verb = command[COMMAND_PREFIX.size()];
+  const Name::Component& verb = command.at(COMMAND_PREFIX.size());
   ControlResponse response;
   if (verb == VERB_SET)
     {
diff --git a/daemon/table/cs-skip-list-entry.cpp b/daemon/table/cs-skip-list-entry.cpp
new file mode 100644
index 0000000..a10b3d2
--- /dev/null
+++ b/daemon/table/cs-skip-list-entry.cpp
@@ -0,0 +1,72 @@
+/* -*- 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/>.
+ *
+ * \author Ilya Moiseenko <http://ilyamoiseenko.com/>
+ * \author Junxiao Shi <http://www.cs.arizona.edu/people/shijunxiao/>
+ * \author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ */
+
+#include "cs-skip-list-entry.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+namespace cs {
+namespace skip_list {
+
+NFD_LOG_INIT("CsSkipListEntry");
+
+void
+Entry::release()
+{
+  BOOST_ASSERT(m_layerIterators.empty());
+
+  reset();
+}
+
+void
+Entry::setIterator(int layer, const Entry::LayerIterators::mapped_type& layerIterator)
+{
+  m_layerIterators[layer] = layerIterator;
+}
+
+void
+Entry::removeIterator(int layer)
+{
+  m_layerIterators.erase(layer);
+}
+
+void
+Entry::printIterators() const
+{
+  for (LayerIterators::const_iterator it = m_layerIterators.begin();
+       it != m_layerIterators.end();
+       ++it)
+    {
+      NFD_LOG_TRACE("[" << it->first << "]" << " " << &(*it->second));
+    }
+}
+
+} // namespace skip_list
+} // namespace cs
+} // namespace nfd
diff --git a/daemon/table/cs-skip-list-entry.hpp b/daemon/table/cs-skip-list-entry.hpp
new file mode 100644
index 0000000..2f5f7bd
--- /dev/null
+++ b/daemon/table/cs-skip-list-entry.hpp
@@ -0,0 +1,89 @@
+/* -*- 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/>.
+ *
+ * \author Ilya Moiseenko <http://ilyamoiseenko.com/>
+ * \author Junxiao Shi <http://www.cs.arizona.edu/people/shijunxiao/>
+ * \author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ */
+
+#ifndef NFD_DAEMON_TABLE_CS_SKIP_LIST_ENTRY_HPP
+#define NFD_DAEMON_TABLE_CS_SKIP_LIST_ENTRY_HPP
+
+#include "common.hpp"
+#include "cs-entry.hpp"
+
+namespace nfd {
+namespace cs {
+namespace skip_list {
+
+/** \brief represents an entry in a CS with skip list implementation
+ */
+class Entry : public cs::Entry
+{
+public:
+  typedef std::map<int, std::list<Entry*>::iterator > LayerIterators;
+
+  Entry() = default;
+
+  /** \brief releases reference counts on shared objects
+   */
+  void
+  release();
+
+  /** \brief saves the iterator pointing to the CS entry on a specific layer of skip list
+   */
+  void
+  setIterator(int layer, const LayerIterators::mapped_type& layerIterator);
+
+  /** \brief removes the iterator pointing to the CS entry on a specific layer of skip list
+   */
+  void
+  removeIterator(int layer);
+
+  /** \brief returns the table containing <layer, iterator> pairs.
+   */
+  const LayerIterators&
+  getIterators() const;
+
+private:
+  /** \brief prints <layer, iterator> pairs.
+   */
+  void
+  printIterators() const;
+
+private:
+  LayerIterators m_layerIterators;
+};
+
+inline const Entry::LayerIterators&
+Entry::getIterators() const
+{
+  return m_layerIterators;
+}
+
+} // namespace skip_list
+} // namespace cs
+} // namespace nfd
+
+#endif // NFD_DAEMON_TABLE_CS_SKIP_LIST_ENTRY_HPP
diff --git a/daemon/table/fib.cpp b/daemon/table/fib.cpp
index c28a695..aaa89c6 100644
--- a/daemon/table/fib.cpp
+++ b/daemon/table/fib.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -27,10 +27,25 @@
 #include "pit-entry.hpp"
 #include "measurements-entry.hpp"
 
+#include <boost/concept/assert.hpp>
+#include <boost/concept_check.hpp>
+#include <type_traits>
+
 namespace nfd {
 
 const shared_ptr<fib::Entry> Fib::s_emptyEntry = make_shared<fib::Entry>(Name());
 
+// http://en.cppreference.com/w/cpp/concept/ForwardIterator
+BOOST_CONCEPT_ASSERT((boost::ForwardIterator<Fib::const_iterator>));
+// boost::ForwardIterator follows SGI standard http://www.sgi.com/tech/stl/ForwardIterator.html,
+// which doesn't require DefaultConstructible
+#ifdef HAVE_IS_DEFAULT_CONSTRUCTIBLE
+static_assert(std::is_default_constructible<Fib::const_iterator>::value,
+              "Fib::const_iterator must be default-constructible");
+#else
+BOOST_CONCEPT_ASSERT((boost::DefaultConstructible<Fib::const_iterator>));
+#endif // HAVE_IS_DEFAULT_CONSTRUCTIBLE
+
 Fib::Fib(NameTree& nameTree)
   : m_nameTree(nameTree)
   , m_nItems(0)
diff --git a/daemon/table/fib.hpp b/daemon/table/fib.hpp
index 3a19949..bde7428 100644
--- a/daemon/table/fib.hpp
+++ b/daemon/table/fib.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -95,15 +95,26 @@
 public: // enumeration
   class const_iterator;
 
+  /** \brief returns an iterator pointing to the first FIB entry
+   *  \note Iteration order is implementation-specific and is undefined
+   *  \note The returned iterator may get invalidated if FIB or another NameTree-based
+   *        table is modified
+   */
   const_iterator
   begin() const;
 
+  /** \brief returns an iterator referring to the past-the-end FIB entry
+   *  \note The returned iterator may get invalidated if FIB or another NameTree-based
+   *        table is modified
+   */
   const_iterator
   end() const;
 
-  class const_iterator : public std::iterator<std::forward_iterator_tag, fib::Entry>
+  class const_iterator : public std::iterator<std::forward_iterator_tag, const fib::Entry>
   {
   public:
+    const_iterator() = default;
+
     explicit
     const_iterator(const NameTree::const_iterator& it);
 
@@ -193,7 +204,7 @@
 inline const fib::Entry&
 Fib::const_iterator::operator*() const
 {
-  return *(m_nameTreeIterator->getFibEntry());
+  return *this->operator->();
 }
 
 inline shared_ptr<fib::Entry>
diff --git a/daemon/table/measurements-accessor.cpp b/daemon/table/measurements-accessor.cpp
index 81b1432..47b4fe2 100644
--- a/daemon/table/measurements-accessor.cpp
+++ b/daemon/table/measurements-accessor.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -30,11 +30,11 @@
 using fw::Strategy;
 
 MeasurementsAccessor::MeasurementsAccessor(Measurements& measurements,
-                                           StrategyChoice& strategyChoice,
-                                           Strategy* strategy)
+                                           const StrategyChoice& strategyChoice,
+                                           const Strategy& strategy)
   : m_measurements(measurements)
   , m_strategyChoice(strategyChoice)
-  , m_strategy(strategy)
+  , m_strategy(&strategy)
 {
 }
 
@@ -43,9 +43,9 @@
 }
 
 shared_ptr<measurements::Entry>
-MeasurementsAccessor::filter(const shared_ptr<measurements::Entry>& entry)
+MeasurementsAccessor::filter(const shared_ptr<measurements::Entry>& entry) const
 {
-  if (!static_cast<bool>(entry)) {
+  if (entry == nullptr) {
     return entry;
   }
 
diff --git a/daemon/table/measurements-accessor.hpp b/daemon/table/measurements-accessor.hpp
index 872cff2..9a280c2 100644
--- a/daemon/table/measurements-accessor.hpp
+++ b/daemon/table/measurements-accessor.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -40,22 +40,22 @@
 class MeasurementsAccessor : noncopyable
 {
 public:
-  MeasurementsAccessor(Measurements& measurements, StrategyChoice& strategyChoice,
-                       fw::Strategy* strategy);
+  MeasurementsAccessor(Measurements& measurements, const StrategyChoice& strategyChoice,
+                       const fw::Strategy& strategy);
 
   ~MeasurementsAccessor();
 
-  /** \brief find or insert a Measurements entry for name
+  /** \brief find or insert a Measurements entry for \p name
    */
   shared_ptr<measurements::Entry>
   get(const Name& name);
 
-  /** \brief find or insert a Measurements entry for fibEntry->getPrefix()
+  /** \brief find or insert a Measurements entry for \p fibEntry->getPrefix()
    */
   shared_ptr<measurements::Entry>
   get(const fib::Entry& fibEntry);
 
-  /** \brief find or insert a Measurements entry for pitEntry->getName()
+  /** \brief find or insert a Measurements entry for \p pitEntry->getName()
    */
   shared_ptr<measurements::Entry>
   get(const pit::Entry& pitEntry);
@@ -66,6 +66,25 @@
   shared_ptr<measurements::Entry>
   getParent(const measurements::Entry& child);
 
+  /** \brief perform a longest prefix match for \p name
+   */
+  shared_ptr<measurements::Entry>
+  findLongestPrefixMatch(const Name& name,
+                         const measurements::EntryPredicate& pred =
+                             measurements::AnyEntry()) const;
+
+  /** \brief perform a longest prefix match for \p pitEntry.getName()
+   */
+  shared_ptr<measurements::Entry>
+  findLongestPrefixMatch(const pit::Entry& pitEntry,
+                         const measurements::EntryPredicate& pred =
+                             measurements::AnyEntry()) const;
+
+  /** \brief perform an exact match
+   */
+  shared_ptr<measurements::Entry>
+  findExactMatch(const Name& name) const;
+
   /** \brief extend lifetime of an entry
    *
    *  The entry will be kept until at least now()+lifetime.
@@ -78,12 +97,12 @@
    *  \return entry if strategy has access to namespace, otherwise nullptr
    */
   shared_ptr<measurements::Entry>
-  filter(const shared_ptr<measurements::Entry>& entry);
+  filter(const shared_ptr<measurements::Entry>& entry) const;
 
 private:
   Measurements& m_measurements;
-  StrategyChoice& m_strategyChoice;
-  fw::Strategy* m_strategy;
+  const StrategyChoice& m_strategyChoice;
+  const fw::Strategy* m_strategy;
 };
 
 inline shared_ptr<measurements::Entry>
@@ -110,6 +129,26 @@
   return this->filter(m_measurements.getParent(child));
 }
 
+inline shared_ptr<measurements::Entry>
+MeasurementsAccessor::findLongestPrefixMatch(const Name& name,
+                                             const measurements::EntryPredicate& pred) const
+{
+  return this->filter(m_measurements.findLongestPrefixMatch(name, pred));
+}
+
+inline shared_ptr<measurements::Entry>
+MeasurementsAccessor::findLongestPrefixMatch(const pit::Entry& pitEntry,
+                                             const measurements::EntryPredicate& pred) const
+{
+  return this->filter(m_measurements.findLongestPrefixMatch(pitEntry, pred));
+}
+
+inline shared_ptr<measurements::Entry>
+MeasurementsAccessor::findExactMatch(const Name& name) const
+{
+  return this->filter(m_measurements.findExactMatch(name));
+}
+
 inline void
 MeasurementsAccessor::extendLifetime(measurements::Entry& entry,
                                      const time::nanoseconds& lifetime)
diff --git a/daemon/table/measurements-entry.hpp b/daemon/table/measurements-entry.hpp
index 6593ee9..f2dd627 100644
--- a/daemon/table/measurements-entry.hpp
+++ b/daemon/table/measurements-entry.hpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * 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.
@@ -20,7 +21,7 @@
  *
  * 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_TABLE_MEASUREMENTS_ENTRY_HPP
 #define NFD_DAEMON_TABLE_MEASUREMENTS_ENTRY_HPP
@@ -58,7 +59,7 @@
 
 private: // lifetime
   time::steady_clock::TimePoint m_expiry;
-  EventId m_cleanup;
+  scheduler::EventId m_cleanup;
   shared_ptr<name_tree::Entry> m_nameTreeEntry;
 
   friend class nfd::NameTree;
diff --git a/daemon/table/measurements.cpp b/daemon/table/measurements.cpp
index 8a24306..03c7927 100644
--- a/daemon/table/measurements.cpp
+++ b/daemon/table/measurements.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -30,20 +30,22 @@
 
 namespace nfd {
 
+using measurements::Entry;
+
 Measurements::Measurements(NameTree& nameTree)
   : m_nameTree(nameTree)
   , m_nItems(0)
 {
 }
 
-shared_ptr<measurements::Entry>
+shared_ptr<Entry>
 Measurements::get(name_tree::Entry& nte)
 {
-  shared_ptr<measurements::Entry> entry = nte.getMeasurementsEntry();
+  shared_ptr<Entry> entry = nte.getMeasurementsEntry();
   if (entry != nullptr)
     return entry;
 
-  entry = make_shared<measurements::Entry>(nte.getPrefix());
+  entry = make_shared<Entry>(nte.getPrefix());
   nte.setMeasurementsEntry(entry);
   ++m_nItems;
 
@@ -54,29 +56,29 @@
   return entry;
 }
 
-shared_ptr<measurements::Entry>
+shared_ptr<Entry>
 Measurements::get(const Name& name)
 {
   shared_ptr<name_tree::Entry> nte = m_nameTree.lookup(name);
   return this->get(*nte);
 }
 
-shared_ptr<measurements::Entry>
+shared_ptr<Entry>
 Measurements::get(const fib::Entry& fibEntry)
 {
   shared_ptr<name_tree::Entry> nte = m_nameTree.get(fibEntry);
   return this->get(*nte);
 }
 
-shared_ptr<measurements::Entry>
+shared_ptr<Entry>
 Measurements::get(const pit::Entry& pitEntry)
 {
   shared_ptr<name_tree::Entry> nte = m_nameTree.get(pitEntry);
   return this->get(*nte);
 }
 
-shared_ptr<measurements::Entry>
-Measurements::getParent(const measurements::Entry& child)
+shared_ptr<Entry>
+Measurements::getParent(const Entry& child)
 {
   if (child.getName().size() == 0) { // the root entry
     return nullptr;
@@ -88,18 +90,38 @@
   return this->get(*nte);
 }
 
-shared_ptr<measurements::Entry>
-Measurements::findLongestPrefixMatch(const Name& name) const
+template<typename K>
+shared_ptr<Entry>
+Measurements::findLongestPrefixMatchImpl(const K& key,
+                                         const measurements::EntryPredicate& pred) const
 {
-  shared_ptr<name_tree::Entry> nte = m_nameTree.findLongestPrefixMatch(name,
-      [] (const name_tree::Entry& nte) { return nte.getMeasurementsEntry() != nullptr; });
-  if (nte != nullptr) {
-    return nte->getMeasurementsEntry();
+  shared_ptr<name_tree::Entry> match = m_nameTree.findLongestPrefixMatch(key,
+      [pred] (const name_tree::Entry& nte) -> bool {
+        shared_ptr<Entry> entry = nte.getMeasurementsEntry();
+        return entry != nullptr && pred(*entry);
+      });
+  if (match != nullptr) {
+    return match->getMeasurementsEntry();
   }
   return nullptr;
 }
 
-shared_ptr<measurements::Entry>
+shared_ptr<Entry>
+Measurements::findLongestPrefixMatch(const Name& name,
+                                     const measurements::EntryPredicate& pred) const
+{
+  return this->findLongestPrefixMatchImpl(name, pred);
+}
+
+shared_ptr<Entry>
+Measurements::findLongestPrefixMatch(const pit::Entry& pitEntry,
+                                     const measurements::EntryPredicate& pred) const
+{
+  shared_ptr<name_tree::Entry> nte = m_nameTree.get(pitEntry);
+  return this->findLongestPrefixMatchImpl(nte, pred);
+}
+
+shared_ptr<Entry>
 Measurements::findExactMatch(const Name& name) const
 {
   shared_ptr<name_tree::Entry> nte = m_nameTree.lookup(name);
@@ -109,7 +131,7 @@
 }
 
 void
-Measurements::extendLifetime(measurements::Entry& entry,
+Measurements::extendLifetime(Entry& entry,
                              const time::nanoseconds& lifetime)
 {
   shared_ptr<name_tree::Entry> nte = m_nameTree.get(entry);
@@ -131,7 +153,7 @@
 }
 
 void
-Measurements::cleanup(measurements::Entry& entry)
+Measurements::cleanup(Entry& entry)
 {
   shared_ptr<name_tree::Entry> nte = m_nameTree.get(entry);
   if (nte != nullptr) {
diff --git a/daemon/table/measurements.hpp b/daemon/table/measurements.hpp
index 71d8db5..5a61caa 100644
--- a/daemon/table/measurements.hpp
+++ b/daemon/table/measurements.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -33,15 +33,44 @@
 
 namespace fib {
 class Entry;
-}
+} // namespace fib
 
 namespace pit {
 class Entry;
-}
+} // namespace pit
 
+namespace measurements {
 
-/** \class Measurement
- *  \brief represents the Measurements table
+/** \brief a predicate that accepts or rejects a \p Entry
+ */
+typedef std::function<bool(const Entry&)> EntryPredicate;
+
+/** \brief an \p EntryPredicate that accepts any \p Entry
+ */
+class AnyEntry
+{
+public:
+  bool
+  operator()(const Entry& entry)
+  {
+    return true;
+  }
+};
+
+template<typename T>
+class EntryWithStrategyInfo
+{
+public:
+  bool
+  operator()(const Entry& entry)
+  {
+    return entry.getStrategyInfo<T>() != nullptr;
+  }
+};
+
+} // namespace measurements
+
+/** \brief represents the Measurements table
  */
 class Measurements : noncopyable
 {
@@ -49,17 +78,17 @@
   explicit
   Measurements(NameTree& nametree);
 
-  /** \brief find or insert a Measurements entry for name
+  /** \brief find or insert a Measurements entry for \p name
    */
   shared_ptr<measurements::Entry>
   get(const Name& name);
 
-  /** \brief find or insert a Measurements entry for fibEntry->getPrefix()
+  /** \brief find or insert a Measurements entry for \p fibEntry.getPrefix()
    */
   shared_ptr<measurements::Entry>
   get(const fib::Entry& fibEntry);
 
-  /** \brief find or insert a Measurements entry for pitEntry->getName()
+  /** \brief find or insert a Measurements entry for \p pitEntry.getName()
    */
   shared_ptr<measurements::Entry>
   get(const pit::Entry& pitEntry);
@@ -70,10 +99,19 @@
   shared_ptr<measurements::Entry>
   getParent(const measurements::Entry& child);
 
-  /** \brief perform a longest prefix match
+  /** \brief perform a longest prefix match for \p name
    */
   shared_ptr<measurements::Entry>
-  findLongestPrefixMatch(const Name& name) const;
+  findLongestPrefixMatch(const Name& name,
+                         const measurements::EntryPredicate& pred =
+                             measurements::AnyEntry()) const;
+
+  /** \brief perform a longest prefix match for \p pitEntry.getName()
+   */
+  shared_ptr<measurements::Entry>
+  findLongestPrefixMatch(const pit::Entry& pitEntry,
+                         const measurements::EntryPredicate& pred =
+                             measurements::AnyEntry()) const;
 
   /** \brief perform an exact match
    */
@@ -100,10 +138,15 @@
   shared_ptr<measurements::Entry>
   get(name_tree::Entry& nte);
 
+  /** \tparam K Name or shared_ptr<name_tree::Entry>
+   */
+  template<typename K>
+  shared_ptr<measurements::Entry>
+  findLongestPrefixMatchImpl(const K& key, const measurements::EntryPredicate& pred) const;
+
 private:
   NameTree& m_nameTree;
   size_t m_nItems;
-  static const time::nanoseconds s_defaultLifetime;
 };
 
 inline time::nanoseconds
diff --git a/daemon/table/name-tree.cpp b/daemon/table/name-tree.cpp
index 61a5364..4e2182b 100644
--- a/daemon/table/name-tree.cpp
+++ b/daemon/table/name-tree.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -27,10 +27,25 @@
 #include "core/logger.hpp"
 #include "core/city-hash.hpp"
 
+#include <boost/concept/assert.hpp>
+#include <boost/concept_check.hpp>
+#include <type_traits>
+
 namespace nfd {
 
 NFD_LOG_INIT("NameTree");
 
+// http://en.cppreference.com/w/cpp/concept/ForwardIterator
+BOOST_CONCEPT_ASSERT((boost::ForwardIterator<NameTree::const_iterator>));
+// boost::ForwardIterator follows SGI standard http://www.sgi.com/tech/stl/ForwardIterator.html,
+// which doesn't require DefaultConstructible
+#ifdef HAVE_IS_DEFAULT_CONSTRUCTIBLE
+static_assert(std::is_default_constructible<NameTree::const_iterator>::value,
+              "NameTree::const_iterator must be default-constructible");
+#else
+BOOST_CONCEPT_ASSERT((boost::DefaultConstructible<NameTree::const_iterator>));
+#endif // HAVE_IS_DEFAULT_CONSTRUCTIBLE
+
 namespace name_tree {
 
 class Hash32
@@ -339,7 +354,7 @@
                 }
             }
 
-          BOOST_ASSERT(isFound == true);
+          BOOST_VERIFY(isFound == true);
         }
 
       // remove this Entry and its Name Tree Node
@@ -569,12 +584,17 @@
   output << "--------------------------\n";
 }
 
+NameTree::const_iterator::const_iterator()
+  : m_nameTree(nullptr)
+{
+}
+
 NameTree::const_iterator::const_iterator(NameTree::IteratorType type,
                             const NameTree& nameTree,
                             shared_ptr<name_tree::Entry> entry,
                             const name_tree::EntrySelector& entrySelector,
                             const name_tree::EntrySubTreeSelector& entrySubTreeSelector)
-  : m_nameTree(nameTree)
+  : m_nameTree(&nameTree)
   , m_entry(entry)
   , m_subTreeRoot(entry)
   , m_entrySelector(make_shared<name_tree::EntrySelector>(entrySelector))
@@ -590,7 +610,7 @@
 {
   NFD_LOG_TRACE("const_iterator::operator++()");
 
-  BOOST_ASSERT(m_entry != m_nameTree.m_end);
+  BOOST_ASSERT(m_entry != m_nameTree->m_end);
 
   if (m_type == FULL_ENUMERATE_TYPE) // fullEnumerate
     {
@@ -608,12 +628,12 @@
 
       // process other buckets
 
-      for (int newLocation = m_entry->m_hash % m_nameTree.m_nBuckets + 1;
-           newLocation < static_cast<int>(m_nameTree.m_nBuckets);
+      for (int newLocation = m_entry->m_hash % m_nameTree->m_nBuckets + 1;
+           newLocation < static_cast<int>(m_nameTree->m_nBuckets);
            ++newLocation)
         {
           // process each bucket
-          name_tree::Node* node = m_nameTree.m_buckets[newLocation];
+          name_tree::Node* node = m_nameTree->m_buckets[newLocation];
           while (node != 0)
             {
               m_entry = node->m_entry;
@@ -625,9 +645,9 @@
               node = node->m_next;
             }
         }
-      BOOST_ASSERT(isFound == false);
+      BOOST_VERIFY(isFound == false);
       // Reach to the end()
-      m_entry = m_nameTree.m_end;
+      m_entry = m_nameTree->m_end;
       return *this;
     }
 
@@ -702,7 +722,7 @@
                     }
                 }
 
-              BOOST_ASSERT(isFound == true);
+              BOOST_VERIFY(isFound == true);
               if (i < parentChildrenList.size() - 1) // m_entry not the last child
                 {
                   m_entry = parentChildrenList[i + 1];
@@ -727,7 +747,7 @@
             }
         }
 
-      m_entry = m_nameTree.m_end;
+      m_entry = m_nameTree->m_end;
       return *this;
     }
 
@@ -745,7 +765,7 @@
         }
 
       // Reach to the end (Root)
-      m_entry = m_nameTree.m_end;
+      m_entry = m_nameTree->m_end;
       return *this;
     }
 
diff --git a/daemon/table/name-tree.hpp b/daemon/table/name-tree.hpp
index 6e936d0..465299a 100644
--- a/daemon/table/name-tree.hpp
+++ b/daemon/table/name-tree.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -184,6 +184,8 @@
    *    ...
    *  }
    *  \endcode
+   *  \note Iteration order is implementation-specific and is undefined
+   *  \note The returned iterator may get invalidated when NameTree is modified
    */
   boost::iterator_range<const_iterator>
   findAllMatches(const Name& prefix,
@@ -201,6 +203,8 @@
    *    ...
    *  }
    *  \endcode
+   *  \note Iteration order is implementation-specific and is undefined
+   *  \note The returned iterator may get invalidated when NameTree is modified
    */
   boost::iterator_range<const_iterator>
   fullEnumerate(const name_tree::EntrySelector& entrySelector = name_tree::AnyEntry()) const;
@@ -216,15 +220,25 @@
    *    ...
    *  }
    *  \endcode
+   *  \note Iteration order is implementation-specific and is undefined
+   *  \note The returned iterator may get invalidated when NameTree is modified
    */
   boost::iterator_range<const_iterator>
   partialEnumerate(const Name& prefix,
                    const name_tree::EntrySubTreeSelector& entrySubTreeSelector =
                          name_tree::AnyEntrySubTree()) const;
 
+  /** \brief Get an iterator pointing to the first NameTree entry
+   *  \note Iteration order is implementation-specific and is undefined
+   *  \note The returned iterator may get invalidated when NameTree is modified
+   */
   const_iterator
   begin() const;
 
+  /** \brief Get an iterator referring to the past-the-end FIB entry
+   *  \note Iteration order is implementation-specific and is undefined
+   *  \note The returned iterator may get invalidated when NameTree is modified
+   */
   const_iterator
   end() const;
 
@@ -234,11 +248,13 @@
     FIND_ALL_MATCHES_TYPE
   };
 
-  class const_iterator : public std::iterator<std::forward_iterator_tag, name_tree::Entry>
+  class const_iterator : public std::iterator<std::forward_iterator_tag, const name_tree::Entry>
   {
   public:
     friend class NameTree;
 
+    const_iterator();
+
     const_iterator(NameTree::IteratorType type,
       const NameTree& nameTree,
       shared_ptr<name_tree::Entry> entry,
@@ -266,7 +282,7 @@
     operator!=(const const_iterator& other) const;
 
   private:
-    const NameTree&                             m_nameTree;
+    const NameTree*                             m_nameTree;
     shared_ptr<name_tree::Entry>                m_entry;
     shared_ptr<name_tree::Entry>                m_subTreeRoot;
     shared_ptr<name_tree::EntrySelector>        m_entrySelector;
diff --git a/daemon/table/pit-entry.hpp b/daemon/table/pit-entry.hpp
index 8fcf4a7..6d59ce8 100644
--- a/daemon/table/pit-entry.hpp
+++ b/daemon/table/pit-entry.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -164,8 +164,8 @@
   hasUnexpiredOutRecords() const;
 
 public:
-  EventId m_unsatisfyTimer;
-  EventId m_stragglerTimer;
+  scheduler::EventId m_unsatisfyTimer;
+  scheduler::EventId m_stragglerTimer;
 
 private:
   shared_ptr<const Interest> m_interest;
diff --git a/daemon/table/pit.cpp b/daemon/table/pit.cpp
index e4199e8..c21ccdf 100644
--- a/daemon/table/pit.cpp
+++ b/daemon/table/pit.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -26,6 +26,10 @@
 #include "pit.hpp"
 #include <type_traits>
 
+#include <boost/concept/assert.hpp>
+#include <boost/concept_check.hpp>
+#include <type_traits>
+
 namespace nfd {
 namespace pit {
 
@@ -36,6 +40,17 @@
 
 } // namespace pit
 
+// http://en.cppreference.com/w/cpp/concept/ForwardIterator
+BOOST_CONCEPT_ASSERT((boost::ForwardIterator<Pit::const_iterator>));
+// boost::ForwardIterator follows SGI standard http://www.sgi.com/tech/stl/ForwardIterator.html,
+// which doesn't require DefaultConstructible
+#ifdef HAVE_IS_DEFAULT_CONSTRUCTIBLE
+static_assert(std::is_default_constructible<Pit::const_iterator>::value,
+              "Pit::const_iterator must be default-constructible");
+#else
+BOOST_CONCEPT_ASSERT((boost::DefaultConstructible<Pit::const_iterator>));
+#endif // HAVE_IS_DEFAULT_CONSTRUCTIBLE
+
 Pit::Pit(NameTree& nameTree)
   : m_nameTree(nameTree)
   , m_nItems(0)
@@ -101,4 +116,11 @@
   --m_nItems;
 }
 
+Pit::const_iterator
+Pit::begin() const
+{
+  return const_iterator(m_nameTree.fullEnumerate(
+    [] (const name_tree::Entry& entry) { return entry.hasPitEntries(); }).begin());
+}
+
 } // namespace nfd
diff --git a/daemon/table/pit.hpp b/daemon/table/pit.hpp
index 00d90ca..e9616d5 100644
--- a/daemon/table/pit.hpp
+++ b/daemon/table/pit.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -78,6 +78,62 @@
   void
   erase(shared_ptr<pit::Entry> pitEntry);
 
+public: // enumeration
+  class const_iterator;
+
+  /** \brief returns an iterator pointing to the first PIT entry
+   *  \note Iteration order is implementation-specific and is undefined
+   *  \note The returned iterator may get invalidated if PIT or another NameTree-based
+   *        table is modified
+   */
+  const_iterator
+  begin() const;
+
+  /** \brief returns an iterator referring to the past-the-end PIT entry
+   *  \note The returned iterator may get invalidated if PIT or another NameTree-based
+   *        table is modified
+   */
+  const_iterator
+  end() const;
+
+  class const_iterator : public std::iterator<std::forward_iterator_tag, const pit::Entry>
+  {
+  public:
+    const_iterator();
+
+    explicit
+    const_iterator(const NameTree::const_iterator& it);
+
+    ~const_iterator();
+
+    const pit::Entry&
+    operator*() const;
+
+    shared_ptr<pit::Entry>
+    operator->() const;
+
+    const_iterator&
+    operator++();
+
+    const_iterator
+    operator++(int);
+
+    bool
+    operator==(const const_iterator& other) const;
+
+    bool
+    operator!=(const const_iterator& other) const;
+
+  private:
+    NameTree::const_iterator m_nameTreeIterator;
+    /** \brief Index of the current visiting PIT entry in NameTree node
+     *
+     * Index is used to ensure that dereferencing of m_nameTreeIterator happens only when
+     * const_iterator is dereferenced or advanced.
+     */
+    size_t m_iPitEntry;
+  };
+
 private:
   NameTree& m_nameTree;
   size_t m_nItems;
@@ -89,6 +145,76 @@
   return m_nItems;
 }
 
+inline Pit::const_iterator
+Pit::end() const
+{
+  return const_iterator(m_nameTree.end());
+}
+
+inline
+Pit::const_iterator::const_iterator()
+  : m_iPitEntry(0)
+{
+}
+
+inline
+Pit::const_iterator::const_iterator(const NameTree::const_iterator& it)
+  : m_nameTreeIterator(it)
+  , m_iPitEntry(0)
+{
+}
+
+inline
+Pit::const_iterator::~const_iterator()
+{
+}
+
+inline Pit::const_iterator
+Pit::const_iterator::operator++(int)
+{
+  Pit::const_iterator temp(*this);
+  ++(*this);
+  return temp;
+}
+
+inline Pit::const_iterator&
+Pit::const_iterator::operator++()
+{
+  ++m_iPitEntry;
+  if (m_iPitEntry < m_nameTreeIterator->getPitEntries().size()) {
+    return *this;
+  }
+
+  ++m_nameTreeIterator;
+  m_iPitEntry = 0;
+  return *this;
+}
+
+inline const pit::Entry&
+Pit::const_iterator::operator*() const
+{
+  return *(this->operator->());
+}
+
+inline shared_ptr<pit::Entry>
+Pit::const_iterator::operator->() const
+{
+  return m_nameTreeIterator->getPitEntries().at(m_iPitEntry);
+}
+
+inline bool
+Pit::const_iterator::operator==(const Pit::const_iterator& other) const
+{
+  return m_nameTreeIterator == other.m_nameTreeIterator &&
+         m_iPitEntry == other.m_iPitEntry;
+}
+
+inline bool
+Pit::const_iterator::operator!=(const Pit::const_iterator& other) const
+{
+  return !(*this == other);
+}
+
 } // namespace nfd
 
 #endif // NFD_DAEMON_TABLE_PIT_HPP
diff --git a/daemon/table/strategy-info-host.cpp b/daemon/table/strategy-info-host.cpp
index 9c46f1e..4961c68 100644
--- a/daemon/table/strategy-info-host.cpp
+++ b/daemon/table/strategy-info-host.cpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * Copyright (c) 2014,  Regents of the University of California,
+ *                      Arizona Board of Regents,
+ *                      Colorado State University,
+ *                      University Pierre & Marie Curie, Sorbonne University,
+ *                      Washington University in St. Louis,
+ *                      Beijing Institute of Technology,
+ *                      The University of Memphis
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -20,7 +21,7 @@
  *
  * You should have received a copy of the GNU General Public License along with
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- **/
+ */
 
 #include "strategy-info-host.hpp"
 
@@ -29,7 +30,7 @@
 void
 StrategyInfoHost::clearStrategyInfo()
 {
-  m_strategyInfo.reset();
+  m_items.clear();
 }
 
 } // namespace nfd
diff --git a/daemon/table/strategy-info-host.hpp b/daemon/table/strategy-info-host.hpp
index c7aa7da..1f6d940 100644
--- a/daemon/table/strategy-info-host.hpp
+++ b/daemon/table/strategy-info-host.hpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * Copyright (c) 2014,  Regents of the University of California,
+ *                      Arizona Board of Regents,
+ *                      Colorado State University,
+ *                      University Pierre & Marie Curie, Sorbonne University,
+ *                      Washington University in St. Louis,
+ *                      Beijing Institute of Technology,
+ *                      The University of Memphis
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -20,7 +21,7 @@
  *
  * 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_TABLE_STRATEGY_INFO_HOST_HPP
 #define NFD_DAEMON_TABLE_STRATEGY_INFO_HOST_HPP
@@ -29,72 +30,88 @@
 
 namespace nfd {
 
-/** \class StrategyInfoHost
- *  \brief base class for an entity onto which StrategyInfo may be placed
+/** \brief base class for an entity onto which StrategyInfo objects may be placed
  */
 class StrategyInfoHost
 {
 public:
-  template<typename T>
-  void
-  setStrategyInfo(shared_ptr<T> strategyInfo);
-
+  /** \brief get a StrategyInfo item
+   *  \tparam T type of StrategyInfo, must be a subclass of from nfd::fw::StrategyInfo
+   *  \retval nullptr if no StrategyInfo of type T is stored
+   */
   template<typename T>
   shared_ptr<T>
   getStrategyInfo() const;
 
+  /** \brief set a StrategyInfo item
+   *  \tparam T type of StrategyInfo, must be a subclass of from nfd::fw::StrategyInfo
+   */
   template<typename T>
-  shared_ptr<T>
-  getOrCreateStrategyInfo();
+  void
+  setStrategyInfo(shared_ptr<T> strategyInfo);
 
-  template<typename T, typename T1>
+  /** \brief get or create a StrategyInfo item
+   *  \tparam T type of StrategyInfo, must be a subclass of from nfd::fw::StrategyInfo
+   *
+   *  If no StrategyInfo of type T is stored, it's created with \p{args};
+   *  otherwise, the existing item is returned.
+   */
+  template<typename T, typename ...A>
   shared_ptr<T>
-  getOrCreateStrategyInfo(T1& a1);
+  getOrCreateStrategyInfo(A&&... args);
 
+  /** \brief clear all StrategyInfo items
+   */
   void
   clearStrategyInfo();
 
 private:
-  shared_ptr<fw::StrategyInfo> m_strategyInfo;
+  std::map<int, shared_ptr<fw::StrategyInfo>> m_items;
 };
 
 
 template<typename T>
-void
-StrategyInfoHost::setStrategyInfo(shared_ptr<T> strategyInfo)
-{
-  m_strategyInfo = strategyInfo;
-}
-
-template<typename T>
 shared_ptr<T>
 StrategyInfoHost::getStrategyInfo() const
 {
-  return static_pointer_cast<T, fw::StrategyInfo>(m_strategyInfo);
+  static_assert(std::is_base_of<fw::StrategyInfo, T>::value,
+                "T must inherit from StrategyInfo");
+
+  auto it = m_items.find(T::getTypeId());
+  if (it == m_items.end()) {
+    return nullptr;
+  }
+  return static_pointer_cast<T, fw::StrategyInfo>(it->second);
 }
 
 template<typename T>
-shared_ptr<T>
-StrategyInfoHost::getOrCreateStrategyInfo()
+void
+StrategyInfoHost::setStrategyInfo(shared_ptr<T> item)
 {
-  shared_ptr<T> info = this->getStrategyInfo<T>();
-  if (!static_cast<bool>(info)) {
-    info = make_shared<T>();
-    this->setStrategyInfo(info);
+  static_assert(std::is_base_of<fw::StrategyInfo, T>::value,
+                "T must inherit from StrategyInfo");
+
+  if (item == nullptr) {
+    m_items.erase(T::getTypeId());
   }
-  return info;
+  else {
+    m_items[T::getTypeId()] = item;
+  }
 }
 
-template<typename T, typename T1>
+template<typename T, typename ...A>
 shared_ptr<T>
-StrategyInfoHost::getOrCreateStrategyInfo(T1& a1)
+StrategyInfoHost::getOrCreateStrategyInfo(A&&... args)
 {
-  shared_ptr<T> info = this->getStrategyInfo<T>();
-  if (!static_cast<bool>(info)) {
-    info = make_shared<T>(ref(a1));
-    this->setStrategyInfo(info);
+  static_assert(std::is_base_of<fw::StrategyInfo, T>::value,
+                "T must inherit from StrategyInfo");
+
+  shared_ptr<T> item = this->getStrategyInfo<T>();
+  if (!static_cast<bool>(item)) {
+    item = make_shared<T>(std::forward<A>(args)...);
+    this->setStrategyInfo(item);
   }
-  return info;
+  return item;
 }
 
 } // namespace nfd
diff --git a/docs/INSTALL.rst b/docs/INSTALL.rst
index 76ccaf9..e04ee48 100644
--- a/docs/INSTALL.rst
+++ b/docs/INSTALL.rst
@@ -16,7 +16,7 @@
 On OS X, NFD can be installed with MacPorts.  Refer to :ref:`Install NFD Using the NDN
 MacPorts Repository on OS X` for more details.
 
-On Ubuntu 12.04, 13.10, or 14.04, NFD can be installed from NDN PPA repository.  Refer to
+On Ubuntu 12.04, 14.04, or 14.10 NFD can be installed from NDN PPA repository.  Refer to
 :ref:`Install NFD Using the NDN PPA Repository on Ubuntu Linux`.
 
 Future releases could include support for other platforms.  Please send us feedback on the
@@ -72,7 +72,7 @@
 Install NFD Using the NDN PPA Repository on Ubuntu Linux
 --------------------------------------------------------
 
-NFD binaries and related tools for Ubuntu 12.04 and 14.04 can be installed using PPA
+NFD binaries and related tools for Ubuntu 12.04, 14.04, or 14.10 can be installed using PPA
 packages from named-data repository.  First, you will need to add ``named-data/ppa``
 repository to binary package sources and update list of available packages.
 
@@ -88,7 +88,7 @@
 
     sudo apt-get install python-software-properties
 
-On Ubuntu **14.04**:
+On Ubuntu **14.04** or **14.10**:
 
 ::
 
@@ -134,18 +134,10 @@
 ::
 
     # Download ndn-cxx
-
     git clone https://github.com/named-data/ndn-cxx
-    cd ndn-cxx
-    git checkout ndn-cxx-0.2.0
-
-    cd ..
 
     # Download NFD
-
     git clone --recursive https://github.com/named-data/NFD
-    cd NFD
-    git checkout NFD-0.2.0
 
 Prerequisites
 ~~~~~~~~~~~~~
@@ -153,9 +145,23 @@
 -  Install the `ndn-cxx library <http://named-data.net/doc/ndn-cxx/current/INSTALL.html>`_
    and its requirements
 
+-  ``pkg-config``
+
+   On OS X 10.8, 10.9, and 10.10 with MacPorts:
+
+   ::
+
+       sudo port install pkgconfig
+
+   On Ubuntu >= 12.04:
+
+   ::
+
+       sudo apt-get install pkg-config
+
 -  ``libpcap``
 
-   Comes with the base system on OS X 10.8 and 10.9.
+   Comes with the base system on OS X 10.8, 10.9, and 10.10.
 
    On Ubuntu >= 12.04:
 
@@ -169,7 +175,7 @@
 -  ``graphviz``
 -  ``python-sphinx``
 
-   On OS X 10.8 and 10.9 with MacPorts:
+   On OS X 10.8, 10.9, and 10.10 with MacPorts:
 
    ::
 
@@ -183,7 +189,7 @@
        sudo apt-get install doxygen graphviz python-sphinx
 
 
-Besides officially supported platforms, NFD is known to work on: Fedora 20, CentOS 6, Gentoo Linux,
+Besides officially supported platforms, NFD is known to work on: Fedora 20, CentOS 6/7, Gentoo Linux,
 Raspberry Pi, OpenWRT, FreeBSD 10.0, and several other platforms.  We are soliciting help
 with documenting common problems / pitfalls in installing/using NFD on different platforms
 on `NFD Wiki
@@ -216,6 +222,11 @@
 Refer to ``./waf --help`` for more options that can be used during ``configure`` stage and
 how to properly configure and run NFD.
 
+.. note::
+   If you are working on a source repository that has been compiled before, and you have
+   upgraded one of the dependencies, please execute ``./waf distclean`` to clear object files
+   and start over.
+
 Debug symbols
 ~~~~~~~~~~~~~
 
@@ -348,7 +359,7 @@
 Sample applications:
 
 -  `Simple examples in ndn-cxx
-   library <http://named-data.net/doc/ndn-cxx/0.2.0/examples.html>`__.
+   library <http://named-data.net/doc/ndn-cxx/current/examples.html>`__.
    If you have installed ndn-cxx from source, you already have compiled
    these:
 
diff --git a/nfd.conf.sample.in b/nfd.conf.sample.in
index dafc88a..2f1e31f 100644
--- a/nfd.conf.sample.in
+++ b/nfd.conf.sample.in
@@ -67,6 +67,11 @@
   ; The unix section contains settings of Unix stream faces and channels.
   ; Unix channel is always listening; delete unix section to disable
   ; Unix stream faces and channels.
+  ;
+  ; The ndn-cxx library expects unix:///var/run/nfd.sock
+  ; to be used as the default transport option. Please change
+  ; the "transport" field in client.conf to an appropriate tcp4 FaceUri
+  ; if you need to disable unix sockets.
   unix
   {
     path /var/run/nfd.sock ; Unix stream listener path
@@ -288,6 +293,16 @@
   ;   ; }
   ; }
 
+  ; The following localhop_security should be enabled when NFD runs on a hub,
+  ; which accepts all remote registrations and is a short-term solution.
+  ; localhop_security
+  ; {
+  ;   trust-anchor
+  ;   {
+  ;     type any
+  ;   }
+  ; }
+
   remote_register
   {
     cost 15 ; forwarding cost of prefix registered on remote router
diff --git a/rib/remote-registrator.cpp b/rib/remote-registrator.cpp
index 2a5a12d..a587653 100644
--- a/rib/remote-registrator.cpp
+++ b/rib/remote-registrator.cpp
@@ -35,10 +35,9 @@
 using ndn::nfd::ControlParameters;
 using ndn::nfd::CommandOptions;
 
-
-const Name RemoteRegistrator::RM_LOCAL_PREFIX = "/localhost";
-const Name RemoteRegistrator::RM_HUB_PREFIX = "/localhop/nfd";
-const name::Component RemoteRegistrator::RM_IGNORE_COMMPONENT("rib");
+const Name RemoteRegistrator::LOCAL_REGISTRATION_PREFIX = "/localhost";
+const Name RemoteRegistrator::REMOTE_HUB_PREFIX = "/localhop/nfd/rib";
+const name::Component RemoteRegistrator::IGNORE_COMMPONENT("rib");
 
 RemoteRegistrator::RemoteRegistrator(ndn::nfd::Controller& controller,
                                      ndn::KeyChain& keyChain,
@@ -100,8 +99,14 @@
      .setOrigin(ndn::nfd::ROUTE_ORIGIN_CLIENT)// set origin to client.
      .setFaceId(0);// the remote hub will take the input face as the faceId.
 
+   Name commandPrefix = REMOTE_HUB_PREFIX;
+   if (IGNORE_COMMPONENT == commandPrefix.at(-1))
+     {
+       commandPrefix = commandPrefix.getPrefix(-1);
+     }
+
    m_commandOptions
-     .setPrefix(RM_HUB_PREFIX)
+     .setPrefix(commandPrefix)
      .setTimeout(time::milliseconds(timeout));
 
    m_nRetries = retry;
@@ -117,15 +122,38 @@
 }
 
 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 (RM_LOCAL_PREFIX.isPrefixOf(prefix))
+  if (LOCAL_REGISTRATION_PREFIX.isPrefixOf(prefix))
     {
       NFD_LOG_INFO("local registration only for " << prefix);
       return;
     }
 
-  bool isHubPrefix = prefix == RM_HUB_PREFIX;
+  bool isHubPrefix = prefix == REMOTE_HUB_PREFIX;
 
   if (isHubPrefix)
     {
@@ -181,7 +209,7 @@
 void
 RemoteRegistrator::unregisterPrefix(const Name& prefix)
 {
-  if (prefix == RM_HUB_PREFIX)
+  if (prefix == REMOTE_HUB_PREFIX)
     {
       NFD_LOG_INFO("disconnected to hub with prefix: " << prefix);
 
@@ -267,7 +295,7 @@
   // longest prefix matching to all indenties.
   for (auto&& i : identities)
     {
-      if (!i.empty() && RM_IGNORE_COMMPONENT == i.at(-1))
+      if (!i.empty() && IGNORE_COMMPONENT == i.at(-1))
         {
           isPrefix = i.getPrefix(-1).isPrefixOf(prefix);
           curLength = i.size() - 1;
@@ -360,9 +388,9 @@
                                 const CommandOptions& options,
                                 int nRetries)
 {
-  NFD_LOG_INFO("fail to unregister " << parameters.getName()
-                                     << "\n\t reason:" << reason
-                                     << "\n\t remain retries:" << nRetries);
+  NFD_LOG_INFO("fail to register " << parameters.getName()
+                                   << "\n\t reason:" << reason
+                                   << "\n\t remain retries:" << nRetries);
 
   if (nRetries > 0)
     {
diff --git a/rib/remote-registrator.hpp b/rib/remote-registrator.hpp
index 41be218..bdd6ca9 100644
--- a/rib/remote-registrator.hpp
+++ b/rib/remote-registrator.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -36,6 +36,7 @@
 #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 {
@@ -72,6 +73,20 @@
   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
@@ -197,7 +212,6 @@
   void
   clearRefreshEvents();
 
-
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   /**
    * When a locally registered prefix triggles remote
@@ -208,7 +222,7 @@
    * to Route Name. So it needs seperate sotrage instead
    * of storing within the RIB.
    */
-  typedef std::unordered_map<Name, EventId> RegisteredList;
+  typedef std::unordered_map<Name, scheduler::EventId> RegisteredList;
   typedef RegisteredList::iterator RegisteredEntryIt;
   typedef RegisteredList::value_type RegisteredEntry;
   RegisteredList m_regEntries;
@@ -217,16 +231,17 @@
   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 RM_LOCAL_PREFIX; // /localhost
-  static const Name RM_HUB_PREFIX; // /localhop/nfd
-  static const name::Component RM_IGNORE_COMMPONENT; // rib
+  static const Name LOCAL_REGISTRATION_PREFIX; // /localhost
+  static const Name REMOTE_HUB_PREFIX; // /localhop/nfd/rib
+  static const name::Component IGNORE_COMMPONENT; // rib
 };
 
 } // namespace rib
diff --git a/rib/rib-entry.cpp b/rib/rib-entry.cpp
index 3e24275..4ded689 100644
--- a/rib/rib-entry.cpp
+++ b/rib/rib-entry.cpp
@@ -34,25 +34,25 @@
 namespace nfd {
 namespace rib {
 
-RibEntry::FaceList::iterator
-RibEntry::findFace(const FaceEntry& face)
+RibEntry::RouteList::iterator
+RibEntry::findRoute(const Route& route)
 {
-  return std::find_if(begin(), end(), bind(&compareFaceIdAndOrigin, _1, face));
+  return std::find_if(begin(), end(), bind(&compareFaceIdAndOrigin, _1, route));
 }
 
 bool
-RibEntry::insertFace(const FaceEntry& entry)
+RibEntry::insertRoute(const Route& route)
 {
-  iterator it = findFace(entry);
+  iterator it = findRoute(route);
 
   if (it == end())
     {
-      if (entry.flags & ndn::nfd::ROUTE_FLAG_CAPTURE)
+      if (route.flags & ndn::nfd::ROUTE_FLAG_CAPTURE)
         {
-          m_nFacesWithCaptureSet++;
+          m_nRoutesWithCaptureSet++;
         }
 
-      m_faces.push_back(entry);
+      m_routes.push_back(route);
       return true;
     }
   else
@@ -62,27 +62,26 @@
 }
 
 void
-RibEntry::eraseFace(const FaceEntry& face)
+RibEntry::eraseRoute(const Route& route)
 {
-  RibEntry::iterator it = std::find_if(begin(), end(), bind(&compareFaceIdAndOrigin, _1, face));
-  eraseFace(it);
+  RibEntry::iterator it = findRoute(route);
+  eraseRoute(it);
 }
 
 bool
-RibEntry::hasFace(const FaceEntry& face)
+RibEntry::hasRoute(const Route& route)
 {
-  RibEntry::const_iterator it = std::find_if(cbegin(), cend(),
-                                             bind(&compareFaceIdAndOrigin, _1, face));
+  RibEntry::const_iterator it = findRoute(route);
 
-  return it != cend();
+  return it != end();
 }
 
 bool
 RibEntry::hasFaceId(const uint64_t faceId) const
 {
-  RibEntry::const_iterator it = std::find_if(cbegin(), cend(), bind(&compareFaceId, _1, faceId));
+  RibEntry::const_iterator it = std::find_if(begin(), end(), bind(&compareFaceId, _1, faceId));
 
-  return it != cend();
+  return it != end();
 }
 
 void
@@ -101,64 +100,64 @@
   m_children.remove(child);
 }
 
-RibEntry::FaceList::iterator
-RibEntry::eraseFace(FaceList::iterator face)
+RibEntry::RouteList::iterator
+RibEntry::eraseRoute(RouteList::iterator route)
 {
-  if (face != m_faces.end())
+  if (route != m_routes.end())
     {
-      if (face->flags & ndn::nfd::ROUTE_FLAG_CAPTURE)
+      if (route->flags & ndn::nfd::ROUTE_FLAG_CAPTURE)
         {
-          m_nFacesWithCaptureSet--;
+          m_nRoutesWithCaptureSet--;
         }
 
       //cancel any scheduled event
-      NFD_LOG_TRACE("Cancelling expiration eventId: " << face->getExpirationEvent());
-      scheduler::cancel(face->getExpirationEvent());
+      NFD_LOG_TRACE("Cancelling expiration eventId: " << route->getExpirationEvent());
+      scheduler::cancel(route->getExpirationEvent());
 
-      return m_faces.erase(face);
+      return m_routes.erase(route);
     }
 
-  return m_faces.end();
+  return m_routes.end();
 }
 
 void
-RibEntry::addInheritedFace(const FaceEntry& face)
+RibEntry::addInheritedRoute(const Route& route)
 {
-  m_inheritedFaces.push_back(face);
+  m_inheritedRoutes.push_back(route);
 }
 
 void
-RibEntry::removeInheritedFace(const FaceEntry& face)
+RibEntry::removeInheritedRoute(const Route& route)
 {
-  FaceList::iterator it = findInheritedFace(face);
-  m_inheritedFaces.erase(it);
+  RouteList::iterator it = findInheritedRoute(route);
+  m_inheritedRoutes.erase(it);
 }
 
-RibEntry::FaceList::iterator
-RibEntry::findInheritedFace(const FaceEntry& face)
+RibEntry::RouteList::iterator
+RibEntry::findInheritedRoute(const Route& route)
 {
-  return std::find_if(m_inheritedFaces.begin(), m_inheritedFaces.end(),
-                      bind(&compareFaceId, _1, face.faceId));
+  return std::find_if(m_inheritedRoutes.begin(), m_inheritedRoutes.end(),
+                      bind(&compareFaceId, _1, route.faceId));
 }
 
 bool
-RibEntry::hasInheritedFace(const FaceEntry& face)
+RibEntry::hasInheritedRoute(const Route& route)
 {
-  FaceList::const_iterator it = findInheritedFace(face);
+  RouteList::const_iterator it = findInheritedRoute(route);
 
-  return (it != m_inheritedFaces.end());
+  return (it != m_inheritedRoutes.end());
 }
 
 bool
 RibEntry::hasCapture() const
 {
-  return m_nFacesWithCaptureSet > 0;
+  return m_nRoutesWithCaptureSet > 0;
 }
 
 bool
 RibEntry::hasChildInheritOnFaceId(uint64_t faceId) const
 {
-  for (RibEntry::const_iterator it = m_faces.begin(); it != m_faces.end(); ++it)
+  for (RibEntry::const_iterator it = m_routes.begin(); it != m_routes.end(); ++it)
     {
       if (it->faceId == faceId && (it->flags & ndn::nfd::ROUTE_FLAG_CHILD_INHERIT))
         {
@@ -169,66 +168,67 @@
   return false;
 }
 
-shared_ptr<FaceEntry>
-RibEntry::getFaceWithLowestCostByFaceId(uint64_t faceId)
+shared_ptr<Route>
+RibEntry::getRouteWithLowestCostByFaceId(uint64_t faceId)
 {
-  shared_ptr<FaceEntry> face;
+  shared_ptr<Route> candidate;
 
-  for (FaceList::iterator it = begin(); it != end(); ++it)
+  for (const Route& route : m_routes)
     {
       // Correct face ID
-      if (it->faceId == faceId)
+      if (route.faceId == faceId)
         {
-          // If this is the first face with this ID found
-          if (!static_cast<bool>(face))
+          // If this is the first route with this Face ID found
+          if (candidate == nullptr)
             {
-              face = make_shared<FaceEntry>(*it);
+              candidate = make_shared<Route>(route);
             }
-          else if (it->cost < face->cost) // Found a face with a lower cost
+          else if (route.cost < candidate->cost) // Found a route with a lower cost
             {
-              face = make_shared<FaceEntry>(*it);
+              candidate = make_shared<Route>(route);
             }
         }
     }
 
-    return face;
+  return candidate;
 }
 
-shared_ptr<FaceEntry>
-RibEntry::getFaceWithLowestCostAndChildInheritByFaceId(uint64_t faceId)
+shared_ptr<Route>
+RibEntry::getRouteWithLowestCostAndChildInheritByFaceId(uint64_t faceId)
 {
-  shared_ptr<FaceEntry> face;
+  shared_ptr<Route> candidate;
 
-  for (FaceList::iterator it = begin(); it != end(); ++it)
+  for (const Route& route : m_routes)
     {
       // Correct face ID and Child Inherit flag set
-      if (it->faceId == faceId && it->flags & ndn::nfd::ROUTE_FLAG_CHILD_INHERIT)
+      if (route.faceId == faceId &&
+          (route.flags & ndn::nfd::ROUTE_FLAG_CHILD_INHERIT) == ndn::nfd::ROUTE_FLAG_CHILD_INHERIT)
         {
-          // If this is the first face with this ID found
-          if (!static_cast<bool>(face))
+          // If this is the first route with this Face ID found
+          if (candidate == nullptr)
             {
-              face = make_shared<FaceEntry>(*it);
+              candidate = make_shared<Route>(route);
             }
-          else if (it->cost < face->cost) // Found a face with a lower cost
+          else if (route.cost < candidate->cost) // Found a route with a lower cost
             {
-              face = make_shared<FaceEntry>(*it);
+              candidate = make_shared<Route>(route);
             }
         }
     }
 
-    return face;
+  return candidate;
 }
 
 std::ostream&
-operator<<(std::ostream& os, const FaceEntry& entry)
+operator<<(std::ostream& os, const Route& route)
 {
-  os << "FaceEntry("
-     << "faceid: " << entry.faceId
-     << ", origin: " << entry.origin
-     << ", cost: " << entry.cost
-     << ", flags: " << entry.flags;
-  if (entry.expires != time::steady_clock::TimePoint::max()) {
-    os << ", expires in: " << (entry.expires - time::steady_clock::now());
+  os << "Route("
+     << "faceid: " << route.faceId
+     << ", origin: " << route.origin
+     << ", cost: " << route.cost
+     << ", flags: " << route.flags;
+  if (route.expires != time::steady_clock::TimePoint::max()) {
+    os << ", expires in: " << (route.expires - time::steady_clock::now());
   }
   else {
     os << ", never expires";
@@ -244,9 +244,9 @@
   os << "RibEntry {\n";
   os << "\tName: " << entry.getName() << "\n";
 
-  for (RibEntry::FaceList::const_iterator faceIt = entry.cbegin(); faceIt != entry.cend(); ++faceIt)
+  for (const Route& route : entry)
     {
-      os << "\t" << (*faceIt) << "\n";
+      os << "\t" << route << "\n";
     }
 
   os << "}";
diff --git a/rib/rib-entry.hpp b/rib/rib-entry.hpp
index b17085a..4cfecc1 100644
--- a/rib/rib-entry.hpp
+++ b/rib/rib-entry.hpp
@@ -26,23 +26,22 @@
 #ifndef NFD_RIB_RIB_ENTRY_HPP
 #define NFD_RIB_RIB_ENTRY_HPP
 
-#include "face-entry.hpp"
+#include "route.hpp"
 
 namespace nfd {
 namespace rib {
 
-/** \class RibEntry
- *  \brief represents a namespace
+/** \brief represents a RIB entry, which contains one or more Routes with the same prefix
  */
 class RibEntry : public enable_shared_from_this<RibEntry>
 {
 public:
-  typedef std::list<FaceEntry> FaceList;
-  typedef FaceList::iterator iterator;
-  typedef FaceList::const_iterator const_iterator;
+  typedef std::list<Route> RouteList;
+  typedef RouteList::iterator iterator;
+  typedef RouteList::const_iterator const_iterator;
 
   RibEntry()
-    : m_nFacesWithCaptureSet(0)
+    : m_nRoutesWithCaptureSet(0)
   {
   }
 
@@ -64,99 +63,99 @@
   void
   removeChild(shared_ptr<RibEntry> child);
 
-  std::list<shared_ptr<RibEntry> >&
+  std::list<shared_ptr<RibEntry>>&
   getChildren();
 
   bool
   hasChildren() const;
 
-  /** \brief inserts a new face into the entry's face list
-   *  If another entry already exists with the same faceId and origin,
-   *  the new face is not inserted.
-   *  \return{ true if the face is inserted, false otherwise }
+  /** \brief inserts a new route into the entry's route list
+   *  If another route already exists with the same faceId and origin,
+   *  the new route is not inserted.
+   *  \return{ true if the route is inserted, false otherwise }
    */
   bool
-  insertFace(const FaceEntry& face);
+  insertRoute(const Route& route);
 
-  /** \brief erases a FaceEntry with the same faceId and origin
+  /** \brief erases a Route with the same faceId and origin
    */
   void
-  eraseFace(const FaceEntry& face);
+  eraseRoute(const Route& route);
 
-  /** \brief erases a FaceEntry with the passed iterator
+  /** \brief erases a Route with the passed iterator
    *  \return{ an iterator to the element that followed the erased iterator }
    */
   iterator
-  eraseFace(FaceList::iterator face);
+  eraseRoute(RouteList::iterator route);
 
   bool
   hasFaceId(const uint64_t faceId) const;
 
-  FaceList&
-  getFaces();
+  RouteList&
+  getRoutes();
 
   iterator
-  findFace(const FaceEntry& face);
+  findRoute(const Route& route);
 
   bool
-  hasFace(const FaceEntry& face);
+  hasRoute(const Route& route);
 
   void
-  addInheritedFace(const FaceEntry& face);
+  addInheritedRoute(const Route& route);
 
   void
-  removeInheritedFace(const FaceEntry& face);
+  removeInheritedRoute(const Route& route);
 
-  /** \brief Returns the faces this namespace has inherited.
-   *  The inherited faces returned represent inherited entries this namespace has in the FIB.
-   *  \return{ faces inherited by this namespace }
+  /** \brief Returns the routes this namespace has inherited.
+   *  The inherited routes returned represent inherited routes this namespace has in the FIB.
+   *  \return{ routes inherited by this namespace }
    */
-  FaceList&
-  getInheritedFaces();
+  RouteList&
+  getInheritedRoutes();
 
-  /** \brief Finds an inherited face with a matching face ID.
-   *  \return{ An iterator to the matching face if one is found;
+  /** \brief Finds an inherited route with a matching face ID.
+   *  \return{ An iterator to the matching route if one is found;
    *           otherwise, an iterator to the end of the entry's
-   *           inherited face list }
+   *           inherited route list }
    */
-  FaceList::iterator
-  findInheritedFace(const FaceEntry& face);
+  RouteList::iterator
+  findInheritedRoute(const Route& route);
 
-  /** \brief Determines if the entry has an inherited face with a matching face ID.
-   *  \return{ True, if a matching inherited face is found; otherwise, false. }
+  /** \brief Determines if the entry has an inherited route with a matching face ID.
+   *  \return{ True, if a matching inherited route is found; otherwise, false. }
    */
   bool
-  hasInheritedFace(const FaceEntry& face);
+  hasInheritedRoute(const Route& route);
 
   bool
   hasCapture() const;
 
-  /** \brief Determines if the entry has an inherited face with the passed
+  /** \brief Determines if the entry has an inherited route with the passed
    *         face ID and its child inherit flag set.
-   *  \return{ True, if a matching inherited face is found; otherwise, false. }
+   *  \return{ True, if a matching inherited route is found; otherwise, false. }
    */
   bool
   hasChildInheritOnFaceId(uint64_t faceId) const;
 
-  /** \brief Returns the face with the lowest cost that has the passed face ID.
-   *  \return{ The face with with the lowest cost that has the passed face ID}
+  /** \brief Returns the route with the lowest cost that has the passed face ID.
+   *  \return{ The route with the lowest cost that has the passed face ID}
    */
-  shared_ptr<FaceEntry>
-  getFaceWithLowestCostByFaceId(uint64_t faceId);
+  shared_ptr<Route>
+  getRouteWithLowestCostByFaceId(uint64_t faceId);
 
-  /** \brief Returns the face with the lowest cost that has the passed face ID
+  /** \brief Returns the route with the lowest cost that has the passed face ID
    *         and its child inherit flag set.
-   *  \return{ The face with with the lowest cost that has the passed face ID
+   *  \return{ The route with the lowest cost that has the passed face ID
    *           and its child inherit flag set }
    */
-  shared_ptr<FaceEntry>
-  getFaceWithLowestCostAndChildInheritByFaceId(uint64_t faceId);
+  shared_ptr<Route>
+  getRouteWithLowestCostAndChildInheritByFaceId(uint64_t faceId);
 
   const_iterator
-  cbegin() const;
+  begin() const;
 
   const_iterator
-  cend() const;
+  end() const;
 
   iterator
   begin();
@@ -170,18 +169,18 @@
 
 private:
   Name m_name;
-  std::list<shared_ptr<RibEntry> > m_children;
+  std::list<shared_ptr<RibEntry>> m_children;
   shared_ptr<RibEntry> m_parent;
-  FaceList m_faces;
-  FaceList m_inheritedFaces;
+  RouteList m_routes;
+  RouteList m_inheritedRoutes;
 
-  /** \brief The number of faces on this namespace with the capture flag set.
+  /** \brief The number of routes on this namespace with the capture flag set.
    *
-   *  This count is used to check if the namespace will block inherited faces.
+   *  This count is used to check if the namespace will block inherited routes.
    *  If the number is greater than zero, a route on the namespace has it's capture
-   *  flag set which means the namespace should not inherit any faces.
+   *  flag set which means the namespace should not inherit any routes.
    */
-  uint64_t m_nFacesWithCaptureSet;
+  uint64_t m_nRoutesWithCaptureSet;
 };
 
 inline void
@@ -208,46 +207,46 @@
   return m_parent;
 }
 
-inline std::list<shared_ptr<RibEntry> >&
+inline std::list<shared_ptr<RibEntry>>&
 RibEntry::getChildren()
 {
   return m_children;
 }
 
-inline RibEntry::FaceList&
-RibEntry::getFaces()
+inline RibEntry::RouteList&
+RibEntry::getRoutes()
 {
-  return m_faces;
+  return m_routes;
 }
 
-inline RibEntry::FaceList&
-RibEntry::getInheritedFaces()
+inline RibEntry::RouteList&
+RibEntry::getInheritedRoutes()
 {
-  return m_inheritedFaces;
+  return m_inheritedRoutes;
 }
 
 inline RibEntry::const_iterator
-RibEntry::cbegin() const
+RibEntry::begin() const
 {
-  return m_faces.begin();
+  return m_routes.begin();
 }
 
 inline RibEntry::const_iterator
-RibEntry::cend() const
+RibEntry::end() const
 {
-  return m_faces.end();
+  return m_routes.end();
 }
 
 inline RibEntry::iterator
 RibEntry::begin()
 {
-  return m_faces.begin();
+  return m_routes.begin();
 }
 
 inline RibEntry::iterator
 RibEntry::end()
 {
-  return m_faces.end();
+  return m_routes.end();
 }
 
 std::ostream&
diff --git a/rib/rib-manager.cpp b/rib/rib-manager.cpp
index e7ea3e9..ae64820 100644
--- a/rib/rib-manager.cpp
+++ b/rib/rib-manager.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -106,7 +106,7 @@
     ControlParameters()
       .setName(commandPrefix)
       .setFaceId(0),
-    bind(&RibManager::onNrdCommandPrefixAddNextHopSuccess, this, cref(commandPrefix)),
+    bind(&RibManager::onNrdCommandPrefixAddNextHopSuccess, this, cref(commandPrefix), _1),
     bind(&RibManager::onNrdCommandPrefixAddNextHopError, this, cref(commandPrefix), _2));
 
   m_face.setInterestFilter(commandPrefix, onRequest);
@@ -144,6 +144,8 @@
                      bool isDryRun,
                      const std::string& filename)
 {
+  bool isRemoteRegisterEnabled = false;
+
   for (ConfigSection::const_iterator i = configSection.begin();
        i != configSection.end(); ++i)
     {
@@ -157,21 +159,23 @@
       else if (i->first == "remote_register")
         {
           m_remoteRegistrator.loadConfig(i->second);
+          isRemoteRegisterEnabled = true;
+          // avoid other actions when isDryRun == true
+          if (isDryRun)
+            {
+              continue;
+            }
 
-          // register callback to the RIB.
-          // do remote registration after an entry is inserted into the RIB.
-          // do remote unregistration after an entry is erased from the RIB.
-          m_managedRib.afterInsertEntry += [this] (const Name& prefix) {
-            m_remoteRegistrator.registerPrefix(prefix);
-          };
-
-          m_managedRib.afterEraseEntry += [this] (const Name& prefix) {
-            m_remoteRegistrator.unregisterPrefix(prefix);
-          };
+          m_remoteRegistrator.enable();
         }
       else
         throw Error("Unrecognized rib property: " + i->first);
     }
+
+  if (!isRemoteRegisterEnabled)
+    {
+      m_remoteRegistrator.disable();
+    }
 }
 
 void
@@ -282,37 +286,35 @@
       parameters.setFaceId(request->getIncomingFaceId());
     }
 
-  FaceEntry faceEntry;
-  faceEntry.faceId = parameters.getFaceId();
-  faceEntry.origin = parameters.getOrigin();
-  faceEntry.cost = parameters.getCost();
-  faceEntry.flags = parameters.getFlags();
+  Route route;
+  route.faceId = parameters.getFaceId();
+  route.origin = parameters.getOrigin();
+  route.cost = parameters.getCost();
+  route.flags = parameters.getFlags();
 
   if (parameters.hasExpirationPeriod() &&
       parameters.getExpirationPeriod() != time::milliseconds::max())
     {
-      faceEntry.expires = time::steady_clock::now() + parameters.getExpirationPeriod();
+      route.expires = time::steady_clock::now() + parameters.getExpirationPeriod();
 
       // Schedule a new event, the old one will be cancelled during rib insertion.
-      EventId eventId;
-      eventId = scheduler::schedule(parameters.getExpirationPeriod(),
-                                    bind(&RibManager::expireEntry,
-                                    this, shared_ptr<Interest>(), parameters));
-      NFD_LOG_TRACE("Scheduled unregistration at: " << faceEntry.expires <<
+      scheduler::EventId eventId = scheduler::schedule(parameters.getExpirationPeriod(),
+          bind(&RibManager::expireEntry, this, shared_ptr<Interest>(), parameters));
+      NFD_LOG_TRACE("Scheduled unregistration at: " << route.expires <<
                     " with EventId: " << eventId);
 
       //set the  NewEventId of this entry
-      faceEntry.setExpirationEvent(eventId);
+      route.setExpirationEvent(eventId);
     }
   else
     {
-      faceEntry.expires = time::steady_clock::TimePoint::max();
+      route.expires = time::steady_clock::TimePoint::max();
     }
 
-  NFD_LOG_TRACE("register prefix: " << faceEntry);
+  NFD_LOG_TRACE("register prefix: " << route);
 
-  m_managedRib.insert(parameters.getName(), faceEntry);
-  m_registeredFaces.insert(faceEntry.faceId);
+  m_managedRib.insert(parameters.getName(), route);
+  m_registeredFaces.insert(route.faceId);
 
   sendUpdatesToFib(request, parameters);
 }
@@ -320,13 +322,13 @@
 void
 RibManager::expireEntry(const shared_ptr<const Interest>& request, ControlParameters& params)
 {
-  FaceEntry face;
-  face.faceId = params.getFaceId();
-  face.origin = params.getOrigin();
-  face.cost = params.getCost();
-  face.flags = params.getFlags();
+  Route route;
+  route.faceId = params.getFaceId();
+  route.origin = params.getOrigin();
+  route.cost = params.getCost();
+  route.flags = params.getFlags();
 
-  NFD_LOG_DEBUG(face << " for " << params.getName() << " has expired");
+  NFD_LOG_DEBUG(route << " for " << params.getName() << " has expired");
   unregisterEntry(request, params);
 }
 
@@ -359,13 +361,13 @@
       parameters.setFaceId(request->getIncomingFaceId());
     }
 
-  FaceEntry faceEntry;
-  faceEntry.faceId = parameters.getFaceId();
-  faceEntry.origin = parameters.getOrigin();
+  Route route;
+  route.faceId = parameters.getFaceId();
+  route.origin = parameters.getOrigin();
 
-  NFD_LOG_TRACE("unregister prefix: " << faceEntry);
+  NFD_LOG_TRACE("unregister prefix: " << route);
 
-  m_managedRib.erase(parameters.getName(), faceEntry);
+  m_managedRib.erase(parameters.getName(), route);
 
   sendUpdatesToFib(request, parameters);
 }
@@ -419,7 +421,7 @@
 void
 RibManager::onCommandError(uint32_t code, const std::string& error,
                            const shared_ptr<const Interest>& request,
-                           const FaceEntry& faceEntry)
+                           const Route& route)
 {
   NFD_LOG_ERROR("NFD returned an error: " << error << " (code: " << code << ")");
 
@@ -445,7 +447,7 @@
 void
 RibManager::onRegSuccess(const shared_ptr<const Interest>& request,
                          const ControlParameters& parameters,
-                         const FaceEntry& faceEntry)
+                         const Route& route)
 {
   ControlResponse response;
 
@@ -453,7 +455,7 @@
   response.setText("Success");
   response.setBody(parameters.wireEncode());
 
-  NFD_LOG_TRACE("onRegSuccess: registered " << faceEntry);
+  NFD_LOG_TRACE("onRegSuccess: registered " << route);
 
   if (static_cast<bool>(request))
     sendResponse(request->getName(), response);
@@ -463,7 +465,7 @@
 void
 RibManager::onUnRegSuccess(const shared_ptr<const Interest>& request,
                            const ControlParameters& parameters,
-                           const FaceEntry& faceEntry)
+                           const Route& route)
 {
   ControlResponse response;
 
@@ -471,7 +473,7 @@
   response.setText("Success");
   response.setBody(parameters.wireEncode());
 
-  NFD_LOG_TRACE("onUnRegSuccess: unregistered " << faceEntry);
+  NFD_LOG_TRACE("onUnRegSuccess: unregistered " << route);
 
   if (static_cast<bool>(request))
     sendResponse(request->getName(), response);
@@ -527,9 +529,22 @@
 }
 
 void
-RibManager::onNrdCommandPrefixAddNextHopSuccess(const Name& prefix)
+RibManager::onNrdCommandPrefixAddNextHopSuccess(const Name& prefix,
+                                                const ndn::nfd::ControlParameters& result)
 {
   NFD_LOG_DEBUG("Successfully registered " + prefix.toUri() + " with NFD");
+
+  // Routes must be inserted into the RIB so route flags can be applied
+  Route route;
+  route.faceId = result.getFaceId();
+  route.origin = ndn::nfd::ROUTE_ORIGIN_APP;
+  route.expires = time::steady_clock::TimePoint::max();
+  route.flags = ndn::nfd::ROUTE_FLAG_CHILD_INHERIT;
+
+  m_managedRib.insert(prefix, route);
+
+  m_registeredFaces.insert(route.faceId);
+  m_managedRib.clearFibUpdates();
 }
 
 void
@@ -715,15 +730,15 @@
 
       if (update->action == FibUpdate::ADD_NEXTHOP)
         {
-          FaceEntry faceEntry;
-          faceEntry.faceId = update->faceId;
-          faceEntry.cost = update->cost;
+          Route route;
+          route.faceId = update->faceId;
+          route.cost = update->cost;
 
           m_nfdController.start<ndn::nfd::FibAddNextHopCommand>(
             ControlParameters()
               .setName(update->name)
-              .setFaceId(faceEntry.faceId)
-              .setCost(faceEntry.cost),
+              .setFaceId(route.faceId)
+              .setCost(route.cost),
             bind(&RibManager::onAddNextHopSuccess, this, request,
                                                          parameters,
                                                          currentTransactionId,
@@ -733,13 +748,13 @@
         }
       else if (update->action == FibUpdate::REMOVE_NEXTHOP)
         {
-          FaceEntry faceEntry;
-          faceEntry.faceId = update->faceId;
+          Route route;
+          route.faceId = update->faceId;
 
           m_nfdController.start<ndn::nfd::FibRemoveNextHopCommand>(
             ControlParameters()
               .setName(update->name)
-              .setFaceId(faceEntry.faceId),
+              .setFaceId(route.faceId),
             bind(&RibManager::onRemoveNextHopSuccess, this, request,
                                                             parameters,
                                                             currentTransactionId,
diff --git a/rib/rib-manager.hpp b/rib/rib-manager.hpp
index 181d210..3819d5a 100644
--- a/rib/rib-manager.hpp
+++ b/rib/rib-manager.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -130,21 +130,24 @@
   void
   onCommandError(uint32_t code, const std::string& error,
                  const shared_ptr<const Interest>& request,
-                 const FaceEntry& faceEntry);
+                 const Route& route);
 
   void
   onRegSuccess(const shared_ptr<const Interest>& request,
                const ControlParameters& parameters,
-               const FaceEntry& faceEntry);
+               const Route& route);
 
   void
   onUnRegSuccess(const shared_ptr<const Interest>& request,
                  const ControlParameters& parameters,
-                 const FaceEntry& faceEntry);
+                 const Route& route);
 
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   void
-  onNrdCommandPrefixAddNextHopSuccess(const Name& prefix);
+  onNrdCommandPrefixAddNextHopSuccess(const Name& prefix,
+                                      const ndn::nfd::ControlParameters& result);
 
+private:
   void
   onNrdCommandPrefixAddNextHopError(const Name& name, const std::string& msg);
 
@@ -272,8 +275,11 @@
   const SignedVerbDispatchTable m_signedVerbDispatch;
 
   static const Name COMMAND_PREFIX; // /localhost/nrd
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   static const Name REMOTE_COMMAND_PREFIX; // /localhop/nrd
 
+private:
   // number of components in an invalid, but not malformed, unsigned command.
   // (/localhost/nrd + verb + options) = 4
   static const size_t COMMAND_UNSIGNED_NCOMPS;
@@ -297,7 +303,7 @@
   static const Name FACES_LIST_DATASET_PREFIX;
 
   static const time::seconds ACTIVE_FACE_FETCH_INTERVAL;
-  EventId m_activeFaceFetchEvent;
+  scheduler::EventId m_activeFaceFetchEvent;
 
   typedef std::set<uint64_t> FaceIdSet;
   /** \brief contains FaceIds with one or more Routes in the RIB
diff --git a/rib/rib-status-publisher.cpp b/rib/rib-status-publisher.cpp
index 76a164c..0b8ede4 100644
--- a/rib/rib-status-publisher.cpp
+++ b/rib/rib-status-publisher.cpp
@@ -61,24 +61,21 @@
       size_t ribEntryLength = 0;
 
       ndn::nfd::RibEntry tlvEntry;
-      const RibEntry::FaceList& faces = entry.getFaces();
 
-      for (RibEntry::FaceList::const_iterator faceIt = faces.begin();
-           faceIt != faces.end(); ++faceIt)
+      for (const Route& route : entry)
         {
-          const FaceEntry& face = *faceIt;
+          ndn::nfd::Route routeEle;
+          routeEle.setFaceId(route.faceId)
+                  .setOrigin(route.origin)
+                  .setCost(route.cost)
+                  .setFlags(route.flags);
 
-          ndn::nfd::Route route;
-          route
-            .setFaceId(face.faceId)
-            .setOrigin(face.origin)
-            .setCost(face.cost)
-            .setFlags(face.flags);
-          if (face.expires < time::steady_clock::TimePoint::max()) {
-            route.setExpirationPeriod(time::duration_cast<time::milliseconds>
-                                      (face.expires - time::steady_clock::now()));
+          if (route.expires < time::steady_clock::TimePoint::max()) {
+            routeEle.setExpirationPeriod(time::duration_cast<time::milliseconds>(
+              route.expires - time::steady_clock::now()));
           }
-          tlvEntry.addRoute(route);
+
+          tlvEntry.addRoute(routeEle);
         }
 
       tlvEntry.setName(prefix);
diff --git a/rib/rib.cpp b/rib/rib.cpp
index 02df45e..0110b19 100644
--- a/rib/rib.cpp
+++ b/rib/rib.cpp
@@ -33,9 +33,9 @@
 namespace rib {
 
 static inline bool
-sortFace(const FaceEntry& entry1, const FaceEntry& entry2)
+sortRoutes(const Route& lhs, const Route& rhs)
 {
-  return entry1.faceId < entry2.faceId;
+  return lhs.faceId < rhs.faceId;
 }
 
 static inline bool
@@ -73,29 +73,29 @@
   return m_rib.find(prefix);
 }
 
-FaceEntry*
-Rib::find(const Name& prefix, const FaceEntry& face) const
+Route*
+Rib::find(const Name& prefix, const Route& route) const
 {
   RibTable::const_iterator ribIt = m_rib.find(prefix);
 
   // Name prefix exists
   if (ribIt != m_rib.end())
     {
-      shared_ptr<RibEntry> entry(ribIt->second);
+      shared_ptr<RibEntry> entry = ribIt->second;
 
-      RibEntry::iterator faceIt = std::find_if(entry->begin(), entry->end(),
-                                                     bind(&compareFaceIdAndOrigin, _1, face));
+      RibEntry::iterator routeIt = entry->findRoute(route);
 
-      if (faceIt != entry->end())
+      if (routeIt != entry->end())
         {
-          return &((*faceIt));
+          return &((*routeIt));
         }
     }
-  return 0;
+
+  return nullptr;
 }
 
 void
-Rib::insert(const Name& prefix, const FaceEntry& face)
+Rib::insert(const Name& prefix, const Route& route)
 {
   RibTable::iterator ribIt = m_rib.find(prefix);
 
@@ -104,49 +104,47 @@
     {
       shared_ptr<RibEntry> entry(ribIt->second);
 
-      RibEntry::iterator faceIt = std::find_if(entry->getFaces().begin(),
-                                               entry->getFaces().end(),
-                                               bind(&compareFaceIdAndOrigin, _1, face));
+      RibEntry::iterator routeIt = entry->findRoute(route);
 
-      if (faceIt == entry->end())
+      if (routeIt == entry->end())
         {
-          // Will the new face change the namespace's capture flag?
-          bool captureWasTurnedOn = (entry->hasCapture() == false && isCaptureFlagSet(face.flags));
+          // Will the new route change the namespace's capture flag?
+          bool captureWasTurnedOn = (entry->hasCapture() == false && isCaptureFlagSet(route.flags));
 
-          // New face
-          entry->insertFace(face);
+          // New route
+          entry->insertRoute(route);
           m_nItems++;
 
           // Register with face lookup table
-          m_faceMap[face.faceId].push_back(entry);
+          m_faceMap[route.faceId].push_back(entry);
 
-          createFibUpdatesForNewFaceEntry(*entry, face, captureWasTurnedOn);
+          createFibUpdatesForNewRoute(*entry, route, captureWasTurnedOn);
         }
-      else // Entry exists, update fields
+      else // Route exists, update fields
         {
           // First cancel old scheduled event, if any, then set the EventId to new one
-          if (static_cast<bool>(faceIt->getExpirationEvent()))
+          if (static_cast<bool>(routeIt->getExpirationEvent()))
             {
               NFD_LOG_TRACE("Cancelling expiration event for " << entry->getName() << " "
-                                                               << *faceIt);
-              scheduler::cancel(faceIt->getExpirationEvent());
+                                                               << *routeIt);
+              scheduler::cancel(routeIt->getExpirationEvent());
             }
 
           // No checks are required here as the iterator needs to be updated in all cases.
-          faceIt->setExpirationEvent(face.getExpirationEvent());
+          routeIt->setExpirationEvent(route.getExpirationEvent());
 
           // Save flags for update processing
-          uint64_t previousFlags = faceIt->flags;
+          uint64_t previousFlags = routeIt->flags;
 
-          // If the entry's cost didn't change and child inherit is not set,
+          // If the route's cost didn't change and child inherit is not set,
           // no need to traverse subtree.
-          uint64_t previousCost = faceIt->cost;
+          uint64_t previousCost = routeIt->cost;
 
-          faceIt->flags = face.flags;
-          faceIt->cost = face.cost;
-          faceIt->expires = face.expires;
+          routeIt->flags = route.flags;
+          routeIt->cost = route.cost;
+          routeIt->expires = route.expires;
 
-          createFibUpdatesForUpdatedEntry(*entry, face, previousFlags, previousCost);
+          createFibUpdatesForUpdatedRoute(*entry, route, previousFlags, previousCost);
         }
     }
   else // New name prefix
@@ -157,7 +155,7 @@
       m_nItems++;
 
       entry->setName(prefix);
-      entry->insertFace(face);
+      entry->insertRoute(route);
 
       // Find prefix's parent
       shared_ptr<RibEntry> parent = findParent(prefix);
@@ -185,9 +183,9 @@
         }
 
       // Register with face lookup table
-      m_faceMap[face.faceId].push_back(entry);
+      m_faceMap[route.faceId].push_back(entry);
 
-      createFibUpdatesForNewRibEntry(*entry, face);
+      createFibUpdatesForNewRibEntry(*entry, route);
 
       // do something after inserting an entry
       afterInsertEntry(prefix);
@@ -195,7 +193,7 @@
 }
 
 void
-Rib::erase(const Name& prefix, const FaceEntry& face)
+Rib::erase(const Name& prefix, const Route& route)
 {
   RibTable::iterator ribIt = m_rib.find(prefix);
 
@@ -206,42 +204,42 @@
 
       const bool hadCapture = entry->hasCapture();
 
-      // Need to copy face to do FIB updates with correct cost and flags since nfdc does not
+      // Need to copy route to do FIB updates with correct cost and flags since nfdc does not
       // pass flags or cost
-      RibEntry::iterator faceIt = entry->findFace(face);
+      RibEntry::iterator routeIt = entry->findRoute(route);
 
-      if (faceIt != entry->end())
+      if (routeIt != entry->end())
         {
-          FaceEntry faceToErase = *faceIt;
-          faceToErase.flags = faceIt->flags;
-          faceToErase.cost = faceIt->cost;
+          Route routeToErase = *routeIt;
+          routeToErase.flags = routeIt->flags;
+          routeToErase.cost = routeIt->cost;
 
-          entry->eraseFace(faceIt);
+          entry->eraseRoute(routeIt);
 
           m_nItems--;
 
           const bool captureWasTurnedOff = (hadCapture && !entry->hasCapture());
 
-          createFibUpdatesForErasedFaceEntry(*entry, faceToErase, captureWasTurnedOff);
+          createFibUpdatesForErasedRoute(*entry, routeToErase, captureWasTurnedOff);
 
           // If this RibEntry no longer has this faceId, unregister from face lookup table
-          if (!entry->hasFaceId(face.faceId))
+          if (!entry->hasFaceId(route.faceId))
             {
-              m_faceMap[face.faceId].remove(entry);
+              m_faceMap[route.faceId].remove(entry);
             }
           else
             {
               // The RibEntry still has the face ID; need to update FIB
-              // with lowest cost for the same face instead of removing the face from the FIB
-              shared_ptr<FaceEntry> lowCostFace = entry->getFaceWithLowestCostByFaceId(face.faceId);
+              // with lowest cost for the same route instead of removing the route from the FIB
+              shared_ptr<Route> lowCostRoute = entry->getRouteWithLowestCostByFaceId(route.faceId);
 
-              BOOST_ASSERT(static_cast<bool>(lowCostFace));
+              BOOST_ASSERT(static_cast<bool>(lowCostRoute));
 
-              createFibUpdatesForNewFaceEntry(*entry, *lowCostFace, false);
+              createFibUpdatesForNewRoute(*entry, *lowCostRoute, false);
             }
 
-          // If a RibEntry's facelist is empty, remove it from the tree
-          if (entry->getFaces().size() == 0)
+          // If a RibEntry's route list is empty, remove it from the tree
+          if (entry->getRoutes().size() == 0)
             {
               eraseEntry(ribIt);
             }
@@ -263,41 +261,34 @@
   RibEntryList& ribEntries = lookupIt->second;
 
   // For each RIB entry that has faceId, remove the face from the entry
-  for (RibEntryList::iterator entryIt = ribEntries.begin(); entryIt != ribEntries.end(); ++entryIt)
+  for (shared_ptr<RibEntry>& entry : ribEntries)
     {
-      shared_ptr<RibEntry> entry = *entryIt;
-
       const bool hadCapture = entry->hasCapture();
 
-      // Find the faces in the entry
-      for (RibEntry::iterator faceIt = entry->begin(); faceIt != entry->end(); ++faceIt)
+      // Find the routes in the entry
+      for (RibEntry::iterator routeIt = entry->begin(); routeIt != entry->end(); ++routeIt)
         {
-          if (faceIt->faceId == faceId)
+          if (routeIt->faceId == faceId)
             {
-              FaceEntry copy = *faceIt;
+              Route copy = *routeIt;
 
-              faceIt = entry->eraseFace(faceIt);
+              routeIt = entry->eraseRoute(routeIt);
               m_nItems--;
 
               const bool captureWasTurnedOff = (hadCapture && !entry->hasCapture());
-              createFibUpdatesForErasedFaceEntry(*entry, copy, captureWasTurnedOff);
+              createFibUpdatesForErasedRoute(*entry, copy, captureWasTurnedOff);
             }
         }
 
-        // If a RibEntry's facelist is empty, remove it from the tree
-        if (entry->getFaces().size() == 0)
+        // If a RibEntry's route list is empty, remove it from the tree
+        if (entry->getRoutes().size() == 0)
           {
             eraseEntry(m_rib.find(entry->getName()));
           }
     }
 
   // Face no longer exists, remove from face lookup table
-  FaceLookupTable::iterator entryToDelete = m_faceMap.find(faceId);
-
-  if (entryToDelete != m_faceMap.end())
-    {
-      m_faceMap.erase(entryToDelete);
-    }
+  m_faceMap.erase(lookupIt);
 }
 
 shared_ptr<RibEntry>
@@ -399,8 +390,7 @@
 void
 Rib::insertFibUpdate(shared_ptr<FibUpdate> update)
 {
-  // If an update with the same name and face already exists,
-  // replace it
+  // If an update with the same name and Face ID already exists, replace it
   FibUpdateList::iterator it = std::find_if(m_fibUpdateList.begin(), m_fibUpdateList.end(),
                                             bind(&compareFibUpdates, _1, update));
 
@@ -419,108 +409,107 @@
 }
 
 void
-Rib::createFibUpdatesForNewRibEntry(RibEntry& entry, const FaceEntry& face)
+Rib::createFibUpdatesForNewRibEntry(RibEntry& entry, const Route& route)
 {
   // Create FIB update for new entry
-  insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), face.faceId, face.cost));
+  insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), route.faceId, route.cost));
 
   // No flags are set
-  if (!isAnyFlagSet(face.flags))
+  if (!isAnyFlagSet(route.flags))
     {
-      // Add ancestor faces to self
-      addInheritedFacesToEntry(entry, getAncestorFaces(entry));
+      // Add ancestor routes to self
+      addInheritedRoutesToEntry(entry, getAncestorRoutes(entry));
     }
-  else if (areBothFlagsSet(face.flags))
+  else if (areBothFlagsSet(route.flags))
     {
-      // Add face to children
-      FaceSet facesToAdd;
-      facesToAdd.insert(face);
+      // Add route to children
+      RouteSet routesToAdd;
+      routesToAdd.insert(route);
 
-      // Remove faces blocked by capture and add self to children
-      modifyChildrensInheritedFaces(entry, facesToAdd, getAncestorFaces(entry));
+      // Remove routes blocked by capture and add self to children
+      modifyChildrensInheritedRoutes(entry, routesToAdd, getAncestorRoutes(entry));
     }
-  else if (isChildInheritFlagSet(face.flags))
+  else if (isChildInheritFlagSet(route.flags))
     {
-      FaceSet ancestorFaces = getAncestorFaces(entry);
+      RouteSet ancestorRoutes = getAncestorRoutes(entry);
 
-      // Add ancestor faces to self
-      addInheritedFacesToEntry(entry, ancestorFaces);
+      // Add ancestor routes to self
+      addInheritedRoutesToEntry(entry, ancestorRoutes);
 
-      // If there is an ancestor face which is the same as the new face, replace it
-      // with the new face
-      FaceSet::iterator it = ancestorFaces.find(face);
+      // If there is an ancestor route with the same Face ID as the new route, replace it
+      // with the new route
+      RouteSet::iterator it = ancestorRoutes.find(route);
 
-      // There is a face that needs to be overwritten, erase and then replace
-      if (it != ancestorFaces.end())
+      // There is a route that needs to be overwritten, erase and then replace
+      if (it != ancestorRoutes.end())
         {
-          ancestorFaces.erase(it);
+          ancestorRoutes.erase(it);
         }
 
-      // Add new face to ancestor list so it can be added to children
-      ancestorFaces.insert(face);
+      // Add new route to ancestor list so it can be added to children
+      ancestorRoutes.insert(route);
 
-      // Add ancestor faces to children
-      modifyChildrensInheritedFaces(entry, ancestorFaces, FaceSet());
+      // Add ancestor routes to children
+      modifyChildrensInheritedRoutes(entry, ancestorRoutes, RouteSet());
     }
-  else if (isCaptureFlagSet(face.flags))
+  else if (isCaptureFlagSet(route.flags))
     {
-      // Remove faces blocked by capture
-      modifyChildrensInheritedFaces(entry, FaceSet(), getAncestorFaces(entry));
+      // Remove routes blocked by capture
+      modifyChildrensInheritedRoutes(entry, RouteSet(), getAncestorRoutes(entry));
     }
 }
 
 void
-Rib::createFibUpdatesForNewFaceEntry(RibEntry& entry, const FaceEntry& face,
-                                     bool captureWasTurnedOn)
+Rib::createFibUpdatesForNewRoute(RibEntry& entry, const Route& route, bool captureWasTurnedOn)
 {
-  // Only update if the new face has a lower cost than a previously installed face
-  shared_ptr<FaceEntry> prevFace = entry.getFaceWithLowestCostAndChildInheritByFaceId(face.faceId);
+  // Only update if the new route has a lower cost than a previously installed route
+  shared_ptr<Route> prevRoute = entry.getRouteWithLowestCostAndChildInheritByFaceId(route.faceId);
 
-  FaceSet facesToAdd;
-  if (isChildInheritFlagSet(face.flags))
+  RouteSet routesToAdd;
+  if (isChildInheritFlagSet(route.flags))
     {
-      // Add to children if this new face doesn't override a previous lower cost, or
-      // add to children if this new is lower cost than a previous face.
-      // Less than equal, since entry may find this face
-      if (!static_cast<bool>(prevFace) || face.cost <= prevFace->cost)
+      // Add to children if this new route doesn't override a previous lower cost, or
+      // add to children if this new route is lower cost than a previous route.
+      // Less than equal, since entry may find this route
+      if (prevRoute == nullptr || route.cost <= prevRoute->cost)
         {
           // Add self to children
-          facesToAdd.insert(face);
+          routesToAdd.insert(route);
         }
     }
 
-  FaceSet facesToRemove;
+  RouteSet routesToRemove;
   if (captureWasTurnedOn)
     {
       // Capture flag on
-      facesToRemove = getAncestorFaces(entry);
+      routesToRemove = getAncestorRoutes(entry);
 
-      // Remove ancestor faces from self
-      removeInheritedFacesFromEntry(entry, facesToRemove);
+      // Remove ancestor routes from self
+      removeInheritedRoutesFromEntry(entry, routesToRemove);
     }
 
-  modifyChildrensInheritedFaces(entry, facesToAdd, facesToRemove);
+  modifyChildrensInheritedRoutes(entry, routesToAdd, routesToRemove);
 
-  // If another face with same faceId and lower cost, don't update.
+  // If another route with same faceId and lower cost, don't update.
   // Must be done last so that add updates replace removal updates
   // Create FIB update for new entry
-  if (face.cost <= entry.getFaceWithLowestCostByFaceId(face.faceId)->cost)
+  if (route.cost <= entry.getRouteWithLowestCostByFaceId(route.faceId)->cost)
     {
-      insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), face.faceId, face.cost));
+      insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), route.faceId, route.cost));
     }
 }
 
 void
-Rib::createFibUpdatesForUpdatedEntry(RibEntry& entry, const FaceEntry& face,
+Rib::createFibUpdatesForUpdatedRoute(RibEntry& entry, const Route& route,
                                      const uint64_t previousFlags, const uint64_t previousCost)
 {
-  const bool costDidChange = (face.cost != previousCost);
+  const bool costDidChange = (route.cost != previousCost);
 
-  // Look for an installed face with the lowest cost and child inherit set
-  shared_ptr<FaceEntry> prevFace = entry.getFaceWithLowestCostAndChildInheritByFaceId(face.faceId);
+  // Look for the installed route with the lowest cost and child inherit set
+  shared_ptr<Route> prevRoute = entry.getRouteWithLowestCostAndChildInheritByFaceId(route.faceId);
 
   // No flags changed and cost didn't change, no change in FIB
-  if (face.flags == previousFlags && !costDidChange)
+  if (route.flags == previousFlags && !costDidChange)
     {
       return;
     }
@@ -528,32 +517,32 @@
   // Cost changed so create update for the entry itself
   if (costDidChange)
     {
-      // Create update if this face's cost is lower than other faces
-       if (face.cost <= entry.getFaceWithLowestCostByFaceId(face.faceId)->cost)
+      // Create update if this route's cost is lower than other routes
+       if (route.cost <= entry.getRouteWithLowestCostByFaceId(route.faceId)->cost)
         {
           // Create FIB update for the updated entry
-         insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), face.faceId, face.cost));
+         insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), route.faceId, route.cost));
         }
-      else if (previousCost < entry.getFaceWithLowestCostByFaceId(face.faceId)->cost)
+      else if (previousCost < entry.getRouteWithLowestCostByFaceId(route.faceId)->cost)
         {
-          // Create update if this face used to be the lowest face but is no longer
-          // the lowest cost face.
-          insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), prevFace->faceId,
-                                                                          prevFace->cost));
+          // Create update if this route used to be the lowest cost route but is no longer
+          // the lowest cost route.
+          insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), prevRoute->faceId,
+                                                                      prevRoute->cost));
         }
 
-      // If another face with same faceId and lower cost and ChildInherit exists,
+      // If another route with same faceId and lower cost and ChildInherit exists,
       // don't update children.
-      if (!static_cast<bool>(prevFace) || face.cost <= prevFace->cost)
+      if (prevRoute == nullptr || route.cost <= prevRoute->cost)
         {
           // If no flags changed but child inheritance is set, need to update children
           // with new cost
-          if ((face.flags == previousFlags) && isChildInheritFlagSet(face.flags))
+          if ((route.flags == previousFlags) && isChildInheritFlagSet(route.flags))
           {
             // Add self to children
-            FaceSet facesToAdd;
-            facesToAdd.insert(face);
-            modifyChildrensInheritedFaces(entry, facesToAdd, FaceSet());
+            RouteSet routesToAdd;
+            routesToAdd.insert(route);
+            modifyChildrensInheritedRoutes(entry, routesToAdd, RouteSet());
 
             return;
           }
@@ -561,140 +550,140 @@
     }
 
   // Child inherit was turned on
-  if (!isChildInheritFlagSet(previousFlags) && isChildInheritFlagSet(face.flags))
+  if (!isChildInheritFlagSet(previousFlags) && isChildInheritFlagSet(route.flags))
     {
-      // If another face with same faceId and lower cost and ChildInherit exists,
+      // If another route with same faceId and lower cost and ChildInherit exists,
       // don't update children.
-      if (!static_cast<bool>(prevFace) || face.cost <= prevFace->cost)
+      if (prevRoute == nullptr || route.cost <= prevRoute->cost)
         {
           // Add self to children
-          FaceSet facesToAdd;
-          facesToAdd.insert(face);
-          modifyChildrensInheritedFaces(entry, facesToAdd, FaceSet());
+          RouteSet routesToAdd;
+          routesToAdd.insert(route);
+          modifyChildrensInheritedRoutes(entry, routesToAdd, RouteSet());
         }
 
     } // Child inherit was turned off
-  else if (isChildInheritFlagSet(previousFlags) && !isChildInheritFlagSet(face.flags))
+  else if (isChildInheritFlagSet(previousFlags) && !isChildInheritFlagSet(route.flags))
     {
       // Remove self from children
-      FaceSet facesToRemove;
-      facesToRemove.insert(face);
+      RouteSet routesToRemove;
+      routesToRemove.insert(route);
 
-      FaceSet facesToAdd;
-      // If another face with same faceId and ChildInherit exists, update children with this face.
-      if (static_cast<bool>(prevFace))
+      RouteSet routesToAdd;
+      // If another route with same faceId and ChildInherit exists, update children with this route.
+      if (prevRoute != nullptr)
         {
-          facesToAdd.insert(*prevFace);
+          routesToAdd.insert(*prevRoute);
         }
       else
         {
           // Look for an ancestor that was blocked previously
-          const FaceSet ancestorFaces = getAncestorFaces(entry);
-          FaceSet::iterator it = ancestorFaces.find(face);
+          const RouteSet ancestorRoutes = getAncestorRoutes(entry);
+          RouteSet::iterator it = ancestorRoutes.find(route);
 
           // If an ancestor is found, add it to children
-          if (it != ancestorFaces.end())
+          if (it != ancestorRoutes.end())
             {
-              facesToAdd.insert(*it);
+              routesToAdd.insert(*it);
             }
         }
 
-      modifyChildrensInheritedFaces(entry, facesToAdd, facesToRemove);
+      modifyChildrensInheritedRoutes(entry, routesToAdd, routesToRemove);
     }
 
   // Capture was turned on
-  if (!isCaptureFlagSet(previousFlags) && isCaptureFlagSet(face.flags))
+  if (!isCaptureFlagSet(previousFlags) && isCaptureFlagSet(route.flags))
     {
-      FaceSet ancestorFaces = getAncestorFaces(entry);
+      RouteSet ancestorRoutes = getAncestorRoutes(entry);
 
-      // Remove ancestor faces from self
-      removeInheritedFacesFromEntry(entry, ancestorFaces);
+      // Remove ancestor routes from self
+      removeInheritedRoutesFromEntry(entry, ancestorRoutes);
 
-      // Remove ancestor faces from children
-      modifyChildrensInheritedFaces(entry, FaceSet(), ancestorFaces);
+      // Remove ancestor routes from children
+      modifyChildrensInheritedRoutes(entry, RouteSet(), ancestorRoutes);
     }  // Capture was turned off
-  else if (isCaptureFlagSet(previousFlags) && !isCaptureFlagSet(face.flags))
+  else if (isCaptureFlagSet(previousFlags) && !isCaptureFlagSet(route.flags))
     {
-      FaceSet ancestorFaces = getAncestorFaces(entry);
+      RouteSet ancestorRoutes = getAncestorRoutes(entry);
 
-      // Add ancestor faces to self
-      addInheritedFacesToEntry(entry, ancestorFaces);
+      // Add ancestor routes to self
+      addInheritedRoutesToEntry(entry, ancestorRoutes);
 
-      // Add ancestor faces to children
-      modifyChildrensInheritedFaces(entry, ancestorFaces, FaceSet());
+      // Add ancestor routes to children
+      modifyChildrensInheritedRoutes(entry, ancestorRoutes, RouteSet());
     }
 }
 
 void
-Rib::createFibUpdatesForErasedFaceEntry(RibEntry& entry, const FaceEntry& face,
-                                        const bool captureWasTurnedOff)
+Rib::createFibUpdatesForErasedRoute(RibEntry& entry, const Route& route,
+                                    const bool captureWasTurnedOff)
 {
-  insertFibUpdate(FibUpdate::createRemoveUpdate(entry.getName(), face.faceId));
+  insertFibUpdate(FibUpdate::createRemoveUpdate(entry.getName(), route.faceId));
 
-  if (areBothFlagsSet(face.flags))
+  if (areBothFlagsSet(route.flags))
     {
       // Remove self from children
-      FaceSet facesToRemove;
-      facesToRemove.insert(face);
+      RouteSet routesToRemove;
+      routesToRemove.insert(route);
 
       // If capture is turned off for the route, need to add ancestors
       // to self and children
-      FaceSet facesToAdd;
+      RouteSet routesToAdd;
       if (captureWasTurnedOff)
         {
-          // Look for an ancestors that were blocked previously
-          facesToAdd = getAncestorFaces(entry);
+          // Look for ancestors that were blocked previously
+          routesToAdd = getAncestorRoutes(entry);
 
-          // Add ancestor faces to self
-          addInheritedFacesToEntry(entry, facesToAdd);
+          // Add ancestor routes to self
+          addInheritedRoutesToEntry(entry, routesToAdd);
         }
 
-      modifyChildrensInheritedFaces(entry, facesToAdd, facesToRemove);
+      modifyChildrensInheritedRoutes(entry, routesToAdd, routesToRemove);
     }
-  else if (isChildInheritFlagSet(face.flags))
+  else if (isChildInheritFlagSet(route.flags))
     {
       // If not blocked by capture, add inherited routes to children
-      FaceSet facesToAdd;
+      RouteSet routesToAdd;
       if (!entry.hasCapture())
         {
-          facesToAdd = getAncestorFaces(entry);
+          routesToAdd = getAncestorRoutes(entry);
         }
 
-      FaceSet facesToRemove;
-      facesToRemove.insert(face);
+      RouteSet routesToRemove;
+      routesToRemove.insert(route);
 
-      // Add ancestor faces to children
-      modifyChildrensInheritedFaces(entry, facesToAdd, facesToRemove);
+      // Add ancestor routes to children
+      modifyChildrensInheritedRoutes(entry, routesToAdd, routesToRemove);
     }
-  else if (isCaptureFlagSet(face.flags))
+  else if (isCaptureFlagSet(route.flags))
     {
       // If capture is turned off for the route, need to add ancestors
       // to self and children
-      FaceSet facesToAdd;
+      RouteSet routesToAdd;
       if (captureWasTurnedOff)
         {
           // Look for an ancestors that were blocked previously
-          facesToAdd = getAncestorFaces(entry);
+          routesToAdd = getAncestorRoutes(entry);
 
-          // Add ancestor faces to self
-          addInheritedFacesToEntry(entry, facesToAdd);
+          // Add ancestor routes to self
+          addInheritedRoutesToEntry(entry, routesToAdd);
         }
 
-      modifyChildrensInheritedFaces(entry, facesToAdd, FaceSet());
+      modifyChildrensInheritedRoutes(entry, routesToAdd, RouteSet());
     }
 
-  // Need to check if the removed face was blocking an inherited route
-  FaceSet ancestorFaces = getAncestorFaces(entry);
+  // Need to check if the removed route was blocking an inherited route
+  RouteSet ancestorRoutes = getAncestorRoutes(entry);
 
   if (!entry.hasCapture())
   {
-    // If there is an ancestor face which is the same as the erased face, add that face
+    // If there is an ancestor route with the same Face ID as the erased route, add that route
     // to the current entry
-    FaceSet::iterator it = ancestorFaces.find(face);
+    RouteSet::iterator it = ancestorRoutes.find(route);
 
-    if (it != ancestorFaces.end())
+    if (it != ancestorRoutes.end())
       {
-        entry.addInheritedFace(*it);
+        entry.addInheritedRoute(*it);
         insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), it->faceId, it->cost));
       }
   }
@@ -703,28 +692,28 @@
 void
 Rib::createFibUpdatesForErasedRibEntry(RibEntry& entry)
 {
-  for (RibEntry::FaceList::iterator it = entry.getInheritedFaces().begin();
-       it != entry.getInheritedFaces().end(); ++it)
+  for (RibEntry::RouteList::iterator it = entry.getInheritedRoutes().begin();
+       it != entry.getInheritedRoutes().end(); ++it)
     {
       insertFibUpdate(FibUpdate::createRemoveUpdate(entry.getName(), it->faceId));
     }
 }
 
-Rib::FaceSet
-Rib::getAncestorFaces(const RibEntry& entry) const
+Rib::RouteSet
+Rib::getAncestorRoutes(const RibEntry& entry) const
 {
-  FaceSet ancestorFaces(&sortFace);
+  RouteSet ancestorRoutes(&sortRoutes);
 
   shared_ptr<RibEntry> parent = entry.getParent();
 
   while (static_cast<bool>(parent))
     {
-      for (RibEntry::iterator it = parent->getFaces().begin();
-           it != parent->getFaces().end(); ++it)
+      for (RibEntry::iterator it = parent->getRoutes().begin();
+           it != parent->getRoutes().end(); ++it)
         {
           if (isChildInheritFlagSet(it->flags))
             {
-              ancestorFaces.insert(*it);
+              ancestorRoutes.insert(*it);
             }
         }
 
@@ -736,52 +725,51 @@
       parent = parent->getParent();
     }
 
-    return ancestorFaces;
+    return ancestorRoutes;
 }
 
 void
-Rib::addInheritedFacesToEntry(RibEntry& entry, const Rib::FaceSet& facesToAdd)
+Rib::addInheritedRoutesToEntry(RibEntry& entry, const Rib::RouteSet& routesToAdd)
 {
-  for (FaceSet::const_iterator it = facesToAdd.begin(); it != facesToAdd.end(); ++it)
+  for (RouteSet::const_iterator it = routesToAdd.begin(); it != routesToAdd.end(); ++it)
     {
-      // Don't add an ancestor faceId if the namespace has an entry for that faceId
+      // Don't add an ancestor route if the namespace has a route with that Face ID
       if (!entry.hasFaceId(it->faceId))
         {
-          entry.addInheritedFace(*it);
+          entry.addInheritedRoute(*it);
           insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), it->faceId, it->cost));
         }
     }
 }
 
 void
-Rib::removeInheritedFacesFromEntry(RibEntry& entry, const Rib::FaceSet& facesToRemove)
+Rib::removeInheritedRoutesFromEntry(RibEntry& entry, const Rib::RouteSet& routesToRemove)
 {
-  for (FaceSet::const_iterator it = facesToRemove.begin(); it != facesToRemove.end(); ++it)
+  for (RouteSet::const_iterator it = routesToRemove.begin(); it != routesToRemove.end(); ++it)
     {
-      // Only remove if the face has been inherited
-      if (entry.hasInheritedFace(*it))
+      // Only remove if the route has been inherited
+      if (entry.hasInheritedRoute(*it))
         {
-          entry.removeInheritedFace(*it);
+          entry.removeInheritedRoute(*it);
           insertFibUpdate(FibUpdate::createRemoveUpdate(entry.getName(), it->faceId));
         }
     }
 }
 
 void
-Rib::modifyChildrensInheritedFaces(RibEntry& entry, const Rib::FaceSet& facesToAdd,
-                                                    const Rib::FaceSet& facesToRemove)
+Rib::modifyChildrensInheritedRoutes(RibEntry& entry, const Rib::RouteSet& routesToAdd,
+                                                     const Rib::RouteSet& routesToRemove)
 {
   RibEntryList children = entry.getChildren();
 
   for (RibEntryList::iterator child = children.begin(); child != children.end(); ++child)
     {
-      traverseSubTree(*(*child), facesToAdd, facesToRemove);
+      traverseSubTree(*(*child), routesToAdd, routesToRemove);
     }
 }
 
 void
-Rib::traverseSubTree(RibEntry& entry, Rib::FaceSet facesToAdd,
-                                      Rib::FaceSet facesToRemove)
+Rib::traverseSubTree(RibEntry& entry, Rib::RouteSet routesToAdd, Rib::RouteSet routesToRemove)
 {
   // If a route on the namespace has the capture flag set, ignore self and children
   if (entry.hasCapture())
@@ -789,51 +777,50 @@
       return;
     }
 
-  // Remove inherited faces from current namespace
-  for (Rib::FaceSet::const_iterator removeIt = facesToRemove.begin();
-       removeIt != facesToRemove.end(); )
+  // Remove inherited routes from current namespace
+  for (Rib::RouteSet::const_iterator removeIt = routesToRemove.begin();
+       removeIt != routesToRemove.end(); )
     {
-      // If a route on the namespace has the same face and child inheritance set, ignore this face
+      // If a route on the namespace has the same face and child inheritance set, ignore this route
       if (entry.hasChildInheritOnFaceId(removeIt->faceId))
         {
-          facesToRemove.erase(removeIt++);
+          routesToRemove.erase(removeIt++);
           continue;
         }
 
-      // Only remove face if it removes an existing inherited route
-      if (entry.hasInheritedFace(*removeIt))
+      // Only remove route if it removes an existing inherited route
+      if (entry.hasInheritedRoute(*removeIt))
         {
-          entry.removeInheritedFace(*removeIt);
+          entry.removeInheritedRoute(*removeIt);
           insertFibUpdate(FibUpdate::createRemoveUpdate(entry.getName(), removeIt->faceId));
         }
 
       ++removeIt;
     }
 
-  // Add inherited faces to current namespace
-  for (Rib::FaceSet::const_iterator addIt = facesToAdd.begin();
-       addIt != facesToAdd.end(); )
+  // Add inherited routes to current namespace
+  for (Rib::RouteSet::const_iterator addIt = routesToAdd.begin(); addIt != routesToAdd.end(); )
     {
-      // If a route on the namespace has the same face and child inherit set, ignore this face
+      // If a route on the namespace has the same face and child inherit set, ignore this route
       if (entry.hasChildInheritOnFaceId(addIt->faceId))
       {
-        facesToAdd.erase(addIt++);
+        routesToAdd.erase(addIt++);
         continue;
       }
 
-      // Only add face if it does not override an existing route
+      // Only add route if it does not override an existing route
       if (!entry.hasFaceId(addIt->faceId))
         {
-          RibEntry::FaceList::iterator faceIt = entry.findInheritedFace(*addIt);
+          RibEntry::RouteList::iterator routeIt = entry.findInheritedRoute(*addIt);
 
-          // If the entry already has the inherited face, just update the face
-          if (faceIt != entry.getInheritedFaces().end())
+          // If the entry already has the inherited route, just update the route
+          if (routeIt != entry.getInheritedRoutes().end())
             {
-              faceIt->cost = addIt->cost;
+              routeIt->cost = addIt->cost;
             }
-          else // Otherwise, this is a newly inherited face
+          else // Otherwise, this is a newly inherited route
             {
-              entry.addInheritedFace(*addIt);
+              entry.addInheritedRoute(*addIt);
             }
 
           insertFibUpdate(FibUpdate::createAddUpdate(entry.getName(), addIt->faceId, addIt->cost));
@@ -844,10 +831,10 @@
 
   Rib::RibEntryList children = entry.getChildren();
 
-  // Apply face operations to current namespace's children
+  // Apply route operations to current namespace's children
   for (Rib::RibEntryList::iterator child = children.begin(); child != children.end(); ++child)
     {
-      traverseSubTree(*(*child), facesToAdd, facesToRemove);
+      traverseSubTree(*(*child), routesToAdd, routesToRemove);
     }
 }
 
diff --git a/rib/rib.hpp b/rib/rib.hpp
index ed9ca4e..698ce17 100644
--- a/rib/rib.hpp
+++ b/rib/rib.hpp
@@ -30,6 +30,7 @@
 #include "fib-update.hpp"
 #include "common.hpp"
 #include <ndn-cxx/management/nfd-control-command.hpp>
+#include <ndn-cxx/util/signal.hpp>
 
 namespace nfd {
 namespace rib {
@@ -39,27 +40,27 @@
 class Rib : noncopyable
 {
 public:
-  typedef std::list<shared_ptr<RibEntry> > RibEntryList;
-  typedef std::map<Name, shared_ptr<RibEntry> > RibTable;
+  typedef std::list<shared_ptr<RibEntry>> RibEntryList;
+  typedef std::map<Name, shared_ptr<RibEntry>> RibTable;
   typedef RibTable::const_iterator const_iterator;
-  typedef std::map<uint64_t, std::list<shared_ptr<RibEntry> > > FaceLookupTable;
-  typedef bool (*FaceComparePredicate)(const FaceEntry&, const FaceEntry&);
-  typedef std::set<FaceEntry, FaceComparePredicate> FaceSet;
-  typedef std::list<shared_ptr<const FibUpdate> > FibUpdateList;
+  typedef std::map<uint64_t, std::list<shared_ptr<RibEntry>>> FaceLookupTable;
+  typedef bool (*RouteComparePredicate)(const Route&, const Route&);
+  typedef std::set<Route, RouteComparePredicate> RouteSet;
+  typedef std::list<shared_ptr<const FibUpdate>> FibUpdateList;
 
   Rib();
 
   const_iterator
   find(const Name& prefix) const;
 
-  FaceEntry*
-  find(const Name& prefix, const FaceEntry& face) const;
+  Route*
+  find(const Name& prefix, const Route& route) const;
 
   void
-  insert(const Name& prefix, const FaceEntry& face);
+  insert(const Name& prefix, const Route& route);
 
   void
-  erase(const Name& prefix, const FaceEntry& face);
+  erase(const Name& prefix, const Route& route);
 
   void
   erase(const uint64_t faceId);
@@ -82,10 +83,10 @@
   /** \brief finds namespaces under the passed prefix
    *  \return{ a list of entries which are under the passed prefix }
    */
-  std::list<shared_ptr<RibEntry> >
+  std::list<shared_ptr<RibEntry>>
   findDescendants(const Name& prefix) const;
 
-  const std::list<shared_ptr<const FibUpdate> >&
+  const std::list<shared_ptr<const FibUpdate>>&
   getFibUpdates() const;
 
   void
@@ -99,46 +100,46 @@
   insertFibUpdate(shared_ptr<FibUpdate> update);
 
   void
-  createFibUpdatesForNewRibEntry(RibEntry& entry, const FaceEntry& face);
+  createFibUpdatesForNewRibEntry(RibEntry& entry, const Route& route);
 
   void
-  createFibUpdatesForNewFaceEntry(RibEntry& entry, const FaceEntry& face,
-                                  const bool captureWasTurnedOn);
+  createFibUpdatesForNewRoute(RibEntry& entry, const Route& route,
+                              const bool captureWasTurnedOn);
 
   void
-  createFibUpdatesForUpdatedEntry(RibEntry& entry, const FaceEntry& face,
+  createFibUpdatesForUpdatedRoute(RibEntry& entry, const Route& route,
                                   const uint64_t previousFlags, const uint64_t previousCost);
   void
-  createFibUpdatesForErasedFaceEntry(RibEntry& entry, const FaceEntry& face,
-                                     const bool captureWasTurnedOff);
+  createFibUpdatesForErasedRoute(RibEntry& entry, const Route& route,
+                                 const bool captureWasTurnedOff);
 
   void
   createFibUpdatesForErasedRibEntry(RibEntry& entry);
 
-  FaceSet
-  getAncestorFaces(const RibEntry& entry) const;
+  RouteSet
+  getAncestorRoutes(const RibEntry& entry) const;
 
   void
-  modifyChildrensInheritedFaces(RibEntry& entry, const Rib::FaceSet& facesToAdd,
-                                                 const Rib::FaceSet& facesToRemove);
+  modifyChildrensInheritedRoutes(RibEntry& entry, const Rib::RouteSet& routesToAdd,
+                                                  const Rib::RouteSet& routesToRemove);
 
   void
-  traverseSubTree(RibEntry& entry, Rib::FaceSet facesToAdd,
-                                   Rib::FaceSet facesToRemove);
+  traverseSubTree(RibEntry& entry, Rib::RouteSet routesToAdd,
+                                   Rib::RouteSet routesToRemove);
 
-  /** \brief Adds passed faces to the entry's inherited faces list
+  /** \brief Adds passed routes to the entry's inherited routes list
    */
   void
-  addInheritedFacesToEntry(RibEntry& entry, const Rib::FaceSet& facesToAdd);
+  addInheritedRoutesToEntry(RibEntry& entry, const Rib::RouteSet& routesToAdd);
 
-  /** \brief Removes passed faces from the entry's inherited faces list
+  /** \brief Removes passed routes from the entry's inherited routes list
    */
   void
-  removeInheritedFacesFromEntry(RibEntry& entry, const Rib::FaceSet& facesToRemove);
+  removeInheritedRoutesFromEntry(RibEntry& entry, const Rib::RouteSet& routesToRemove);
 
 public:
-  ndn::util::EventEmitter<Name> afterInsertEntry;
-  ndn::util::EventEmitter<Name> afterEraseEntry;
+  ndn::util::signal::Signal<Rib, Name> afterInsertEntry;
+  ndn::util::signal::Signal<Rib, Name> afterEraseEntry;
 
 private:
   RibTable m_rib;
diff --git a/rib/face-entry.hpp b/rib/route.hpp
similarity index 60%
rename from rib/face-entry.hpp
rename to rib/route.hpp
index c9eeb33..68769ef 100644
--- a/rib/face-entry.hpp
+++ b/rib/route.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -23,8 +23,8 @@
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef NFD_RIB_FACE_ENTRY_HPP
-#define NFD_RIB_FACE_ENTRY_HPP
+#ifndef NFD_RIB_ROUTE_HPP
+#define NFD_RIB_ROUTE_HPP
 
 #include "common.hpp"
 #include "core/scheduler.hpp"
@@ -32,13 +32,12 @@
 namespace nfd {
 namespace rib {
 
-/** \class FaceEntry
- *  \brief represents a route for a name prefix
+/** \brief represents a route for a name prefix
  */
-class FaceEntry
+class Route
 {
 public:
-  FaceEntry()
+  Route()
     : faceId(0)
     , origin(0)
     , flags(0)
@@ -50,12 +49,12 @@
 
 public:
   void
-  setExpirationEvent(const EventId eid)
+  setExpirationEvent(const scheduler::EventId eid)
   {
     m_expirationEvent = eid;
   }
 
-  const EventId&
+  const scheduler::EventId&
   getExpirationEvent() const
   {
     return m_expirationEvent;
@@ -69,26 +68,26 @@
   time::steady_clock::TimePoint expires;
 
 private:
-  EventId m_expirationEvent;
+  scheduler::EventId m_expirationEvent;
 };
 
 inline bool
-compareFaceIdAndOrigin(const FaceEntry& entry1, const FaceEntry& entry2)
+compareFaceIdAndOrigin(const Route& lhs, const Route& rhs)
 {
-  return (entry1.faceId == entry2.faceId && entry1.origin == entry2.origin);
+  return (lhs.faceId == rhs.faceId && lhs.origin == rhs.origin);
 }
 
 inline bool
-compareFaceId(const FaceEntry& entry, const uint64_t faceId)
+compareFaceId(const Route& route, const uint64_t faceId)
 {
-  return (entry.faceId == faceId);
+  return (route.faceId == faceId);
 }
 
 // Method definition in rib-entry.cpp
 std::ostream&
-operator<<(std::ostream& os, const FaceEntry& entry);
+operator<<(std::ostream& os, const Route& route);
 
 } // namespace rib
 } // namespace nfd
 
-#endif // NFD_RIB_RIB_ENTRY_HPP
+#endif // NFD_RIB_ROUTE_HPP
diff --git a/tests/core/network-interface.cpp b/tests/core/network-interface.cpp
index 5ac5a31..c26d46a 100644
--- a/tests/core/network-interface.cpp
+++ b/tests/core/network-interface.cpp
@@ -30,7 +30,7 @@
 
 BOOST_FIXTURE_TEST_SUITE(CoreNetworkInterface, BaseFixture)
 
-BOOST_AUTO_TEST_CASE(ListNetworkInterfaces)
+BOOST_AUTO_TEST_CASE(ListRealNetworkInterfaces)
 {
   std::vector<NetworkInterfaceInfo> netifs;
   BOOST_CHECK_NO_THROW(netifs = listNetworkInterfaces());
@@ -48,6 +48,64 @@
   }
 }
 
+class FakeNetworkInterfaceFixture : public BaseFixture
+{
+public:
+  FakeNetworkInterfaceFixture()
+  {
+    using namespace boost::asio::ip;
+
+    auto fakeInterfaces = make_shared<std::vector<NetworkInterfaceInfo>>();
+
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {0, "lo0",
+        ethernet::Address(),
+        {address_v4::from_string("127.0.0.1")},
+        {address_v6::from_string("fe80::1")},
+        address_v4::from_string("127.255.255.255"),
+        IFF_LOOPBACK | IFF_UP});
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {1, "eth0",
+        ethernet::Address::fromString("3e:15:c2:8b:65:00"),
+        {address_v4::from_string("192.168.2.1")},
+        {},
+        address_v4::from_string("192.168.2.255"),
+        0});
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {2, "eth1",
+        ethernet::Address::fromString("3e:15:c2:8b:65:00"),
+        {address_v4::from_string("198.51.100.1")},
+        {address_v6::from_string("2001:db8::1")},
+        address_v4::from_string("198.51.100.255"),
+        IFF_MULTICAST | IFF_BROADCAST | IFF_UP});
+
+    setDebugNetworkInterfaces(fakeInterfaces);
+  }
+
+  ~FakeNetworkInterfaceFixture()
+  {
+    setDebugNetworkInterfaces(nullptr);
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(ListFakeNetworkInterfaces, FakeNetworkInterfaceFixture)
+{
+  std::vector<NetworkInterfaceInfo> netifs;
+  BOOST_CHECK_NO_THROW(netifs = listNetworkInterfaces());
+
+  BOOST_REQUIRE_EQUAL(netifs.size(), 3);
+
+  BOOST_CHECK_EQUAL(netifs[0].index, 0);
+  BOOST_CHECK_EQUAL(netifs[1].index, 1);
+  BOOST_CHECK_EQUAL(netifs[2].index, 2);
+
+  BOOST_CHECK_EQUAL(netifs[0].name, "lo0");
+  BOOST_CHECK_EQUAL(netifs[1].name, "eth0");
+  BOOST_CHECK_EQUAL(netifs[2].name, "eth1");
+
+  // no real value of testing other parameters
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests
diff --git a/tests/core/resolver.cpp b/tests/core/resolver.cpp
deleted file mode 100644
index 18d35b7..0000000
--- a/tests/core/resolver.cpp
+++ /dev/null
@@ -1,246 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon).
- * See AUTHORS.md for complete list of NFD authors and contributors.
- *
- * NFD is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- **/
-
-#include "core/resolver.hpp"
-#include "core/logger.hpp"
-
-#include "tests/test-common.hpp"
-
-namespace nfd {
-namespace tests {
-
-NFD_LOG_INIT("tests.CoreResolver");
-
-using boost::asio::ip::address_v4;
-using boost::asio::ip::address_v6;
-using boost::asio::ip::tcp;
-using boost::asio::ip::udp;
-
-BOOST_FIXTURE_TEST_SUITE(CoreResolver, BaseFixture)
-
-template<class Protocol>
-class ResolverFixture : protected BaseFixture
-{
-public:
-  ResolverFixture()
-    : m_nFailures(0)
-    , m_nSuccesses(0)
-  {
-  }
-
-  void
-  onSuccess(const typename Protocol::endpoint& resolvedEndpoint,
-            const typename Protocol::endpoint& expectedEndpoint,
-            bool isValid, bool wantCheckAddress = false)
-  {
-    NFD_LOG_DEBUG("Resolved to: " << resolvedEndpoint);
-    ++m_nSuccesses;
-
-    if (!isValid)
-      {
-        BOOST_FAIL("Resolved to " + boost::lexical_cast<std::string>(resolvedEndpoint)
-                   + ", but it should have failed");
-      }
-
-    BOOST_CHECK_EQUAL(resolvedEndpoint.port(), expectedEndpoint.port());
-
-    BOOST_CHECK_EQUAL(resolvedEndpoint.address().is_v4(), expectedEndpoint.address().is_v4());
-
-    // checking address is not deterministic and should be enabled only
-    // if only one IP address will be returned by resolution
-    if (wantCheckAddress)
-      {
-        BOOST_CHECK_EQUAL(resolvedEndpoint.address(), expectedEndpoint.address());
-      }
-  }
-
-  void
-  onFailure(bool isValid)
-  {
-    ++m_nFailures;
-
-    if (!isValid)
-      BOOST_FAIL("Resolution should not have failed");
-
-    BOOST_CHECK_MESSAGE(true, "Resolution failed as expected");
-  }
-
-
-  uint32_t m_nFailures;
-  uint32_t m_nSuccesses;
-};
-
-BOOST_FIXTURE_TEST_CASE(Tcp, ResolverFixture<tcp>)
-{
-  TcpResolver::asyncResolve("www.named-data.net", "6363",
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v4(), 6363), true, false),
-                            bind(&ResolverFixture<tcp>::onFailure, this, false));
-
-  TcpResolver::asyncResolve("www.named-data.net", "notport",
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v4(), 0), false, false),
-                            bind(&ResolverFixture<tcp>::onFailure, this, true)); // should fail
-
-
-  TcpResolver::asyncResolve("nothost.nothost.nothost.arpa", "6363",
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v4(), 6363), false, false),
-                            bind(&ResolverFixture<tcp>::onFailure, this, true)); // should fail
-
-  TcpResolver::asyncResolve("www.google.com", "80",
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v4(), 80), true, false),
-                            bind(&ResolverFixture<tcp>::onFailure, this, false),
-                            resolver::Ipv4Address()); // request IPv4 address
-
-  TcpResolver::asyncResolve("www.google.com", "80",
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v6(), 80), true, false),
-                            bind(&ResolverFixture<tcp>::onFailure, this, false),
-                            resolver::Ipv6Address()); // request IPv6 address
-
-  TcpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v6(), 80), true, false),
-                            bind(&ResolverFixture<tcp>::onFailure, this, false));
-
-  TcpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v6(), 80), true, false),
-                            bind(&ResolverFixture<tcp>::onFailure, this, false),
-                            resolver::Ipv6Address());
-
-  TcpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v6(), 80), false, false),
-                            bind(&ResolverFixture<tcp>::onFailure, this, true), // should fail
-                            resolver::Ipv4Address());
-
-  TcpResolver::asyncResolve("192.0.2.1", "80",
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v4::from_string("192.0.2.1"), 80), true, true),
-                            bind(&ResolverFixture<tcp>::onFailure, this, false));
-
-  TcpResolver::asyncResolve("2001:db8:3f9:0:3025:ccc5:eeeb:86d3", "80",
-                            bind(&ResolverFixture<tcp>::onSuccess, this, _1,
-                                 tcp::endpoint(address_v6::
-                                               from_string("2001:db8:3f9:0:3025:ccc5:eeeb:86d3"),
-                                               80), true, true),
-                            bind(&ResolverFixture<tcp>::onFailure, this, false));
-
-  g_io.run();
-
-  BOOST_CHECK_EQUAL(m_nFailures, 3);
-  BOOST_CHECK_EQUAL(m_nSuccesses, 7);
-}
-
-BOOST_AUTO_TEST_CASE(SyncTcp)
-{
-  tcp::endpoint endpoint;
-  BOOST_CHECK_NO_THROW(endpoint = TcpResolver::syncResolve("www.named-data.net", "6363"));
-  NFD_LOG_DEBUG("Resolved to: " << endpoint);
-  BOOST_CHECK_EQUAL(endpoint.address().is_v4(), true);
-  BOOST_CHECK_EQUAL(endpoint.port(), 6363);
-}
-
-BOOST_FIXTURE_TEST_CASE(Udp, ResolverFixture<udp>)
-{
-  UdpResolver::asyncResolve("www.named-data.net", "6363",
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v4(), 6363), true, false),
-                            bind(&ResolverFixture<udp>::onFailure, this, false));
-
-  UdpResolver::asyncResolve("www.named-data.net", "notport",
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v4(), 0), false, false),
-                            bind(&ResolverFixture<udp>::onFailure, this, true)); // should fail
-
-
-  UdpResolver::asyncResolve("nothost.nothost.nothost.arpa", "6363",
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v4(), 6363), false, false),
-                            bind(&ResolverFixture<udp>::onFailure, this, true)); // should fail
-
-  UdpResolver::asyncResolve("www.google.com", "80",
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v4(), 80), true, false),
-                            bind(&ResolverFixture<udp>::onFailure, this, false),
-                            resolver::Ipv4Address()); // request IPv4 address
-
-  UdpResolver::asyncResolve("www.google.com", "80",
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v6(), 80), true, false),
-                            bind(&ResolverFixture<udp>::onFailure, this, false),
-                            resolver::Ipv6Address()); // request IPv6 address
-
-  UdpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v6(), 80), true, false),
-                            bind(&ResolverFixture<udp>::onFailure, this, false));
-
-  UdpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v6(), 80), true, false),
-                            bind(&ResolverFixture<udp>::onFailure, this, false),
-                            resolver::Ipv6Address());
-
-  UdpResolver::asyncResolve("ipv6.google.com", "80", // only IPv6 address should be available
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v6(), 80), false, false),
-                            bind(&ResolverFixture<udp>::onFailure, this, true), // should fail
-                            resolver::Ipv4Address());
-
-  UdpResolver::asyncResolve("192.0.2.1", "80",
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v4::from_string("192.0.2.1"), 80), true, true),
-                            bind(&ResolverFixture<udp>::onFailure, this, false));
-
-  UdpResolver::asyncResolve("2001:db8:3f9:0:3025:ccc5:eeeb:86d3", "80",
-                            bind(&ResolverFixture<udp>::onSuccess, this, _1,
-                                 udp::endpoint(address_v6::
-                                               from_string("2001:db8:3f9:0:3025:ccc5:eeeb:86d3"),
-                                               80), true, true),
-                            bind(&ResolverFixture<udp>::onFailure, this, false));
-
-  g_io.run();
-
-  BOOST_CHECK_EQUAL(m_nFailures, 3);
-  BOOST_CHECK_EQUAL(m_nSuccesses, 7);
-}
-
-BOOST_AUTO_TEST_CASE(SyncUdp)
-{
-  udp::endpoint endpoint;
-  BOOST_CHECK_NO_THROW(endpoint = UdpResolver::syncResolve("www.named-data.net", "6363"));
-  NFD_LOG_DEBUG("Resolved to: " << endpoint);
-  BOOST_CHECK_EQUAL(endpoint.address().is_v4(), true);
-  BOOST_CHECK_EQUAL(endpoint.port(), 6363);
-}
-
-
-BOOST_AUTO_TEST_SUITE_END()
-
-} // namespace tests
-} // namespace nfd
diff --git a/tests/core/scheduler.cpp b/tests/core/scheduler.cpp
index e30a41e..dcafac2 100644
--- a/tests/core/scheduler.cpp
+++ b/tests/core/scheduler.cpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * 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.
@@ -20,7 +21,7 @@
  *
  * 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/scheduler.hpp"
 
@@ -29,6 +30,9 @@
 namespace nfd {
 namespace tests {
 
+using scheduler::EventId;
+using scheduler::ScopedEventId;
+
 BOOST_FIXTURE_TEST_SUITE(CoreScheduler, BaseFixture)
 
 class SchedulerFixture : protected BaseFixture
@@ -111,6 +115,49 @@
   BOOST_REQUIRE_NO_THROW(g_io.run());
 }
 
+BOOST_FIXTURE_TEST_CASE(ScopedEventIdDestruct, UnitTestTimeFixture)
+{
+  int hit = 0;
+  {
+    ScopedEventId se = scheduler::schedule(time::milliseconds(10), [&] { ++hit; });
+  } // se goes out of scope
+  this->advanceClocks(time::milliseconds(1), 15);
+  BOOST_CHECK_EQUAL(hit, 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(ScopedEventIdAssign, UnitTestTimeFixture)
+{
+  int hit1 = 0, hit2 = 0;
+  ScopedEventId se1 = scheduler::schedule(time::milliseconds(10), [&] { ++hit1; });
+  se1 = scheduler::schedule(time::milliseconds(10), [&] { ++hit2; });
+  this->advanceClocks(time::milliseconds(1), 15);
+  BOOST_CHECK_EQUAL(hit1, 0);
+  BOOST_CHECK_EQUAL(hit2, 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(ScopedEventIdRelease, UnitTestTimeFixture)
+{
+  int hit = 0;
+  {
+    ScopedEventId se = scheduler::schedule(time::milliseconds(10), [&] { ++hit; });
+    se.release();
+  } // se goes out of scope
+  this->advanceClocks(time::milliseconds(1), 15);
+  BOOST_CHECK_EQUAL(hit, 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(ScopedEventIdMove, UnitTestTimeFixture)
+{
+  int hit = 0;
+  unique_ptr<scheduler::ScopedEventId> se2;
+  {
+    ScopedEventId se = scheduler::schedule(time::milliseconds(10), [&] { ++hit; });
+    se2.reset(new ScopedEventId(std::move(se)));
+  } // se goes out of scope
+  this->advanceClocks(time::milliseconds(1), 15);
+  BOOST_CHECK_EQUAL(hit, 1);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests
diff --git a/tests/daemon/face/dummy-face.hpp b/tests/daemon/face/dummy-face.hpp
index ec0adbf..0158a3d 100644
--- a/tests/daemon/face/dummy-face.hpp
+++ b/tests/daemon/face/dummy-face.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -52,7 +52,7 @@
   virtual void
   sendInterest(const Interest& interest)
   {
-    this->onSendInterest(interest);
+    this->emitSignal(onSendInterest, interest);
     m_sentInterests.push_back(interest);
     this->afterSend();
   }
@@ -60,7 +60,7 @@
   virtual void
   sendData(const Data& data)
   {
-    this->onSendData(data);
+    this->emitSignal(onSendData, data);
     m_sentDatas.push_back(data);
     this->afterSend();
   }
@@ -68,22 +68,22 @@
   virtual void
   close()
   {
-    this->onFail("close");
+    this->fail("close");
   }
 
   void
   receiveInterest(const Interest& interest)
   {
-    this->onReceiveInterest(interest);
+    this->emitSignal(onReceiveInterest, interest);
   }
 
   void
   receiveData(const Data& data)
   {
-    this->onReceiveData(data);
+    this->emitSignal(onReceiveData, data);
   }
 
-  EventEmitter<> afterSend;
+  signal::Signal<DummyFaceImpl<FaceBase>> afterSend;
 
 public:
   std::vector<Interest> m_sentInterests;
diff --git a/tests/daemon/face/ethernet.cpp b/tests/daemon/face/ethernet.cpp
index ab0181c..6f8218a 100644
--- a/tests/daemon/face/ethernet.cpp
+++ b/tests/daemon/face/ethernet.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -121,7 +121,7 @@
   BOOST_CHECK_EQUAL(face->getCounters().getNInBytes(), 0);
   BOOST_CHECK_EQUAL(face->getCounters().getNOutBytes(), 0);
 
-  face->onFail += [] (const std::string& reason) { BOOST_FAIL(reason); };
+  face->onFail.connect([] (const std::string& reason) { BOOST_FAIL(reason); });
 
   shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
   shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
@@ -171,9 +171,10 @@
   std::vector<Interest> recInterests;
   std::vector<Data>     recDatas;
 
-  face->onFail            += [] (const std::string& reason) { BOOST_FAIL(reason); };
-  face->onReceiveInterest += [&recInterests] (const Interest& i) { recInterests.push_back(i); };
-  face->onReceiveData     += [&recDatas]     (const Data& d)     { recDatas.push_back(d);     };
+  face->onFail.connect([] (const std::string& reason) { BOOST_FAIL(reason); });
+  face->onReceiveInterest.connect(
+      [&recInterests] (const Interest& i) { recInterests.push_back(i); });
+  face->onReceiveData.connect([&recDatas] (const Data& d) { recDatas.push_back(d); });
 
   // check that packet data is not accessed if pcap didn't capture anything (caplen == 0)
   static const pcap_pkthdr header1{};
diff --git a/tests/daemon/face/face-history.hpp b/tests/daemon/face/face-history.hpp
new file mode 100644
index 0000000..263a23a
--- /dev/null
+++ b/tests/daemon/face/face-history.hpp
@@ -0,0 +1,93 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_TESTS_DAEMON_FACE_FACE_HISTORY_HPP
+#define NFD_TESTS_DAEMON_FACE_FACE_HISTORY_HPP
+
+#include "face/face.hpp"
+
+namespace nfd {
+namespace tests {
+
+/** \brief captures signals from Face
+ */
+class FaceHistory : noncopyable
+{
+public:
+  explicit
+  FaceHistory(Face& face)
+    : m_limitedIo(nullptr)
+  {
+    this->construct(face);
+  }
+
+  FaceHistory(Face& face, LimitedIo& limitedIo)
+    : m_limitedIo(&limitedIo)
+  {
+    this->construct(face);
+  }
+
+private:
+  void
+  construct(Face& face)
+  {
+    m_receiveInterestConn = face.onReceiveInterest.connect([this] (const Interest& interest) {
+      this->receivedInterests.push_back(interest);
+      this->afterOp();
+    });
+    m_receiveDataConn = face.onReceiveData.connect([this] (const Data& data) {
+      this->receivedData.push_back(data);
+      this->afterOp();
+    });
+    m_failConn = face.onFail.connect([this] (const std::string& reason) {
+      this->failures.push_back(reason);
+      this->afterOp();
+    });
+  }
+
+  void
+  afterOp()
+  {
+    if (m_limitedIo != nullptr) {
+      m_limitedIo->afterOp();
+    }
+  }
+
+public:
+  std::vector<Interest> receivedInterests;
+  std::vector<Data> receivedData;
+  std::vector<std::string> failures;
+
+private:
+  LimitedIo* m_limitedIo;
+  signal::ScopedConnection m_receiveInterestConn;
+  signal::ScopedConnection m_receiveDataConn;
+  signal::ScopedConnection m_failConn;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_DAEMON_FACE_FACE_HISTORY_HPP
diff --git a/tests/daemon/face/face.cpp b/tests/daemon/face/face.cpp
index 143dfe6..6ed17bf 100644
--- a/tests/daemon/face/face.cpp
+++ b/tests/daemon/face/face.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -21,7 +21,7 @@
  *
  * You should have received a copy of the GNU General Public License along with
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- **/
+ */
 
 #include "face/face.hpp"
 #include "face/local-face.hpp"
@@ -65,7 +65,7 @@
   FaceFailTestFace()
     : failCount(0)
   {
-    this->onFail += bind(&FaceFailTestFace::failHandler, this, _1);
+    this->onFail.connect(bind(&FaceFailTestFace::failHandler, this, _1));
   }
 
   void
diff --git a/tests/daemon/face/ndnlp.cpp b/tests/daemon/face/ndnlp.cpp
index f5ba8e1..c80e4f7 100644
--- a/tests/daemon/face/ndnlp.cpp
+++ b/tests/daemon/face/ndnlp.cpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * 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.
@@ -20,7 +21,7 @@
  *
  * You should have received a copy of the GNU General Public License along with
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- **/
+ */
 
 #include "face/ndnlp-sequence-generator.hpp"
 #include "face/ndnlp-slicer.hpp"
@@ -156,10 +157,9 @@
   ReassembleFixture()
     : m_slicer(1500)
   {
-    m_partialMessageStore.onReceive +=
-      // push_back in C++11 has 2 overloads, and specific version needs to be selected
-      bind(static_cast<void (std::vector<Block>::*)(const Block&)>(&std::vector<Block>::push_back),
-           &m_received, _1);
+    m_partialMessageStore.onReceive.connect([this] (const Block& block) {
+      m_received.push_back(block);
+    });
   }
 
   Block
diff --git a/tests/daemon/face/tcp.cpp b/tests/daemon/face/tcp.cpp
index 99c2d5b..23899e6 100644
--- a/tests/daemon/face/tcp.cpp
+++ b/tests/daemon/face/tcp.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -24,7 +24,7 @@
  */
 
 #include "face/tcp-factory.hpp"
-#include "core/resolver.hpp"
+#include <ndn-cxx/util/dns.hpp>
 #include "core/network-interface.hpp"
 #include <ndn-cxx/security/key-chain.hpp>
 
@@ -98,17 +98,16 @@
 {
   TcpFactory factory = TcpFactory();
 
-  factory.createFace(FaceUri("tcp4://127.0.0.1"),
+  factory.createFace(FaceUri("tcp4://127.0.0.1:6363"),
+                     bind(&FaceCreateFixture::ignore, this),
+                     bind(&FaceCreateFixture::checkError, this, _1,
+                          "No channels available to connect to 127.0.0.1:6363"));
+
+  factory.createChannel("127.0.0.1", "20071");
+
+  factory.createFace(FaceUri("tcp4://127.0.0.1:20070"),
                      bind(&FaceCreateFixture::ignore, this),
                      bind(&FaceCreateFixture::failIfError, this, _1));
-
-  factory.createFace(FaceUri("tcp4://127.0.0.1/"),
-                     bind(&FaceCreateFixture::ignore, this),
-                     bind(&FaceCreateFixture::failIfError, this, _1));
-
-  factory.createFace(FaceUri("tcp4://127.0.0.1/path"),
-                     bind(&FaceCreateFixture::ignore, this),
-                     bind(&FaceCreateFixture::checkError, this, _1, "Invalid URI"));
 }
 
 class EndToEndFixture : protected BaseFixture
@@ -119,12 +118,9 @@
   {
     BOOST_CHECK(!static_cast<bool>(face1));
     face1 = newFace;
-    face1->onReceiveInterest +=
-      bind(&EndToEndFixture::face1_onReceiveInterest, this, _1);
-    face1->onReceiveData +=
-      bind(&EndToEndFixture::face1_onReceiveData, this, _1);
-    face1->onFail +=
-      bind(&EndToEndFixture::face1_onFail, this);
+    face1->onReceiveInterest.connect(bind(&EndToEndFixture::face1_onReceiveInterest, this, _1));
+    face1->onReceiveData.connect(bind(&EndToEndFixture::face1_onReceiveData, this, _1));
+    face1->onFail.connect(bind(&EndToEndFixture::face1_onFail, this));
 
     limitedIo.afterOp();
   }
@@ -165,12 +161,9 @@
   {
     BOOST_CHECK(!static_cast<bool>(face2));
     face2 = newFace;
-    face2->onReceiveInterest +=
-      bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
-    face2->onReceiveData +=
-      bind(&EndToEndFixture::face2_onReceiveData, this, _1);
-    face2->onFail +=
-      bind(&EndToEndFixture::face2_onFail, this);
+    face2->onReceiveInterest.connect(bind(&EndToEndFixture::face2_onReceiveInterest, this, _1));
+    face2->onReceiveData.connect(bind(&EndToEndFixture::face2_onReceiveData, this, _1));
+    face2->onFail.connect(bind(&EndToEndFixture::face2_onFail, this));
 
     limitedIo.afterOp();
   }
@@ -269,7 +262,7 @@
   shared_ptr<TcpChannel> channel2 = factory2.createChannel("127.0.0.2", "20070");
   factory2.createChannel("127.0.0.2", "20071");
 
-  factory2.createFace(FaceUri("tcp://127.0.0.1:20070"),
+  factory2.createFace(FaceUri("tcp4://127.0.0.1:20070"),
                       bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
                       bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
 
@@ -354,7 +347,7 @@
 
   factory2.createChannel("::2", "20070");
 
-  factory2.createFace(FaceUri("tcp://[::1]:20070"),
+  factory2.createFace(FaceUri("tcp6://[::1]:20070"),
                       bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
                       bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
 
@@ -492,12 +485,9 @@
   void
   onFaceCreated(const shared_ptr<Face>& face)
   {
-    face->onReceiveInterest +=
-      bind(&SimpleEndToEndFixture::onReceiveInterest, this, _1);
-    face->onReceiveData +=
-      bind(&SimpleEndToEndFixture::onReceiveData, this, _1);
-    face->onFail +=
-      bind(&SimpleEndToEndFixture::onFail, this, face);
+    face->onReceiveInterest.connect(bind(&SimpleEndToEndFixture::onReceiveInterest, this, _1));
+    face->onReceiveData.connect(bind(&SimpleEndToEndFixture::onReceiveData, this, _1));
+    face->onFail.connect(bind(&SimpleEndToEndFixture::onFail, this, face));
 
     if (static_cast<bool>(dynamic_pointer_cast<LocalFace>(face))) {
       static_pointer_cast<LocalFace>(face)->setLocalControlHeaderFeature(
@@ -559,7 +549,8 @@
   BOOST_REQUIRE_EQUAL(channel->isListening(), true);
 
   DummyStreamSender<boost::asio::ip::tcp, Dataset> sender;
-  sender.start(Resolver<boost::asio::ip::tcp>::syncResolve("127.0.0.1", "20070"));
+  tcp::Endpoint endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 20070);
+  sender.start(endpoint);
 
   BOOST_CHECK_MESSAGE(limitedIo.run(LimitedIo::UNLIMITED_OPS,
                                     time::seconds(1)) == LimitedIo::EXCEED_TIME,
@@ -591,7 +582,8 @@
   BOOST_REQUIRE_EQUAL(channel->isListening(), true);
 
   DummyStreamSender<boost::asio::ip::tcp, Dataset> sender;
-  sender.start(Resolver<boost::asio::ip::tcp>::syncResolve(someIpv4Address, "20070"));
+  tcp::Endpoint endpoint(ndn::dns::syncResolve(someIpv4Address, getGlobalIoService()), 20070);
+  sender.start(endpoint);
 
   BOOST_CHECK_MESSAGE(limitedIo.run(LimitedIo::UNLIMITED_OPS,
                                     time::seconds(1)) == LimitedIo::EXCEED_TIME,
@@ -630,7 +622,7 @@
   TcpFactory factory;
   shared_ptr<TcpChannel> channel = factory.createChannel("0.0.0.0", "20070");
 
-  factory.createFace(FaceUri("tcp://192.0.2.1:20070"),
+  factory.createFace(FaceUri("tcp4://192.0.2.1:20070"),
                      bind(&FaceCreateTimeoutFixture::onFaceCreated, this, _1),
                      bind(&FaceCreateTimeoutFixture::onConnectFailed, this, _1));
 
@@ -659,7 +651,7 @@
   shared_ptr<TcpChannel> channel2 = factory2.createChannel("127.0.0.2", "20070");
   factory2.createChannel("127.0.0.2", "20071");
 
-  factory2.createFace(FaceUri("tcp://127.0.0.1:20070"),
+  factory2.createFace(FaceUri("tcp4://127.0.0.1:20070"),
                       bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
                       bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
 
@@ -685,6 +677,88 @@
   BOOST_TEST_MESSAGE("Unexpected assertion test passed");
 }
 
+class FakeNetworkInterfaceFixture : public BaseFixture
+{
+public:
+  FakeNetworkInterfaceFixture()
+  {
+    using namespace boost::asio::ip;
+
+    auto fakeInterfaces = make_shared<std::vector<NetworkInterfaceInfo>>();
+
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {0, "eth0",
+        ethernet::Address::fromString("3e:15:c2:8b:65:00"),
+        {address_v4::from_string("0.0.0.0")},
+        {address_v6::from_string("::")},
+        address_v4(),
+        IFF_UP});
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {1, "eth0",
+        ethernet::Address::fromString("3e:15:c2:8b:65:00"),
+        {address_v4::from_string("192.168.2.1"), address_v4::from_string("192.168.2.2")},
+        {},
+        address_v4::from_string("192.168.2.255"),
+        0});
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {2, "eth1",
+        ethernet::Address::fromString("3e:15:c2:8b:65:00"),
+        {address_v4::from_string("198.51.100.1")},
+        {address_v6::from_string("2001:db8::2"), address_v6::from_string("2001:db8::3")},
+        address_v4::from_string("198.51.100.255"),
+        IFF_MULTICAST | IFF_BROADCAST | IFF_UP});
+
+    setDebugNetworkInterfaces(fakeInterfaces);
+  }
+
+  ~FakeNetworkInterfaceFixture()
+  {
+    setDebugNetworkInterfaces(nullptr);
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(Bug2292, FakeNetworkInterfaceFixture)
+{
+  using namespace boost::asio::ip;
+
+  TcpFactory factory;
+  factory.prohibitEndpoint(tcp::Endpoint(address_v4::from_string("192.168.2.1"), 1024));
+  BOOST_REQUIRE_EQUAL(factory.m_prohibitedEndpoints.size(), 1);
+  BOOST_CHECK((factory.m_prohibitedEndpoints ==
+               std::set<tcp::Endpoint> {
+                 tcp::Endpoint(address_v4::from_string("192.168.2.1"), 1024),
+               }));
+
+  factory.m_prohibitedEndpoints.clear();
+  factory.prohibitEndpoint(tcp::Endpoint(address_v6::from_string("2001:db8::1"), 2048));
+  BOOST_REQUIRE_EQUAL(factory.m_prohibitedEndpoints.size(), 1);
+  BOOST_CHECK((factory.m_prohibitedEndpoints ==
+               std::set<tcp::Endpoint> {
+                 tcp::Endpoint(address_v6::from_string("2001:db8::1"), 2048)
+               }));
+
+  factory.m_prohibitedEndpoints.clear();
+  factory.prohibitEndpoint(tcp::Endpoint(address_v4(), 1024));
+  BOOST_REQUIRE_EQUAL(factory.m_prohibitedEndpoints.size(), 4);
+  BOOST_CHECK((factory.m_prohibitedEndpoints ==
+               std::set<tcp::Endpoint> {
+                 tcp::Endpoint(address_v4::from_string("192.168.2.1"), 1024),
+                 tcp::Endpoint(address_v4::from_string("192.168.2.2"), 1024),
+                 tcp::Endpoint(address_v4::from_string("198.51.100.1"), 1024),
+                 tcp::Endpoint(address_v4::from_string("0.0.0.0"), 1024)
+               }));
+
+  factory.m_prohibitedEndpoints.clear();
+  factory.prohibitEndpoint(tcp::Endpoint(address_v6(), 2048));
+  BOOST_REQUIRE_EQUAL(factory.m_prohibitedEndpoints.size(), 3);
+  BOOST_CHECK((factory.m_prohibitedEndpoints ==
+               std::set<tcp::Endpoint> {
+                 tcp::Endpoint(address_v6::from_string("2001:db8::2"), 2048),
+                 tcp::Endpoint(address_v6::from_string("2001:db8::3"), 2048),
+                 tcp::Endpoint(address_v6::from_string("::"), 2048)
+               }));
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests
diff --git a/tests/daemon/face/udp.cpp b/tests/daemon/face/udp.cpp
index 87ee8f0..4d29e7d 100644
--- a/tests/daemon/face/udp.cpp
+++ b/tests/daemon/face/udp.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -24,9 +24,11 @@
  */
 
 #include "face/udp-factory.hpp"
+#include "core/network-interface.hpp"
 
 #include "tests/test-common.hpp"
 #include "tests/limited-io.hpp"
+#include "face-history.hpp"
 
 namespace nfd {
 namespace tests {
@@ -230,762 +232,457 @@
 {
   UdpFactory factory = UdpFactory();
 
-  factory.createFace(FaceUri("udp4://127.0.0.1"),
+  factory.createFace(FaceUri("udp4://127.0.0.1:6363"),
                      bind(&FaceCreateFixture::ignore, this),
-                     bind(&FaceCreateFixture::failIfError, this, _1));
-
-  factory.createFace(FaceUri("udp4://127.0.0.1/"),
-                     bind(&FaceCreateFixture::ignore, this),
-                     bind(&FaceCreateFixture::failIfError, this, _1));
-
-  factory.createFace(FaceUri("udp4://127.0.0.1/path"),
-                     bind(&FaceCreateFixture::ignore, this),
-                     bind(&FaceCreateFixture::checkError, this, _1, "Invalid URI"));
-
-}
-
-class EndToEndFixture : protected BaseFixture
-{
-public:
-  void
-  channel1_onFaceCreated(const shared_ptr<Face>& newFace)
-  {
-    BOOST_CHECK(!static_cast<bool>(face1));
-    channel1_onFaceCreatedNoCheck(newFace);
-  }
-
-  void
-  channel1_onFaceCreatedNoCheck(const shared_ptr<Face>& newFace)
-  {
-    face1 = newFace;
-    face1->onReceiveInterest +=
-      bind(&EndToEndFixture::face1_onReceiveInterest, this, _1);
-    face1->onReceiveData +=
-      bind(&EndToEndFixture::face1_onReceiveData, this, _1);
-    face1->onFail +=
-      bind(&EndToEndFixture::face1_onFail, this);
-    BOOST_CHECK_MESSAGE(true, "channel 1 face created");
-
-    faces.push_back(face1);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  channel1_onConnectFailed(const std::string& reason)
-  {
-    BOOST_CHECK_MESSAGE(false, reason);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  face1_onReceiveInterest(const Interest& interest)
-  {
-    face1_receivedInterests.push_back(interest);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  face1_onReceiveData(const Data& data)
-  {
-    face1_receivedDatas.push_back(data);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  face1_onFail()
-  {
-    face1.reset();
-    limitedIo.afterOp();
-  }
-
-  void
-  channel2_onFaceCreated(const shared_ptr<Face>& newFace)
-  {
-    BOOST_CHECK(!static_cast<bool>(face2));
-    face2 = newFace;
-    face2->onReceiveInterest +=
-      bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
-    face2->onReceiveData +=
-      bind(&EndToEndFixture::face2_onReceiveData, this, _1);
-    face2->onFail +=
-      bind(&EndToEndFixture::face2_onFail, this);
-
-    faces.push_back(face2);
-
-    BOOST_CHECK_MESSAGE(true, "channel 2 face created");
-    limitedIo.afterOp();
-  }
-
-  void
-  channel2_onConnectFailed(const std::string& reason)
-  {
-    BOOST_CHECK_MESSAGE(false, reason);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  face2_onReceiveInterest(const Interest& interest)
-  {
-    face2_receivedInterests.push_back(interest);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  face2_onReceiveData(const Data& data)
-  {
-    face2_receivedDatas.push_back(data);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  face2_onFail()
-  {
-    face2.reset();
-    limitedIo.afterOp();
-  }
-
-  void
-  channel3_onFaceCreated(const shared_ptr<Face>& newFace)
-  {
-    BOOST_CHECK(!static_cast<bool>(face1));
-    face3 = newFace;
-    faces.push_back(newFace);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  channel_onFaceCreated(const shared_ptr<Face>& newFace)
-  {
-    faces.push_back(newFace);
-    limitedIo.afterOp();
-  }
-
-  void
-  channel_onConnectFailed(const std::string& reason)
-  {
-    BOOST_CHECK_MESSAGE(false, reason);
-
-    limitedIo.afterOp();
-  }
-
-  void
-  channel_onConnectFailedOk(const std::string& reason)
-  {
-    // it's ok, it was supposed to fail
-    limitedIo.afterOp();
-  }
-
-  void
-  checkFaceList(size_t shouldBe)
-  {
-    BOOST_CHECK_EQUAL(faces.size(), shouldBe);
-  }
-
-  void
-  connect(const shared_ptr<UdpChannel>& channel,
-          const std::string& remoteHost,
-          const std::string& remotePort)
-  {
-    channel->connect(remoteHost, remotePort,
-                     bind(&EndToEndFixture::channel_onFaceCreated, this, _1),
-                     bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
-  }
-
-public:
-  LimitedIo limitedIo;
-
-  shared_ptr<Face> face1;
-  std::vector<Interest> face1_receivedInterests;
-  std::vector<Data> face1_receivedDatas;
-  shared_ptr<Face> face2;
-  std::vector<Interest> face2_receivedInterests;
-  std::vector<Data> face2_receivedDatas;
-  shared_ptr<Face> face3;
-
-  std::list< shared_ptr<Face> > faces;
-};
-
-
-BOOST_FIXTURE_TEST_CASE(EndToEnd4, EndToEndFixture)
-{
-  UdpFactory factory;
+                     bind(&FaceCreateFixture::checkError, this, _1,
+                          "No channels available to connect to 127.0.0.1:6363"));
 
   factory.createChannel("127.0.0.1", "20071");
 
   factory.createFace(FaceUri("udp4://127.0.0.1:20070"),
-                     bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
-                     bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+                     bind(&FaceCreateFixture::ignore, this),
+                     bind(&FaceCreateFixture::failIfError, this, _1));
+}
 
+class EndToEndIpv4
+{
+public:
+  static const std::string
+  getScheme()
+  {
+    return "udp4";
+  }
 
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot connect or cannot accept connection");
+  static const std::string
+  getLocalIp()
+  {
+    return "127.0.0.1";
+  }
 
-  BOOST_REQUIRE(static_cast<bool>(face2));
-  BOOST_CHECK_EQUAL(face2->getRemoteUri().toString(), "udp4://127.0.0.1:20070");
-  BOOST_CHECK_EQUAL(face2->getLocalUri().toString(), "udp4://127.0.0.1:20071");
-  BOOST_CHECK_EQUAL(face2->isLocal(), false);
+  static const std::string
+  getPort1()
+  {
+    return "20071";
+  }
+
+  static const std::string
+  getPort2()
+  {
+    return "20072";
+  }
+
+  static const std::string
+  getPort3()
+  {
+    return "20073";
+  }
+
+  static const FaceUri
+  getFaceUri1()
+  {
+    return FaceUri("udp4://127.0.0.1:20071");
+  }
+
+  static const FaceUri
+  getFaceUri2()
+  {
+    return FaceUri("udp4://127.0.0.1:20072");
+  }
+
+  static const FaceUri
+  getFaceUri3()
+  {
+    return FaceUri("udp4://127.0.0.1:20073");
+  }
+};
+
+class EndToEndIpv6
+{
+public:
+  static const std::string
+  getScheme()
+  {
+    return "udp6";
+  }
+
+  static const std::string
+  getLocalIp()
+  {
+    return "::1";
+  }
+
+  static const std::string
+  getPort1()
+  {
+    return "20071";
+  }
+
+  static const std::string
+  getPort2()
+  {
+    return "20072";
+  }
+
+  static const std::string
+  getPort3()
+  {
+    return "20073";
+  }
+
+  static const FaceUri
+  getFaceUri1()
+  {
+    return FaceUri("udp6://[::1]:20071");
+  }
+
+  static const FaceUri
+  getFaceUri2()
+  {
+    return FaceUri("udp6://[::1]:20072");
+  }
+
+  static const FaceUri
+  getFaceUri3()
+  {
+    return FaceUri("udp6://[::1]:20073");
+  }
+};
+
+typedef boost::mpl::list<EndToEndIpv4, EndToEndIpv6> EndToEndAddresses;
+
+// end to end communication
+BOOST_AUTO_TEST_CASE_TEMPLATE(EndToEnd, A, EndToEndAddresses)
+{
+  LimitedIo limitedIo;
+  UdpFactory factory;
+
+  shared_ptr<UdpChannel> channel1 = factory.createChannel(A::getLocalIp(), A::getPort1());
+
+  // face1 (on channel1) connects to face2 (on channel2, to be created)
+  shared_ptr<Face> face1;
+  unique_ptr<FaceHistory> history1;
+  factory.createFace(A::getFaceUri2(),
+                     [&] (shared_ptr<Face> newFace) {
+                       face1 = newFace;
+                       history1.reset(new FaceHistory(*face1, limitedIo));
+                       limitedIo.afterOp();
+                     },
+                     [] (const std::string& reason) { BOOST_ERROR(reason); });
+
+  limitedIo.run(1, time::seconds(1));
+  BOOST_REQUIRE(face1 != nullptr);
+  BOOST_CHECK_EQUAL(face1->getRemoteUri(), A::getFaceUri2());
+  BOOST_CHECK_EQUAL(face1->getLocalUri(), A::getFaceUri1());
+  BOOST_CHECK_EQUAL(face1->isLocal(), false); // UdpFace is never local
+  BOOST_CHECK_EQUAL(face1->getCounters().getNInBytes(), 0);
+  BOOST_CHECK_EQUAL(face1->getCounters().getNOutBytes(), 0);
+
+  // channel2 creation must be after face1 creation,
+  // otherwise channel2's endpoint would be prohibited
+  shared_ptr<UdpChannel> channel2 = factory.createChannel(A::getLocalIp(), A::getPort2());
+  shared_ptr<Face> face2;
+  unique_ptr<FaceHistory> history2;
+  channel2->listen([&] (shared_ptr<Face> newFace) {
+                     BOOST_CHECK(face2 == nullptr);
+                     face2 = newFace;
+                     history2.reset(new FaceHistory(*face2, limitedIo));
+                     limitedIo.afterOp();
+                   },
+                   [] (const std::string& reason) { BOOST_ERROR(reason); });
+
+  limitedIo.run(1, time::seconds(1));
+  BOOST_CHECK(face2 == nullptr); // face2 shouldn't be created until face1 sends something
+
+  shared_ptr<Interest> interest1 = makeInterest("/I1");
+  shared_ptr<Interest> interest2 = makeInterest("/I2");
+  shared_ptr<Data> data1 = makeData("/D1");
+  shared_ptr<Data> data2 = makeData("/D2");
+
+  // face1 sends to channel2, creates face2
+  face1->sendInterest(*interest1);
+  face1->sendData(*data1);
+  face1->sendData(*data1);
+  face1->sendData(*data1);
+  size_t nBytesSent1 = interest1->wireEncode().size() + 3 * data1->wireEncode().size();
+
+  limitedIo.run(5, time::seconds(1)); // 1 accept, 4 receives
+
+  BOOST_REQUIRE(face2 != nullptr);
+  BOOST_CHECK_EQUAL(face2->getRemoteUri(), A::getFaceUri1());
+  BOOST_CHECK_EQUAL(face2->getLocalUri(), A::getFaceUri2());
+  BOOST_CHECK_EQUAL(face2->isLocal(), false); // UdpFace is never local
+  BOOST_CHECK_EQUAL(face2->getCounters().getNInBytes(), nBytesSent1);
   BOOST_CHECK_EQUAL(face2->getCounters().getNOutBytes(), 0);
-  BOOST_CHECK_EQUAL(face2->getCounters().getNInBytes(), 0);
-  // face1 is not created yet
 
-  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
-  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
-                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+  BOOST_REQUIRE_EQUAL(history2->receivedInterests.size(), 1);
+  BOOST_CHECK_EQUAL(history2->receivedInterests.front().getName(), interest1->getName());
+  BOOST_REQUIRE_EQUAL(history2->receivedData.size(), 3);
+  BOOST_CHECK_EQUAL(history2->receivedData.front().getName(), data1->getName());
 
-  Interest interest1("ndn:/TpnzGvW9R");
-  Data     data1    ("ndn:/KfczhUqVix");
-  data1.setContent(0, 0);
-  Interest interest2("ndn:/QWiIMfj5sL");
-  Data     data2    ("ndn:/XNBV796f");
-  data2.setContent(0, 0);
-  Interest interest3("ndn:/QWiIhjgkj5sL");
-  Data     data3    ("ndn:/XNBV794f");
-  data3.setContent(0, 0);
+  // face2 sends to face1
+  face2->sendInterest(*interest2);
+  face2->sendInterest(*interest2);
+  face2->sendInterest(*interest2);
+  face2->sendData(*data2);
+  size_t nBytesSent2 = 3 * interest2->wireEncode().size() + data2->wireEncode().size();
 
+  limitedIo.run(4, time::seconds(1)); // 4 receives
 
-  ndn::SignatureSha256WithRsa fakeSignature;
-  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue,
-                                        reinterpret_cast<const uint8_t*>(0),
-                                        0));
+  BOOST_REQUIRE_EQUAL(history1->receivedInterests.size(), 3);
+  BOOST_CHECK_EQUAL(history1->receivedInterests.front().getName(), interest2->getName());
+  BOOST_REQUIRE_EQUAL(history1->receivedData.size(), 1);
+  BOOST_CHECK_EQUAL(history1->receivedData.front().getName(), data2->getName());
 
-  // set fake signature on data1 and data2
-  data1.setSignature(fakeSignature);
-  data2.setSignature(fakeSignature);
-  data3.setSignature(fakeSignature);
-
-  face2->sendInterest(interest2);
-  face2->sendData    (data2    );
-  face2->sendData    (data2    );
-  face2->sendData    (data2    );
-  size_t nBytesSent2 = interest2.wireEncode().size() + data2.wireEncode().size() * 3;
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(5,//4 send + 1 listen return
-                      time::seconds(4)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-  BOOST_REQUIRE(static_cast<bool>(face1));
-  BOOST_CHECK_EQUAL(face1->getRemoteUri().toString(), "udp4://127.0.0.1:20071");
-  BOOST_CHECK_EQUAL(face1->getLocalUri().toString(), "udp4://127.0.0.1:20070");
-  BOOST_CHECK_EQUAL(face1->isLocal(), false);
-
-  face1->sendInterest(interest1);
-  face1->sendInterest(interest1);
-  face1->sendInterest(interest1);
-  face1->sendData    (data1    );
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(4, time::seconds(4)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
-  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 3);
-  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 3);
-  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
-
-  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
-  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
-  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
-  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
-
-
-
-  //checking if the connection accepting mechanism works properly.
-
-  face2->sendData    (data3    );
-  face2->sendInterest(interest3);
-  nBytesSent2 += data3.wireEncode().size() + interest3.wireEncode().size();
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 2);
-  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 4);
-
-  BOOST_CHECK_EQUAL(face1_receivedInterests[1].getName(), interest3.getName());
-  BOOST_CHECK_EQUAL(face1_receivedDatas    [3].getName(), data3.getName());
-
+  // counters
   const FaceCounters& counters1 = face1->getCounters();
-  BOOST_CHECK_EQUAL(counters1.getNInInterests() , 2);
-  BOOST_CHECK_EQUAL(counters1.getNInDatas()     , 4);
-  BOOST_CHECK_EQUAL(counters1.getNOutInterests(), 3);
-  BOOST_CHECK_EQUAL(counters1.getNOutDatas()    , 1);
+  BOOST_CHECK_EQUAL(counters1.getNInInterests(), 3);
+  BOOST_CHECK_EQUAL(counters1.getNInDatas(), 1);
+  BOOST_CHECK_EQUAL(counters1.getNOutInterests(), 1);
+  BOOST_CHECK_EQUAL(counters1.getNOutDatas(), 3);
   BOOST_CHECK_EQUAL(counters1.getNInBytes(), nBytesSent2);
+  BOOST_CHECK_EQUAL(counters1.getNOutBytes(), nBytesSent1);
 
   const FaceCounters& counters2 = face2->getCounters();
-  BOOST_CHECK_EQUAL(counters2.getNInInterests() , 3);
-  BOOST_CHECK_EQUAL(counters2.getNInDatas()     , 1);
-  BOOST_CHECK_EQUAL(counters2.getNOutInterests(), 2);
-  BOOST_CHECK_EQUAL(counters2.getNOutDatas()    , 4);
+  BOOST_CHECK_EQUAL(counters2.getNInInterests(), 1);
+  BOOST_CHECK_EQUAL(counters2.getNInDatas(), 3);
+  BOOST_CHECK_EQUAL(counters2.getNOutInterests(), 3);
+  BOOST_CHECK_EQUAL(counters2.getNOutDatas(), 1);
+  BOOST_CHECK_EQUAL(counters2.getNInBytes(), nBytesSent1);
   BOOST_CHECK_EQUAL(counters2.getNOutBytes(), nBytesSent2);
 }
 
-BOOST_FIXTURE_TEST_CASE(EndToEnd6, EndToEndFixture)
+// channel accepting multiple incoming connections
+BOOST_AUTO_TEST_CASE_TEMPLATE(MultipleAccepts, A, EndToEndAddresses)
 {
+  LimitedIo limitedIo;
   UdpFactory factory;
 
-  factory.createChannel("::1", "20071");
+  // channel1 is listening
+  shared_ptr<UdpChannel> channel1 = factory.createChannel(A::getLocalIp(), A::getPort1());
+  std::vector<shared_ptr<Face>> faces1;
+  channel1->listen([&] (shared_ptr<Face> newFace) {
+                     faces1.push_back(newFace);
+                     limitedIo.afterOp();
+                   },
+                   [] (const std::string& reason) { BOOST_ERROR(reason); });
 
-  factory.createFace(FaceUri("udp://[::1]:20070"),
-                     bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
-                     bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+  // face2 (on channel2) connects to channel1
+  shared_ptr<UdpChannel> channel2 = factory.createChannel(A::getLocalIp(), A::getPort2());
+  BOOST_CHECK_NE(channel1, channel2);
+  shared_ptr<Face> face2;
+  boost::asio::ip::address ipAddress2 = boost::asio::ip::address::from_string(A::getLocalIp());
+  udp::Endpoint endpoint2(ipAddress2, boost::lexical_cast<uint16_t>(A::getPort1()));
+  channel2->connect(endpoint2,
+                    [&] (shared_ptr<Face> newFace) {
+                      face2 = newFace;
+                      limitedIo.afterOp();
+                    },
+                    [] (const std::string& reason) { BOOST_ERROR(reason); });
 
+  limitedIo.run(1, time::seconds(1)); // 1 create (on channel2)
+  BOOST_REQUIRE(face2 != nullptr);
+  BOOST_CHECK_EQUAL(faces1.size(), 0); // channel1 won't create face until face2 sends something
 
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot connect or cannot accept connection");
+  // face3 (on channel3) connects to channel1
+  shared_ptr<UdpChannel> channel3 = factory.createChannel(A::getLocalIp(), A::getPort3());
+  BOOST_CHECK_NE(channel1, channel3);
+  shared_ptr<Face> face3;
+  boost::asio::ip::address ipAddress3 = boost::asio::ip::address::from_string(A::getLocalIp());
+  udp::Endpoint endpoint3(ipAddress3, boost::lexical_cast<uint16_t>(A::getPort1()));
+  channel3->connect(endpoint3,
+                    [&] (shared_ptr<Face> newFace) {
+                      face3 = newFace;
+                      limitedIo.afterOp();
+                    },
+                    [] (const std::string& reason) { BOOST_ERROR(reason); });
 
-  BOOST_REQUIRE(static_cast<bool>(face2));
-  BOOST_CHECK_EQUAL(face2->getRemoteUri().toString(), "udp6://[::1]:20070");
-  BOOST_CHECK_EQUAL(face2->getLocalUri().toString(), "udp6://[::1]:20071");
-  BOOST_CHECK_EQUAL(face2->isLocal(), false);
-  // face1 is not created yet
+  limitedIo.run(1, time::seconds(1)); // 1 create (on channel3)
+  BOOST_REQUIRE(face3 != nullptr);
+  BOOST_CHECK_EQUAL(faces1.size(), 0); // channel1 won't create face until face3 sends something
 
-  shared_ptr<UdpChannel> channel1 = factory.createChannel("::1", "20070");
-  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
-                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+  // face2 sends to channel1
+  shared_ptr<Interest> interest2 = makeInterest("/I2");
+  face2->sendInterest(*interest2);
+  limitedIo.run(1, time::milliseconds(100)); // 1 accept (on channel1)
+  BOOST_REQUIRE_EQUAL(faces1.size(), 1);
+  BOOST_CHECK_EQUAL(channel1->size(), 1);
+  BOOST_CHECK_EQUAL(faces1.at(0)->getRemoteUri(), A::getFaceUri2());
 
-  Interest interest1("ndn:/TpnzGvW9R");
-  Data     data1    ("ndn:/KfczhUqVix");
-  data1.setContent(0, 0);
-  Interest interest2("ndn:/QWiIMfj5sL");
-  Data     data2    ("ndn:/XNBV796f");
-  data2.setContent(0, 0);
-  Interest interest3("ndn:/QWiIhjgkj5sL");
-  Data     data3    ("ndn:/XNBV794f");
-  data3.setContent(0, 0);
-
-
-  ndn::SignatureSha256WithRsa fakeSignature;
-  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue,
-                                        reinterpret_cast<const uint8_t*>(0),
-                                        0));
-
-  // set fake signature on data1 and data2
-  data1.setSignature(fakeSignature);
-  data2.setSignature(fakeSignature);
-  data3.setSignature(fakeSignature);
-
-  face2->sendInterest(interest2);
-  face2->sendData    (data2    );
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(3,//2 send + 1 listen return
-                      time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-  BOOST_REQUIRE(static_cast<bool>(face1));
-  BOOST_CHECK_EQUAL(face1->getRemoteUri().toString(), "udp6://[::1]:20071");
-  BOOST_CHECK_EQUAL(face1->getLocalUri().toString(), "udp6://[::1]:20070");
-  BOOST_CHECK_EQUAL(face1->isLocal(), false);
-
-  face1->sendInterest(interest1);
-  face1->sendData    (data1    );
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-
-  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
-  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 1);
-  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
-  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
-
-  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
-  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
-  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
-  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
-
-
-
-  //checking if the connection accepting mechanism works properly.
-
-  face2->sendData    (data3    );
-  face2->sendInterest(interest3);
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 2);
-  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 2);
-
-  BOOST_CHECK_EQUAL(face1_receivedInterests[1].getName(), interest3.getName());
-  BOOST_CHECK_EQUAL(face1_receivedDatas    [1].getName(), data3.getName());
+  // face3 sends to channel1
+  shared_ptr<Data> data3 = makeData("/D3");
+  face3->sendData(*data3);
+  limitedIo.run(1, time::milliseconds(100)); // 1 accept (on channel1)
+  BOOST_REQUIRE_EQUAL(faces1.size(), 2);
+  BOOST_CHECK_EQUAL(channel1->size(), 2);
+  BOOST_CHECK_EQUAL(faces1.at(1)->getRemoteUri(), A::getFaceUri3());
 }
 
-BOOST_FIXTURE_TEST_CASE(MultipleAccepts, EndToEndFixture)
+// manually close a face
+BOOST_AUTO_TEST_CASE_TEMPLATE(ManualClose, A, EndToEndAddresses)
 {
-  Interest interest1("ndn:/TpnzGvW9R");
-  Interest interest2("ndn:/QWiIMfj5sL");
-
+  LimitedIo limitedIo;
   UdpFactory factory;
 
-  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
-  shared_ptr<UdpChannel> channel2 = factory.createChannel("127.0.0.1", "20071");
+  shared_ptr<UdpChannel> channel1 = factory.createChannel(A::getLocalIp(), A::getPort1());
+  shared_ptr<Face> face1;
+  unique_ptr<FaceHistory> history1;
+  factory.createFace(A::getFaceUri2(),
+                     [&] (shared_ptr<Face> newFace) {
+                       face1 = newFace;
+                       history1.reset(new FaceHistory(*face1, limitedIo));
+                       limitedIo.afterOp();
+                     },
+                     [] (const std::string& reason) { BOOST_ERROR(reason); });
 
-  channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,   this, _1),
-                   bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+  limitedIo.run(1, time::milliseconds(100));
+  BOOST_REQUIRE(face1 != nullptr);
+  BOOST_CHECK_EQUAL(channel1->size(), 1);
 
-  channel2->connect("127.0.0.1", "20070",
-                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
-                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
-
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot connect or cannot accept connection");
-
-  BOOST_CHECK_EQUAL(faces.size(), 1);
-
-  shared_ptr<UdpChannel> channel3 = factory.createChannel("127.0.0.1", "20072");
-  channel3->connect("127.0.0.1", "20070",
-                    bind(&EndToEndFixture::channel3_onFaceCreated, this, _1),
-                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
-
-  shared_ptr<UdpChannel> channel4 = factory.createChannel("127.0.0.1", "20073");
-
-  BOOST_CHECK_NE(channel3, channel4);
-
-  scheduler::schedule(time::milliseconds(500),
-                      bind(&EndToEndFixture::connect, this, channel4, "127.0.0.1", "20070"));
-
-  scheduler::schedule(time::milliseconds(400), bind(&EndToEndFixture::checkFaceList, this, 2));
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(2,// 2 connects
-                      time::seconds(4)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot connect or cannot accept multiple connections");
-
-  BOOST_CHECK_EQUAL(faces.size(), 3);
-
-
-  face2->sendInterest(interest1);
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-  BOOST_CHECK_EQUAL(faces.size(), 4);
-
-  face3->sendInterest(interest2);
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-  //channel1 should have created 2 faces, one when face2 sent an interest, one when face3 sent an interest
-  BOOST_CHECK_EQUAL(faces.size(), 5);
-  BOOST_CHECK_THROW(channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,     this, _1),
-                                     bind(&EndToEndFixture::channel_onConnectFailedOk, this, _1)),
-                    UdpChannel::Error);
-}
-
-//Test commented because it required to be run in a machine that can resolve ipv6 query
-//BOOST_FIXTURE_TEST_CASE(EndToEndIpv6, EndToEndFixture)
-//{
-//  UdpFactory factory = UdpFactory();
-//  Scheduler scheduler(g_io); // to limit the amount of time the test may take
-//
-//  EventId abortEvent =
-//  scheduler.scheduleEvent(time::seconds(1),
-//                          bind(&EndToEndFixture::abortTestCase, this,
-//                              "UdpChannel error: cannot connect or cannot accept connection"));
-//
-//  limitedIoRemaining = 1;
-//
-//  shared_ptr<UdpChannel> channel1 = factory.createChannel("::1", "20070");
-//  shared_ptr<UdpChannel> channel2 = factory.createChannel("::1", "20071");
-//
-//  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
-//                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
-//
-//  channel2->connect("::1", "20070",
-//                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
-//                    bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
-//  g_io.run();
-//  g_io.reset();
-//  scheduler.cancelEvent(abortEvent);
-//
-//  BOOST_REQUIRE(static_cast<bool>(face2));
-//
-//  abortEvent =
-//  scheduler.scheduleEvent(time::seconds(1),
-//                          bind(&EndToEndFixture::abortTestCase, this,
-//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
-//
-//  Interest interest1("ndn:/TpnzGvW9R");
-//  Data     data1    ("ndn:/KfczhUqVix");
-//  data1.setContent(0, 0);
-//  Interest interest2("ndn:/QWiIMfj5sL");
-//  Data     data2    ("ndn:/XNBV796f");
-//  data2.setContent(0, 0);
-//  Interest interest3("ndn:/QWiIhjgkj5sL");
-//  Data     data3    ("ndn:/XNBV794f");
-//  data3.setContent(0, 0);
-//
-//  ndn::SignatureSha256WithRsa fakeSignature;
-//  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue, reinterpret_cast<const uint8_t*>(0), 0));
-//
-//  // set fake signature on data1 and data2
-//  data1.setSignature(fakeSignature);
-//  data2.setSignature(fakeSignature);
-//  data3.setSignature(fakeSignature);
-//
-//  face2->sendInterest(interest2);
-//  face2->sendData    (data2    );
-//
-//  limitedIoRemaining = 3; //2 send + 1 listen return
-//  g_io.run();
-//  g_io.reset();
-//  scheduler.cancelEvent(abortEvent);
-//
-//  BOOST_REQUIRE(static_cast<bool>(face1));
-//
-//  face1->sendInterest(interest1);
-//  face1->sendData    (data1    );
-//
-//  abortEvent =
-//  scheduler.scheduleEvent(time::seconds(1),
-//                          bind(&EndToEndFixture::abortTestCase, this,
-//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
-//  limitedIoRemaining = 2;
-//  g_io.run();
-//  g_io.reset();
-//  scheduler.cancelEvent(abortEvent);
-//
-//  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
-//  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 1);
-//  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
-//  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
-//
-//  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
-//  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
-//  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
-//  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
-//
-//  //checking if the connection accepting mechanism works properly.
-//
-//  face2->sendData    (data3    );
-//  face2->sendInterest(interest3);
-//
-//  abortEvent =
-//  scheduler.scheduleEvent(time::seconds(1),
-//                          bind(&EndToEndFixture::abortTestCase, this,
-//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
-//  limitedIoRemaining = 2;
-//  g_io.run();
-//  g_io.reset();
-//  scheduler.cancelEvent(abortEvent);
-//
-//  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 2);
-//  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 2);
-//
-//  BOOST_CHECK_EQUAL(face1_receivedInterests[1].getName(), interest3.getName());
-//  BOOST_CHECK_EQUAL(face1_receivedDatas    [1].getName(), data3.getName());
-//
-//}
-
-
-//Test commented because it required to be run in a machine that can resolve ipv6 query
-//BOOST_FIXTURE_TEST_CASE(MultipleAcceptsIpv6, EndToEndFixture)
-//{
-//  Interest interest1("ndn:/TpnzGvW9R");
-//  Interest interest2("ndn:/QWiIMfj5sL");
-//  Interest interest3("ndn:/QWiIhjgkj5sL");
-//
-//  UdpFactory factory = UdpFactory();
-//  Scheduler scheduler(g_io); // to limit the amount of time the test may take
-//
-//  EventId abortEvent =
-//  scheduler.scheduleEvent(time::seconds(4),
-//                          bind(&EndToEndFixture::abortTestCase, this,
-//                               "UdpChannel error: cannot connect or cannot accept connection"));
-//
-//  shared_ptr<UdpChannel> channel1 = factory.createChannel("::1", "20070");
-//  shared_ptr<UdpChannel> channel2 = factory.createChannel("::1", "20071");
-//
-//  channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,   this, _1),
-//                   bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
-//
-//  channel2->connect("::1", "20070",
-//                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
-//                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
-//
-//  limitedIoRemaining = 1;
-//  g_io.run();
-//  g_io.reset();
-//  scheduler.cancelEvent(abortEvent);
-//
-//  BOOST_CHECK_EQUAL(faces.size(), 1);
-//
-//  shared_ptr<UdpChannel> channel3 = factory.createChannel("::1", "20072");
-//  channel3->connect("::1", "20070",
-//                    bind(&EndToEndFixture::channel3_onFaceCreated, this, _1),
-//                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
-//
-//  shared_ptr<UdpChannel> channel4 = factory.createChannel("::1", "20073");
-//
-//  BOOST_CHECK_NE(channel3, channel4);
-//
-//  scheduler
-//  .scheduleEvent(time::milliseconds(500),
-//                 bind(&UdpChannel::connect, channel4,
-//                      "::1", "20070",
-//                      static_cast<UdpChannel::FaceCreatedCallback>(bind(&EndToEndFixture::
-//                                                                        channel_onFaceCreated, this, _1)),
-//                      static_cast<UdpChannel::ConnectFailedCallback>(bind(&EndToEndFixture::
-//                                                                          channel_onConnectFailed, this, _1))));
-//
-//  limitedIoRemaining = 2; // 2 connects
-//  abortEvent =
-//  scheduler.scheduleEvent(time::seconds(4),
-//                          bind(&EndToEndFixture::abortTestCase, this,
-//                               "UdpChannel error: cannot connect or cannot accept multiple connections"));
-//
-//  scheduler.scheduleEvent(time::seconds(0.4),
-//                          bind(&EndToEndFixture::checkFaceList, this, 2));
-//
-//  g_io.run();
-//  g_io.reset();
-//
-//  BOOST_CHECK_EQUAL(faces.size(), 3);
-//
-//
-//  face2->sendInterest(interest1);
-//  abortEvent =
-//  scheduler.scheduleEvent(time::seconds(1),
-//                          bind(&EndToEndFixture::abortTestCase, this,
-//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
-//  limitedIoRemaining = 1;
-//  g_io.run();
-//  g_io.reset();
-//  scheduler.cancelEvent(abortEvent);
-//
-//  BOOST_CHECK_EQUAL(faces.size(), 4);
-//
-//  face3->sendInterest(interest2);
-//  abortEvent =
-//  scheduler.scheduleEvent(time::seconds(1),
-//                          bind(&EndToEndFixture::abortTestCase, this,
-//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
-//  limitedIoRemaining = 1;
-//  g_io.run();
-//  g_io.reset();
-//  scheduler.cancelEvent(abortEvent);
-//
-//  //channel1 should have created 2 faces, one when face2 sent an interest, one when face3 sent an interest
-//  BOOST_CHECK_EQUAL(faces.size(), 5);
-//  BOOST_CHECK_THROW(channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,
-//                                          this,
-//                                          _1),
-//                                      bind(&EndToEndFixture::channel_onConnectFailedOk,
-//                                          this,
-//                                          _1)),
-//                    UdpChannel::Error);
-//}
-
-
-BOOST_FIXTURE_TEST_CASE(FaceClosing, EndToEndFixture)
-{
-  UdpFactory factory = UdpFactory();
-
-  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
-  shared_ptr<UdpChannel> channel2 = factory.createChannel("127.0.0.1", "20071");
-
-  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
-                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
-
-  channel2->connect("127.0.0.1", "20070",
-                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
-                    bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot connect or cannot accept connection");
-
-  BOOST_CHECK_EQUAL(channel2->size(), 1);
-
-  BOOST_CHECK(static_cast<bool>(face2));
-
-  // Face::close must be invoked during io run to be counted as an op
-  scheduler::schedule(time::milliseconds(100), bind(&Face::close, face2));
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
-                      "FaceClosing error: cannot properly close faces");
-
-  BOOST_CHECK(!static_cast<bool>(face2));
-
-  BOOST_CHECK_EQUAL(channel2->size(), 0);
-}
-
-BOOST_FIXTURE_TEST_CASE(ClosingIdleFace, EndToEndFixture)
-{
-  Interest interest1("ndn:/TpnzGvW9R");
-  Interest interest2("ndn:/QWiIMfj5sL");
-
-  UdpFactory factory;
-
-  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070",
-                                                          time::seconds(2));
-  shared_ptr<UdpChannel> channel2 = factory.createChannel("127.0.0.1", "20071",
-                                                          time::seconds(2));
-
-  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
-                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
-
-  channel2->connect("127.0.0.1", "20070",
-                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
-                    bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot connect or cannot accept connection");
-
-  face2->sendInterest(interest1);
-  BOOST_CHECK(!face2->isOnDemand());
-
-  BOOST_CHECK_MESSAGE(limitedIo.run(2,//1 send + 1 listen return
-                                      time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-
-  BOOST_CHECK_EQUAL(faces.size(), 2);
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(2)) == LimitedIo::EXCEED_TIME,
-                      "Idle face should be still open because has been used recently");
-  BOOST_CHECK_EQUAL(faces.size(), 2);
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
-                      "Closing idle face error: face should be closed by now");
-
-  //the face on listen should be closed now
+  face1->close();
+  getGlobalIoService().poll();
+  BOOST_CHECK_EQUAL(history1->failures.size(), 1);
   BOOST_CHECK_EQUAL(channel1->size(), 0);
-  //checking that face2 has not been closed
-  BOOST_CHECK_EQUAL(channel2->size(), 1);
-  BOOST_REQUIRE(static_cast<bool>(face2));
+}
 
-  face2->sendInterest(interest2);
-  BOOST_CHECK_MESSAGE(limitedIo.run(2,//1 send + 1 listen return
-                                      time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot send or receive Interest/Data packets");
-  //channel1 should have created a new face by now
+// automatically close an idle incoming face
+BOOST_AUTO_TEST_CASE_TEMPLATE(IdleClose, A, EndToEndAddresses)
+{
+  LimitedIo limitedIo;
+  UdpFactory factory;
+
+  // channel1 is listening
+  shared_ptr<UdpChannel> channel1 = factory.createChannel(A::getLocalIp(), A::getPort1(),
+                                                          time::seconds(2));
+  shared_ptr<Face> face1;
+  unique_ptr<FaceHistory> history1;
+  channel1->listen([&] (shared_ptr<Face> newFace) {
+                     BOOST_CHECK(face1 == nullptr);
+                     face1 = newFace;
+                     history1.reset(new FaceHistory(*face1, limitedIo));
+                     limitedIo.afterOp();
+                   },
+                   [] (const std::string& reason) { BOOST_ERROR(reason); });
+
+  // face2 (on channel2) connects to channel1
+  shared_ptr<UdpChannel> channel2 = factory.createChannel(A::getLocalIp(), A::getPort2(),
+                                                          time::seconds(2));
+  shared_ptr<Face> face2;
+  unique_ptr<FaceHistory> history2;
+  boost::asio::ip::address ipAddress = boost::asio::ip::address::from_string(A::getLocalIp());
+  udp::Endpoint endpoint(ipAddress, boost::lexical_cast<uint16_t>(A::getPort1()));
+  channel2->connect(endpoint,
+                    [&] (shared_ptr<Face> newFace) {
+                      face2 = newFace;
+                      history2.reset(new FaceHistory(*face2, limitedIo));
+                      limitedIo.afterOp();
+                    },
+                    [] (const std::string& reason) { BOOST_ERROR(reason); });
+
+  limitedIo.run(1, time::milliseconds(100)); // 1 create (on channel2)
+  BOOST_REQUIRE(face2 != nullptr);
+  BOOST_CHECK_EQUAL(face2->isOnDemand(), false);
+
+  // face2 sends to channel1, creates face1
+  shared_ptr<Interest> interest2 = makeInterest("/I2");
+  face2->sendInterest(*interest2);
+
+  limitedIo.run(2, time::seconds(1)); // 1 accept (on channel1), 1 receive (on face1)
   BOOST_CHECK_EQUAL(channel1->size(), 1);
-  BOOST_CHECK_EQUAL(channel2->size(), 1);
-  BOOST_REQUIRE(static_cast<bool>(face1));
-  BOOST_CHECK(face1->isOnDemand());
+  BOOST_REQUIRE(face1 != nullptr);
+  BOOST_CHECK_EQUAL(face1->isOnDemand(), true);
 
-  channel1->connect("127.0.0.1", "20071",
-                    bind(&EndToEndFixture::channel1_onFaceCreatedNoCheck, this, _1),
-                    bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+  limitedIo.defer(time::seconds(1));
+  BOOST_CHECK_EQUAL(history1->failures.size(), 0); // face1 is not idle
+  BOOST_CHECK_EQUAL(history2->failures.size(), 0); // face2 is outgoing face and never closed
 
-  BOOST_CHECK_MESSAGE(limitedIo.run(1,//1 connect
-                                      time::seconds(1)) == LimitedIo::EXCEED_OPS,
-                      "UdpChannel error: cannot connect");
+  limitedIo.defer(time::seconds(4));
+  BOOST_CHECK_EQUAL(history1->failures.size(), 1); // face1 is idle and automatically closed
+  BOOST_CHECK_EQUAL(channel1->size(), 0);
+  BOOST_CHECK_EQUAL(history2->failures.size(), 0); // face2 is outgoing face and never closed
+}
 
-  BOOST_CHECK(!face1->isOnDemand());
+class FakeNetworkInterfaceFixture : public BaseFixture
+{
+public:
+  FakeNetworkInterfaceFixture()
+  {
+    using namespace boost::asio::ip;
 
-  //the connect should have set face1 as permanent face,
-  //but it shouln't have created any additional faces
-  BOOST_CHECK_EQUAL(channel1->size(), 1);
-  BOOST_CHECK_EQUAL(channel2->size(), 1);
-  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_TIME,
-                      "Idle face should be still open because it's permanent now");
-  //both faces are permanent, nothing should have changed
-  BOOST_CHECK_EQUAL(channel1->size(), 1);
-  BOOST_CHECK_EQUAL(channel2->size(), 1);
+    auto fakeInterfaces = make_shared<std::vector<NetworkInterfaceInfo>>();
+
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {0, "eth0",
+        ethernet::Address::fromString("3e:15:c2:8b:65:00"),
+        {address_v4::from_string("0.0.0.0")},
+        {address_v6::from_string("::")},
+        address_v4(),
+        IFF_UP});
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {1, "eth0",
+        ethernet::Address::fromString("3e:15:c2:8b:65:00"),
+        {address_v4::from_string("192.168.2.1"), address_v4::from_string("192.168.2.2")},
+        {},
+        address_v4::from_string("192.168.2.255"),
+        0});
+    fakeInterfaces->push_back(
+      NetworkInterfaceInfo {2, "eth1",
+        ethernet::Address::fromString("3e:15:c2:8b:65:00"),
+        {address_v4::from_string("198.51.100.1")},
+        {address_v6::from_string("2001:db8::2"), address_v6::from_string("2001:db8::3")},
+        address_v4::from_string("198.51.100.255"),
+        IFF_MULTICAST | IFF_BROADCAST | IFF_UP});
+
+    setDebugNetworkInterfaces(fakeInterfaces);
+  }
+
+  ~FakeNetworkInterfaceFixture()
+  {
+    setDebugNetworkInterfaces(nullptr);
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(Bug2292, FakeNetworkInterfaceFixture)
+{
+  using namespace boost::asio::ip;
+
+  UdpFactory factory;
+  factory.prohibitEndpoint(udp::Endpoint(address_v4::from_string("192.168.2.1"), 1024));
+  BOOST_REQUIRE_EQUAL(factory.m_prohibitedEndpoints.size(), 1);
+  BOOST_CHECK((factory.m_prohibitedEndpoints ==
+               std::set<udp::Endpoint> {
+                 udp::Endpoint(address_v4::from_string("192.168.2.1"), 1024),
+               }));
+
+  factory.m_prohibitedEndpoints.clear();
+  factory.prohibitEndpoint(udp::Endpoint(address_v6::from_string("2001:db8::1"), 2048));
+  BOOST_REQUIRE_EQUAL(factory.m_prohibitedEndpoints.size(), 1);
+  BOOST_CHECK((factory.m_prohibitedEndpoints ==
+               std::set<udp::Endpoint> {
+                 udp::Endpoint(address_v6::from_string("2001:db8::1"), 2048),
+               }));
+
+  factory.m_prohibitedEndpoints.clear();
+  factory.prohibitEndpoint(udp::Endpoint(address_v4(), 1024));
+  BOOST_REQUIRE_EQUAL(factory.m_prohibitedEndpoints.size(), 6);
+  BOOST_CHECK((factory.m_prohibitedEndpoints ==
+               std::set<udp::Endpoint> {
+                 udp::Endpoint(address_v4::from_string("192.168.2.1"), 1024),
+                 udp::Endpoint(address_v4::from_string("192.168.2.2"), 1024),
+                 udp::Endpoint(address_v4::from_string("198.51.100.1"), 1024),
+                 udp::Endpoint(address_v4::from_string("198.51.100.255"), 1024),
+                 udp::Endpoint(address_v4::from_string("255.255.255.255"), 1024),
+                 udp::Endpoint(address_v4::from_string("0.0.0.0"), 1024)
+               }));
+
+  factory.m_prohibitedEndpoints.clear();
+  factory.prohibitEndpoint(udp::Endpoint(address_v6(), 2048));
+  BOOST_REQUIRE_EQUAL(factory.m_prohibitedEndpoints.size(), 3);
+  BOOST_CHECK((factory.m_prohibitedEndpoints ==
+               std::set<udp::Endpoint> {
+                 udp::Endpoint(address_v6::from_string("2001:db8::2"), 2048),
+                 udp::Endpoint(address_v6::from_string("2001:db8::3"), 2048),
+                 udp::Endpoint(address_v6::from_string("::"), 2048),
+               }));
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/daemon/face/unix-stream.cpp b/tests/daemon/face/unix-stream.cpp
index 24154ec..02a48d9 100644
--- a/tests/daemon/face/unix-stream.cpp
+++ b/tests/daemon/face/unix-stream.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -96,10 +96,8 @@
   {
     BOOST_CHECK(!static_cast<bool>(face1));
     face1 = static_pointer_cast<UnixStreamFace>(newFace);
-    face1->onReceiveInterest +=
-      bind(&EndToEndFixture::face1_onReceiveInterest, this, _1);
-    face1->onReceiveData +=
-      bind(&EndToEndFixture::face1_onReceiveData, this, _1);
+    face1->onReceiveInterest.connect(bind(&EndToEndFixture::face1_onReceiveInterest, this, _1));
+    face1->onReceiveData.connect(bind(&EndToEndFixture::face1_onReceiveData, this, _1));
 
     limitedIo.afterOp();
   }
@@ -199,10 +197,8 @@
                     face1localUri.size() - std::string(CHANNEL_PATH1).size());
 
   face2 = make_shared<UnixStreamFace>(client);
-  face2->onReceiveInterest +=
-    bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
-  face2->onReceiveData +=
-    bind(&EndToEndFixture::face2_onReceiveData, this, _1);
+  face2->onReceiveInterest.connect(bind(&EndToEndFixture::face2_onReceiveInterest, this, _1));
+  face2->onReceiveData.connect(bind(&EndToEndFixture::face2_onReceiveData, this, _1));
 
   shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
   shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
@@ -282,12 +278,12 @@
 
   // we should still be able to send/receive with the other one
   face1 = faces.back();
-  face1->onReceiveInterest += bind(&EndToEndFixture::face1_onReceiveInterest, this, _1);
-  face1->onReceiveData += bind(&EndToEndFixture::face1_onReceiveData, this, _1);
+  face1->onReceiveInterest.connect(bind(&EndToEndFixture::face1_onReceiveInterest, this, _1));
+  face1->onReceiveData.connect(bind(&EndToEndFixture::face1_onReceiveData, this, _1));
 
   face2 = make_shared<UnixStreamFace>(client2);
-  face2->onReceiveInterest += bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
-  face2->onReceiveData += bind(&EndToEndFixture::face2_onReceiveData, this, _1);
+  face2->onReceiveInterest.connect(bind(&EndToEndFixture::face2_onReceiveInterest, this, _1));
+  face2->onReceiveData.connect(bind(&EndToEndFixture::face2_onReceiveData, this, _1));
 
   shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
   shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
@@ -336,10 +332,8 @@
   BOOST_REQUIRE(static_cast<bool>(face1));
 
   face2 = make_shared<UnixStreamFace>(client);
-  face2->onReceiveInterest +=
-    bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
-  face2->onReceiveData +=
-    bind(&EndToEndFixture::face2_onReceiveData, this, _1);
+  face2->onReceiveInterest.connect(bind(&EndToEndFixture::face2_onReceiveInterest, this, _1));
+  face2->onReceiveData.connect(bind(&EndToEndFixture::face2_onReceiveData, this, _1));
 
   shared_ptr<Interest> interest1 = makeInterest("ndn:/TpnzGvW9R");
   shared_ptr<Data>     data1     = makeData("ndn:/KfczhUqVix");
@@ -425,12 +419,9 @@
   void
   onFaceCreated(const shared_ptr<Face>& face)
   {
-    face->onReceiveInterest +=
-      bind(&SimpleEndToEndFixture::onReceiveInterest, this, _1);
-    face->onReceiveData +=
-      bind(&SimpleEndToEndFixture::onReceiveData, this, _1);
-    face->onFail +=
-      bind(&SimpleEndToEndFixture::onFail, this, face);
+    face->onReceiveInterest.connect(bind(&SimpleEndToEndFixture::onReceiveInterest, this, _1));
+    face->onReceiveData.connect(bind(&SimpleEndToEndFixture::onReceiveData, this, _1));
+    face->onFail.connect(bind(&SimpleEndToEndFixture::onFail, this, face));
 
     if (static_cast<bool>(dynamic_pointer_cast<LocalFace>(face))) {
       static_pointer_cast<LocalFace>(face)->setLocalControlHeaderFeature(
diff --git a/tests/daemon/face/websocket.cpp b/tests/daemon/face/websocket.cpp
index fa93b6a..b83604e 100644
--- a/tests/daemon/face/websocket.cpp
+++ b/tests/daemon/face/websocket.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -70,12 +70,9 @@
   {
     BOOST_CHECK(!static_cast<bool>(face1));
     face1 = newFace;
-    face1->onReceiveInterest +=
-      bind(&EndToEndFixture::face1_onReceiveInterest, this, _1);
-    face1->onReceiveData +=
-      bind(&EndToEndFixture::face1_onReceiveData, this, _1);
-    face1->onFail +=
-      bind(&EndToEndFixture::face1_onFail, this);
+    face1->onReceiveInterest.connect(bind(&EndToEndFixture::face1_onReceiveInterest, this, _1));
+    face1->onReceiveData.connect(bind(&EndToEndFixture::face1_onReceiveData, this, _1));
+    face1->onFail.connect(bind(&EndToEndFixture::face1_onFail, this));
 
     limitedIo.afterOp();
   }
diff --git a/tests/daemon/fw/best-route-strategy2.cpp b/tests/daemon/fw/best-route-strategy2.cpp
index 3a0005b..687bfcb 100644
--- a/tests/daemon/fw/best-route-strategy2.cpp
+++ b/tests/daemon/fw/best-route-strategy2.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -27,17 +27,15 @@
 #include "strategy-tester.hpp"
 
 #include "tests/test-common.hpp"
-#include "tests/limited-io.hpp"
 #include "tests/daemon/face/dummy-face.hpp"
 
 namespace nfd {
 namespace tests {
 
-BOOST_FIXTURE_TEST_SUITE(FwBestRouteStrategy2, BaseFixture)
+BOOST_FIXTURE_TEST_SUITE(FwBestRouteStrategy2, UnitTestTimeFixture)
 
 BOOST_AUTO_TEST_CASE(Forward)
 {
-  LimitedIo limitedIo;
   Forwarder forwarder;
   typedef StrategyTester<fw::BestRouteStrategy2> BestRouteStrategy2Tester;
   BestRouteStrategy2Tester strategy(forwarder);
@@ -63,12 +61,14 @@
   Pit& pit = forwarder.getPit();
   shared_ptr<pit::Entry> pitEntry = pit.insert(*interest).first;
 
+  const time::nanoseconds TICK = time::duration_cast<time::nanoseconds>(
+    fw::RetransmissionSuppression::MIN_RETRANSMISSION_INTERVAL * 0.01);
   const time::nanoseconds RETRANSMISSION_10P = time::duration_cast<time::nanoseconds>(
-    fw::BestRouteStrategy2::MIN_RETRANSMISSION_INTERVAL * 0.1); // 10%
+    fw::RetransmissionSuppression::MIN_RETRANSMISSION_INTERVAL * 0.1); // 10%
   const time::nanoseconds RETRANSMISSION_70P = time::duration_cast<time::nanoseconds>(
-    fw::BestRouteStrategy2::MIN_RETRANSMISSION_INTERVAL * 0.7); // 70%
+    fw::RetransmissionSuppression::MIN_RETRANSMISSION_INTERVAL * 0.7); // 70%
   const time::nanoseconds RETRANSMISSION_2 = time::duration_cast<time::nanoseconds>(
-    fw::BestRouteStrategy2::MIN_RETRANSMISSION_INTERVAL * 2.0); // x2
+    fw::RetransmissionSuppression::MIN_RETRANSMISSION_INTERVAL * 2.0); // x2
 
   // first Interest goes to nexthop with lowest FIB cost,
   // however face1 is downstream so it cannot be used
@@ -99,7 +99,7 @@
     retxFrom4Evt = scheduler::schedule(RETRANSMISSION_10P, periodicalRetxFrom4);
   };
   periodicalRetxFrom4();
-  limitedIo.defer(fw::BestRouteStrategy2::MIN_RETRANSMISSION_INTERVAL * 16);
+  this->advanceClocks(TICK, fw::RetransmissionSuppression::MIN_RETRANSMISSION_INTERVAL * 16);
   scheduler::cancel(retxFrom4Evt);
 
   // nexthops for accepted retransmissions: follow FIB cost,
@@ -115,7 +115,7 @@
 
   strategy.m_sendInterestHistory.clear();
   for (int i = 0; i < 3; ++i) {
-    limitedIo.defer(RETRANSMISSION_2);
+    this->advanceClocks(TICK, RETRANSMISSION_2);
     pitEntry->insertOrUpdateInRecord(face5, *interest);
     strategy.afterReceiveInterest(*face5, *interest, fibEntry, pitEntry);
   }
diff --git a/tests/daemon/fw/face-table.cpp b/tests/daemon/fw/face-table.cpp
index 0dd8432..b2bfab4 100644
--- a/tests/daemon/fw/face-table.cpp
+++ b/tests/daemon/fw/face-table.cpp
@@ -34,12 +34,6 @@
 
 BOOST_FIXTURE_TEST_SUITE(FwFaceTable, BaseFixture)
 
-static inline void
-saveFaceId(std::vector<FaceId>& faceIds, shared_ptr<Face> face)
-{
-  faceIds.push_back(face->getId());
-}
-
 BOOST_AUTO_TEST_CASE(AddRemove)
 {
   Forwarder forwarder;
@@ -47,8 +41,12 @@
   FaceTable& faceTable = forwarder.getFaceTable();
   std::vector<FaceId> onAddHistory;
   std::vector<FaceId> onRemoveHistory;
-  faceTable.onAdd    += bind(&saveFaceId, ndn::ref(onAddHistory   ), _1);
-  faceTable.onRemove += bind(&saveFaceId, ndn::ref(onRemoveHistory), _1);
+  faceTable.onAdd.connect([&] (shared_ptr<Face> face) {
+    onAddHistory.push_back(face->getId());
+  });
+  faceTable.onRemove.connect([&] (shared_ptr<Face> face) {
+    onRemoveHistory.push_back(face->getId());
+  });
 
   shared_ptr<Face> face1 = make_shared<DummyFace>();
   shared_ptr<Face> face2 = make_shared<DummyFace>();
diff --git a/tests/daemon/fw/forwarder.cpp b/tests/daemon/fw/forwarder.cpp
index 1b74f37..d707a17 100644
--- a/tests/daemon/fw/forwarder.cpp
+++ b/tests/daemon/fw/forwarder.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -50,8 +50,8 @@
 
   shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
   shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
-  face1->afterSend += afterOp;
-  face2->afterSend += afterOp;
+  face1->afterSend.connect(afterOp);
+  face2->afterSend.connect(afterOp);
   forwarder.addFace(face1);
   forwarder.addFace(face2);
 
@@ -433,9 +433,9 @@
   forwarder.addFace(face2);
 
   // cause an Interest sent out of face2 to loop back into face1 after a delay
-  face2->onSendInterest += [&face1] (const Interest& interest) {
+  face2->onSendInterest.connect([&face1] (const Interest& interest) {
     scheduler::schedule(time::milliseconds(170), [&] { face1->receiveInterest(interest); });
-  };
+  });
 
   Fib& fib = forwarder.getFib();
   shared_ptr<fib::Entry> fibEntry = fib.insert(Name("ndn:/A")).first;
diff --git a/tests/daemon/fw/ncc-strategy.cpp b/tests/daemon/fw/ncc-strategy.cpp
index 09d54be..da70e86 100644
--- a/tests/daemon/fw/ncc-strategy.cpp
+++ b/tests/daemon/fw/ncc-strategy.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -33,7 +33,7 @@
 namespace nfd {
 namespace tests {
 
-BOOST_FIXTURE_TEST_SUITE(FwNccStrategy, BaseFixture)
+BOOST_FIXTURE_TEST_SUITE(FwNccStrategy, UnitTestTimeFixture)
 
 // NccStrategy is fairly complex.
 // The most important property is:
@@ -41,11 +41,11 @@
 // and favors this upstream in subsequent Interests.
 BOOST_AUTO_TEST_CASE(FavorRespondingUpstream)
 {
-  LimitedIo limitedIo;
+  LimitedIo limitedIo(this);
   Forwarder forwarder;
   typedef StrategyTester<fw::NccStrategy> NccStrategyTester;
   shared_ptr<NccStrategyTester> strategy = make_shared<NccStrategyTester>(ref(forwarder));
-  strategy->onAction += bind(&LimitedIo::afterOp, &limitedIo);
+  strategy->onAction.connect(bind(&LimitedIo::afterOp, &limitedIo));
 
   shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
   shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
@@ -80,7 +80,7 @@
   BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[0].get<1>(), face1);
 
   // forwards to face2 because face1 doesn't respond
-  limitedIo.run(1, time::milliseconds(500));
+  limitedIo.run(1, time::milliseconds(500), time::milliseconds(10));
   BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 2);
   BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[1].get<1>(), face2);
 
@@ -88,7 +88,7 @@
   shared_ptr<Data> data1p = makeData("ndn:/0Jm1ajrW/%00");
   Data& data1 = *data1p;
   strategy->beforeSatisfyInterest(pitEntry1, *face2, data1);
-  limitedIo.defer(time::milliseconds(500));
+  this->advanceClocks(time::milliseconds(10), time::milliseconds(500));
 
   // second Interest: strategy knows face2 is best
   shared_ptr<Interest> interest2p = makeInterest("ndn:/0Jm1ajrW/%00%01");
@@ -100,18 +100,16 @@
   strategy->afterReceiveInterest(*face3, interest2, fibEntry, pitEntry2);
 
   // forwards to face2 because it responds previously
-  limitedIo.defer(time::milliseconds(1));
+  this->advanceClocks(time::milliseconds(1));
   BOOST_REQUIRE_GE(strategy->m_sendInterestHistory.size(), 3);
   BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[2].get<1>(), face2);
 }
 
 BOOST_AUTO_TEST_CASE(Bug1853)
 {
-  LimitedIo limitedIo;
   Forwarder forwarder;
   typedef StrategyTester<fw::NccStrategy> NccStrategyTester;
   shared_ptr<NccStrategyTester> strategy = make_shared<NccStrategyTester>(ref(forwarder));
-  strategy->onAction += bind(&LimitedIo::afterOp, &limitedIo);
 
   shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
   shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
@@ -138,14 +136,14 @@
   pitEntry1->insertOrUpdateInRecord(face3, *interest1);
   strategy->afterReceiveInterest(*face3, *interest1, fibEntry, pitEntry1);
 
-  limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(1));
+  this->advanceClocks(time::milliseconds(1));
   BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 1);
   BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[0].get<1>(), face1);
 
   // face1 responds
   shared_ptr<Data> data1 = makeData("ndn:/nztwIvHX/%00");
   strategy->beforeSatisfyInterest(pitEntry1, *face1, *data1);
-  limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(500));
+  this->advanceClocks(time::milliseconds(10), time::milliseconds(500));
 
   // second Interest: bestFace is face1, nUpstreams becomes 0,
   // therefore pitEntryInfo->maxInterval cannot be calculated from deferRange and nUpstreams
@@ -158,16 +156,16 @@
 
   // FIB entry is changed before doPropagate executes
   fibEntry->addNextHop(face2, 20);
-  limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(1000));// should not crash
+  this->advanceClocks(time::milliseconds(10), time::milliseconds(1000));// should not crash
 }
 
 BOOST_AUTO_TEST_CASE(Bug1961)
 {
-  LimitedIo limitedIo;
+  LimitedIo limitedIo(this);
   Forwarder forwarder;
   typedef StrategyTester<fw::NccStrategy> NccStrategyTester;
   shared_ptr<NccStrategyTester> strategy = make_shared<NccStrategyTester>(ref(forwarder));
-  strategy->onAction += bind(&LimitedIo::afterOp, &limitedIo);
+  strategy->onAction.connect(bind(&LimitedIo::afterOp, &limitedIo));
 
   shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
   shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
@@ -194,7 +192,8 @@
 
   pitEntry1->insertOrUpdateInRecord(face3, *interest1);
   strategy->afterReceiveInterest(*face3, *interest1, fibEntry, pitEntry1);
-  limitedIo.run(2 - strategy->m_sendInterestHistory.size(), time::milliseconds(2000));
+  limitedIo.run(2 - strategy->m_sendInterestHistory.size(),
+                time::milliseconds(2000), time::milliseconds(10));
   BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 2);
   BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[0].get<1>(), face1);
   BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[1].get<1>(), face2);
@@ -203,10 +202,10 @@
   shared_ptr<Data> data1 = makeData("ndn:/seRMz5a6/%00");
   strategy->beforeSatisfyInterest(pitEntry1, *face1, *data1);
   pitEntry1->deleteInRecords();
-  limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(10));
+  this->advanceClocks(time::milliseconds(10));
   // face2 also responds
   strategy->beforeSatisfyInterest(pitEntry1, *face2, *data1);
-  limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(10));
+  this->advanceClocks(time::milliseconds(10));
 
   // second Interest: bestFace should be face 1
   shared_ptr<Interest> interest2 = makeInterest("ndn:/seRMz5a6/%01");
@@ -215,7 +214,8 @@
 
   pitEntry2->insertOrUpdateInRecord(face3, *interest2);
   strategy->afterReceiveInterest(*face3, *interest2, fibEntry, pitEntry2);
-  limitedIo.run(3 - strategy->m_sendInterestHistory.size(), time::milliseconds(2000));
+  limitedIo.run(3 - strategy->m_sendInterestHistory.size(),
+                time::milliseconds(2000), time::milliseconds(10));
 
   BOOST_REQUIRE_GE(strategy->m_sendInterestHistory.size(), 3);
   BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[2].get<1>(), face1);
@@ -223,11 +223,11 @@
 
 BOOST_AUTO_TEST_CASE(Bug1971)
 {
-  LimitedIo limitedIo;
+  LimitedIo limitedIo(this);
   Forwarder forwarder;
   typedef StrategyTester<fw::NccStrategy> NccStrategyTester;
   shared_ptr<NccStrategyTester> strategy = make_shared<NccStrategyTester>(ref(forwarder));
-  strategy->onAction += bind(&LimitedIo::afterOp, &limitedIo);
+  strategy->onAction.connect(bind(&LimitedIo::afterOp, &limitedIo));
 
   shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
   shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
@@ -251,7 +251,8 @@
 
   pitEntry1->insertOrUpdateInRecord(face1, *interest1);
   strategy->afterReceiveInterest(*face1, *interest1, fibEntry, pitEntry1);
-  limitedIo.run(1 - strategy->m_sendInterestHistory.size(), time::milliseconds(2000));
+  limitedIo.run(1 - strategy->m_sendInterestHistory.size(),
+                time::milliseconds(2000), time::milliseconds(10));
   BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 1);
   BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[0].get<1>(), face2);
 
@@ -261,16 +262,51 @@
   strategy->beforeSatisfyInterest(pitEntry1, *face2, *data1);
   pitEntry1->deleteOutRecord(*face2);
   pitEntry1->deleteInRecords();
-  limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(10));
+  this->advanceClocks(time::milliseconds(10));
 
   // similar Interest: strategy should still forward it
   pitEntry1->insertOrUpdateInRecord(face1, *interest1);
   strategy->afterReceiveInterest(*face1, *interest1, fibEntry, pitEntry1);
-  limitedIo.run(2 - strategy->m_sendInterestHistory.size(), time::milliseconds(2000));
+  limitedIo.run(2 - strategy->m_sendInterestHistory.size(),
+                time::milliseconds(2000), time::milliseconds(10));
   BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 2);
   BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[1].get<1>(), face2);
 }
 
+BOOST_AUTO_TEST_CASE(Bug1998)
+{
+  Forwarder forwarder;
+  typedef StrategyTester<fw::NccStrategy> NccStrategyTester;
+  shared_ptr<NccStrategyTester> strategy = make_shared<NccStrategyTester>(ref(forwarder));
+
+  shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+
+  Fib& fib = forwarder.getFib();
+  shared_ptr<fib::Entry> fibEntry = fib.insert(Name()).first;
+  fibEntry->addNextHop(face1, 10); // face1 is top-ranked nexthop
+  fibEntry->addNextHop(face2, 20);
+
+  StrategyChoice& strategyChoice = forwarder.getStrategyChoice();
+  strategyChoice.install(strategy);
+  strategyChoice.insert(Name(), strategy->getName());
+
+  Pit& pit = forwarder.getPit();
+
+  // Interest comes from face1, which is sole downstream
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/tFy5HzUzD4");
+  shared_ptr<pit::Entry> pitEntry1 = pit.insert(*interest1).first;
+  pitEntry1->insertOrUpdateInRecord(face1, *interest1);
+
+  strategy->afterReceiveInterest(*face1, *interest1, fibEntry, pitEntry1);
+
+  // Interest shall go to face2, not loop back to face1
+  BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 1);
+  BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[0].get<1>(), face2);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests
diff --git a/tests/daemon/fw/retransmission-suppression.cpp b/tests/daemon/fw/retransmission-suppression.cpp
new file mode 100644
index 0000000..c1ba968
--- /dev/null
+++ b/tests/daemon/fw/retransmission-suppression.cpp
@@ -0,0 +1,90 @@
+/* -*- 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 "fw/retransmission-suppression.hpp"
+#include "strategy-tester.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+namespace nfd {
+namespace tests {
+
+using fw::RetransmissionSuppression;
+
+BOOST_FIXTURE_TEST_SUITE(FwRetransmissionSuppression, UnitTestTimeFixture)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  Forwarder forwarder;
+  Pit& pit = forwarder.getPit();
+  RetransmissionSuppression rs;
+
+  shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face3 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+  forwarder.addFace(face3);
+
+  shared_ptr<Interest> interest = makeInterest("ndn:/0JiimvmxK8");
+  shared_ptr<pit::Entry> pitEntry = pit.insert(*interest).first;
+
+  const time::nanoseconds RETRANSMISSION_10P = time::duration_cast<time::nanoseconds>(
+    fw::RetransmissionSuppression::MIN_RETRANSMISSION_INTERVAL * 0.1);
+
+  // @ time 0
+  pitEntry->insertOrUpdateInRecord(face1, *interest);
+  BOOST_CHECK_EQUAL(rs.decide(*face1, *interest, *pitEntry), RetransmissionSuppression::NEW);
+  pitEntry->insertOrUpdateOutRecord(face3, *interest);
+
+  this->advanceClocks(RETRANSMISSION_10P, 5); // @ time 0.5 interval
+  pitEntry->insertOrUpdateInRecord(face1, *interest);
+  BOOST_CHECK_EQUAL(rs.decide(*face1, *interest, *pitEntry), RetransmissionSuppression::SUPPRESS);
+  pitEntry->insertOrUpdateInRecord(face2, *interest);
+  BOOST_CHECK_EQUAL(rs.decide(*face2, *interest, *pitEntry), RetransmissionSuppression::SUPPRESS);
+
+  this->advanceClocks(RETRANSMISSION_10P, 6); // @ time 1.1 interval
+  pitEntry->insertOrUpdateInRecord(face2, *interest);
+  BOOST_CHECK_EQUAL(rs.decide(*face2, *interest, *pitEntry), RetransmissionSuppression::FORWARD);
+  // but strategy decides not to forward
+
+  this->advanceClocks(RETRANSMISSION_10P, 1); // @ time 1.2 interval
+  pitEntry->insertOrUpdateInRecord(face1, *interest);
+  BOOST_CHECK_EQUAL(rs.decide(*face1, *interest, *pitEntry), RetransmissionSuppression::FORWARD);
+  // retransmission suppress shall still give clearance for forwarding
+  pitEntry->insertOrUpdateOutRecord(face3, *interest); // and strategy forwards
+
+  this->advanceClocks(RETRANSMISSION_10P, 2); // @ time 1.4 interval
+  pitEntry->insertOrUpdateInRecord(face1, *interest);
+  BOOST_CHECK_EQUAL(rs.decide(*face1, *interest, *pitEntry), RetransmissionSuppression::SUPPRESS);
+  pitEntry->insertOrUpdateInRecord(face2, *interest);
+  BOOST_CHECK_EQUAL(rs.decide(*face2, *interest, *pitEntry), RetransmissionSuppression::SUPPRESS);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/fw/strategy-tester.hpp b/tests/daemon/fw/strategy-tester.hpp
index a1b920d..980144d 100644
--- a/tests/daemon/fw/strategy-tester.hpp
+++ b/tests/daemon/fw/strategy-tester.hpp
@@ -48,7 +48,7 @@
   }
 
   /// fires after each Action
-  EventEmitter<> onAction;
+  signal::Signal<StrategyTester<S>> onAction;
 
 protected:
   virtual void
diff --git a/tests/daemon/fw/strategy.cpp b/tests/daemon/fw/strategy.cpp
new file mode 100644
index 0000000..066b280
--- /dev/null
+++ b/tests/daemon/fw/strategy.cpp
@@ -0,0 +1,97 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014,  Regents of the University of California,
+ *                      Arizona Board of Regents,
+ *                      Colorado State University,
+ *                      University Pierre & Marie Curie, Sorbonne University,
+ *                      Washington University in St. Louis,
+ *                      Beijing Institute of Technology,
+ *                      The University of Memphis
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "fw/strategy.hpp"
+#include "dummy-strategy.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+#include <boost/range/adaptor/filtered.hpp>
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FwStrategy, BaseFixture)
+
+class FaceTableAccessTestStrategy : public DummyStrategy
+{
+public:
+  explicit
+  FaceTableAccessTestStrategy(Forwarder& forwarder)
+    : DummyStrategy(forwarder, Name("ndn:/strategy"))
+  {
+    this->afterAddFace.connect([this] (shared_ptr<Face> face) {
+      this->addedFaces.push_back(face->getId());
+    });
+    this->beforeRemoveFace.connect([this] (shared_ptr<Face> face) {
+      this->removedFaces.push_back(face->getId());
+    });
+  }
+
+  std::vector<FaceId>
+  getLocalFaces()
+  {
+    auto enumerable = this->getFaceTable() |
+                      boost::adaptors::filtered([] (shared_ptr<Face> face) {
+                        return face->isLocal();
+                      });
+
+    std::vector<FaceId> results;
+    for (shared_ptr<Face> face : enumerable) {
+      results.push_back(face->getId());
+    }
+    return results;
+  }
+
+public:
+  std::vector<FaceId> addedFaces;
+  std::vector<FaceId> removedFaces;
+};
+
+BOOST_AUTO_TEST_CASE(FaceTableAccess)
+{
+  Forwarder forwarder;
+  FaceTableAccessTestStrategy strategy(forwarder);
+
+  auto face1 = make_shared<DummyFace>();
+  auto face2 = make_shared<DummyLocalFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+  FaceId id1 = face1->getId();
+  FaceId id2 = face2->getId();
+
+  BOOST_CHECK(strategy.getLocalFaces() == std::vector<FaceId>{id2});
+
+  face2->close();
+  face1->close();
+
+  BOOST_CHECK((strategy.addedFaces   == std::vector<FaceId>{id1, id2}));
+  BOOST_CHECK((strategy.removedFaces == std::vector<FaceId>{id2, id1}));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/channel-status-publisher.cpp b/tests/daemon/mgmt/channel-status-publisher.cpp
index 370efe1..08fbe67 100644
--- a/tests/daemon/mgmt/channel-status-publisher.cpp
+++ b/tests/daemon/mgmt/channel-status-publisher.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -144,8 +144,7 @@
       m_expectedEntries[expectedEntry.getLocalUri()] = expectedEntry;
     }
 
-  m_face->onReceiveData +=
-    bind(&ChannelStatusPublisherFixture::validatePublish, this, _1);
+  m_face->onReceiveData.connect(bind(&ChannelStatusPublisherFixture::validatePublish, this, _1));
 
   m_publisher.publish();
   BOOST_REQUIRE(m_finished);
@@ -171,8 +170,7 @@
       m_expectedEntries[expectedEntry.getLocalUri()] = expectedEntry;
     }
 
-  m_face->onReceiveData +=
-    bind(&ChannelStatusPublisherFixture::validatePublish, this, _1);
+  m_face->onReceiveData.connect(bind(&ChannelStatusPublisherFixture::validatePublish, this, _1));
 
   m_publisher.publish();
   BOOST_REQUIRE(m_finished);
diff --git a/tests/daemon/mgmt/face-manager.cpp b/tests/daemon/mgmt/face-manager.cpp
index 2b2a15e..f9a2771 100644
--- a/tests/daemon/mgmt/face-manager.cpp
+++ b/tests/daemon/mgmt/face-manager.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -793,13 +793,13 @@
 
 #endif // HAVE_LIBPCAP
 
-BOOST_AUTO_TEST_CASE(TestFireInterestFilter)
+BOOST_AUTO_TEST_CASE(ShortName)
 {
   shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/faces"));
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  };
+  });
 
   getFace()->sendInterest(*command);
   g_io.run_one();
@@ -811,9 +811,9 @@
 {
   shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/faces"));
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  };
+  });
 
   getManager().onFaceRequest(*command);
 
@@ -823,7 +823,7 @@
 BOOST_AUTO_TEST_CASE(UnsignedCommand)
 {
   ControlParameters parameters;
-  parameters.setUri("tcp://127.0.0.1");
+  parameters.setUri("tcp4://127.0.0.1:6363");
 
   Block encodedParameters(parameters.wireEncode());
 
@@ -833,9 +833,9 @@
 
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 401, "Signature required");
-  };
+  });
 
   getManager().onFaceRequest(*command);
 
@@ -845,7 +845,7 @@
 BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand, UnauthorizedCommandFixture<FaceManagerFixture>)
 {
   ControlParameters parameters;
-  parameters.setUri("tcp://127.0.0.1");
+  parameters.setUri("tcp4://127.0.0.1:6363");
 
   Block encodedParameters(parameters.wireEncode());
 
@@ -856,9 +856,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 403, "Unauthorized command");
-  };
+  });
 
   getManager().onFaceRequest(*command);
 
@@ -894,9 +894,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 501, "Unsupported command");
-  };
+  });
 
   getManager().onFaceRequest(*command);
 
@@ -910,7 +910,7 @@
 public:
 
   ValidatedFaceRequestFixture()
-    : FaceManager(TestFaceTableFixture::m_faceTable, TestFaceManagerCommon::m_face, m_testKeyChain),
+    : FaceManager(TestFaceTableFixture::m_faceTable, getFace(), m_testKeyChain),
       m_createFaceFired(false),
       m_destroyFaceFired(false)
   {
@@ -964,9 +964,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  };
+  });
 
   onValidatedFaceRequest(command);
 
@@ -977,7 +977,7 @@
                         AuthorizedCommandFixture<ValidatedFaceRequestFixture>)
 {
   ControlParameters parameters;
-  parameters.setUri("tcp://127.0.0.1");
+  parameters.setUri("tcp4://127.0.0.1:6363");
 
   Block encodedParameters(parameters.wireEncode());
 
@@ -996,7 +996,7 @@
                         AuthorizedCommandFixture<ValidatedFaceRequestFixture>)
 {
   ControlParameters parameters;
-  parameters.setUri("tcp://127.0.0.1");
+  parameters.setUri("tcp4://127.0.0.1:6363");
 
   Block encodedParameters(parameters.wireEncode());
 
@@ -1035,7 +1035,7 @@
 {
 public:
   LocalControlFixture()
-    : FaceManager(FaceTableFixture::m_faceTable, TestFaceManagerCommon::m_face, m_testKeyChain)
+    : FaceManager(FaceTableFixture::m_faceTable, getFace(), m_testKeyChain)
   {
   }
 };
@@ -1060,11 +1060,11 @@
 
   generateCommand(*enableCommand);
 
-  TestFaceManagerCommon::m_face->onReceiveData +=
-  [this, enableCommand, encodedParameters] (const Data& response) {
-    this->validateControlResponse(response, enableCommand->getName(),
-                                  200, "Success", encodedParameters);
-  };
+  signal::Connection conn = getFace()->onReceiveData.connect(
+      [this, enableCommand, encodedParameters] (const Data& response) {
+        this->validateControlResponse(response, enableCommand->getName(),
+                                      200, "Success", encodedParameters);
+      });
 
   onValidatedFaceRequest(enableCommand);
 
@@ -1072,7 +1072,7 @@
   BOOST_REQUIRE(dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
   BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
 
-  TestFaceManagerCommon::m_face->onReceiveData.clear();
+  conn.disconnect();
   resetCallbackFired();
 
   Name disable("/localhost/nfd/faces/disable-local-control");
@@ -1083,11 +1083,11 @@
 
   generateCommand(*disableCommand);
 
-  TestFaceManagerCommon::m_face->onReceiveData +=
-  [this, disableCommand, encodedParameters] (const Data& response) {
-    this->validateControlResponse(response, disableCommand->getName(),
-                                  200, "Success", encodedParameters);
-  };
+  getFace()->onReceiveData.connect(
+      [this, disableCommand, encodedParameters] (const Data& response) {
+        this->validateControlResponse(response, disableCommand->getName(),
+                                      200, "Success", encodedParameters);
+      });
 
   onValidatedFaceRequest(disableCommand);
 
@@ -1116,9 +1116,10 @@
 
   generateCommand(*enableCommand);
 
-  TestFaceManagerCommon::m_face->onReceiveData += [this, enableCommand] (const Data& response) {
-    this->validateControlResponse(response, enableCommand->getName(), 410, "Face not found");
-  };
+  signal::Connection conn = getFace()->onReceiveData.connect(
+      [this, enableCommand] (const Data& response) {
+        this->validateControlResponse(response, enableCommand->getName(), 410, "Face not found");
+      });
 
   onValidatedFaceRequest(enableCommand);
 
@@ -1126,7 +1127,7 @@
   BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
   BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
 
-  TestFaceManagerCommon::m_face->onReceiveData.clear();
+  conn.disconnect();
   resetCallbackFired();
 
   Name disable("/localhost/nfd/faces/disable-local-control");
@@ -1137,9 +1138,10 @@
 
   generateCommand(*disableCommand);
 
-  TestFaceManagerCommon::m_face->onReceiveData += [this, disableCommand] (const Data& response) {
-    this->validateControlResponse(response, disableCommand->getName(), 410, "Face not found");
-  };
+  getFace()->onReceiveData.connect(
+      [this, disableCommand] (const Data& response) {
+        this->validateControlResponse(response, disableCommand->getName(), 410, "Face not found");
+      });
 
   onValidatedFaceRequest(disableCommand);
 
@@ -1167,9 +1169,11 @@
 
   generateCommand(*enableCommand);
 
-  TestFaceManagerCommon::m_face->onReceiveData += [this, enableCommand] (const Data& response) {
-    this->validateControlResponse(response, enableCommand->getName(), 400, "Malformed command");
-  };
+  signal::Connection conn = getFace()->onReceiveData.connect(
+      [this, enableCommand] (const Data& response) {
+        this->validateControlResponse(response, enableCommand->getName(),
+                                      400, "Malformed command");
+      });
 
   onValidatedFaceRequest(enableCommand);
 
@@ -1177,7 +1181,7 @@
   BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
   BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
 
-  TestFaceManagerCommon::m_face->onReceiveData.clear();
+  conn.disconnect();
   resetCallbackFired();
 
   Name disable("/localhost/nfd/faces/disable-local-control");
@@ -1188,9 +1192,11 @@
 
   generateCommand(*disableCommand);
 
-  TestFaceManagerCommon::m_face->onReceiveData += [this, disableCommand] (const Data& response) {
-    this->validateControlResponse(response, disableCommand->getName(), 400, "Malformed command");
-  };
+  getFace()->onReceiveData.connect(
+      [this, disableCommand] (const Data& response) {
+        this->validateControlResponse(response, disableCommand->getName(),
+                                      400, "Malformed command");
+      });
 
   onValidatedFaceRequest(disableCommand);
 
@@ -1219,15 +1225,17 @@
 
   generateCommand(*enableCommand);
 
-  TestFaceManagerCommon::m_face->onReceiveData += [this, enableCommand] (const Data& response) {
-    this->validateControlResponse(response, enableCommand->getName(), 412, "Face is non-local");
-  };
+  signal::Connection conn = getFace()->onReceiveData.connect(
+      [this, enableCommand] (const Data& response) {
+        this->validateControlResponse(response, enableCommand->getName(),
+                                      412, "Face is non-local");
+      });
 
   onValidatedFaceRequest(enableCommand);
 
   BOOST_REQUIRE(didCallbackFire());
 
-  TestFaceManagerCommon::m_face->onReceiveData.clear();
+  conn.disconnect();
   resetCallbackFired();
 
   Name disable("/localhost/nfd/faces/disable-local-control");
@@ -1238,9 +1246,11 @@
 
   generateCommand(*disableCommand);
 
-  TestFaceManagerCommon::m_face->onReceiveData += [this, disableCommand] (const Data& response) {
-    this->validateControlResponse(response, disableCommand->getName(), 412, "Face is non-local");
-  };
+  getFace()->onReceiveData.connect(
+      [this, disableCommand] (const Data& response) {
+        this->validateControlResponse(response, disableCommand->getName(),
+                                      412, "Face is non-local");
+      });
 
   onValidatedFaceRequest(disableCommand);
 
@@ -1267,11 +1277,11 @@
 
   generateCommand(*enableCommand);
 
-  TestFaceManagerCommon::m_face->onReceiveData +=
-  [this, enableCommand, encodedParameters] (const Data& response) {
-    this->validateControlResponse(response, enableCommand->getName(),
-                                  200, "Success", encodedParameters);
-  };
+  signal::Connection conn = getFace()->onReceiveData.connect(
+      [this, enableCommand, encodedParameters] (const Data& response) {
+        this->validateControlResponse(response, enableCommand->getName(),
+                                      200, "Success", encodedParameters);
+      });
 
   onValidatedFaceRequest(enableCommand);
 
@@ -1280,7 +1290,7 @@
   BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
 
 
-  TestFaceManagerCommon::m_face->onReceiveData.clear();
+  conn.disconnect();
   resetCallbackFired();
 
   Name disable("/localhost/nfd/faces/disable-local-control");
@@ -1291,11 +1301,11 @@
 
   generateCommand(*disableCommand);
 
-  TestFaceManagerCommon::m_face->onReceiveData +=
-  [this, disableCommand, encodedParameters] (const Data& response) {
-    this->validateControlResponse(response, disableCommand->getName(),
-                                  200, "Success", encodedParameters);
-  };
+  getFace()->onReceiveData.connect(
+      [this, disableCommand, encodedParameters] (const Data& response) {
+        this->validateControlResponse(response, disableCommand->getName(),
+                                      200, "Success", encodedParameters);
+      });
 
   onValidatedFaceRequest(disableCommand);
 
@@ -1324,9 +1334,10 @@
 
   generateCommand(*enableCommand);
 
-  TestFaceManagerCommon::m_face->onReceiveData += [this, enableCommand] (const Data& response) {
-    this->validateControlResponse(response, enableCommand->getName(), 410, "Face not found");
-  };
+  signal::Connection conn = getFace()->onReceiveData.connect(
+      [this, enableCommand] (const Data& response) {
+        this->validateControlResponse(response, enableCommand->getName(), 410, "Face not found");
+      });
 
   onValidatedFaceRequest(enableCommand);
 
@@ -1335,7 +1346,7 @@
   BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
 
 
-  TestFaceManagerCommon::m_face->onReceiveData.clear();
+  conn.disconnect();
   resetCallbackFired();
 
   Name disable("/localhost/nfd/faces/disable-local-control");
@@ -1346,9 +1357,11 @@
 
   generateCommand(*disableCommand);
 
-  TestFaceManagerCommon::m_face->onReceiveData += [this, disableCommand] (const Data& response) {
-    this->validateControlResponse(response, disableCommand->getName(), 410, "Face not found");
-  };
+  getFace()->onReceiveData.connect(
+      [this, disableCommand] (const Data& response) {
+        this->validateControlResponse(response, disableCommand->getName(),
+                                      410, "Face not found");
+      });
 
   onValidatedFaceRequest(disableCommand);
 
@@ -1377,15 +1390,17 @@
 
   generateCommand(*enableCommand);
 
-  TestFaceManagerCommon::m_face->onReceiveData += [this, enableCommand] (const Data& response) {
-    this->validateControlResponse(response, enableCommand->getName(), 412, "Face is non-local");
-  };
+  signal::Connection conn = getFace()->onReceiveData.connect(
+      [this, enableCommand] (const Data& response) {
+        this->validateControlResponse(response, enableCommand->getName(),
+                                      412, "Face is non-local");
+      });
 
   onValidatedFaceRequest(enableCommand);
 
   BOOST_REQUIRE(didCallbackFire());
 
-  TestFaceManagerCommon::m_face->onReceiveData.clear();
+  conn.disconnect();
   resetCallbackFired();
 
   Name disable("/localhost/nfd/faces/disable-local-control");
@@ -1396,9 +1411,11 @@
 
   generateCommand(*disableCommand);
 
-  TestFaceManagerCommon::m_face->onReceiveData += [this, disableCommand] (const Data& response) {
-    this->validateControlResponse(response, disableCommand->getName(), 412, "Face is non-local");
-  };
+  getFace()->onReceiveData.connect(
+      [this, disableCommand] (const Data& response) {
+        this->validateControlResponse(response, disableCommand->getName(),
+                                      412, "Face is non-local");
+      });
 
   onValidatedFaceRequest(disableCommand);
 
@@ -1412,7 +1429,7 @@
 public:
   FaceFixture()
     : FaceManager(FaceTableFixture::m_faceTable,
-                  TestFaceManagerCommon::m_face,
+                  getFace(),
                   m_testKeyChain)
     , m_receivedNotification(false)
   {
@@ -1503,7 +1520,7 @@
 BOOST_FIXTURE_TEST_CASE(CreateFaceBadUri, AuthorizedCommandFixture<FaceFixture>)
 {
   ControlParameters parameters;
-  parameters.setUri("tcp:/127.0.0.1");
+  parameters.setUri("tcp4:/127.0.0.1:6363");
 
   Block encodedParameters(parameters.wireEncode());
 
@@ -1514,9 +1531,31 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  };
+  });
+
+  createFace(*command, parameters);
+
+  BOOST_REQUIRE(didCallbackFire());
+}
+BOOST_FIXTURE_TEST_CASE(CreateFaceNoncanonicalUri, AuthorizedCommandFixture<FaceFixture>)
+{
+  ControlParameters parameters;
+  parameters.setUri("tcp://127.0.0.1");
+
+  Block encodedParameters(parameters.wireEncode());
+
+  Name commandName("/localhost/nfd/faces");
+  commandName.append("create");
+  commandName.append(encodedParameters);
+
+  shared_ptr<Interest> command(make_shared<Interest>(commandName));
+  generateCommand(*command);
+
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
+    this->validateControlResponse(response, command->getName(), 400, "Non-canonical URI");
+  });
 
   createFace(*command, parameters);
 
@@ -1536,9 +1575,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  };
+  });
 
   createFace(*command, parameters);
 
@@ -1550,7 +1589,7 @@
   ControlParameters parameters;
   // this will be an unsupported protocol because no factories have been
   // added to the face manager
-  parameters.setUri("tcp://127.0.0.1");
+  parameters.setUri("tcp4://127.0.0.1:6363");
 
   Block encodedParameters(parameters.wireEncode());
 
@@ -1561,9 +1600,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 501, "Unsupported protocol");
-  };
+  });
 
   createFace(*command, parameters);
 
@@ -1573,7 +1612,7 @@
 BOOST_FIXTURE_TEST_CASE(OnCreated, AuthorizedCommandFixture<FaceFixture>)
 {
   ControlParameters parameters;
-  parameters.setUri("tcp://127.0.0.1");
+  parameters.setUri("tcp4://127.0.0.1:6363");
 
   Block encodedParameters(parameters.wireEncode());
 
@@ -1600,11 +1639,11 @@
 
   Block encodedResultParameters(resultParameters.wireEncode());
 
-  getFace()->onReceiveData +=
-  [this, command, encodedResultParameters, expectedFaceEvent] (const Data& response) {
-    this->callbackDispatch(response,command->getName(), 200, "Success",
-                           encodedResultParameters, expectedFaceEvent);
-  };
+  getFace()->onReceiveData.connect(
+      [this, command, encodedResultParameters, expectedFaceEvent] (const Data& response) {
+        this->callbackDispatch(response,command->getName(), 200, "Success",
+                               encodedResultParameters, expectedFaceEvent);
+      });
 
   onCreated(command->getName(), parameters, dummy);
 
@@ -1615,7 +1654,7 @@
 BOOST_FIXTURE_TEST_CASE(OnConnectFailed, AuthorizedCommandFixture<FaceFixture>)
 {
   ControlParameters parameters;
-  parameters.setUri("tcp://127.0.0.1");
+  parameters.setUri("tcp4://127.0.0.1:6363");
 
   Block encodedParameters(parameters.wireEncode());
 
@@ -1626,9 +1665,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 408, "unit-test-reason");
-  };
+  });
 
   onConnectFailed(command->getName(), "unit-test-reason");
 
@@ -1662,11 +1701,11 @@
                    .setFaceScope(ndn::nfd::FACE_SCOPE_NON_LOCAL)
                    .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
 
-  getFace()->onReceiveData +=
-  [this, command, encodedParameters, expectedFaceEvent] (const Data& response) {
-    this->callbackDispatch(response,command->getName(), 200, "Success",
-                           encodedParameters, expectedFaceEvent);
-  };
+  getFace()->onReceiveData.connect(
+      [this, command, encodedParameters, expectedFaceEvent] (const Data& response) {
+        this->callbackDispatch(response,command->getName(), 200, "Success",
+                               encodedParameters, expectedFaceEvent);
+      });
 
   destroyFace(*command, parameters);
 
@@ -1715,8 +1754,8 @@
 
   ndn::EncodingBuffer buffer;
 
-  m_face->onReceiveData +=
-    bind(&FaceStatusPublisherFixture::decodeFaceStatusBlock, this, _1);
+  m_face->onReceiveData.connect(bind(&FaceStatusPublisherFixture::decodeFaceStatusBlock,
+                                     this, _1));
 
   m_manager.listFaces(*command);
   BOOST_REQUIRE(m_finished);
@@ -1755,8 +1794,8 @@
   ndn::nfd::ChannelStatus expectedEntry;
   expectedEntry.setLocalUri(DummyChannel("dummy://").getUri().toString());
 
-  m_face->onReceiveData +=
-    bind(&ChannelStatusFixture::validatePublish, this, _1, expectedEntry);
+  m_face->onReceiveData.connect(bind(&ChannelStatusFixture::validatePublish,
+                                     this, _1, expectedEntry));
 
   m_manager.listChannels(*request);
   BOOST_REQUIRE(m_callbackFired);
@@ -1806,8 +1845,8 @@
   shared_ptr<DummyLocalFace> face2(make_shared<DummyLocalFace>("tcp://", "tcp://"));
   add(face2);
 
-  m_face->onReceiveData +=
-    bind(&FaceQueryStatusPublisherFixture::decodeFaceStatusBlock, this, _1);
+  m_face->onReceiveData.connect(bind(&FaceQueryStatusPublisherFixture::decodeFaceStatusBlock,
+                                     this, _1));
 
   m_manager.listQueriedFaces(*query);
   BOOST_REQUIRE(m_finished);
@@ -1824,8 +1863,7 @@
   shared_ptr<DummyLocalFace> face(make_shared<DummyLocalFace>());
   add(face);
 
-  m_face->onReceiveData +=
-    bind(&FaceQueryStatusPublisherFixture::decodeNackBlock, this, _1);
+  m_face->onReceiveData.connect(bind(&FaceQueryStatusPublisherFixture::decodeNackBlock, this, _1));
 
   m_manager.listQueriedFaces(*query);
   BOOST_REQUIRE(m_finished);
diff --git a/tests/daemon/mgmt/face-status-publisher.cpp b/tests/daemon/mgmt/face-status-publisher.cpp
index 145fe0e..5a8bdd0 100644
--- a/tests/daemon/mgmt/face-status-publisher.cpp
+++ b/tests/daemon/mgmt/face-status-publisher.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -53,8 +53,8 @@
 
   ndn::EncodingBuffer buffer;
 
-  m_face->onReceiveData +=
-    bind(&FaceStatusPublisherFixture::decodeFaceStatusBlock, this, _1);
+  m_face->onReceiveData.connect(
+      bind(&FaceStatusPublisherFixture::decodeFaceStatusBlock, this, _1));
 
   m_publisher.publish();
   BOOST_REQUIRE(m_finished);
diff --git a/tests/daemon/mgmt/fib-enumeration-publisher.cpp b/tests/daemon/mgmt/fib-enumeration-publisher.cpp
index ed330aa..0a79fdb 100644
--- a/tests/daemon/mgmt/fib-enumeration-publisher.cpp
+++ b/tests/daemon/mgmt/fib-enumeration-publisher.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -77,8 +77,8 @@
 
   ndn::EncodingBuffer buffer;
 
-  m_face->onReceiveData +=
-    bind(&FibEnumerationPublisherFixture::decodeFibEntryBlock, this, _1);
+  m_face->onReceiveData.connect(
+      bind(&FibEnumerationPublisherFixture::decodeFibEntryBlock, this, _1));
 
   m_publisher.publish();
   BOOST_REQUIRE(m_finished);
diff --git a/tests/daemon/mgmt/fib-manager.cpp b/tests/daemon/mgmt/fib-manager.cpp
index 22bf276..82e70b9 100644
--- a/tests/daemon/mgmt/fib-manager.cpp
+++ b/tests/daemon/mgmt/fib-manager.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -235,15 +235,15 @@
   return false;
 }
 
-BOOST_AUTO_TEST_CASE(TestFireInterestFilter)
+BOOST_AUTO_TEST_CASE(ShortName)
 {
   shared_ptr<InternalFace> face = getInternalFace();
 
   shared_ptr<Interest> command = makeInterest("/localhost/nfd/fib");
 
-  face->onReceiveData += [this, command] (const Data& response) {
+  face->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  };
+  });
 
   face->sendInterest(*command);
   g_io.run_one();
@@ -259,9 +259,9 @@
 
   Interest command("/localhost/nfd/fib");
 
-  face->onReceiveData += [this, command] (const Data& response) {
+  face->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command.getName(), 400, "Malformed command");
-  };
+  });
 
   getFibManager().onFibRequest(command);
 
@@ -286,9 +286,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  face->onReceiveData += [this, command] (const Data& response) {
+  face->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 501, "Unsupported command");
-  };
+  });
 
   getFibManager().onFibRequest(*command);
 
@@ -314,9 +314,9 @@
 
   Interest command(commandName);
 
-  face->onReceiveData += [this, command] (const Data& response) {
+  face->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command.getName(), 401, "Signature required");
-  };
+  });
 
   getFibManager().onFibRequest(command);
 
@@ -344,9 +344,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  face->onReceiveData += [this, command] (const Data& response) {
+  face->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 403, "Unauthorized command");
-  };
+  });
 
   getFibManager().onFibRequest(*command);
 
@@ -367,9 +367,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  face->onReceiveData += [this, command] (const Data& response) {
+  face->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  };
+  });
 
   getFibManager().onFibRequest(*command);
 
@@ -396,9 +396,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  face->onReceiveData += [this, command] (const Data& response) {
+  face->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 410, "Face not found");
-  };
+  });
 
   getFibManager().onFibRequest(*command);
 
@@ -436,17 +436,18 @@
     command->setIncomingFaceId(1);
     generateCommand(*command);
 
-    face->onReceiveData += [this, command, encodedExpectedParameters] (const Data& response) {
-      this->validateControlResponse(response, command->getName(),
-                                    200, "Success", encodedExpectedParameters);
-    };
+    signal::Connection conn = face->onReceiveData.connect(
+        [this, command, encodedExpectedParameters] (const Data& response) {
+          this->validateControlResponse(response, command->getName(),
+                                        200, "Success", encodedExpectedParameters);
+        });
 
     getFibManager().onFibRequest(*command);
 
     BOOST_REQUIRE(didCallbackFire());
     BOOST_REQUIRE(addedNextHopWithFace(getFib(), "/hello", 0, 101, getFace(1)));
 
-    face->onReceiveData.clear();
+    conn.disconnect();
     getFib().erase("/hello");
     BOOST_REQUIRE_EQUAL(getFib().size(), 0);
   }
@@ -472,10 +473,10 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  face->onReceiveData += [this, command, encodedParameters] (const Data& response) {
+  face->onReceiveData.connect([this, command, encodedParameters] (const Data& response) {
     this->validateControlResponse(response, command->getName(),
                                   200, "Success", encodedParameters);
-  };
+  });
 
   getFibManager().onFibRequest(*command);
 
@@ -507,10 +508,10 @@
   resultParameters.setFaceId(1);
   resultParameters.setCost(0);
 
-  face->onReceiveData += [this, command, resultParameters] (const Data& response) {
+  face->onReceiveData.connect([this, command, resultParameters] (const Data& response) {
     this->validateControlResponse(response, command->getName(),
                                   200, "Success", resultParameters.wireEncode());
-  };
+  });
 
   getFibManager().onFibRequest(*command);
 
@@ -540,10 +541,11 @@
       shared_ptr<Interest> command(make_shared<Interest>(commandName));
       generateCommand(*command);
 
-      face->onReceiveData += [this, command, encodedParameters] (const Data& response) {
-        this->validateControlResponse(response, command->getName(),
-                                      200, "Success", encodedParameters);
-      };
+      signal::Connection conn = face->onReceiveData.connect(
+          [this, command, encodedParameters] (const Data& response) {
+            this->validateControlResponse(response, command->getName(),
+                                          200, "Success", encodedParameters);
+          });
 
       getFibManager().onFibRequest(*command);
       BOOST_REQUIRE(didCallbackFire());
@@ -564,7 +566,7 @@
           BOOST_FAIL("Failed to find expected fib entry");
         }
 
-      face->onReceiveData.clear();
+      conn.disconnect();
     }
 }
 
@@ -589,18 +591,19 @@
     shared_ptr<Interest> command(make_shared<Interest>(commandName));
     generateCommand(*command);
 
-    face->onReceiveData += [this, command, encodedParameters] (const Data& response) {
-      this->validateControlResponse(response, command->getName(),
-                                    200, "Success", encodedParameters);
-    };
+    signal::Connection conn = face->onReceiveData.connect(
+        [this, command, encodedParameters] (const Data& response) {
+          this->validateControlResponse(response, command->getName(),
+                                        200, "Success", encodedParameters);
+        });
 
     getFibManager().onFibRequest(*command);
 
     BOOST_REQUIRE(didCallbackFire());
-  }
 
-  resetCallbackFired();
-  face->onReceiveData.clear();
+    resetCallbackFired();
+    conn.disconnect();
+  }
 
   {
     parameters.setCost(102);
@@ -614,10 +617,10 @@
     shared_ptr<Interest> command(make_shared<Interest>(commandName));
     generateCommand(*command);
 
-    face->onReceiveData += [this, command, encodedParameters] (const Data& response) {
+    face->onReceiveData.connect([this, command, encodedParameters] (const Data& response) {
       this->validateControlResponse(response, command->getName(),
                                     200, "Success", encodedParameters);
-    };
+    });
 
     getFibManager().onFibRequest(*command);
 
@@ -661,9 +664,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  face->onReceiveData += [this, command] (const Data& response) {
+  face->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  };
+  });
 
   getFibManager().onFibRequest(*command);
 
@@ -705,17 +708,18 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   fixture->generateCommand(*command);
 
-  face->onReceiveData += [fixture, command, encodedParameters] (const Data& response) {
-    fixture->validateControlResponse(response, command->getName(),
-                                     200, "Success", encodedParameters);
-  };
+  signal::Connection conn = face->onReceiveData.connect(
+      [fixture, command, encodedParameters] (const Data& response) {
+        fixture->validateControlResponse(response, command->getName(),
+                                         200, "Success", encodedParameters);
+      });
 
   manager.onFibRequest(*command);
 
   BOOST_REQUIRE(fixture->didCallbackFire());
 
   fixture->resetCallbackFired();
-  face->onReceiveData.clear();
+  conn.disconnect();
 }
 
 BOOST_AUTO_TEST_CASE(RemoveNextHop)
@@ -767,10 +771,10 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  face->onReceiveData += [this, command, encodedParameters] (const Data& response) {
+  face->onReceiveData.connect([this, command, encodedParameters] (const Data& response) {
     this->validateControlResponse(response, command->getName(),
                                   200, "Success", encodedParameters);
-  };
+  });
 
   getFibManager().onFibRequest(*command);
 
@@ -796,10 +800,10 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  face->onReceiveData += [this, command, encodedParameters] (const Data& response) {
+  face->onReceiveData.connect([this, command, encodedParameters] (const Data& response) {
     this->validateControlResponse(response, command->getName(),
                                   200, "Success", encodedParameters);
-  };
+  });
 
   getFibManager().onFibRequest(*command);
 
@@ -824,9 +828,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  face->onReceiveData += [this, command] (const Data& response) {
+  face->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  };
+  });
 
   getFibManager().onFibRequest(*command);
 
@@ -859,16 +863,17 @@
     resultParameters.setFaceId(1);
     resultParameters.setName("/hello");
 
-    face->onReceiveData += [this, command, resultParameters] (const Data& response) {
-      this->validateControlResponse(response, command->getName(),
-                                    200, "Success", resultParameters.wireEncode());
-    };
+    signal::Connection conn = face->onReceiveData.connect(
+        [this, command, resultParameters] (const Data& response) {
+          this->validateControlResponse(response, command->getName(),
+                                        200, "Success", resultParameters.wireEncode());
+        });
 
     getFibManager().onFibRequest(*command);
 
     BOOST_REQUIRE(didCallbackFire());
 
-    face->onReceiveData.clear();
+    conn.disconnect();
   }
 }
 
@@ -905,8 +910,8 @@
 
   ndn::EncodingBuffer buffer;
 
-  m_face->onReceiveData +=
-    bind(&FibEnumerationPublisherFixture::decodeFibEntryBlock, this, _1);
+  m_face->onReceiveData.connect(bind(&FibEnumerationPublisherFixture::decodeFibEntryBlock,
+                                     this, _1));
 
   shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/fib/list"));
 
diff --git a/tests/daemon/mgmt/internal-face.cpp b/tests/daemon/mgmt/internal-face.cpp
index 2215c0e..29b602c 100644
--- a/tests/daemon/mgmt/internal-face.cpp
+++ b/tests/daemon/mgmt/internal-face.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -31,8 +31,6 @@
 namespace nfd {
 namespace tests {
 
-NFD_LOG_INIT("InternalFaceTest");
-
 class InternalFaceFixture : protected BaseFixture
 {
 public:
@@ -112,7 +110,7 @@
 
   bool didPutData = false;
   Name dataName("/hello");
-  face->onReceiveData += bind(&validatePutData, ref(didPutData), dataName, _1);
+  face->onReceiveData.connect(bind(&validatePutData, ref(didPutData), dataName, _1));
 
   Data testData(dataName);
   m_keyChain.sign(testData);
diff --git a/tests/daemon/mgmt/manager-base.cpp b/tests/daemon/mgmt/manager-base.cpp
index 0aa7077..1b78175 100644
--- a/tests/daemon/mgmt/manager-base.cpp
+++ b/tests/daemon/mgmt/manager-base.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -166,9 +166,9 @@
   ndn::nfd::ControlParameters parameters;
   parameters.setName("/test/body");
 
-  getInternalFace()->onReceiveData += [this, parameters] (const Data& response) {
+  getInternalFace()->onReceiveData.connect([this, parameters] (const Data& response) {
     this->validateControlResponse(response, "/response", 100, "test", parameters.wireEncode());
-  };
+  });
 
   testSendResponse("/response", 100, "test", parameters.wireEncode());
   BOOST_REQUIRE(didCallbackFire());
@@ -177,9 +177,9 @@
 
 BOOST_AUTO_TEST_CASE(SendResponse3Arg)
 {
-  getInternalFace()->onReceiveData += [this] (const Data& response) {
+  getInternalFace()->onReceiveData.connect([this] (const Data& response) {
     this->validateControlResponse(response, "/response", 100, "test");
-  };
+  });
 
   testSendResponse("/response", 100, "test");
   BOOST_REQUIRE(didCallbackFire());
@@ -187,9 +187,9 @@
 
 BOOST_AUTO_TEST_CASE(SendResponse2Arg)
 {
-  getInternalFace()->onReceiveData += [this] (const Data& response) {
+  getInternalFace()->onReceiveData.connect([this] (const Data& response) {
     this->validateControlResponse(response, "/response", 100, "test");
-  };
+  });
 
   ControlResponse response(100, "test");
 
diff --git a/tests/daemon/mgmt/status-server.cpp b/tests/daemon/mgmt/status-server.cpp
index bda69a4..4cf7c13 100644
--- a/tests/daemon/mgmt/status-server.cpp
+++ b/tests/daemon/mgmt/status-server.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -50,7 +50,7 @@
   time::system_clock::TimePoint t1 = time::system_clock::now();
   Forwarder forwarder;
   shared_ptr<InternalFace> internalFace = make_shared<InternalFace>();
-  internalFace->onReceiveData += &interceptResponse;
+  internalFace->onReceiveData.connect(&interceptResponse);
   ndn::KeyChain keyChain;
   StatusServer statusServer(internalFace, ref(forwarder), keyChain);
   time::system_clock::TimePoint t2 = time::system_clock::now();
diff --git a/tests/daemon/mgmt/strategy-choice-manager.cpp b/tests/daemon/mgmt/strategy-choice-manager.cpp
index 2ae23c5..d0c425d 100644
--- a/tests/daemon/mgmt/strategy-choice-manager.cpp
+++ b/tests/daemon/mgmt/strategy-choice-manager.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -221,13 +221,13 @@
 BOOST_FIXTURE_TEST_SUITE(MgmtStrategyChoiceManager,
                          AuthorizedCommandFixture<AllStrategiesFixture>)
 
-BOOST_FIXTURE_TEST_CASE(TestFireInterestFilter, AllStrategiesFixture)
+BOOST_FIXTURE_TEST_CASE(ShortName, AllStrategiesFixture)
 {
   shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/strategy-choice"));
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  };
+  });
 
   getFace()->sendInterest(*command);
   g_io.run_one();
@@ -239,9 +239,9 @@
 {
   shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/strategy-choice"));
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  };
+  });
 
   getManager().onStrategyChoiceRequest(*command);
 
@@ -262,9 +262,9 @@
 
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 401, "Signature required");
-  };
+  });
 
   getManager().onStrategyChoiceRequest(*command);
 
@@ -287,9 +287,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 403, "Unauthorized command");
-  };
+  });
 
   getManager().onStrategyChoiceRequest(*command);
 
@@ -311,9 +311,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 501, "Unsupported command");
-  };
+  });
 
   getManager().onValidatedStrategyChoiceRequest(command);
 
@@ -329,9 +329,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  };
+  });
 
   getManager().onValidatedStrategyChoiceRequest(command);
 
@@ -352,10 +352,10 @@
 
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
 
-  getFace()->onReceiveData += [this, command, encodedParameters] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command, encodedParameters] (const Data& response) {
     this->validateControlResponse(response, command->getName(),
                                   200, "Success", encodedParameters);
-  };
+  });
 
   getManager().onValidatedStrategyChoiceRequest(command);
 
@@ -378,10 +378,10 @@
 
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
 
-  getFace()->onReceiveData += [this, command, encodedParameters] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command, encodedParameters] (const Data& response) {
     this->validateControlResponse(response, command->getName(),
                                   200, "Success", encodedParameters);
-  };
+  });
 
   getManager().onValidatedStrategyChoiceRequest(command);
 
@@ -408,10 +408,10 @@
   responseParameters.setName("/test");
   responseParameters.setStrategy("/localhost/nfd/strategy/test-strategy-c/%FD%02");
 
-  getFace()->onReceiveData += [this, command, responseParameters] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command, responseParameters] (const Data& response) {
     this->validateControlResponse(response, command->getName(),
                                   200, "Success", responseParameters.wireEncode());
-  };
+  });
 
   getManager().onValidatedStrategyChoiceRequest(command);
 
@@ -433,9 +433,9 @@
 
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  };
+  });
 
   getManager().onValidatedStrategyChoiceRequest(command);
 
@@ -455,9 +455,9 @@
 
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  };
+  });
 
   getManager().onValidatedStrategyChoiceRequest(command);
 
@@ -481,9 +481,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 504, "Unsupported strategy");
-  };
+  });
 
   getManager().onValidatedStrategyChoiceRequest(command);
 
@@ -555,10 +555,10 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  getFace()->onReceiveData += [this, command, encodedParameters] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command, encodedParameters] (const Data& response) {
     this->validateControlResponse(response, command->getName(),
                                   200, "Success", encodedParameters);
-  };
+  });
 
   getManager().onValidatedStrategyChoiceRequest(command);
 
@@ -582,10 +582,10 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(),
                                   403, "Cannot unset root prefix strategy");
-  };
+  });
 
   getManager().onValidatedStrategyChoiceRequest(command);
 
@@ -612,9 +612,9 @@
   shared_ptr<Interest> command(make_shared<Interest>(commandName));
   generateCommand(*command);
 
-  getFace()->onReceiveData += [this, command] (const Data& response) {
+  getFace()->onReceiveData.connect([this, command] (const Data& response) {
     this->validateControlResponse(response, command->getName(), 400, "Malformed command");
-  };
+  });
 
   getManager().onValidatedStrategyChoiceRequest(command);
 
@@ -633,8 +633,8 @@
   expectedChoice.setStrategy("/localhost/nfd/strategy/test-strategy-a");
   expectedChoice.setName("/");
 
-  getFace()->onReceiveData +=
-    bind(&StrategyChoiceManagerFixture::validateList, this, _1, expectedChoice);
+  getFace()->onReceiveData.connect(bind(&StrategyChoiceManagerFixture::validateList,
+                                        this, _1, expectedChoice));
 
   m_manager.onStrategyChoiceRequest(*command);
   BOOST_REQUIRE(didCallbackFire());
diff --git a/tests/daemon/mgmt/strategy-choice-publisher.cpp b/tests/daemon/mgmt/strategy-choice-publisher.cpp
index e85bc4a..8f98cb5 100644
--- a/tests/daemon/mgmt/strategy-choice-publisher.cpp
+++ b/tests/daemon/mgmt/strategy-choice-publisher.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -153,8 +153,7 @@
   m_expectedEntries["/test/a"] = expectedEntryA;
   m_expectedEntries["/test/b"] = expectedEntryB;
 
-  m_face->onReceiveData +=
-    bind(&StrategyChoicePublisherFixture::validatePublish, this, _1);
+  m_face->onReceiveData.connect(bind(&StrategyChoicePublisherFixture::validatePublish, this, _1));
 
   m_publisher.publish();
   BOOST_REQUIRE(m_finished);
diff --git a/tests/daemon/table/dead-nonce-list.cpp b/tests/daemon/table/dead-nonce-list.cpp
index 970d537..e795d35 100644
--- a/tests/daemon/table/dead-nonce-list.cpp
+++ b/tests/daemon/table/dead-nonce-list.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -26,7 +26,6 @@
 #include "table/dead-nonce-list.hpp"
 
 #include "tests/test-common.hpp"
-#include "tests/limited-io.hpp"
 
 namespace nfd {
 namespace tests {
@@ -57,7 +56,7 @@
 }
 
 /// A Fixture that periodically inserts Nonces
-class PeriodicalInsertionFixture : public BaseFixture
+class PeriodicalInsertionFixture : public UnitTestTimeFixture
 {
 protected:
   PeriodicalInsertionFixture()
@@ -65,15 +64,11 @@
     , name("ndn:/N")
     , addNonceBatch(0)
     , addNonceInterval(LIFETIME / DeadNonceList::EXPECTED_MARK_COUNT)
+    , timeUnit(addNonceInterval / 2)
   {
     this->addNonce();
   }
 
-  ~PeriodicalInsertionFixture()
-  {
-    scheduler::cancel(addNonceEvent);
-  }
-
   void
   setRate(size_t nNoncesPerLifetime)
   {
@@ -87,13 +82,20 @@
       dnl.add(name, ++lastNonce);
     }
 
-    scheduler::cancel(addNonceEvent); // avoid double schedules
     if (addNonceInterval > time::nanoseconds::zero()) {
       addNonceEvent = scheduler::schedule(addNonceInterval,
                                           bind(&PeriodicalInsertionFixture::addNonce, this));
     }
   }
 
+  /** \brief advance clocks by LIFETIME*t
+   */
+  void
+  advanceClocksByLifetime(float t)
+  {
+    this->advanceClocks(timeUnit, time::duration_cast<time::nanoseconds>(LIFETIME * t));
+  }
+
 protected:
   static const time::nanoseconds LIFETIME;
   DeadNonceList dnl;
@@ -101,7 +103,8 @@
   uint32_t lastNonce;
   size_t addNonceBatch;
   time::nanoseconds addNonceInterval;
-  scheduler::EventId addNonceEvent;
+  time::nanoseconds timeUnit;
+  scheduler::ScopedEventId addNonceEvent;
 };
 const time::nanoseconds PeriodicalInsertionFixture::LIFETIME = time::milliseconds(200);
 
@@ -109,11 +112,9 @@
 {
   BOOST_CHECK_EQUAL(dnl.getLifetime(), LIFETIME);
 
-  LimitedIo limitedIo;
-
   const int RATE = DeadNonceList::INITIAL_CAPACITY / 2;
   this->setRate(RATE);
-  limitedIo.defer(LIFETIME * 10);
+  this->advanceClocksByLifetime(10.0);
 
   Name nameC("ndn:/C");
   const uint32_t nonceC = 0x25390656;
@@ -121,22 +122,20 @@
   dnl.add(nameC, nonceC);
   BOOST_CHECK_EQUAL(dnl.has(nameC, nonceC), true);
 
-  limitedIo.defer(LIFETIME / 2); // -50%, entry should exist
+  this->advanceClocksByLifetime(0.5); // -50%, entry should exist
   BOOST_CHECK_EQUAL(dnl.has(nameC, nonceC), true);
 
-  limitedIo.defer(LIFETIME); // +50%, entry should be gone
+  this->advanceClocksByLifetime(1.0); // +50%, entry should be gone
   BOOST_CHECK_EQUAL(dnl.has(nameC, nonceC), false);
 }
 
 BOOST_FIXTURE_TEST_CASE(CapacityDown, PeriodicalInsertionFixture)
 {
-  LimitedIo limitedIo;
-
   ssize_t cap0 = dnl.m_capacity;
 
   const int RATE = DeadNonceList::INITIAL_CAPACITY / 3;
   this->setRate(RATE);
-  limitedIo.defer(LIFETIME * 10);
+  this->advanceClocksByLifetime(10.0);
 
   ssize_t cap1 = dnl.m_capacity;
   BOOST_CHECK_LT(std::abs(cap1 - RATE), std::abs(cap0 - RATE));
@@ -144,13 +143,11 @@
 
 BOOST_FIXTURE_TEST_CASE(CapacityUp, PeriodicalInsertionFixture)
 {
-  LimitedIo limitedIo;
-
   ssize_t cap0 = dnl.m_capacity;
 
   const int RATE = DeadNonceList::INITIAL_CAPACITY * 3;
   this->setRate(RATE);
-  limitedIo.defer(LIFETIME * 10);
+  this->advanceClocksByLifetime(10.0);
 
   ssize_t cap1 = dnl.m_capacity;
   BOOST_CHECK_LT(std::abs(cap1 - RATE), std::abs(cap0 - RATE));
diff --git a/tests/daemon/table/measurements-accessor.cpp b/tests/daemon/table/measurements-accessor.cpp
index 8668368..98a39c3 100644
--- a/tests/daemon/table/measurements-accessor.cpp
+++ b/tests/daemon/table/measurements-accessor.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -32,7 +32,7 @@
 namespace nfd {
 namespace tests {
 
-BOOST_FIXTURE_TEST_SUITE(TableMeasurementsAccessor, BaseFixture)
+using measurements::Entry;
 
 class MeasurementsAccessorTestStrategy : public DummyStrategy
 {
@@ -49,49 +49,109 @@
 
 public: // accessors
   MeasurementsAccessor&
-  getMeasurements_accessor()
+  getMeasurementsAccessor()
   {
     return this->getMeasurements();
   }
 };
 
-BOOST_AUTO_TEST_CASE(Access)
+class MeasurementsAccessorFixture : public BaseFixture
 {
+protected:
+  MeasurementsAccessorFixture()
+    : strategy1(make_shared<MeasurementsAccessorTestStrategy>(ref(forwarder), "ndn:/strategy1"))
+    , strategy2(make_shared<MeasurementsAccessorTestStrategy>(ref(forwarder), "ndn:/strategy2"))
+    , measurements(forwarder.getMeasurements())
+    , accessor1(strategy1->getMeasurementsAccessor())
+    , accessor2(strategy2->getMeasurementsAccessor())
+  {
+    StrategyChoice& strategyChoice = forwarder.getStrategyChoice();
+    strategyChoice.install(strategy1);
+    strategyChoice.install(strategy2);
+    strategyChoice.insert("/"   , strategy1->getName());
+    strategyChoice.insert("/A"  , strategy2->getName());
+    strategyChoice.insert("/A/B", strategy1->getName());
+  }
+
+protected:
   Forwarder forwarder;
+  shared_ptr<MeasurementsAccessorTestStrategy> strategy1;
+  shared_ptr<MeasurementsAccessorTestStrategy> strategy2;
+  Measurements& measurements;
+  MeasurementsAccessor& accessor1;
+  MeasurementsAccessor& accessor2;
+};
 
-  auto strategy1 = make_shared<MeasurementsAccessorTestStrategy>(ref(forwarder), "ndn:/strategy1");
-  auto strategy2 = make_shared<MeasurementsAccessorTestStrategy>(ref(forwarder), "ndn:/strategy2");
+BOOST_FIXTURE_TEST_SUITE(TableMeasurementsAccessor, MeasurementsAccessorFixture)
 
-  Name nameRoot("ndn:/");
-  Name nameA   ("ndn:/A");
-  Name nameAB  ("ndn:/A/B");
-  Name nameABC ("ndn:/A/B/C");
-  Name nameAD  ("ndn:/A/D");
+BOOST_AUTO_TEST_CASE(Get)
+{
+  BOOST_CHECK(accessor1.get("/"     ) != nullptr);
+  BOOST_CHECK(accessor1.get("/A"    ) == nullptr);
+  BOOST_CHECK(accessor1.get("/A/B"  ) != nullptr);
+  BOOST_CHECK(accessor1.get("/A/B/C") != nullptr);
+  BOOST_CHECK(accessor1.get("/A/D"  ) == nullptr);
 
-  StrategyChoice& strategyChoice = forwarder.getStrategyChoice();
-  strategyChoice.install(strategy1);
-  strategyChoice.install(strategy2);
-  strategyChoice.insert(nameRoot, strategy1->getName());
-  strategyChoice.insert(nameA   , strategy2->getName());
-  strategyChoice.insert(nameAB  , strategy1->getName());
+  BOOST_CHECK(accessor2.get("/"     ) == nullptr);
+  BOOST_CHECK(accessor2.get("/A"    ) != nullptr);
+  BOOST_CHECK(accessor2.get("/A/B"  ) == nullptr);
+  BOOST_CHECK(accessor2.get("/A/B/C") == nullptr);
+  BOOST_CHECK(accessor2.get("/A/D"  ) != nullptr);
+}
 
-  MeasurementsAccessor& accessor1 = strategy1->getMeasurements_accessor();
-  MeasurementsAccessor& accessor2 = strategy2->getMeasurements_accessor();
+BOOST_AUTO_TEST_CASE(GetParent)
+{
+  shared_ptr<Entry> entryRoot = measurements.get("/");
+  BOOST_CHECK(accessor1.getParent(*entryRoot) == nullptr);
+  BOOST_CHECK(accessor2.getParent(*entryRoot) == nullptr);
 
-  BOOST_CHECK_EQUAL(static_cast<bool>(accessor1.get(nameRoot)), true);
-  BOOST_CHECK_EQUAL(static_cast<bool>(accessor1.get(nameA   )), false);
-  BOOST_CHECK_EQUAL(static_cast<bool>(accessor1.get(nameAB  )), true);
-  BOOST_CHECK_EQUAL(static_cast<bool>(accessor1.get(nameABC )), true);
-  BOOST_CHECK_EQUAL(static_cast<bool>(accessor1.get(nameAD  )), false);
+  shared_ptr<Entry> entryABC = measurements.get("/A/B/C");
+  BOOST_CHECK(accessor1.getParent(*entryABC) != nullptr);
+  BOOST_CHECK(accessor2.getParent(*entryABC) == nullptr);
 
-  shared_ptr<measurements::Entry> entryRoot = forwarder.getMeasurements().get(nameRoot);
-  BOOST_CHECK_NO_THROW(accessor1.getParent(*entryRoot));
+  shared_ptr<Entry> entryAB = measurements.get("/A/B");
+  BOOST_CHECK(accessor1.getParent(*entryAB) == nullptr);
+  // whether accessor2.getParent(*entryAB) can return an Entry is undefined,
+  // because strategy2 shouldn't obtain entryAB in the first place
+}
 
-  BOOST_CHECK_EQUAL(static_cast<bool>(accessor2.get(nameRoot)), false);
-  BOOST_CHECK_EQUAL(static_cast<bool>(accessor2.get(nameA   )), true);
-  BOOST_CHECK_EQUAL(static_cast<bool>(accessor2.get(nameAB  )), false);
-  BOOST_CHECK_EQUAL(static_cast<bool>(accessor2.get(nameABC )), false);
-  BOOST_CHECK_EQUAL(static_cast<bool>(accessor2.get(nameAD  )), true);
+BOOST_AUTO_TEST_CASE(FindLongestPrefixMatch)
+{
+  shared_ptr<Interest> interest = makeInterest("/A/B/C");
+  shared_ptr<pit::Entry> pitEntry = forwarder.getPit().insert(*interest).first;
+
+  measurements.get("/");
+  BOOST_CHECK(accessor1.findLongestPrefixMatch("/A/B") != nullptr);
+  BOOST_CHECK(accessor1.findLongestPrefixMatch(*pitEntry) != nullptr);
+
+  measurements.get("/A");
+  BOOST_CHECK(accessor1.findLongestPrefixMatch("/A/B") == nullptr);
+  BOOST_CHECK(accessor1.findLongestPrefixMatch(*pitEntry) == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(FindExactMatch)
+{
+  measurements.get("/");
+  measurements.get("/A");
+  measurements.get("/A/B");
+  measurements.get("/A/B/C");
+  measurements.get("/A/D");
+
+  BOOST_CHECK(accessor1.findExactMatch("/"     ) != nullptr);
+  BOOST_CHECK(accessor1.findExactMatch("/A"    ) == nullptr);
+  BOOST_CHECK(accessor1.findExactMatch("/A/B"  ) != nullptr);
+  BOOST_CHECK(accessor1.findExactMatch("/A/B/C") != nullptr);
+  BOOST_CHECK(accessor1.findExactMatch("/A/D"  ) == nullptr);
+  BOOST_CHECK(accessor1.findExactMatch("/A/E"  ) == nullptr);
+  BOOST_CHECK(accessor1.findExactMatch("/F"    ) == nullptr);
+
+  BOOST_CHECK(accessor2.findExactMatch("/"     ) == nullptr);
+  BOOST_CHECK(accessor2.findExactMatch("/A"    ) != nullptr);
+  BOOST_CHECK(accessor2.findExactMatch("/A/B"  ) == nullptr);
+  BOOST_CHECK(accessor2.findExactMatch("/A/B/C") == nullptr);
+  BOOST_CHECK(accessor2.findExactMatch("/A/D"  ) != nullptr);
+  BOOST_CHECK(accessor2.findExactMatch("/A/E"  ) == nullptr);
+  BOOST_CHECK(accessor2.findExactMatch("/F"    ) == nullptr);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/daemon/table/measurements.cpp b/tests/daemon/table/measurements.cpp
index 63254bd..81fb275 100644
--- a/tests/daemon/table/measurements.cpp
+++ b/tests/daemon/table/measurements.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -24,9 +24,9 @@
  */
 
 #include "table/measurements.hpp"
+#include "table/pit.hpp"
 
 #include "tests/test-common.hpp"
-#include "tests/limited-io.hpp"
 
 namespace nfd {
 namespace tests {
@@ -57,6 +57,76 @@
   BOOST_CHECK_EQUAL(entry0, entry0c);
 }
 
+class DummyStrategyInfo1 : public fw::StrategyInfo
+{
+public:
+  static constexpr int
+  getTypeId()
+  {
+    return 21;
+  }
+};
+
+class DummyStrategyInfo2 : public fw::StrategyInfo
+{
+public:
+  static constexpr int
+  getTypeId()
+  {
+    return 22;
+  }
+};
+
+BOOST_AUTO_TEST_CASE(FindLongestPrefixMatch)
+{
+  NameTree nameTree;
+  Measurements measurements(nameTree);
+
+  measurements.get("/A");
+  measurements.get("/A/B/C")->getOrCreateStrategyInfo<DummyStrategyInfo1>();
+  measurements.get("/A/B/C/D");
+
+  shared_ptr<measurements::Entry> found1 = measurements.findLongestPrefixMatch("/A/B/C/D/E");
+  BOOST_REQUIRE(found1 != nullptr);
+  BOOST_CHECK_EQUAL(found1->getName(), "/A/B/C/D");
+
+  shared_ptr<measurements::Entry> found2 = measurements.findLongestPrefixMatch("/A/B/C/D/E",
+      measurements::EntryWithStrategyInfo<DummyStrategyInfo1>());
+  BOOST_REQUIRE(found2 != nullptr);
+  BOOST_CHECK_EQUAL(found2->getName(), "/A/B/C");
+
+  shared_ptr<measurements::Entry> found3 = measurements.findLongestPrefixMatch("/A/B/C/D/E",
+      measurements::EntryWithStrategyInfo<DummyStrategyInfo2>());
+  BOOST_CHECK(found3 == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(FindLongestPrefixMatchWithPitEntry)
+{
+  NameTree nameTree;
+  Measurements measurements(nameTree);
+  Pit pit(nameTree);
+
+  measurements.get("/A");
+  measurements.get("/A/B/C")->getOrCreateStrategyInfo<DummyStrategyInfo1>();
+  measurements.get("/A/B/C/D");
+
+  shared_ptr<Interest> interest = makeInterest("/A/B/C/D/E");
+  shared_ptr<pit::Entry> pitEntry = pit.insert(*interest).first;
+
+  shared_ptr<measurements::Entry> found1 = measurements.findLongestPrefixMatch(*pitEntry);
+  BOOST_REQUIRE(found1 != nullptr);
+  BOOST_CHECK_EQUAL(found1->getName(), "/A/B/C/D");
+
+  shared_ptr<measurements::Entry> found2 = measurements.findLongestPrefixMatch(*pitEntry,
+      measurements::EntryWithStrategyInfo<DummyStrategyInfo1>());
+  BOOST_REQUIRE(found2 != nullptr);
+  BOOST_CHECK_EQUAL(found2->getName(), "/A/B/C");
+
+  shared_ptr<measurements::Entry> found3 = measurements.findLongestPrefixMatch(*pitEntry,
+      measurements::EntryWithStrategyInfo<DummyStrategyInfo2>());
+  BOOST_CHECK(found3 == nullptr);
+}
+
 BOOST_FIXTURE_TEST_CASE(Lifetime, UnitTestTimeFixture)
 {
   NameTree nameTree;
diff --git a/tests/daemon/table/pit.cpp b/tests/daemon/table/pit.cpp
index dcf5053..5de6fe3 100644
--- a/tests/daemon/table/pit.cpp
+++ b/tests/daemon/table/pit.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -450,6 +450,48 @@
 
 }
 
+BOOST_AUTO_TEST_CASE(Iterator)
+{
+  NameTree nameTree(16);
+  Pit pit(nameTree);
+
+  shared_ptr<Interest> interestA    = makeInterest("/A");
+  shared_ptr<Interest> interestABC1 = makeInterest("/A/B/C");
+  shared_ptr<Interest> interestABC2 = makeInterest("/A/B/C");
+  interestABC2->setSelectors(ndn::Selectors().setMinSuffixComponents(10));
+  shared_ptr<Interest> interestD    = makeInterest("/D");
+
+  BOOST_CHECK_EQUAL(pit.size(), 0);
+  BOOST_CHECK(pit.begin() == pit.end());
+
+  pit.insert(*interestABC1);
+  BOOST_CHECK_EQUAL(pit.size(), 1);
+  BOOST_CHECK(pit.begin() != pit.end());
+  BOOST_CHECK(pit.begin()->getInterest() == *interestABC1);
+  BOOST_CHECK((*pit.begin()).getInterest() == *interestABC1);
+
+  auto i = pit.begin();
+  auto j = pit.begin();
+  BOOST_CHECK(++i == pit.end());
+  BOOST_CHECK(j++ == pit.begin());
+  BOOST_CHECK(j == pit.end());
+
+  pit.insert(*interestA);
+  pit.insert(*interestABC2);
+  pit.insert(*interestD);
+
+  std::set<const Interest*> expected = {&*interestA, &*interestABC1, &*interestABC2, &*interestD};
+  std::set<const Interest*> actual;
+  for (const auto& pitEntry : pit) {
+    actual.insert(&pitEntry.getInterest());
+  }
+  BOOST_CHECK(actual == expected);
+  for (auto actualIt = actual.begin(), expectedIt = expected.begin();
+       actualIt != actual.end() && expectedIt != expected.end(); ++actualIt, ++expectedIt) {
+    BOOST_CHECK_EQUAL(**actualIt, **expectedIt);
+  }
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests
diff --git a/tests/daemon/table/strategy-choice.cpp b/tests/daemon/table/strategy-choice.cpp
index 7817d51..494c76b 100644
--- a/tests/daemon/table/strategy-choice.cpp
+++ b/tests/daemon/table/strategy-choice.cpp
@@ -164,6 +164,12 @@
 
 class PStrategyInfo : public fw::StrategyInfo
 {
+public:
+  static constexpr int
+  getTypeId()
+  {
+    return 10;
+  }
 };
 
 BOOST_AUTO_TEST_CASE(ClearStrategyInfo)
diff --git a/tests/daemon/table/strategy-info-host.cpp b/tests/daemon/table/strategy-info-host.cpp
index 82d4fcc..5c0c14f 100644
--- a/tests/daemon/table/strategy-info-host.cpp
+++ b/tests/daemon/table/strategy-info-host.cpp
@@ -1,11 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014  Regents of the University of California,
- *                     Arizona Board of Regents,
- *                     Colorado State University,
- *                     University Pierre & Marie Curie, Sorbonne University,
- *                     Washington University in St. Louis,
- *                     Beijing Institute of Technology
+ * Copyright (c) 2014,  Regents of the University of California,
+ *                      Arizona Board of Regents,
+ *                      Colorado State University,
+ *                      University Pierre & Marie Curie, Sorbonne University,
+ *                      Washington University in St. Louis,
+ *                      Beijing Institute of Technology,
+ *                      The University of Memphis
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -20,7 +21,7 @@
  *
  * 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 "table/strategy-info-host.hpp"
 
@@ -29,18 +30,27 @@
 namespace nfd {
 namespace tests {
 
+using fw::StrategyInfo;
+
 static int g_DummyStrategyInfo_count = 0;
 
-class DummyStrategyInfo : public fw::StrategyInfo
+class DummyStrategyInfo : public StrategyInfo
 {
 public:
+  static constexpr int
+  getTypeId()
+  {
+    return 1;
+  }
+
   DummyStrategyInfo(int id)
     : m_id(id)
   {
     ++g_DummyStrategyInfo_count;
   }
 
-  virtual ~DummyStrategyInfo()
+  virtual
+  ~DummyStrategyInfo()
   {
     --g_DummyStrategyInfo_count;
   }
@@ -48,31 +58,82 @@
   int m_id;
 };
 
+class DummyStrategyInfo2 : public StrategyInfo
+{
+public:
+  static constexpr int
+  getTypeId()
+  {
+    return 2;
+  }
+
+  DummyStrategyInfo2(int id)
+    : m_id(id)
+  {
+  }
+
+  int m_id;
+};
+
 BOOST_FIXTURE_TEST_SUITE(TableStrategyInfoHost, BaseFixture)
 
 BOOST_AUTO_TEST_CASE(SetGetClear)
 {
   StrategyInfoHost host;
 
-  BOOST_CHECK(!static_cast<bool>(host.getStrategyInfo<DummyStrategyInfo>()));
+  BOOST_CHECK(host.getStrategyInfo<DummyStrategyInfo>() == nullptr);
 
   g_DummyStrategyInfo_count = 0;
 
   shared_ptr<DummyStrategyInfo> info = make_shared<DummyStrategyInfo>(7591);
   host.setStrategyInfo(info);
-  BOOST_REQUIRE(static_cast<bool>(host.getStrategyInfo<DummyStrategyInfo>()));
+  BOOST_REQUIRE(host.getStrategyInfo<DummyStrategyInfo>() != nullptr);
   BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo>()->m_id, 7591);
 
   info.reset(); // unlink local reference
   // host should still have a reference to info
-  BOOST_REQUIRE(static_cast<bool>(host.getStrategyInfo<DummyStrategyInfo>()));
+  BOOST_REQUIRE(host.getStrategyInfo<DummyStrategyInfo>() != nullptr);
   BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo>()->m_id, 7591);
 
   host.clearStrategyInfo();
-  BOOST_CHECK(!static_cast<bool>(host.getStrategyInfo<DummyStrategyInfo>()));
+  BOOST_CHECK(host.getStrategyInfo<DummyStrategyInfo>() == nullptr);
   BOOST_CHECK_EQUAL(g_DummyStrategyInfo_count, 0);
 }
 
+BOOST_AUTO_TEST_CASE(Create)
+{
+  StrategyInfoHost host;
+
+  host.getOrCreateStrategyInfo<DummyStrategyInfo>(3503);
+  BOOST_REQUIRE(host.getStrategyInfo<DummyStrategyInfo>() != nullptr);
+  BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo>()->m_id, 3503);
+
+  host.getOrCreateStrategyInfo<DummyStrategyInfo>(1032);
+  BOOST_REQUIRE(host.getStrategyInfo<DummyStrategyInfo>() != nullptr);
+  BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo>()->m_id, 3503);
+
+  host.setStrategyInfo<DummyStrategyInfo>(nullptr);
+  host.getOrCreateStrategyInfo<DummyStrategyInfo>(9956);
+  BOOST_REQUIRE(host.getStrategyInfo<DummyStrategyInfo>() != nullptr);
+  BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo>()->m_id, 9956);
+}
+
+BOOST_AUTO_TEST_CASE(Types)
+{
+  StrategyInfoHost host;
+
+  host.getOrCreateStrategyInfo<DummyStrategyInfo>(8063);
+  BOOST_REQUIRE(host.getStrategyInfo<DummyStrategyInfo>() != nullptr);
+  BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo>()->m_id, 8063);
+
+  host.getOrCreateStrategyInfo<DummyStrategyInfo2>(2871);
+  BOOST_REQUIRE(host.getStrategyInfo<DummyStrategyInfo2>() != nullptr);
+  BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo2>()->m_id, 2871);
+
+  BOOST_REQUIRE(host.getStrategyInfo<DummyStrategyInfo>() != nullptr);
+  BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo>()->m_id, 8063);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests
diff --git a/tests/limited-io.cpp b/tests/limited-io.cpp
index d6280c9..4bfd802 100644
--- a/tests/limited-io.cpp
+++ b/tests/limited-io.cpp
@@ -35,13 +35,21 @@
 const time::nanoseconds LimitedIo::UNLIMITED_TIME = time::nanoseconds::min();
 
 LimitedIo::LimitedIo()
-  : m_isRunning(false)
+  : m_uttf(nullptr)
+  , m_isRunning(false)
+  , m_nOpsRemaining(0)
+{
+}
+
+LimitedIo::LimitedIo(UnitTestTimeFixture* uttf)
+  : m_uttf(uttf)
+  , m_isRunning(false)
   , m_nOpsRemaining(0)
 {
 }
 
 LimitedIo::StopReason
-LimitedIo::run(int nOpsLimit, const time::nanoseconds& timeLimit)
+LimitedIo::run(int nOpsLimit, const time::nanoseconds& timeLimit, const time::nanoseconds& tick)
 {
   BOOST_ASSERT(!m_isRunning);
 
@@ -58,7 +66,15 @@
   }
 
   try {
-    getGlobalIoService().run();
+    if (m_uttf == nullptr) {
+      getGlobalIoService().run();
+    }
+    else {
+      // timeLimit is enforced by afterTimeout
+      m_uttf->advanceClocks(tick, time::nanoseconds::max());
+    }
+  }
+  catch (StopException&) {
   }
   catch (std::exception& ex) {
     m_reason = EXCEPTION;
@@ -77,7 +93,6 @@
 {
   if (!m_isRunning) {
     // Do not proceed further if .afterOp() is invoked out of .run(),
-    // because io_service.stop() without io_service.reset() would leave it unusable.
     return;
   }
 
@@ -85,6 +100,9 @@
   if (m_nOpsRemaining <= 0) {
     m_reason = EXCEED_OPS;
     getGlobalIoService().stop();
+    if (m_uttf != nullptr) {
+      throw StopException();
+    }
   }
 }
 
@@ -93,6 +111,9 @@
 {
   m_reason = EXCEED_TIME;
   getGlobalIoService().stop();
+  if (m_uttf != nullptr) {
+    throw StopException();
+  }
 }
 
 const std::exception&
diff --git a/tests/limited-io.hpp b/tests/limited-io.hpp
index 431032a..3e4be61 100644
--- a/tests/limited-io.hpp
+++ b/tests/limited-io.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -26,6 +26,7 @@
 #ifndef NFD_TESTS_LIMITED_IO_HPP
 #define NFD_TESTS_LIMITED_IO_HPP
 
+#include "test-common.hpp"
 #include "core/global-io.hpp"
 #include "core/scheduler.hpp"
 
@@ -39,6 +40,10 @@
 public:
   LimitedIo();
 
+  /** \brief construct with UnitTestTimeFixture
+   */
+  LimitedIo(UnitTestTimeFixture* uttf);
+
   /// indicates why .run returns
   enum StopReason
   {
@@ -53,15 +58,14 @@
   };
 
   /** \brief g_io.run() with operation count and/or time limit
-   *
    *  \param nOpsLimit operation count limit, pass UNLIMITED_OPS for no limit
    *  \param timeLimit time limit, pass UNLIMITED_TIME for no limit
-   *
-   *  \warning if timeLimit is used with UnitTestTimeFixture,
-   *           some other code must advance steady clock in order to exceed time limit
+   *  \param tick if this LimitedIo is constructed with UnitTestTimeFixture,
+   *              this is passed to .advanceClocks(), otherwise ignored
    */
   StopReason
-  run(int nOpsLimit, const time::nanoseconds& timeLimit);
+  run(int nOpsLimit, const time::nanoseconds& timeLimit,
+      const time::nanoseconds& tick = time::milliseconds(1));
 
   /// count an operation
   void
@@ -81,6 +85,12 @@
   }
 
 private:
+  /** \brief an exception to stop IO operation
+   */
+  class StopException
+  {
+  };
+
   void
   afterTimeout();
 
@@ -89,9 +99,10 @@
   static const time::nanoseconds UNLIMITED_TIME;
 
 private:
+  UnitTestTimeFixture* m_uttf;
   bool m_isRunning;
   int m_nOpsRemaining;
-  EventId m_timeout;
+  scheduler::EventId m_timeout;
   StopReason m_reason;
   std::exception m_lastException;
 };
diff --git a/tests/rib/fib-updates-common.hpp b/tests/rib/fib-updates-common.hpp
index c8c0af4..f3edd8f 100644
--- a/tests/rib/fib-updates-common.hpp
+++ b/tests/rib/fib-updates-common.hpp
@@ -27,10 +27,10 @@
 namespace rib {
 namespace tests {
 
-inline FaceEntry
-createFaceEntry(uint64_t faceId, uint64_t origin, uint64_t cost, uint64_t flags)
+inline Route
+createRoute(uint64_t faceId, uint64_t origin, uint64_t cost, uint64_t flags)
 {
-  FaceEntry temp;
+  Route temp;
   temp.faceId = faceId;
   temp.origin = origin;
   temp.cost = cost;
@@ -73,25 +73,25 @@
 {
 public:
   void
-  insertFaceEntry(const Name& name, uint64_t faceId, uint64_t origin, uint64_t cost, uint64_t flags)
+  insertRoute(const Name& name, uint64_t faceId, uint64_t origin, uint64_t cost, uint64_t flags)
   {
-    rib::FaceEntry faceEntry;
-    faceEntry.faceId = faceId;
-    faceEntry.origin = origin;
-    faceEntry.cost = cost;
-    faceEntry.flags = flags;
+    rib::Route route;
+    route.faceId = faceId;
+    route.origin = origin;
+    route.cost = cost;
+    route.flags = flags;
 
-    rib.insert(name, faceEntry);
+    rib.insert(name, route);
   }
 
   void
-  eraseFaceEntry(const Name& name, uint64_t faceId, uint64_t origin)
+  eraseRoute(const Name& name, uint64_t faceId, uint64_t origin)
   {
-    rib::FaceEntry faceEntry;
-    faceEntry.faceId = faceId;
-    faceEntry.origin = origin;
+    rib::Route route;
+    route.faceId = faceId;
+    route.origin = origin;
 
-    rib.erase(name, faceEntry);
+    rib.erase(name, route);
   }
 
 
diff --git a/tests/rib/fib-updates-erase-face.cpp b/tests/rib/fib-updates-erase-face.cpp
index e56c0d3..1a98847 100644
--- a/tests/rib/fib-updates-erase-face.cpp
+++ b/tests/rib/fib-updates-erase-face.cpp
@@ -38,15 +38,15 @@
 
 BOOST_AUTO_TEST_CASE(WithInheritedFace_Root)
 {
-  insertFaceEntry("/", 1, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a/b", 2, 0, 75, 0);
+  insertRoute("/", 1, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a/b", 2, 0, 75, 0);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Should generate 1 updates: 1 to remove face 1 from /
-  eraseFaceEntry("/", 1, 0);
+  eraseRoute("/", 1, 0);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 1);
@@ -59,20 +59,20 @@
 
 BOOST_AUTO_TEST_CASE(WithInheritedFace)
 {
-  insertFaceEntry("/a", 5, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 5, 255, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 20, 0);
-  insertFaceEntry("/a/b", 3, 0, 5, 0);
+  insertRoute("/a", 5, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 5, 255, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 20, 0);
+  insertRoute("/a/b", 3, 0, 5, 0);
 
   // /a should have face 5 with cost 10; /a/b should have face 3 with cost 5 and
   // face 5 with cost 10
-  eraseFaceEntry("/a", 5, 255);
+  eraseRoute("/a", 5, 255);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
-  // Should generate 2 updates: 1 to remove face 3 from /a/b and one to remove inherited face.
-  eraseFaceEntry("/a/b", 3, 0);
+  // Should generate 2 updates: 1 to remove face 3 from /a/b and one to remove inherited route
+  eraseRoute("/a/b", 3, 0);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
@@ -90,14 +90,14 @@
 
 BOOST_AUTO_TEST_CASE(MultipleFaces)
 {
-  insertFaceEntry("/a", 5, 0, 10, 0);
-  insertFaceEntry("/a", 5, 255, 5, 0);
+  insertRoute("/a", 5, 0, 10, 0);
+  insertRoute("/a", 5, 255, 5, 0);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Should generate 1 updates: 1 to update cost to 10 for /a
-  eraseFaceEntry("/a", 5, 255);
+  eraseRoute("/a", 5, 255);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 1);
@@ -111,17 +111,17 @@
 
 BOOST_AUTO_TEST_CASE(NoFlags_NoCaptureChange_NoCaptureOnRoute)
 {
-  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 10, 0);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 1, 0, 100, 0);
-  insertFaceEntry("/a", 1, 128, 50, 0);
+  insertRoute("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 10, 0);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 1, 0, 100, 0);
+  insertRoute("/a", 1, 128, 50, 0);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Should generate 1 updates: 1 to update cost for /a
-  eraseFaceEntry("/a", 1, 128);
+  eraseRoute("/a", 1, 128);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 1);
@@ -135,13 +135,13 @@
 
 BOOST_AUTO_TEST_CASE(MakeRibEmpty)
 {
-  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
-  // Should generate 1 updates: 1 to remove face from /
-  eraseFaceEntry("/", 1, 0);
+  // Should generate 1 updates: 1 to remove route from /
+  eraseRoute("/", 1, 0);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 1);
@@ -154,17 +154,17 @@
 
 BOOST_AUTO_TEST_CASE(NoFlags_NoCaptureChange_CaptureOnRoute)
 {
-  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 1, 0, 100, 0);
-  insertFaceEntry("/a", 1, 128, 50, 0);
+  insertRoute("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 1, 0, 100, 0);
+  insertRoute("/a", 1, 128, 50, 0);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
-  // Should generate 1 updates: 1 to remove face from /a
-  eraseFaceEntry("/a", 1, 128);
+  // Should generate 1 updates: 1 to remove route from /a
+  eraseRoute("/a", 1, 128);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 1);
@@ -177,19 +177,19 @@
 
 BOOST_AUTO_TEST_CASE(BothFlags_NoCaptureChange_CaptureOnRoute)
 {
-  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 1, 0, 100, 0);
-  insertFaceEntry("/a", 1, 128, 50, (ndn::nfd::ROUTE_FLAG_CHILD_INHERIT |
+  insertRoute("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 1, 0, 100, 0);
+  insertRoute("/a", 1, 128, 50, (ndn::nfd::ROUTE_FLAG_CHILD_INHERIT |
                                      ndn::nfd::ROUTE_FLAG_CAPTURE));
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Should generate 2 updates: 1 to remove face1 from /a and
-  // 1 to remove face1 to /a/b
-  eraseFaceEntry("/a", 1, 128);
+  // 1 to remove face1 from /a/b
+  eraseRoute("/a", 1, 128);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
@@ -207,11 +207,11 @@
 
 BOOST_AUTO_TEST_CASE(BothFlags_CaptureChange_NoCaptureOnRoute)
 {
-  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 10, 0);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 1, 0, 100, 0);
-  insertFaceEntry("/a", 1, 128, 50, (ndn::nfd::ROUTE_FLAG_CHILD_INHERIT |
+  insertRoute("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 10, 0);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 1, 0, 100, 0);
+  insertRoute("/a", 1, 128, 50, (ndn::nfd::ROUTE_FLAG_CHILD_INHERIT |
                                      ndn::nfd::ROUTE_FLAG_CAPTURE));
 
   // Clear updates generated from previous insertions
@@ -219,7 +219,7 @@
 
   // Should generate 2 updates: 1 to add face1 to /a and
   // 1 to add face1 to /a/b
-  eraseFaceEntry("/a", 1, 128);
+  eraseRoute("/a", 1, 128);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
@@ -239,17 +239,17 @@
 
 BOOST_AUTO_TEST_CASE(ChildInherit_NoCaptureChange_NoCaptureOnRoute)
 {
-  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 10, 0);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 1, 0, 100, 0);
-  insertFaceEntry("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 10, 0);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 1, 0, 100, 0);
+  insertRoute("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Should generate 2 updates: 2 to add face1 to /a and /a/b
-  eraseFaceEntry("/a", 1, 128);
+  eraseRoute("/a", 1, 128);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
@@ -269,17 +269,17 @@
 
 BOOST_AUTO_TEST_CASE(ChildInherit_NoCaptureChange_CaptureOnRoute)
 {
-  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 1, 0, 100, 0);
-  insertFaceEntry("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 1, 0, 100, 0);
+  insertRoute("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Should generate 2 updates: 2 to remove face 1 from /a and /a/b
-  eraseFaceEntry("/a", 1, 128);
+  eraseRoute("/a", 1, 128);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
@@ -297,18 +297,18 @@
 
 BOOST_AUTO_TEST_CASE(Capture_CaptureChange_NoCaptureOnRoute)
 {
-  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 10, 0);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 1, 0, 100, 0);
-  insertFaceEntry("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertRoute("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 10, 0);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 1, 0, 100, 0);
+  insertRoute("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CAPTURE);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Should generate 2 updates: 1 to update cost on /a and
   // 1 to add face1 to /a/b
-  eraseFaceEntry("/a", 1 ,128);
+  eraseRoute("/a", 1 ,128);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
@@ -328,17 +328,17 @@
 
 BOOST_AUTO_TEST_CASE(Capture_NoCaptureChange_CaptureOnRoute)
 {
-  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 1, 0, 100, 0);
-  insertFaceEntry("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertRoute("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 1, 0, 100, 0);
+  insertRoute("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CAPTURE);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
-  // Should generate 1 updates: 1 to remove face from /a
-  eraseFaceEntry("/a", 1, 128);
+  // Should generate 1 updates: 1 to remove route from /a
+  eraseRoute("/a", 1, 128);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 1);
@@ -351,11 +351,11 @@
 
 BOOST_AUTO_TEST_CASE(EraseFaceById)
 {
-  insertFaceEntry("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 10, 0);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 4, 0, 100, 0);
-  insertFaceEntry("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/", 1, 0, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 10, 0);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 4, 0, 100, 0);
+  insertRoute("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
diff --git a/tests/rib/fib-updates-new-face.cpp b/tests/rib/fib-updates-new-face.cpp
index b8e7d69..4f41488 100644
--- a/tests/rib/fib-updates-new-face.cpp
+++ b/tests/rib/fib-updates-new-face.cpp
@@ -39,7 +39,7 @@
 BOOST_AUTO_TEST_CASE(Basic)
 {
   // should generate 1 update
-  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   Rib::FibUpdateList updates = rib.getFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 1);
@@ -55,7 +55,7 @@
   rib.clearFibUpdates();
 
   // should generate 2 updates
-  insertFaceEntry("/a", 2, 0, 50, 0);
+  insertRoute("/a", 2, 0, 50, 0);
 
   updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
@@ -76,7 +76,7 @@
   rib.clearFibUpdates();
 
   // should generate 2 updates
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/b", 3, 0, 10, 0);
 
   updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
@@ -96,27 +96,27 @@
 
 BOOST_AUTO_TEST_CASE(UpdateOnLowerCostNoChildInherit)
 {
-  insertFaceEntry("/", 1, 0, 50, 0);
+  insertRoute("/", 1, 0, 50, 0);
 
   // Clear any updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Should generate 0 updates
-  insertFaceEntry("/", 1, 128, 75, 0);
+  insertRoute("/", 1, 128, 75, 0);
 
   BOOST_CHECK_EQUAL(rib.getFibUpdates().size(), 0);
 }
 
 BOOST_AUTO_TEST_CASE(UpdateOnLowerCostOnly)
 {
-  insertFaceEntry("/",  1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 10, 0);
+  insertRoute("/",  1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 10, 0);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Should generate 2 updates: to update cost for face 1 on / and /a
-  insertFaceEntry("/", 1, 0, 25, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/", 1, 0, 25, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
@@ -137,23 +137,23 @@
   rib.clearFibUpdates();
 
   // Should generate 0 updates
-  insertFaceEntry("/", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   BOOST_CHECK_EQUAL(rib.getFibUpdates().size(), 0);
 }
 
 BOOST_AUTO_TEST_CASE(NoCaptureChangeWithoutChildInherit)
 {
-  insertFaceEntry("/",    1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a",   2, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 4, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertRoute("/",    1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a",   2, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 4, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Should generate 1 update: 1 to add face 5 to /a
-  insertFaceEntry("/a", 5, 128, 50, 0);
+  insertRoute("/a", 5, 128, 50, 0);
 
   const Rib::FibUpdateList& updates = rib.getFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 1);
@@ -168,17 +168,17 @@
 
 BOOST_AUTO_TEST_CASE(NoCaptureChangeWithChildInherit)
 {
-  insertFaceEntry("/",    1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a",   2, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 4, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertRoute("/",    1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a",   2, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 4, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
-  // Should generate 2 updates: one for the inserted face and
-  // one to add face to /a/b
-  insertFaceEntry("/a", 4, 128, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  // Should generate 2 updates: one for the inserted route and
+  // one to add route to /a/b
+  insertRoute("/a", 4, 128, 5, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
@@ -198,18 +198,18 @@
 
 BOOST_AUTO_TEST_CASE(CaptureTurnedOnWithoutChildInherit)
 {
-  insertFaceEntry("/",    1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a",   2, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 4, 0, 10, 0);
+  insertRoute("/",    1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a",   2, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 4, 0, 10, 0);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Should generate 3 updates:
-  // - one for the inserted face for /a and
+  // - one for the inserted route for /a and
   // - two to remove face1 from /a/b and /a/c
-  insertFaceEntry("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertRoute("/a", 1, 128, 50, ndn::nfd::ROUTE_FLAG_CAPTURE);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 3);
@@ -233,18 +233,18 @@
 
 BOOST_AUTO_TEST_CASE(CaptureTurnedOnWithChildInherit)
 {
-  insertFaceEntry("/",    1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a",   2, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 4, 0, 10, 0);
+  insertRoute("/",    1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a",   2, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 4, 0, 10, 0);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Should generate 2 updates:
-  // - one for the inserted face for /a and
+  // - one for the inserted route for /a and
   // - one to update /a/b with the new cost
-  insertFaceEntry("/a", 1, 128, 50, (ndn::nfd::ROUTE_FLAG_CAPTURE |
+  insertRoute("/a", 1, 128, 50, (ndn::nfd::ROUTE_FLAG_CAPTURE |
                                      ndn::nfd::ROUTE_FLAG_CHILD_INHERIT));
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
diff --git a/tests/rib/fib-updates-new-namespace.cpp b/tests/rib/fib-updates-new-namespace.cpp
index e409c51..5339530 100644
--- a/tests/rib/fib-updates-new-namespace.cpp
+++ b/tests/rib/fib-updates-new-namespace.cpp
@@ -38,8 +38,8 @@
 
 BOOST_AUTO_TEST_CASE(NoFlags)
 {
-  // No flags, empty RIB, should generate 1 update for the inserted face
-  insertFaceEntry("/a/b", 1, 0, 10, 0);
+  // No flags, empty RIB, should generate 1 update for the inserted route
+  insertRoute("/a/b", 1, 0, 10, 0);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 1);
@@ -51,18 +51,18 @@
   BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
 
   // Reset RIB
-  eraseFaceEntry("/a/b", 1, 0);
+  eraseRoute("/a/b", 1, 0);
   rib.clearFibUpdates();
 
   // Parent with child inherit flag
-  insertFaceEntry("/a", 2, 0, 70, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 3, 0, 30, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 70, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 3, 0, 30, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
-  // Should generate 3 updates, 1 for the inserted face and 2 from inheritance
-  insertFaceEntry("/a/b", 1, 0, 10, 0);
+  // Should generate 3 updates, 1 for the inserted route and 2 from inheritance
+  insertRoute("/a/b", 1, 0, 10, 0);
 
   updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 3);
@@ -88,9 +88,8 @@
 
 BOOST_AUTO_TEST_CASE(BothFlags)
 {
-  // Empty RIB, should generate 1 update for the inserted face
-  insertFaceEntry("/a", 1, 0, 10, (ndn::nfd::ROUTE_FLAG_CHILD_INHERIT |
-                                   ndn::nfd::ROUTE_FLAG_CAPTURE));
+  // Empty RIB, should generate 1 update for the inserted route
+  insertRoute("/a", 1, 0, 10, (ndn::nfd::ROUTE_FLAG_CHILD_INHERIT | ndn::nfd::ROUTE_FLAG_CAPTURE));
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 1);
@@ -102,19 +101,18 @@
   BOOST_CHECK_EQUAL((*update)->action, FibUpdate::ADD_NEXTHOP);
 
   // Reset RIB
-  eraseFaceEntry("/a", 1, 0);
+  eraseRoute("/a", 1, 0);
   rib.clearFibUpdates();
 
-  insertFaceEntry("/", 2, 0, 70, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a/b", 3, 0, 30, 0);
+  insertRoute("/", 2, 0, 70, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a/b", 3, 0, 30, 0);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
-  // Should generate 3 updates, 1 for the inserted face, 1 to add the face to the child,
-  // and 1 to remove the previously inherited entry
-  insertFaceEntry("/a", 1, 0, 10, (ndn::nfd::ROUTE_FLAG_CHILD_INHERIT |
-                                   ndn::nfd::ROUTE_FLAG_CAPTURE));
+  // Should generate 3 updates, 1 for the inserted route, 1 to add the route to the child,
+  // and 1 to remove the previously inherited route
+  insertRoute("/a", 1, 0, 10, (ndn::nfd::ROUTE_FLAG_CHILD_INHERIT | ndn::nfd::ROUTE_FLAG_CAPTURE));
 
   updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 3);
@@ -139,15 +137,15 @@
 
 BOOST_AUTO_TEST_CASE(ChildInherit)
 {
-  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a/b", 2, 0, 10, 0);
-  insertFaceEntry("/a/c", 3, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertRoute("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a/b", 2, 0, 10, 0);
+  insertRoute("/a/c", 3, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
-  // Should generate 2 updates: 1 for the inserted face and 1 to add the face to "/a/b"
-  insertFaceEntry("/a", 1, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  // Should generate 2 updates: 1 for the inserted route and 1 to add the route to "/a/b"
+  insertRoute("/a", 1, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
@@ -167,16 +165,16 @@
 
 BOOST_AUTO_TEST_CASE(Capture)
 {
-  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a/b", 2, 0, 10, 0);
-  insertFaceEntry("/a/c", 3, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertRoute("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a/b", 2, 0, 10, 0);
+  insertRoute("/a/c", 3, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
-  // Should generate 2 updates: 1 for the inserted face and
-  // 1 to remove inherited face from "/a/b"
-  insertFaceEntry("/a", 1, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  // Should generate 2 updates: 1 for the inserted route and
+  // 1 to remove the inherited route from "/a/b"
+  insertRoute("/a", 1, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
diff --git a/tests/rib/fib-updates-update-face.cpp b/tests/rib/fib-updates-update-face.cpp
index 13f5e21..e3dcd45 100644
--- a/tests/rib/fib-updates-update-face.cpp
+++ b/tests/rib/fib-updates-update-face.cpp
@@ -38,16 +38,16 @@
 
 BOOST_AUTO_TEST_CASE(TurnOffChildInheritLowerCost)
 {
-  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 10, 0);
-  insertFaceEntry("/", 1, 128, 25, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 10, 0);
+  insertRoute("/", 1, 128, 25, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Should generate 2 updates: 1 to update the cost of / face 1 to 50 and
   // 1 to update the cost of /a face 1 to 50
-  insertFaceEntry("/", 1, 128, 75, 0);
+  insertRoute("/", 1, 128, 75, 0);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
@@ -67,15 +67,15 @@
 
 BOOST_AUTO_TEST_CASE(UpdateOnLowerCostOnly)
 {
-  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 10, 0);
-  insertFaceEntry("/", 1, 128, 100, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 10, 0);
+  insertRoute("/", 1, 128, 100, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Should generate 0 updates
-  insertFaceEntry("/", 1, 128, 75, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/", 1, 128, 75, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 0);
@@ -84,7 +84,7 @@
   rib.clearFibUpdates();
 
   // Should generate 2 updates
-  insertFaceEntry("/", 1, 128, 25, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/", 1, 128, 25, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
@@ -104,16 +104,16 @@
 
 BOOST_AUTO_TEST_CASE(NoChangeInCost)
 {
-  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 100, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 4, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertRoute("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 100, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 4, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Should generate 0 updates
-  insertFaceEntry("/a", 2, 0, 100, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 100, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 0);
@@ -121,17 +121,17 @@
 
 BOOST_AUTO_TEST_CASE(ChangeCost)
 {
-  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 100, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 4, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertRoute("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 100, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 4, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Should generate 2 updates: 1 to add face2 with new cost to /a and
   // 1 to add face2 with new cost to /a/b
-  insertFaceEntry("/a", 2, 0, 300, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 300, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
@@ -151,17 +151,17 @@
 
 BOOST_AUTO_TEST_CASE(TurnOnChildInherit)
 {
-  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 10, 0);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 4, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertRoute("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 10, 0);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 4, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Turn on child inherit flag for the entry in /a
   // Should generate 1 updates: 1 to add face to /a/b
-  insertFaceEntry("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 1);
@@ -175,17 +175,17 @@
 
 BOOST_AUTO_TEST_CASE(TurnOffChildInherit)
 {
-  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 1, 0, 100, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a/b", 2, 0, 10, 0);
-  insertFaceEntry("/a/c", 1, 0, 25, 0);
+  insertRoute("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 1, 0, 100, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a/b", 2, 0, 10, 0);
+  insertRoute("/a/c", 1, 0, 25, 0);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
 
   // Turn off child inherit flag for the entry in /a
   // Should generate 1 update: 1 to add face1 to /a/b
-  insertFaceEntry("/a", 1, 0, 100, 0);
+  insertRoute("/a", 1, 0, 100, 0);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 1);
@@ -199,10 +199,10 @@
 
 BOOST_AUTO_TEST_CASE(TurnOnCapture)
 {
-  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 10, 0);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 1, 0, 10, 0);
+  insertRoute("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 10, 0);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 1, 0, 10, 0);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
@@ -210,7 +210,7 @@
   // Turn on capture flag for the entry in /a
   // Should generate 2 updates: 1 to remove face1 from /a and
   // 1 to remove face1 from /a/b
-  insertFaceEntry("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertRoute("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
@@ -228,10 +228,10 @@
 
 BOOST_AUTO_TEST_CASE(TurnOffCapture)
 {
-  insertFaceEntry("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
-  insertFaceEntry("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
-  insertFaceEntry("/a/b", 3, 0, 10, 0);
-  insertFaceEntry("/a/c", 1, 0, 10, 0);
+  insertRoute("/", 1, 0, 50, ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  insertRoute("/a", 2, 0, 10, ndn::nfd::ROUTE_FLAG_CAPTURE);
+  insertRoute("/a/b", 3, 0, 10, 0);
+  insertRoute("/a/c", 1, 0, 10, 0);
 
   // Clear updates generated from previous insertions
   rib.clearFibUpdates();
@@ -239,7 +239,7 @@
   // Turn off capture flag for the entry in /a
   // Should generate 2 updates: 1 to add face1 to /a and
   // 1 to add face1 to /a/b
-  insertFaceEntry("/a", 2, 0, 10, 0);
+  insertRoute("/a", 2, 0, 10, 0);
 
   Rib::FibUpdateList updates = getSortedFibUpdates();
   BOOST_REQUIRE_EQUAL(updates.size(), 2);
diff --git a/tests/rib/remote-registrator.cpp b/tests/rib/remote-registrator.cpp
index e0b4d3a..8e30218 100644
--- a/tests/rib/remote-registrator.cpp
+++ b/tests/rib/remote-registrator.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -34,10 +34,11 @@
 namespace tests {
 
 class RemoteRegistratorFixture : public nfd::tests::IdentityManagementFixture
+                               , public nfd::tests::UnitTestTimeFixture
 {
 public:
   RemoteRegistratorFixture()
-    : face(ndn::util::makeDummyClientFace())
+    : face(ndn::util::makeDummyClientFace(getGlobalIoService()))
     , controller(make_shared<ndn::nfd::Controller>(std::ref(*face), m_keyChain))
     , remoteRegistrator(make_shared<RemoteRegistrator>(std::ref(*controller),
                                                        m_keyChain,
@@ -48,41 +49,52 @@
   {
     readConfig();
 
-    registerCallback();
+    remoteRegistrator->enable();
 
-    face->processEvents(time::milliseconds(1));
+    advanceClocks(time::milliseconds(1));
     face->sentInterests.clear();
   }
 
   void
-  readConfig()
+  readConfig(bool isSetRetry = false)
   {
     ConfigFile config;
     config.addSectionHandler("remote_register",
                              bind(&RemoteRegistrator::loadConfig, remoteRegistrator, _1));
 
-    const std::string CONFIG_STRING =
-    "remote_register\n"
-    "{\n"
-    "  cost 15\n"
-    "  timeout 10000\n"
-    "  retry 2\n"
-    "  refresh_interval 5\n"
-    "}";
 
-    config.parse(CONFIG_STRING, true, "test-remote-register");
+    if (isSetRetry)
+      {
+        const std::string CONFIG_STRING =
+        "remote_register\n"
+        "{\n"
+        "  cost 15\n"
+        "  timeout 1000\n"
+        "  retry 1\n"
+        "  refresh_interval 5\n"
+        "}";
+
+        config.parse(CONFIG_STRING, true, "test-remote-register");
+      }
+    else
+      {
+        const std::string CONFIG_STRING =
+        "remote_register\n"
+        "{\n"
+        "  cost 15\n"
+        "  timeout 100000\n"
+        "  retry 0\n"
+        "  refresh_interval 5\n"
+        "}";
+
+        config.parse(CONFIG_STRING, true, "test-remote-register");
+      }
   }
 
   void
-  registerCallback()
+  waitForTimeout()
   {
-    rib.afterInsertEntry += [this] (const Name& prefix) {
-      remoteRegistrator->registerPrefix(prefix);
-    };
-
-    rib.afterEraseEntry += [this] (const Name& prefix) {
-      remoteRegistrator->unregisterPrefix(prefix);
-    };
+    advanceClocks(time::milliseconds(100), time::seconds(1));
   }
 
   void
@@ -92,12 +104,12 @@
   {
     BOOST_CHECK_EQUAL(addIdentity(identity), true);
 
-    FaceEntry faceEntry;
-    faceEntry.faceId = faceId;
+    Route route;
+    route.faceId = faceId;
 
-    rib.insert(identity.append(appName), faceEntry);
+    rib.insert(identity.append(appName), route);
 
-    face->processEvents(time::milliseconds(1));
+    advanceClocks(time::milliseconds(1));
   }
 
   void
@@ -105,12 +117,12 @@
                              name::Component appName = DEFAULT_APP_NAME,
                              uint64_t faceId = 0)
   {
-    FaceEntry faceEntry;
-    faceEntry.faceId = faceId;
+    Route route;
+    route.faceId = faceId;
 
-    rib.insert(identity.append(appName), faceEntry);
+    rib.insert(identity.append(appName), route);
 
-    face->processEvents(time::milliseconds(1));
+    advanceClocks(time::milliseconds(1));
   }
 
   void
@@ -120,12 +132,12 @@
   {
     BOOST_CHECK_EQUAL(addIdentity(identity), true);
 
-    FaceEntry faceEntry;
-    faceEntry.faceId = faceId;
+    Route route;
+    route.faceId = faceId;
 
-    rib.erase(identity.append(appName), faceEntry);
+    rib.erase(identity.append(appName), route);
 
-    face->processEvents(time::milliseconds(1));
+    advanceClocks(time::milliseconds(1));
   }
 
   void
@@ -133,12 +145,12 @@
                             name::Component appName = DEFAULT_APP_NAME,
                             uint64_t faceId = 0)
   {
-    FaceEntry faceEntry;
-    faceEntry.faceId = faceId;
+    Route route;
+    route.faceId = faceId;
 
-    rib.erase(identity.append(appName), faceEntry);
+    rib.erase(identity.append(appName), route);
 
-    face->processEvents(time::milliseconds(1));
+    advanceClocks(time::milliseconds(1));
   }
 
   void
@@ -146,23 +158,23 @@
   {
     rib.erase(faceId);
 
-    face->processEvents(time::milliseconds(1));
+    advanceClocks(time::milliseconds(1));
   }
 
   void
   connectToHub()
   {
-    rib.insert(Name("/localhop/nfd"), FaceEntry());
+    rib.insert(COMMAND_PREFIX, Route());
 
-    face->processEvents(time::milliseconds(1));
+    advanceClocks(time::milliseconds(1));
   }
 
   void
   disconnectToHub()
   {
-    rib.erase(Name("/localhop/nfd"), FaceEntry());
+    rib.erase(COMMAND_PREFIX, Route());
 
-    face->processEvents(time::milliseconds(1));
+    advanceClocks(time::milliseconds(1));
   }
 
   void
@@ -270,6 +282,55 @@
   BOOST_CHECK_EQUAL(extractedParameters.getName(), identity);
 }
 
+BOOST_FIXTURE_TEST_CASE(RegisterWithRedundantCallback, RemoteRegistratorFixture)
+{
+  remoteRegistrator->enable();
+
+  connectToHub();
+
+  Name identity("/remote/register");
+  insertEntryWithIdentity(identity);
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 1);
+
+  Interest& request = face->sentInterests[0];
+
+  ndn::nfd::ControlParameters extractedParameters;
+  Name::Component verb;
+  extractParameters(request, verb, extractedParameters);
+
+  BOOST_CHECK_EQUAL(verb, REGISTER_VERB);
+  BOOST_CHECK_EQUAL(extractedParameters.getName(), identity);
+}
+
+BOOST_FIXTURE_TEST_CASE(RegisterRetry, RemoteRegistratorFixture)
+{
+  // setRetry
+  readConfig(true);
+
+  connectToHub();
+
+  Name identity("/remote/register");
+  insertEntryWithIdentity(identity);
+
+  waitForTimeout();
+
+  BOOST_REQUIRE_EQUAL(face->sentInterests.size(), 2);
+
+  Interest& requestFirst  = face->sentInterests[0];
+  Interest& requestSecond = face->sentInterests[1];
+
+  ndn::nfd::ControlParameters extractedParametersFirst, extractedParametersSecond;
+  Name::Component verbFirst, verbSecond;
+  extractParameters(requestFirst,  verbFirst,  extractedParametersFirst);
+  extractParameters(requestSecond, verbSecond, extractedParametersSecond);
+
+  BOOST_CHECK_EQUAL(verbFirst,  REGISTER_VERB);
+  BOOST_CHECK_EQUAL(verbSecond, REGISTER_VERB);
+  BOOST_CHECK_EQUAL(extractedParametersFirst.getName(),  identity);
+  BOOST_CHECK_EQUAL(extractedParametersSecond.getName(), identity);
+}
+
 BOOST_FIXTURE_TEST_CASE(UnregisterWithoutInsert, RemoteRegistratorFixture)
 {
   connectToHub();
@@ -287,7 +348,7 @@
 
   Name indentity("/remote/register");
   remoteRegistrator->m_regEntries.insert(
-            nfd::rib::RemoteRegistrator::RegisteredEntry(indentity, EventId()));
+            nfd::rib::RemoteRegistrator::RegisteredEntry(indentity, scheduler::EventId()));
 
   eraseEntryWithIdentity(indentity);
 
@@ -325,7 +386,7 @@
 
   insertEntryWithIdentity(identity);
 
-  EventId event;
+  scheduler::EventId event;
 
   remoteRegistrator->m_regEntries.insert(
           nfd::rib::RemoteRegistrator::RegisteredEntry(identity, event));
@@ -351,8 +412,8 @@
   Name identityShort("/remote/register");
   Name identityLong("/remote/register/long");
 
-  EventId eventShort;
-  EventId eventLong;
+  scheduler::EventId eventShort;
+  scheduler::EventId eventLong;
 
   insertEntryWithIdentity(identityShort, name::Component("appA"));
 
@@ -400,7 +461,7 @@
 
   insertEntryWithIdentity(identity, DEFAULT_APP_NAME, faceId);
 
-  EventId event;
+  scheduler::EventId event;
 
   remoteRegistrator->m_regEntries.insert(
           nfd::rib::RemoteRegistrator::RegisteredEntry(identity, event));
@@ -427,7 +488,7 @@
 
   insertEntryWithIdentity(identity);
 
-  EventId event;
+  scheduler::EventId event;
 
   remoteRegistrator->m_regEntries.insert(
           nfd::rib::RemoteRegistrator::RegisteredEntry(identity, event));
diff --git a/tests/rib/rib-manager.cpp b/tests/rib/rib-manager.cpp
index afef2f3..2f5c61c 100644
--- a/tests/rib/rib-manager.cpp
+++ b/tests/rib/rib-manager.cpp
@@ -71,8 +71,11 @@
 
   void receiveCommandInterest(Name& name, ControlParameters& parameters)
   {
-    name.append(parameters.wireEncode());
+    receiveCommandInterest(name.append(parameters.wireEncode()));
+  }
 
+  void receiveCommandInterest(const Name& name)
+  {
     Interest command(name);
 
     face->receive(command);
@@ -116,6 +119,13 @@
 
 BOOST_FIXTURE_TEST_SUITE(RibManager, RibManagerFixture)
 
+BOOST_FIXTURE_TEST_CASE(ShortName, AuthorizedRibManager)
+{
+  Name commandName("/localhost/nfd/rib");
+  receiveCommandInterest(commandName);
+  // TODO verify error response
+}
+
 BOOST_FIXTURE_TEST_CASE(Basic, AuthorizedRibManager)
 {
   ControlParameters parameters;
@@ -224,20 +234,20 @@
 
 BOOST_FIXTURE_TEST_CASE(RibStatusRequest, AuthorizedRibManager)
 {
-  FaceEntry entry;
+  Route route;
   Name name("/");
-  entry.faceId = 1;
-  entry.origin = 128;
-  entry.cost = 32;
-  entry.flags = ndn::nfd::ROUTE_FLAG_CAPTURE;
+  route.faceId = 1;
+  route.origin = 128;
+  route.cost = 32;
+  route.flags = ndn::nfd::ROUTE_FLAG_CAPTURE;
 
   ControlParameters parameters;
   parameters
     .setName(name)
-    .setFaceId(entry.faceId)
-    .setOrigin(entry.origin)
-    .setCost(entry.cost)
-    .setFlags(entry.flags)
+    .setFaceId(route.faceId)
+    .setOrigin(route.origin)
+    .setCost(route.cost)
+    .setFlags(route.flags)
     .setExpirationPeriod(ndn::time::milliseconds::max());
 
   Name commandName("/localhost/nfd/rib/register");
@@ -252,7 +262,7 @@
   face->processEvents(time::milliseconds(1));
 
   BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
-  RibStatusPublisherFixture::decodeRibEntryBlock(face->sentDatas[0], name, entry);
+  RibStatusPublisherFixture::decodeRibEntryBlock(face->sentDatas[0], name, route);
 }
 
 BOOST_FIXTURE_TEST_CASE(CancelExpirationEvent, AuthorizedRibManager)
@@ -343,6 +353,41 @@
   BOOST_CHECK_EQUAL(entry->hasFaceId(2), false);
 }
 
+BOOST_FIXTURE_TEST_CASE(LocalHopInherit, AuthorizedRibManager)
+{
+  using nfd::rib::RibManager;
+
+  // Simulate NFD response
+  ControlParameters result;
+  result.setFaceId(261);
+
+  manager->onNrdCommandPrefixAddNextHopSuccess(RibManager::REMOTE_COMMAND_PREFIX, result);
+
+  // Register route that localhop prefix should inherit
+  ControlParameters parameters;
+  parameters
+    .setName("/localhop/nfd")
+    .setFaceId(262)
+    .setCost(25)
+    .setFlags(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+
+  Name commandName("/localhost/nfd/rib/register");
+
+  receiveCommandInterest(commandName, parameters);
+
+  // REMOTE_COMMAND_PREFIX should have its original route and the inherited route
+  auto it = manager->m_managedRib.find(RibManager::REMOTE_COMMAND_PREFIX);
+
+  BOOST_REQUIRE(it != manager->m_managedRib.end());
+  const RibEntry::RouteList& inheritedRoutes = (*(it->second)).getInheritedRoutes();
+
+  BOOST_CHECK_EQUAL(inheritedRoutes.size(), 1);
+  auto routeIt = inheritedRoutes.begin();
+
+  BOOST_CHECK_EQUAL(routeIt->faceId, 262);
+  BOOST_CHECK_EQUAL(routeIt->cost, 25);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests
diff --git a/tests/rib/rib-status-publisher-common.hpp b/tests/rib/rib-status-publisher-common.hpp
index 58ccd68..a0f1f42 100644
--- a/tests/rib/rib-status-publisher-common.hpp
+++ b/tests/rib/rib-status-publisher-common.hpp
@@ -45,7 +45,7 @@
 {
 public:
   static void
-  validateRibEntry(const Block& block, const Name& referenceName, const FaceEntry& referenceFace)
+  validateRibEntry(const Block& block, const Name& referenceName, const Route& referenceRoute)
   {
     ndn::nfd::RibEntry entry;
     BOOST_REQUIRE_NO_THROW(entry.wireDecode(block));
@@ -55,14 +55,14 @@
     std::list<ndn::nfd::Route> routes = entry.getRoutes();
 
     std::list<ndn::nfd::Route>::iterator it = routes.begin();
-    BOOST_CHECK_EQUAL(it->getFaceId(), referenceFace.faceId);
-    BOOST_CHECK_EQUAL(it->getOrigin(), referenceFace.origin);
-    BOOST_CHECK_EQUAL(it->getCost(), referenceFace.cost);
-    BOOST_CHECK_EQUAL(it->getFlags(), referenceFace.flags);
+    BOOST_CHECK_EQUAL(it->getFaceId(), referenceRoute.faceId);
+    BOOST_CHECK_EQUAL(it->getOrigin(), referenceRoute.origin);
+    BOOST_CHECK_EQUAL(it->getCost(), referenceRoute.cost);
+    BOOST_CHECK_EQUAL(it->getFlags(), referenceRoute.flags);
   }
 
   static void
-  decodeRibEntryBlock(const Data& data, const Name& referenceName, const FaceEntry& referenceFace)
+  decodeRibEntryBlock(const Data& data, const Name& referenceName, const Route& referenceRoute)
   {
     ndn::EncodingBuffer buffer;
 
@@ -81,7 +81,7 @@
       BOOST_FAIL("expected RibEntry, got type #" << i->type());
     }
     else {
-      validateRibEntry(*i, referenceName, referenceFace);
+      validateRibEntry(*i, referenceName, referenceRoute);
     }
   }
 };
diff --git a/tests/rib/rib-status-publisher.cpp b/tests/rib/rib-status-publisher.cpp
index b1414cc..1f81900 100644
--- a/tests/rib/rib-status-publisher.cpp
+++ b/tests/rib/rib-status-publisher.cpp
@@ -39,13 +39,13 @@
 {
   Rib rib;
 
-  FaceEntry entry;
+  Route route;
   Name name("/");
-  entry.faceId = 1;
-  entry.origin = 128;
-  entry.cost = 32;
-  entry.flags = ndn::nfd::ROUTE_FLAG_CAPTURE;
-  rib.insert(name, entry);
+  route.faceId = 1;
+  route.origin = 128;
+  route.cost = 32;
+  route.flags = ndn::nfd::ROUTE_FLAG_CAPTURE;
+  rib.insert(name, route);
 
   ndn::KeyChain keyChain;
   shared_ptr<ndn::util::DummyClientFace> face = ndn::util::makeDummyClientFace();
@@ -55,7 +55,7 @@
   face->processEvents(time::milliseconds(1));
 
   BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
-  decodeRibEntryBlock(face->sentDatas[0], name, entry);
+  decodeRibEntryBlock(face->sentDatas[0], name, route);
 }
 
 
diff --git a/tests/rib/rib.cpp b/tests/rib/rib.cpp
index 3226e57..3427c95 100644
--- a/tests/rib/rib.cpp
+++ b/tests/rib/rib.cpp
@@ -37,102 +37,102 @@
 {
   rib::RibEntry entry;
 
-  rib::FaceEntry face1;
-  face1.faceId = 1;
-  face1.origin = 0;
+  rib::Route route1;
+  route1.faceId = 1;
+  route1.origin = 0;
 
-  entry.insertFace(face1);
-  BOOST_CHECK_EQUAL(entry.getFaces().size(), 1);
+  entry.insertRoute(route1);
+  BOOST_CHECK_EQUAL(entry.getRoutes().size(), 1);
 
-  FaceEntry face2;
-  face2.faceId = 1;
-  face2.origin = 128;
+  Route route2;
+  route2.faceId = 1;
+  route2.origin = 128;
 
-  entry.insertFace(face2);
-  BOOST_CHECK_EQUAL(entry.getFaces().size(), 2);
+  entry.insertRoute(route2);
+  BOOST_CHECK_EQUAL(entry.getRoutes().size(), 2);
 
-  entry.eraseFace(face1);
-  BOOST_CHECK_EQUAL(entry.getFaces().size(), 1);
+  entry.eraseRoute(route1);
+  BOOST_CHECK_EQUAL(entry.getRoutes().size(), 1);
 
-  BOOST_CHECK(entry.findFace(face1) == entry.getFaces().end());
-  BOOST_CHECK(entry.findFace(face2) != entry.getFaces().end());
+  BOOST_CHECK(entry.findRoute(route1) == entry.getRoutes().end());
+  BOOST_CHECK(entry.findRoute(route2) != entry.getRoutes().end());
 
-  entry.insertFace(face2);
-  BOOST_CHECK_EQUAL(entry.getFaces().size(), 1);
+  entry.insertRoute(route2);
+  BOOST_CHECK_EQUAL(entry.getRoutes().size(), 1);
 
-  entry.eraseFace(face1);
-  BOOST_CHECK_EQUAL(entry.getFaces().size(), 1);
-  BOOST_CHECK(entry.findFace(face2) != entry.getFaces().end());
+  entry.eraseRoute(route1);
+  BOOST_CHECK_EQUAL(entry.getRoutes().size(), 1);
+  BOOST_CHECK(entry.findRoute(route2) != entry.getRoutes().end());
 }
 
 BOOST_AUTO_TEST_CASE(Parent)
 {
   rib::Rib rib;
 
-  FaceEntry root;
+  Route root;
   Name name1("/");
   root.faceId = 1;
   root.origin = 20;
   rib.insert(name1, root);
 
-  FaceEntry entry1;
+  Route route1;
   Name name2("/hello");
-  entry1.faceId = 2;
-  entry1.origin = 20;
-  rib.insert(name2, entry1);
+  route1.faceId = 2;
+  route1.origin = 20;
+  rib.insert(name2, route1);
 
-  FaceEntry entry2;
+  Route route2;
   Name name3("/hello/world");
-  entry2.faceId = 3;
-  entry2.origin = 20;
-  rib.insert(name3, entry2);
+  route2.faceId = 3;
+  route2.origin = 20;
+  rib.insert(name3, route2);
 
   shared_ptr<rib::RibEntry> ribEntry = rib.findParent(name3);
   BOOST_REQUIRE(static_cast<bool>(ribEntry));
-  BOOST_CHECK_EQUAL(ribEntry->getFaces().front().faceId, 2);
+  BOOST_CHECK_EQUAL(ribEntry->getRoutes().front().faceId, 2);
 
   ribEntry = rib.findParent(name2);
   BOOST_REQUIRE(static_cast<bool>(ribEntry));
-  BOOST_CHECK_EQUAL(ribEntry->getFaces().front().faceId, 1);
+  BOOST_CHECK_EQUAL(ribEntry->getRoutes().front().faceId, 1);
 
-  FaceEntry entry3;
+  Route route3;
   Name name4("/hello/test/foo/bar");
-  entry2.faceId = 3;
-  entry2.origin = 20;
-  rib.insert(name4, entry3);
+  route2.faceId = 3;
+  route2.origin = 20;
+  rib.insert(name4, route3);
 
   ribEntry = rib.findParent(name4);
   BOOST_CHECK(ribEntry != shared_ptr<rib::RibEntry>());
-  BOOST_CHECK(ribEntry->getFaces().front().faceId == 2);
+  BOOST_CHECK(ribEntry->getRoutes().front().faceId == 2);
 }
 
 BOOST_AUTO_TEST_CASE(Children)
 {
   rib::Rib rib;
 
-  FaceEntry entry1;
+  Route route1;
   Name name1("/");
-  entry1.faceId = 1;
-  entry1.origin = 20;
-  rib.insert(name1, entry1);
+  route1.faceId = 1;
+  route1.origin = 20;
+  rib.insert(name1, route1);
 
-  FaceEntry entry2;
+  Route route2;
   Name name2("/hello/world");
-  entry2.faceId = 2;
-  entry2.origin = 20;
-  rib.insert(name2, entry2);
+  route2.faceId = 2;
+  route2.origin = 20;
+  rib.insert(name2, route2);
 
-  FaceEntry entry3;
+  Route route3;
   Name name3("/hello/test/foo/bar");
-  entry3.faceId = 3;
-  entry3.origin = 20;
-  rib.insert(name3, entry3);
+  route3.faceId = 3;
+  route3.origin = 20;
+  rib.insert(name3, route3);
 
   BOOST_CHECK_EQUAL((rib.find(name1)->second)->getChildren().size(), 2);
   BOOST_CHECK_EQUAL((rib.find(name2)->second)->getChildren().size(), 0);
   BOOST_CHECK_EQUAL((rib.find(name3)->second)->getChildren().size(), 0);
 
-  FaceEntry entry4;
+  Route entry4;
   Name name4("/hello");
   entry4.faceId = 4;
   entry4.origin = 20;
@@ -156,41 +156,41 @@
 {
   rib::Rib rib;
 
-  FaceEntry entry1;
+  Route route1;
   Name name1("/");
-  entry1.faceId = 1;
-  entry1.origin = 20;
-  rib.insert(name1, entry1);
+  route1.faceId = 1;
+  route1.origin = 20;
+  rib.insert(name1, route1);
 
-  FaceEntry entry2;
+  Route route2;
   Name name2("/hello/world");
-  entry2.faceId = 2;
-  entry2.origin = 20;
-  rib.insert(name2, entry2);
+  route2.faceId = 2;
+  route2.origin = 20;
+  rib.insert(name2, route2);
 
-  FaceEntry entry3;
+  Route route3;
   Name name3("/hello/world");
-  entry3.faceId = 1;
-  entry3.origin = 20;
-  rib.insert(name3, entry3);
+  route3.faceId = 1;
+  route3.origin = 20;
+  rib.insert(name3, route3);
 
-  FaceEntry entry4;
+  Route entry4;
   Name name4("/not/inserted");
   entry4.faceId = 1;
   entry4.origin = 20;
 
   rib.erase(name4, entry4);
-  rib.erase(name1, entry1);
+  rib.erase(name1, route1);
 
   BOOST_CHECK(rib.find(name1) == rib.end());
-  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getFaces().size(), 2);
+  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getRoutes().size(), 2);
 
-  rib.erase(name2, entry2);
+  rib.erase(name2, route2);
 
-  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getFaces().size(), 1);
-  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getFaces().front().faceId, 1);
+  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getRoutes().size(), 1);
+  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getRoutes().front().faceId, 1);
 
-  rib.erase(name3, entry3);
+  rib.erase(name3, route3);
 
   BOOST_CHECK(rib.find(name2) == rib.end());
 
@@ -201,23 +201,23 @@
 {
   rib::Rib rib;
 
-  FaceEntry entry1;
+  Route route1;
   Name name1("/");
-  entry1.faceId = 1;
-  entry1.origin = 20;
-  rib.insert(name1, entry1);
+  route1.faceId = 1;
+  route1.origin = 20;
+  rib.insert(name1, route1);
 
-  FaceEntry entry2;
+  Route route2;
   Name name2("/hello");
-  entry2.faceId = 2;
-  entry2.origin = 20;
-  rib.insert(name2, entry2);
+  route2.faceId = 2;
+  route2.origin = 20;
+  rib.insert(name2, route2);
 
-  FaceEntry entry3;
+  Route route3;
   Name name3("/hello/world");
-  entry3.faceId = 1;
-  entry3.origin = 20;
-  rib.insert(name3, entry3);
+  route3.faceId = 1;
+  route3.origin = 20;
+  rib.insert(name3, route3);
 
   shared_ptr<rib::RibEntry> ribEntry1 = rib.find(name1)->second;
   shared_ptr<rib::RibEntry> ribEntry2 = rib.find(name2)->second;
@@ -226,7 +226,7 @@
   BOOST_CHECK(ribEntry1->getChildren().front() == ribEntry2);
   BOOST_CHECK(ribEntry3->getParent() == ribEntry2);
 
-  rib.erase(name2, entry2);
+  rib.erase(name2, route2);
   BOOST_CHECK(ribEntry1->getChildren().front() == ribEntry3);
   BOOST_CHECK(ribEntry3->getParent() == ribEntry1);
 }
@@ -235,30 +235,30 @@
 {
   rib::Rib rib;
 
-  FaceEntry entry1;
+  Route route1;
   Name name1("/");
-  entry1.faceId = 1;
-  entry1.origin = 20;
-  rib.insert(name1, entry1);
+  route1.faceId = 1;
+  route1.origin = 20;
+  rib.insert(name1, route1);
 
-  FaceEntry entry2;
+  Route route2;
   Name name2("/hello/world");
-  entry2.faceId = 2;
-  entry2.origin = 20;
-  rib.insert(name2, entry2);
+  route2.faceId = 2;
+  route2.origin = 20;
+  rib.insert(name2, route2);
 
-  FaceEntry entry3;
+  Route route3;
   Name name3("/hello/world");
-  entry3.faceId = 1;
-  entry3.origin = 20;
-  rib.insert(name3, entry3);
+  route3.faceId = 1;
+  route3.origin = 20;
+  rib.insert(name3, route3);
 
   rib.erase(1);
   BOOST_CHECK(rib.find(name1) == rib.end());
-  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getFaces().size(), 1);
+  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getRoutes().size(), 1);
 
   rib.erase(3);
-  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getFaces().size(), 1);
+  BOOST_CHECK_EQUAL((rib.find(name2)->second)->getRoutes().size(), 1);
 
   rib.erase(2);
   BOOST_CHECK(rib.find(name2) == rib.end());
@@ -270,61 +270,61 @@
 {
   rib::Rib rib;
 
-  FaceEntry entry1;
+  Route route1;
   Name name1("/hello/world");
-  entry1.faceId = 1;
-  entry1.origin = 20;
-  entry1.cost = 10;
-  entry1.flags = ndn::nfd::ROUTE_FLAG_CHILD_INHERIT | ndn::nfd::ROUTE_FLAG_CAPTURE;
-  entry1.expires = time::steady_clock::now() + time::milliseconds(1500);
+  route1.faceId = 1;
+  route1.origin = 20;
+  route1.cost = 10;
+  route1.flags = ndn::nfd::ROUTE_FLAG_CHILD_INHERIT | ndn::nfd::ROUTE_FLAG_CAPTURE;
+  route1.expires = time::steady_clock::now() + time::milliseconds(1500);
 
-  rib.insert(name1, entry1);
+  rib.insert(name1, route1);
   BOOST_CHECK_EQUAL(rib.size(), 1);
 
-  rib.insert(name1, entry1);
+  rib.insert(name1, route1);
   BOOST_CHECK_EQUAL(rib.size(), 1);
 
-  FaceEntry entry2;
+  Route route2;
   Name name2("/hello/world");
-  entry2.faceId = 1;
-  entry2.origin = 20;
-  entry2.cost = 100;
-  entry2.flags = ndn::nfd::ROUTE_FLAG_CHILD_INHERIT;
-  entry2.expires = time::steady_clock::now() + time::seconds(0);
+  route2.faceId = 1;
+  route2.origin = 20;
+  route2.cost = 100;
+  route2.flags = ndn::nfd::ROUTE_FLAG_CHILD_INHERIT;
+  route2.expires = time::steady_clock::now() + time::seconds(0);
 
-  rib.insert(name2, entry2);
+  rib.insert(name2, route2);
   BOOST_CHECK_EQUAL(rib.size(), 1);
 
-  entry2.faceId = 2;
-  rib.insert(name2, entry2);
+  route2.faceId = 2;
+  rib.insert(name2, route2);
   BOOST_CHECK_EQUAL(rib.size(), 2);
 
-  BOOST_CHECK(rib.find(name1)->second->hasFaceId(entry1.faceId));
-  BOOST_CHECK(rib.find(name1)->second->hasFaceId(entry2.faceId));
+  BOOST_CHECK(rib.find(name1)->second->hasFaceId(route1.faceId));
+  BOOST_CHECK(rib.find(name1)->second->hasFaceId(route2.faceId));
 
   Name name3("/foo/bar");
-  rib.insert(name3, entry2);
+  rib.insert(name3, route2);
   BOOST_CHECK_EQUAL(rib.size(), 3);
 
-  entry2.origin = 1;
-  rib.insert(name3, entry2);
+  route2.origin = 1;
+  rib.insert(name3, route2);
   BOOST_CHECK_EQUAL(rib.size(), 4);
 
-  rib.erase(name3, entry2);
+  rib.erase(name3, route2);
   BOOST_CHECK_EQUAL(rib.size(), 3);
 
   Name name4("/hello/world");
-  rib.erase(name4, entry2);
+  rib.erase(name4, route2);
   BOOST_CHECK_EQUAL(rib.size(), 3);
 
-  entry2.origin = 20;
-  rib.erase(name4, entry2);
+  route2.origin = 20;
+  rib.erase(name4, route2);
   BOOST_CHECK_EQUAL(rib.size(), 2);
 
-  BOOST_CHECK_EQUAL(rib.find(name2, entry2), static_cast<FaceEntry*>(0));
-  BOOST_CHECK_NE(rib.find(name1, entry1), static_cast<FaceEntry*>(0));
+  BOOST_CHECK_EQUAL(rib.find(name2, route2), static_cast<Route*>(0));
+  BOOST_CHECK_NE(rib.find(name1, route1), static_cast<Route*>(0));
 
-  rib.erase(name1, entry1);
+  rib.erase(name1, route1);
   BOOST_CHECK_EQUAL(rib.size(), 1);
 }
 
diff --git a/tests/test-common.hpp b/tests/test-common.hpp
index e3bc0b5..247337d 100644
--- a/tests/test-common.hpp
+++ b/tests/test-common.hpp
@@ -127,6 +127,8 @@
     }
   }
 
+  friend class LimitedIo;
+
 protected:
   shared_ptr<time::UnitTestSteadyClock> steadyClock;
   shared_ptr<time::UnitTestSystemClock> systemClock;
diff --git a/tools/ndn-autoconfig.cpp b/tools/ndn-autoconfig.cpp
index fdd8ba9..09f5dba 100644
--- a/tools/ndn-autoconfig.cpp
+++ b/tools/ndn-autoconfig.cpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -273,8 +273,7 @@
 
     // Get Uri
     Block::element_const_iterator blockValue = content.find(tlv::nfd::Uri);
-    if (blockValue == content.elements_end())
-    {
+    if (blockValue == content.elements_end()) {
       discoverHubStage2("Incorrect reply to stage1");
       return;
     }
@@ -302,15 +301,13 @@
                                 sizeof(queryAnswer));
 
     // 2nd stage failed - move on to the third stage
-    if (answerSize < 0)
-    {
+    if (answerSize < 0) {
       discoverHubStage3("Failed to find NDN router using default suffix DNS query");
     }
     else
     {
       bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
-      if (isParsed == false)
-      {
+      if (isParsed == false) {
         // Failed to parse DNS response, try stage 3
         discoverHubStage3("Failed to parse DNS response");
       }
@@ -328,8 +325,7 @@
     Name identity = keyChain.getDefaultIdentity();
     std::string serverName = "_ndn._udp.";
 
-    for (Name::const_reverse_iterator i = identity.rbegin(); i != identity.rend(); i++)
-    {
+    for (Name::const_reverse_iterator i = identity.rbegin(); i != identity.rend(); i++) {
       serverName.append(i->toUri());
       serverName.append(".");
     }
@@ -346,16 +342,14 @@
 
 
     // 3rd stage failed - abort
-    if (answerSize < 0)
-    {
+    if (answerSize < 0) {
       std::cerr << "Failed to find a home router" << std::endl;
       std::cerr << "exit" << std::endl;
     }
     else
     {
       bool isParsed = parseHostAndConnectToHub(queryAnswer, answerSize);
-      if (isParsed == false)
-      {
+      if (isParsed == false) {
         // Failed to parse DNS response
         throw Error("Failed to parse DNS response");
       }
@@ -363,17 +357,104 @@
 
   }
 
+  bool
+  parseHostAndConnectToHub(QueryAnswer& queryAnswer, int answerSize)
+  {
+    // The references of the next classes are:
+    // http://www.diablotin.com/librairie/networking/dnsbind/ch14_02.htm
+    // https://gist.github.com/mologie/6027597
+
+    struct rechdr
+    {
+      uint16_t type;
+      uint16_t iclass;
+      uint32_t ttl;
+      uint16_t length;
+    };
+
+    struct srv_t
+    {
+      uint16_t priority;
+      uint16_t weight;
+      uint16_t port;
+      uint8_t* target;
+    };
+
+    if (ntohs(queryAnswer.header.ancount) == 0) {
+      std::cerr << "No records found\n" << std::endl;
+      return false;
+    }
+
+    uint8_t* blob = queryAnswer.buf + NS_HFIXEDSZ;
+
+    blob += dn_skipname(blob, queryAnswer.buf + answerSize) + NS_QFIXEDSZ;
+
+    for (int i = 0; i < ntohs(queryAnswer.header.ancount); i++) {
+      char srvName[NS_MAXDNAME];
+      int serverNameSize = dn_expand(queryAnswer.buf,               // message pointer
+                                     queryAnswer.buf + answerSize,  // end of message
+                                     blob,                          // compressed server name
+                                     srvName,                       // expanded server name
+                                     NS_MAXDNAME);
+      if (serverNameSize < 0) {
+        return false;
+      }
+
+      srv_t* server = reinterpret_cast<srv_t*>(&blob[sizeof(rechdr)]);
+      uint16_t convertedPort = be16toh(server->port);
+
+      blob += serverNameSize + NS_HFIXEDSZ + NS_QFIXEDSZ;
+
+      char hostName[NS_MAXDNAME];
+      int hostNameSize = dn_expand(queryAnswer.buf,               // message pointer
+                                   queryAnswer.buf + answerSize,  // end of message
+                                   blob,                          // compressed host name
+                                   hostName,                      // expanded host name
+                                   NS_MAXDNAME);
+      if (hostNameSize < 0) {
+        return false;
+      }
+
+      std::string uri = "udp://";
+      uri.append(hostName);
+      uri.append(":");
+      uri.append(boost::lexical_cast<std::string>(convertedPort));
+
+      connectToHub(uri);
+
+      return true;
+    }
+    return false;
+  }
+
   void
   connectToHub(const std::string& uri)
   {
-    std::cerr << "about to connect to: " << uri << std::endl;
+    ndn::util::FaceUri faceUri(uri);
+
+    faceUri.canonize(bind(&NdnAutoconfig::onCanonizeSuccess, this, _1),
+                     bind(&NdnAutoconfig::onCanonizeFailure, this, _1),
+                     m_face.getIoService(), ndn::time::seconds(4));
+
+  }
+
+  void onCanonizeSuccess(const ndn::util::FaceUri& canonicalUri)
+  {
+    std::cerr << "about to connect to: " << canonicalUri.toString() << std::endl;
 
     m_controller.start<nfd::FaceCreateCommand>(
       nfd::ControlParameters()
-        .setUri(uri),
+        .setUri(canonicalUri.toString()),
       bind(&NdnAutoconfig::onHubConnectSuccess, this, _1),
-      bind(&NdnAutoconfig::onHubConnectError, this, _1, _2)
-    );
+      bind(&NdnAutoconfig::onHubConnectError, this, _1, _2));
+  }
+
+  void
+  onCanonizeFailure(const std::string& reason)
+  {
+    std::ostringstream os;
+    os << "Canonize faceUri failed: " << reason;
+    throw Error(os.str());
   }
 
   void
@@ -402,80 +483,6 @@
     throw Error(os.str());
   }
 
-
-  bool parseHostAndConnectToHub(QueryAnswer& queryAnswer, int answerSize)
-  {
-    // The references of the next classes are:
-    // http://www.diablotin.com/librairie/networking/dnsbind/ch14_02.htm
-    // https://gist.github.com/mologie/6027597
-
-    struct rechdr
-    {
-      uint16_t type;
-      uint16_t iclass;
-      uint32_t ttl;
-      uint16_t length;
-    };
-
-    struct srv_t
-    {
-      uint16_t priority;
-      uint16_t weight;
-      uint16_t port;
-      uint8_t* target;
-    };
-
-    if (ntohs(queryAnswer.header.ancount) == 0)
-    {
-      std::cerr << "No records found\n" << std::endl;
-      return false;
-    }
-
-    uint8_t* blob = queryAnswer.buf + NS_HFIXEDSZ;
-
-    blob += dn_skipname(blob, queryAnswer.buf + answerSize) + NS_QFIXEDSZ;
-
-    for (int i = 0; i < ntohs(queryAnswer.header.ancount); i++)
-    {
-      char srvName[NS_MAXDNAME];
-      int serverNameSize = dn_expand(queryAnswer.buf,               // message pointer
-                                     queryAnswer.buf + answerSize,  // end of message
-                                     blob,                          // compressed server name
-                                     srvName,                       // expanded server name
-                                     NS_MAXDNAME);
-      if (serverNameSize < 0)
-      {
-        return false;
-      }
-
-      srv_t* server = reinterpret_cast<srv_t*>(&blob[sizeof(rechdr)]);
-      uint16_t convertedPort = be16toh(server->port);
-
-      blob += serverNameSize + NS_HFIXEDSZ + NS_QFIXEDSZ;
-
-      char hostName[NS_MAXDNAME];
-      int hostNameSize = dn_expand(queryAnswer.buf,               // message pointer
-                                   queryAnswer.buf + answerSize,  // end of message
-                                   blob,                          // compressed host name
-                                   hostName,                      // expanded host name
-                                   NS_MAXDNAME);
-      if (hostNameSize < 0)
-      {
-        return false;
-      }
-
-      std::string uri = "udp://";
-      uri.append(hostName);
-      uri.append(":");
-      uri.append(boost::lexical_cast<std::string>(convertedPort));
-
-      connectToHub(uri);
-      return true;
-    }
-
-    return false;
-  }
-
   void
   onPrefixRegistrationSuccess(const nfd::ControlParameters& commandSuccessResult)
   {
diff --git a/tools/nfdc.cpp b/tools/nfdc.cpp
index dcb228c..6c53b60 100644
--- a/tools/nfdc.cpp
+++ b/tools/nfdc.cpp
@@ -415,13 +415,13 @@
 void
 Nfdc::onCanonizeFailure(const std::string& reason)
 {
-  std::cerr << reason << std::endl;
+  throw Error(reason);
 }
 
 void
 Nfdc::onObtainFaceIdFailure(const std::string& message)
 {
-  std::cerr << "Obtain faceId failure: " << message << std::endl;
+  throw Error(message);
 }
 
 void