Merge remote-tracking branch 'named-data/master' into release-0.2.0

Change-Id: I086a5cce154fa6db26bb05d8f1e45715636c9656
diff --git a/src/common.hpp b/src/common.hpp
index 4a9914c..9d15e49 100644
--- a/src/common.hpp
+++ b/src/common.hpp
@@ -37,6 +37,12 @@
 #define DEPRECATED(func) func
 #endif
 
+namespace ndn {
+
+const size_t MAX_NDN_PACKET_SIZE = 8800;
+
+} // namespace ndn
+
 #ifdef NDN_CXX_HAVE_CXX11
 
 #if defined(__GNUC__)
@@ -49,7 +55,6 @@
 #  error "NDN-CXX library is configured and compiled in C++11 mode, but the current compiler is not C++11 enabled"
 #endif // defined(__clang__) && (__cplusplus < 201103L)
 
-
 #include <memory>
 #include <functional>
 
@@ -60,6 +65,7 @@
 
 using std::shared_ptr;
 using std::weak_ptr;
+using std::bad_weak_ptr;
 using std::make_shared;
 using std::enable_shared_from_this;
 
@@ -102,6 +108,7 @@
 
 using boost::shared_ptr;
 using boost::weak_ptr;
+using boost::bad_weak_ptr;
 using boost::make_shared;
 using boost::enable_shared_from_this;
 
diff --git a/src/detail/face-impl.hpp b/src/detail/face-impl.hpp
index 7c259aa..187d7fa 100644
--- a/src/detail/face-impl.hpp
+++ b/src/detail/face-impl.hpp
@@ -137,6 +137,27 @@
     m_pendingInterestTable.remove_if(MatchPendingInterestId(pendingInterestId));
   }
 
+  void
+  asyncPutData(const shared_ptr<const Data>& data)
+  {
+    if (!m_face.m_transport->isConnected())
+      m_face.m_transport->connect(*m_face.m_ioService,
+                                  bind(&Face::onReceiveElement, &m_face, _1));
+
+    if (!m_face.m_transport->isExpectingData())
+      m_face.m_transport->resume();
+
+    if (!data->getLocalControlHeader().empty(false, true))
+      {
+        m_face.m_transport->send(data->getLocalControlHeader().wireEncode(*data, false, true),
+                                 data->wireEncode());
+      }
+    else
+      {
+        m_face.m_transport->send(data->wireEncode());
+      }
+  }
+
   /////////////////////////////////////////////////////////////////////////////////////////////////
   /////////////////////////////////////////////////////////////////////////////////////////////////
 
diff --git a/src/encoding/block.cpp b/src/encoding/block.cpp
index ed265b0..eeb0c3f 100644
--- a/src/encoding/block.cpp
+++ b/src/encoding/block.cpp
@@ -31,6 +31,7 @@
 #include "buffer-stream.hpp"
 
 #include <boost/lexical_cast.hpp>
+#include <boost/asio/buffer.hpp>
 
 namespace ndn {
 
@@ -397,4 +398,10 @@
               boost::lexical_cast<std::string>(type) + "] from Block");
 }
 
+Block::operator boost::asio::const_buffer() const
+{
+  return boost::asio::const_buffer(wire(), size());
+}
+
+
 } // namespace ndn
diff --git a/src/encoding/block.hpp b/src/encoding/block.hpp
index c91d12c..17192d8 100644
--- a/src/encoding/block.hpp
+++ b/src/encoding/block.hpp
@@ -29,6 +29,12 @@
 #include "buffer.hpp"
 #include "tlv.hpp"
 
+namespace boost {
+namespace asio {
+class const_buffer;
+} // namespace asio
+} // namespace boost
+
 namespace ndn {
 
 template<bool> class EncodingImpl;
@@ -281,6 +287,9 @@
   bool
   operator!=(const Block& other) const;
 
+public: // ConvertibleToConstBuffer
+  operator boost::asio::const_buffer() const;
+
 protected:
   ConstBufferPtr m_buffer;
 
diff --git a/src/encoding/tlv-nfd.hpp b/src/encoding/tlv-nfd.hpp
index 63776fb..ba00cad 100644
--- a/src/encoding/tlv-nfd.hpp
+++ b/src/encoding/tlv-nfd.hpp
@@ -55,10 +55,6 @@
   NPitEntries          = 133,
   NMeasurementsEntries = 134,
   NCsEntries           = 135,
-  NInInterests         = 144,
-  NInDatas             = 145,
-  NOutInterests        = 146,
-  NOutDatas            = 147,
 
   // Face Management
   FaceStatus            = 128,
@@ -68,12 +64,24 @@
   FaceEventNotification = 192,
   FaceEventKind         = 193,
 
+  // ForwarderStatus and FaceStatus counters
+  NInInterests  = 144,
+  NInDatas      = 145,
+  NOutInterests = 146,
+  NOutDatas     = 147,
+  NInBytes      = 148,
+  NOutBytes     = 149,
+
   // FIB Management
   FibEntry      = 128,
   NextHopRecord = 129,
 
   // Strategy Choice Management
-  StrategyChoice = 128
+  StrategyChoice = 128,
+
+  // RIB Management
+  RibEntry = 128,
+  Route    = 129
 
 };
 
@@ -89,7 +97,7 @@
 
 namespace nfd {
 
-const uint64_t INVALID_FACE_ID = std::numeric_limits<uint64_t>::max();
+static const uint64_t INVALID_FACE_ID = std::numeric_limits<uint64_t>::max();
 
 } // namespace nfd
 
diff --git a/src/face.cpp b/src/face.cpp
index 6f13570..f547f45 100644
--- a/src/face.cpp
+++ b/src/face.cpp
@@ -144,6 +144,10 @@
 {
   shared_ptr<Interest> interestToExpress = make_shared<Interest>(interest);
 
+  // Use `interestToExpress` to avoid wire format creation for the original Interest
+  if (interestToExpress->wireEncode().size() > MAX_NDN_PACKET_SIZE)
+    throw Error("Interest size exceeds maximum limit");
+
   // If the same ioService thread, dispatch directly calls the method
   m_ioService->dispatch(bind(&Impl::asyncExpressInterest, m_impl,
                              interestToExpress, onData, onTimeout));
@@ -165,19 +169,22 @@
 void
 Face::put(const Data& data)
 {
-  if (!m_transport->isConnected())
-    m_transport->connect(*m_ioService,
-                         bind(&Face::onReceiveElement, this, _1));
+  // Use original `data`, since wire format should already exist for the original Data
+  if (data.wireEncode().size() > MAX_NDN_PACKET_SIZE)
+    throw Error("Data size exceeds maximum limit");
 
-  if (!data.getLocalControlHeader().empty(false, true))
-    {
-      m_transport->send(data.getLocalControlHeader().wireEncode(data, false, true),
-                        data.wireEncode());
-    }
-  else
-    {
-      m_transport->send(data.wireEncode());
-    }
+  shared_ptr<const Data> dataPtr;
+  try {
+    dataPtr = data.shared_from_this();
+  }
+  catch (const bad_weak_ptr& e) {
+    std::cerr << "Face::put WARNING: the supplied Data should be created using make_shared<Data>()"
+              << std::endl;
+    dataPtr = make_shared<Data>(data);
+  }
+
+  // If the same ioService thread, dispatch directly calls the method
+  m_ioService->dispatch(bind(&Impl::asyncPutData, m_impl, dataPtr));
 }
 
 void
@@ -186,8 +193,6 @@
   m_ioService->post(bind(&Impl::asyncRemovePendingInterest, m_impl, pendingInterestId));
 }
 
-
-
 const RegisteredPrefixId*
 Face::setInterestFilter(const InterestFilter& interestFilter,
                         const OnInterest& onInterest,
@@ -308,43 +313,40 @@
 Face::processEvents(const time::milliseconds& timeout/* = time::milliseconds::zero()*/,
                     bool keepThread/* = false*/)
 {
-  try
-    {
-      if (timeout < time::milliseconds::zero())
-        {
-          // do not block if timeout is negative, but process pending events
-          m_ioService->poll();
-          return;
-        }
-
-      if (timeout > time::milliseconds::zero())
-        {
-          m_impl->m_processEventsTimeoutTimer->expires_from_now(time::milliseconds(timeout));
-          m_impl->m_processEventsTimeoutTimer->async_wait(&fireProcessEventsTimeout);
-        }
-
-      if (keepThread) {
-        // work will ensure that m_ioService is running until work object exists
-        m_impl->m_ioServiceWork = make_shared<boost::asio::io_service::work>(ref(*m_ioService));
+  try {
+    if (timeout < time::milliseconds::zero())
+      {
+        // do not block if timeout is negative, but process pending events
+        m_ioService->poll();
+        return;
       }
 
-      m_ioService->run();
-      m_ioService->reset(); // so it is possible to run processEvents again (if necessary)
+    if (timeout > time::milliseconds::zero())
+      {
+        m_impl->m_processEventsTimeoutTimer->expires_from_now(time::milliseconds(timeout));
+        m_impl->m_processEventsTimeoutTimer->async_wait(&fireProcessEventsTimeout);
+      }
+
+    if (keepThread) {
+      // work will ensure that m_ioService is running until work object exists
+      m_impl->m_ioServiceWork = make_shared<boost::asio::io_service::work>(ref(*m_ioService));
     }
-  catch (Face::ProcessEventsTimeout&)
-    {
-      // break
-      m_impl->m_ioServiceWork.reset();
-      m_ioService->reset();
-    }
-  catch (std::exception&)
-    {
-      m_impl->m_ioServiceWork.reset();
-      m_ioService->reset();
-      m_impl->m_pendingInterestTable.clear();
-      m_impl->m_registeredPrefixTable.clear();
-      throw;
-    }
+
+    m_ioService->run();
+    m_ioService->reset(); // so it is possible to run processEvents again (if necessary)
+  }
+  catch (Face::ProcessEventsTimeout&) {
+    // break
+    m_impl->m_ioServiceWork.reset();
+    m_ioService->reset();
+  }
+  catch (...) {
+    m_impl->m_ioServiceWork.reset();
+    m_ioService->reset();
+    m_impl->m_pendingInterestTable.clear();
+    m_impl->m_registeredPrefixTable.clear();
+    throw;
+  }
 }
 
 void
diff --git a/src/face.hpp b/src/face.hpp
index a5ece29..9427b03 100644
--- a/src/face.hpp
+++ b/src/face.hpp
@@ -192,6 +192,8 @@
    * @param onTimeout (optional) A function object to call if the interest times out
    *
    * @return The pending interest ID which can be used with removePendingInterest
+   *
+   * @throws Error when Interest size exceeds maximum limit (MAX_NDN_PACKET_SIZE)
    */
   const PendingInterestId*
   expressInterest(const Interest& interest,
@@ -206,6 +208,8 @@
    * @param onTimeout (optional) A function object to call if the interest times out
    *
    * @return Opaque pending interest ID which can be used with removePendingInterest
+   *
+   * @throws Error when Interest size exceeds maximum limit (MAX_NDN_PACKET_SIZE)
    */
   const PendingInterestId*
   expressInterest(const Name& name,
@@ -440,7 +444,14 @@
    * @brief Publish data packet
    *
    * This method can be called to satisfy the incoming Interest or to put Data packet into
-   * the cache of the local NDN forwarder
+   * the cache of the local NDN forwarder.
+   *
+   * @param data Data packet to publish.  It is highly recommended to use Data packet that
+   *             was created using make_shared<Data>(...).  Otherwise, put() will make an
+   *             extra copy of the Data packet to ensure validity of published Data until
+   *             asynchronous put() operation finishes.
+   *
+   * @throws Error when Data size exceeds maximum limit (MAX_NDN_PACKET_SIZE)
    */
   void
   put(const Data& data);
diff --git a/src/interest.cpp b/src/interest.cpp
index db3744c..f5ff57b 100644
--- a/src/interest.cpp
+++ b/src/interest.cpp
@@ -21,10 +21,9 @@
  * Based on code originally written by Jeff Thompson <jefft0@remap.ucla.edu>
  */
 
-#include "common.hpp"
-
 #include "interest.hpp"
 #include "util/random.hpp"
+#include "util/crypto.hpp"
 #include "data.hpp"
 
 namespace ndn {
@@ -102,10 +101,69 @@
 bool
 Interest::matchesData(const Data& data) const
 {
-  if (!this->matchesName(data.getFullName())) {
+  size_t interestNameLength = m_name.size();
+  const Name& dataName = data.getName();
+  size_t fullNameLength = dataName.size() + 1;
+
+  // check MinSuffixComponents
+  bool hasMinSuffixComponents = getMinSuffixComponents() >= 0;
+  size_t minSuffixComponents = hasMinSuffixComponents ?
+                               static_cast<size_t>(getMinSuffixComponents()) : 0;
+  if (!(interestNameLength + minSuffixComponents <= fullNameLength))
     return false;
+
+  // check MaxSuffixComponents
+  bool hasMaxSuffixComponents = getMaxSuffixComponents() >= 0;
+  if (hasMaxSuffixComponents &&
+      !(interestNameLength + getMaxSuffixComponents() >= fullNameLength))
+    return false;
+
+  // check prefix
+  if (interestNameLength == fullNameLength) {
+    bool mightEndWithDigest = (interestNameLength > 0) &&
+                              (m_name.get(-1).value_size() == crypto::SHA256_DIGEST_SIZE);
+    if (mightEndWithDigest) {
+      // Interest Name is same length as Data full Name, last component could match digest
+      if (!m_name.isPrefixOf(data.getFullName()))
+        return false;
+    }
+    else {
+      // Interest Name is same length as Data full Name, but last component isn't digest
+      // so there's no possibility of matching
+      return false;
+    }
+  }
+  else {
+    // Interest Name is a strict prefix of Data full Name
+    if (!m_name.isPrefixOf(dataName))
+      return false;
   }
 
+  // check Exclude
+  // Exclude won't be violated if Interest Name is same as Data full Name
+  if (!getExclude().empty() && fullNameLength > interestNameLength) {
+    if (interestNameLength == fullNameLength - 1) {
+      // component to exclude is the digest
+      if (getExclude().isExcluded(data.getFullName().get(interestNameLength)))
+        return false;
+      // There's opportunity to inspect the Exclude filter and determine whether
+      // the digest would make a difference.
+      // eg. "Exclude=<Any>AA" won't exclude any digest - fullName not needed
+      //     "Exclude=ZZ<Any>" excludes all digests - fullName not needed
+      //     "Exclude=<Any>80000000000000000000000000000000"
+      //         excludes half of the digests - fullName required
+      // But Interests that contain the exact Data Name before digest and also
+      // contain Exclude filter is too rare to optimize for, so we request
+      // fullName no mater what's in the Exclude filter.
+    }
+    else {
+      // component to exclude is not the digest
+      if (getExclude().isExcluded(dataName.get(interestNameLength)))
+        return false;
+    }
+  }
+
+  // check PublisherPublicKeyLocator
   const KeyLocator& publisherPublicKeyLocator = this->getPublisherPublicKeyLocator();
   if (!publisherPublicKeyLocator.empty()) {
     const Signature& signature = data.getSignature();
diff --git a/src/management/nfd-control-command.hpp b/src/management/nfd-control-command.hpp
index 0068317..252812c 100644
--- a/src/management/nfd-control-command.hpp
+++ b/src/management/nfd-control-command.hpp
@@ -495,7 +495,7 @@
       .required(CONTROL_PARAMETER_ORIGIN)
       .required(CONTROL_PARAMETER_COST)
       .required(CONTROL_PARAMETER_FLAGS)
-      .required(CONTROL_PARAMETER_EXPIRATION_PERIOD);
+      .optional(CONTROL_PARAMETER_EXPIRATION_PERIOD);
   }
 
   virtual void
@@ -513,14 +513,6 @@
     if (!parameters.hasFlags()) {
       parameters.setFlags(ROUTE_FLAG_CHILD_INHERIT);
     }
-    if (!parameters.hasExpirationPeriod()) {
-      if (parameters.getFaceId() == 0) {
-        parameters.setExpirationPeriod(time::milliseconds::max());
-      }
-      else {
-        parameters.setExpirationPeriod(time::hours(1));
-      }
-    }
   }
 
   virtual void
diff --git a/src/management/nfd-face-status.cpp b/src/management/nfd-face-status.cpp
new file mode 100644
index 0000000..adf29c2
--- /dev/null
+++ b/src/management/nfd-face-status.cpp
@@ -0,0 +1,227 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2014 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "nfd-face-status.hpp"
+
+namespace ndn {
+namespace nfd {
+
+FaceStatus::FaceStatus()
+  : m_faceId(0)
+  , m_hasExpirationPeriod(false)
+  , m_flags(0)
+  , m_nInInterests(0)
+  , m_nInDatas(0)
+  , m_nOutInterests(0)
+  , m_nOutDatas(0)
+  , m_nInBytes(0)
+  , m_nOutBytes(0)
+{
+}
+
+template<bool T>
+size_t
+FaceStatus::wireEncode(EncodingImpl<T>& encoder) const
+{
+  size_t totalLength = 0;
+
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::NOutBytes, m_nOutBytes);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::NInBytes, m_nInBytes);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::NOutDatas, m_nOutDatas);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::NOutInterests, m_nOutInterests);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::NInDatas, m_nInDatas);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::NInInterests, m_nInInterests);
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::FaceFlags, m_flags);
+  if (m_hasExpirationPeriod) {
+    totalLength += prependNonNegativeIntegerBlock(encoder,
+                   tlv::nfd::ExpirationPeriod, m_expirationPeriod.count());
+  }
+  totalLength += prependByteArrayBlock(encoder, tlv::nfd::LocalUri,
+                 reinterpret_cast<const uint8_t*>(m_localUri.c_str()), m_localUri.size());
+  totalLength += prependByteArrayBlock(encoder, tlv::nfd::Uri,
+                 reinterpret_cast<const uint8_t*>(m_remoteUri.c_str()), m_remoteUri.size());
+  totalLength += prependNonNegativeIntegerBlock(encoder,
+                 tlv::nfd::FaceId, m_faceId);
+
+  totalLength += encoder.prependVarNumber(totalLength);
+  totalLength += encoder.prependVarNumber(tlv::nfd::FaceStatus);
+  return totalLength;
+}
+
+template size_t
+FaceStatus::wireEncode<true>(EncodingImpl<true>& block) const;
+
+template size_t
+FaceStatus::wireEncode<false>(EncodingImpl<false>& block) const;
+
+const Block&
+FaceStatus::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+FaceStatus::wireDecode(const Block& block)
+{
+  if (block.type() != tlv::nfd::FaceStatus) {
+    throw Error("expecting FaceStatus block");
+  }
+  m_wire = block;
+  m_wire.parse();
+  Block::element_const_iterator val = m_wire.elements_begin();
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::FaceId) {
+    m_faceId = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    throw Error("missing required FaceId field");
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::Uri) {
+    m_remoteUri.assign(reinterpret_cast<const char*>(val->value()), val->value_size());
+    ++val;
+  }
+  else {
+    throw Error("missing required Uri field");
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::LocalUri) {
+    m_localUri.assign(reinterpret_cast<const char*>(val->value()), val->value_size());
+    ++val;
+  }
+  else {
+    throw Error("missing required LocalUri field");
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::ExpirationPeriod) {
+    m_expirationPeriod = time::milliseconds(readNonNegativeInteger(*val));
+    m_hasExpirationPeriod = true;
+    ++val;
+  }
+  else {
+    m_hasExpirationPeriod = false;
+    // ExpirationPeriod is optional
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::FaceFlags) {
+    m_flags = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    throw Error("missing required FaceFlags field");
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NInInterests) {
+    m_nInInterests = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    throw Error("missing required NInInterests field");
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NInDatas) {
+    m_nInDatas = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    throw Error("missing required NInDatas field");
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NOutInterests) {
+    m_nOutInterests = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    throw Error("missing required NOutInterests field");
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NOutDatas) {
+    m_nOutDatas = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    throw Error("missing required NOutDatas field");
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NInBytes) {
+    m_nInBytes = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    throw Error("missing required NInBytes field");
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NOutBytes) {
+    m_nOutBytes = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    throw Error("missing required NOutBytes field");
+  }
+}
+
+std::ostream&
+operator<<(std::ostream& os, const FaceStatus& status)
+{
+  os << "FaceStatus("
+     << "FaceID: " << status.getFaceId() << ",\n"
+     << "RemoteUri: " << status.getRemoteUri() << ",\n"
+     << "LocalUri: " << status.getLocalUri() << ",\n";
+
+  if (status.hasExpirationPeriod()) {
+    os << "ExpirationPeriod: " << status.getExpirationPeriod() << ",\n";
+  }
+  else {
+    os << "ExpirationPeriod: infinite,\n";
+  }
+
+  os << "Flags: " << status.getFlags() << ",\n"
+     << "Counters: { Interests: {in: " << status.getNInInterests() << ", "
+     << "out: " << status.getNOutInterests() << "},\n"
+     << "            Data: {in: " << status.getNInDatas() << ", "
+     << "out: " << status.getNOutDatas() << "},\n"
+     << "            bytes: {in: " << status.getNInBytes() << ", "
+     << "out: " << status.getNOutBytes() << "} }\n"
+     << ")";
+  return os;
+}
+
+} // namespace nfd
+} // namespace ndn
+
diff --git a/src/management/nfd-face-status.hpp b/src/management/nfd-face-status.hpp
index a92e154..1b33dea 100644
--- a/src/management/nfd-face-status.hpp
+++ b/src/management/nfd-face-status.hpp
@@ -209,6 +209,34 @@
     return *this;
   }
 
+  uint64_t
+  getNInBytes() const
+  {
+    return m_nInBytes;
+  }
+
+  FaceStatus&
+  setNInBytes(uint64_t nInBytes)
+  {
+    m_wire.reset();
+    m_nInBytes = nInBytes;
+    return *this;
+  }
+
+  uint64_t
+  getNOutBytes() const
+  {
+    return m_nOutBytes;
+  }
+
+  FaceStatus&
+  setNOutBytes(uint64_t nOutBytes)
+  {
+    m_wire.reset();
+    m_nOutBytes = nOutBytes;
+    return *this;
+  }
+
 private:
   uint64_t m_faceId;
   std::string m_remoteUri;
@@ -220,176 +248,14 @@
   uint64_t m_nInDatas;
   uint64_t m_nOutInterests;
   uint64_t m_nOutDatas;
+  uint64_t m_nInBytes;
+  uint64_t m_nOutBytes;
 
   mutable Block m_wire;
 };
 
-inline
-FaceStatus::FaceStatus()
-  : m_faceId(0)
-  , m_hasExpirationPeriod(false)
-  , m_flags(0)
-  , m_nInInterests(0)
-  , m_nInDatas(0)
-  , m_nOutInterests(0)
-  , m_nOutDatas(0)
-{
-}
-
-template<bool T>
-inline size_t
-FaceStatus::wireEncode(EncodingImpl<T>& encoder) const
-{
-  size_t totalLength = 0;
-
-  totalLength += prependNonNegativeIntegerBlock(encoder,
-                 tlv::nfd::NOutDatas, m_nOutDatas);
-  totalLength += prependNonNegativeIntegerBlock(encoder,
-                 tlv::nfd::NOutInterests, m_nOutInterests);
-  totalLength += prependNonNegativeIntegerBlock(encoder,
-                 tlv::nfd::NInDatas, m_nInDatas);
-  totalLength += prependNonNegativeIntegerBlock(encoder,
-                 tlv::nfd::NInInterests, m_nInInterests);
-  totalLength += prependNonNegativeIntegerBlock(encoder,
-                 tlv::nfd::FaceFlags, m_flags);
-  if (m_hasExpirationPeriod) {
-    totalLength += prependNonNegativeIntegerBlock(encoder,
-                   tlv::nfd::ExpirationPeriod, m_expirationPeriod.count());
-  }
-  totalLength += prependByteArrayBlock(encoder, tlv::nfd::LocalUri,
-                 reinterpret_cast<const uint8_t*>(m_localUri.c_str()), m_localUri.size());
-  totalLength += prependByteArrayBlock(encoder, tlv::nfd::Uri,
-                 reinterpret_cast<const uint8_t*>(m_remoteUri.c_str()), m_remoteUri.size());
-  totalLength += prependNonNegativeIntegerBlock(encoder,
-                 tlv::nfd::FaceId, m_faceId);
-
-  totalLength += encoder.prependVarNumber(totalLength);
-  totalLength += encoder.prependVarNumber(tlv::nfd::FaceStatus);
-  return totalLength;
-}
-
-inline const Block&
-FaceStatus::wireEncode() const
-{
-  if (m_wire.hasWire())
-    return m_wire;
-
-  EncodingEstimator estimator;
-  size_t estimatedSize = wireEncode(estimator);
-
-  EncodingBuffer buffer(estimatedSize, 0);
-  wireEncode(buffer);
-
-  m_wire = buffer.block();
-  return m_wire;
-}
-
-inline void
-FaceStatus::wireDecode(const Block& block)
-{
-  if (block.type() != tlv::nfd::FaceStatus) {
-    throw Error("expecting FaceStatus block");
-  }
-  m_wire = block;
-  m_wire.parse();
-  Block::element_const_iterator val = m_wire.elements_begin();
-
-  if (val != m_wire.elements_end() && val->type() == tlv::nfd::FaceId) {
-    m_faceId = readNonNegativeInteger(*val);
-    ++val;
-  }
-  else {
-    throw Error("missing required FaceId field");
-  }
-
-  if (val != m_wire.elements_end() && val->type() == tlv::nfd::Uri) {
-    m_remoteUri.assign(reinterpret_cast<const char*>(val->value()), val->value_size());
-    ++val;
-  }
-  else {
-    throw Error("missing required Uri field");
-  }
-
-  if (val != m_wire.elements_end() && val->type() == tlv::nfd::LocalUri) {
-    m_localUri.assign(reinterpret_cast<const char*>(val->value()), val->value_size());
-    ++val;
-  }
-  else {
-    throw Error("missing required LocalUri field");
-  }
-
-  if (val != m_wire.elements_end() && val->type() == tlv::nfd::ExpirationPeriod) {
-    m_expirationPeriod = time::milliseconds(readNonNegativeInteger(*val));
-    m_hasExpirationPeriod = true;
-    ++val;
-  }
-  else {
-    m_hasExpirationPeriod = false;
-    // ExpirationPeriod is optional
-  }
-
-  if (val != m_wire.elements_end() && val->type() == tlv::nfd::FaceFlags) {
-    m_flags = readNonNegativeInteger(*val);
-    ++val;
-  }
-  else {
-    throw Error("missing required FaceFlags field");
-  }
-
-  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NInInterests) {
-    m_nInInterests = readNonNegativeInteger(*val);
-    ++val;
-  }
-  else {
-    throw Error("missing required NInInterests field");
-  }
-
-  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NInDatas) {
-    m_nInDatas = readNonNegativeInteger(*val);
-    ++val;
-  }
-  else {
-    throw Error("missing required NInDatas field");
-  }
-
-  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NOutInterests) {
-    m_nOutInterests = readNonNegativeInteger(*val);
-    ++val;
-  }
-  else {
-    throw Error("missing required NOutInterests field");
-  }
-
-  if (val != m_wire.elements_end() && val->type() == tlv::nfd::NOutDatas) {
-    m_nOutDatas = readNonNegativeInteger(*val);
-    ++val;
-  }
-  else {
-    throw Error("missing required NOutDatas field");
-  }
-}
-
-inline std::ostream&
-operator<<(std::ostream& os, const FaceStatus& status)
-{
-  os << "FaceStatus("
-     << "FaceID: " << status.getFaceId() << ", "
-     << "RemoteUri: " << status.getRemoteUri() << ", "
-     << "LocalUri: " << status.getLocalUri() << ", ";
-
-  if (status.hasExpirationPeriod()) {
-    os << "ExpirationPeriod: " << status.getExpirationPeriod() << ", ";
-  }
-  else {
-    os << "ExpirationPeriod: infinite, ";
-  }
-
-  os << "Flags: " << status.getFlags() << ", "
-     << "Counters: " << status.getNInInterests() << "|" << status.getNInDatas()
-     << "|" << status.getNOutInterests() << "|" << status.getNOutDatas()
-     << ")";
-  return os;
-}
+std::ostream&
+operator<<(std::ostream& os, const FaceStatus& status);
 
 } // namespace nfd
 } // namespace ndn
diff --git a/src/management/nfd-rib-entry.cpp b/src/management/nfd-rib-entry.cpp
new file mode 100644
index 0000000..d075cc6
--- /dev/null
+++ b/src/management/nfd-rib-entry.cpp
@@ -0,0 +1,307 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2014 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "nfd-rib-entry.hpp"
+
+#include "management/nfd-control-command.hpp"
+
+namespace ndn {
+namespace nfd {
+
+const time::milliseconds Route::INFINITE_EXPIRATION_PERIOD(time::milliseconds::max());
+
+Route::Route()
+  : m_faceId(0)
+  , m_origin(0)
+  , m_cost(0)
+  , m_flags(ROUTE_FLAG_CHILD_INHERIT)
+  , m_expirationPeriod(INFINITE_EXPIRATION_PERIOD)
+  , m_hasInfiniteExpirationPeriod(true)
+{
+}
+
+Route::Route(const Block& block)
+{
+  wireDecode(block);
+}
+
+template<bool T>
+size_t
+Route::wireEncode(EncodingImpl<T>& block) const
+{
+  size_t totalLength = 0;
+
+  // Absence of an ExpirationPeriod signifies non-expiration
+  if (!m_hasInfiniteExpirationPeriod) {
+    totalLength += prependNonNegativeIntegerBlock(block,
+                                                  ndn::tlv::nfd::ExpirationPeriod,
+                                                  m_expirationPeriod.count());
+  }
+
+  totalLength += prependNonNegativeIntegerBlock(block,
+                                                ndn::tlv::nfd::Flags,
+                                                m_flags);
+
+  totalLength += prependNonNegativeIntegerBlock(block,
+                                                ndn::tlv::nfd::Cost,
+                                                m_cost);
+
+  totalLength += prependNonNegativeIntegerBlock(block,
+                                                ndn::tlv::nfd::Origin,
+                                                m_origin);
+
+  totalLength += prependNonNegativeIntegerBlock(block,
+                                                ndn::tlv::nfd::FaceId,
+                                                m_faceId);
+
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(ndn::tlv::nfd::Route);
+
+  return totalLength;
+}
+
+template size_t
+Route::wireEncode<true>(EncodingImpl<true>& block) const;
+
+template size_t
+Route::wireEncode<false>(EncodingImpl<false>& block) const;
+
+const Block&
+Route::wireEncode() const
+{
+  if (m_wire.hasWire()) {
+    return m_wire;
+  }
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+
+  return m_wire;
+}
+
+void
+Route::wireDecode(const Block& wire)
+{
+  m_faceId = 0;
+  m_origin = 0;
+  m_cost = 0;
+  m_flags = 0;
+  m_expirationPeriod = time::milliseconds::min();
+
+  m_wire = wire;
+
+  if (m_wire.type() != tlv::nfd::Route) {
+    std::stringstream error;
+    error << "Expected Route Block, but Block is of a different type: #"
+          << m_wire.type();
+    throw Error(error.str());
+  }
+
+  m_wire.parse();
+
+  Block::element_const_iterator val = m_wire.elements_begin();
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::FaceId) {
+    m_faceId = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    throw Error("Missing required FaceId field");
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::Origin) {
+    m_origin = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    throw Error("Missing required Origin field");
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::Cost) {
+    m_cost = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    throw Error("Missing required Cost field");
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::Flags) {
+    m_flags = readNonNegativeInteger(*val);
+    ++val;
+  }
+  else {
+    throw Error("Missing required Flags field");
+  }
+
+  if (val != m_wire.elements_end() && val->type() == tlv::nfd::ExpirationPeriod) {
+    m_expirationPeriod = time::milliseconds(readNonNegativeInteger(*val));
+    m_hasInfiniteExpirationPeriod = false;
+  }
+  else {
+    m_expirationPeriod = INFINITE_EXPIRATION_PERIOD;
+    m_hasInfiniteExpirationPeriod = true;
+  }
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Route& route)
+{
+  os << "Route("
+     << "FaceId: " << route.getFaceId() << ", "
+     << "Origin: " << route.getOrigin() << ", "
+     << "Cost: " << route.getCost() << ", "
+     << "Flags: " << route.getFlags() << ", ";
+
+  if (!route.hasInfiniteExpirationPeriod()) {
+    os << "ExpirationPeriod: " << route.getExpirationPeriod();
+  }
+  else {
+    os << "ExpirationPeriod: Infinity";
+  }
+
+  os << ")";
+
+  return os;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+
+
+RibEntry::RibEntry()
+{
+}
+
+RibEntry::RibEntry(const Block& block)
+{
+  wireDecode(block);
+}
+
+
+template<bool T>
+size_t
+RibEntry::wireEncode(EncodingImpl<T>& block) const
+{
+  size_t totalLength = 0;
+
+  for (std::list<Route>::const_reverse_iterator it = m_routes.rbegin();
+       it != m_routes.rend(); ++it)
+    {
+      totalLength += it->wireEncode(block);
+    }
+
+  totalLength += m_prefix.wireEncode(block);
+
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::nfd::RibEntry);
+
+  return totalLength;
+}
+
+template size_t
+RibEntry::wireEncode<true>(EncodingImpl<true>& block) const;
+
+template size_t
+RibEntry::wireEncode<false>(EncodingImpl<false>& block) const;
+
+const Block&
+RibEntry::wireEncode() const
+{
+  if (m_wire.hasWire()) {
+    return m_wire;
+  }
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+
+  return m_wire;
+}
+
+void
+RibEntry::wireDecode(const Block& wire)
+{
+  m_prefix.clear();
+  m_routes.clear();
+
+  m_wire = wire;
+
+  if (m_wire.type() != tlv::nfd::RibEntry) {
+    std::stringstream error;
+    error << "Expected RibEntry Block, but Block is of a different type: #"
+          << m_wire.type();
+    throw Error(error.str());
+  }
+
+  m_wire.parse();
+
+  Block::element_const_iterator val = m_wire.elements_begin();
+
+  if (val != m_wire.elements_end() && val->type() == Tlv::Name) {
+    m_prefix.wireDecode(*val);
+    ++val;
+  }
+  else {
+    throw Error("Missing required Name field");
+  }
+
+  for (; val != m_wire.elements_end(); ++val) {
+
+    if (val->type() == tlv::nfd::Route) {
+      m_routes.push_back(Route(*val));
+    }
+    else {
+      std::stringstream error;
+      error << "Expected Route Block, but Block is of a different type: #"
+            << m_wire.type();
+      throw Error(error.str());
+    }
+  }
+}
+
+std::ostream&
+operator<<(std::ostream& os, const RibEntry& entry)
+{
+  os << "RibEntry{\n"
+     << "  Name: " << entry.getName() << "\n";
+
+  for (RibEntry::iterator it = entry.begin(); it != entry.end(); ++it) {
+    os << "  " << *it << "\n";
+  }
+
+  os << "}";
+
+  return os;
+}
+
+} // namespace nfd
+} // namespace ndn
diff --git a/src/management/nfd-rib-entry.hpp b/src/management/nfd-rib-entry.hpp
new file mode 100644
index 0000000..9ea84c2
--- /dev/null
+++ b/src/management/nfd-rib-entry.hpp
@@ -0,0 +1,282 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2014 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_MANAGEMENT_NFD_RIB_ENTRY_HPP
+#define NDN_MANAGEMENT_NFD_RIB_ENTRY_HPP
+
+#include "../encoding/block.hpp"
+#include "../encoding/encoding-buffer.hpp"
+#include "../encoding/tlv-nfd.hpp"
+#include "../name.hpp"
+
+#include <list>
+
+namespace ndn {
+namespace nfd {
+
+/**
+ * @ingroup management
+ *
+ * @brief Data abstraction for Route
+ *
+ * A route indicates the availability of content via a certain face and
+ * provides meta-information about the face.
+ *
+ *     Route := ROUTE-TYPE TLV-LENGTH
+ *                FaceId
+ *                Origin
+ *                Cost
+ *                Flags
+ *                ExpirationPeriod?
+ *
+ * @sa http://redmine.named-data.net/projects/nfd/wiki/RibMgmt
+ */
+class Route
+{
+public:
+  class Error : public Tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what) : Tlv::Error(what)
+    {
+    }
+  };
+
+  Route();
+
+  explicit
+  Route(const Block& block);
+
+  uint64_t
+  getFaceId() const
+  {
+    return m_faceId;
+  }
+
+  Route&
+  setFaceId(uint64_t faceId)
+  {
+    m_faceId = faceId;
+    m_wire.reset();
+    return *this;
+  }
+
+  uint64_t
+  getOrigin() const
+  {
+    return m_origin;
+  }
+
+  Route&
+  setOrigin(uint64_t origin)
+  {
+    m_origin = origin;
+    m_wire.reset();
+    return *this;
+  }
+
+  uint64_t
+  getCost() const
+  {
+    return m_cost;
+  }
+
+  Route&
+  setCost(uint64_t cost)
+  {
+    m_cost = cost;
+    m_wire.reset();
+    return *this;
+  }
+
+  uint64_t
+  getFlags() const
+  {
+    return m_flags;
+  }
+
+  Route&
+  setFlags(uint64_t flags)
+  {
+    m_flags = flags;
+    m_wire.reset();
+    return *this;
+  }
+
+  static const time::milliseconds INFINITE_EXPIRATION_PERIOD;
+
+  const time::milliseconds&
+  getExpirationPeriod() const
+  {
+    return m_expirationPeriod;
+  }
+
+  Route&
+  setExpirationPeriod(const time::milliseconds& expirationPeriod)
+  {
+    m_expirationPeriod = expirationPeriod;
+
+    m_hasInfiniteExpirationPeriod = m_expirationPeriod == INFINITE_EXPIRATION_PERIOD;
+
+    m_wire.reset();
+    return *this;
+  }
+
+  bool
+  hasInfiniteExpirationPeriod() const
+  {
+    return m_hasInfiniteExpirationPeriod;
+  }
+
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T>& block) const;
+
+  const Block&
+  wireEncode() const;
+
+  void
+  wireDecode(const Block& wire);
+
+private:
+  uint64_t m_faceId;
+  uint64_t m_origin;
+  uint64_t m_cost;
+  uint64_t m_flags;
+  time::milliseconds m_expirationPeriod;
+  bool m_hasInfiniteExpirationPeriod;
+
+  mutable Block m_wire;
+};
+
+std::ostream&
+operator<<(std::ostream& os, const Route& route);
+
+/**
+ * @ingroup management
+ *
+ * @brief Data abstraction for RIB entry
+ *
+ * A RIB entry contains one or more routes for the name prefix
+ *
+ *     RibEntry := RIB-ENTRY-TYPE TLV-LENGTH
+ *                Name
+ *                Route+
+ *
+ * @sa http://redmine.named-data.net/projects/nfd/wiki/RibMgmt
+ */
+class RibEntry
+{
+public:
+  class Error : public Tlv::Error
+  {
+  public:
+    Error(const std::string& what) : Tlv::Error(what)
+    {
+    }
+  };
+
+  typedef std::list<Route> RouteList;
+  typedef RouteList::const_iterator iterator;
+
+  RibEntry();
+
+  explicit
+  RibEntry(const Block& block);
+
+  const Name&
+  getName() const
+  {
+    return m_prefix;
+  }
+
+  RibEntry&
+  setName(const Name& prefix)
+  {
+    m_prefix = prefix;
+    m_wire.reset();
+    return *this;
+  }
+
+  const std::list<Route>&
+  getRoutes() const
+  {
+    return m_routes;
+  }
+
+  RibEntry&
+  addRoute(const Route& route)
+  {
+    m_routes.push_back(route);
+    m_wire.reset();
+    return *this;
+  }
+
+  RibEntry&
+  clearRoutes()
+  {
+    m_routes.clear();
+    return *this;
+  }
+
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T>& block) const;
+
+  const Block&
+  wireEncode() const;
+
+  void
+  wireDecode(const Block& wire);
+
+  iterator
+  begin() const;
+
+  iterator
+  end() const;
+
+private:
+  Name m_prefix;
+  RouteList m_routes;
+
+  mutable Block m_wire;
+};
+
+inline RibEntry::iterator
+RibEntry::begin() const
+{
+  return m_routes.begin();
+}
+
+inline RibEntry::iterator
+RibEntry::end() const
+{
+  return m_routes.end();
+}
+
+std::ostream&
+operator<<(std::ostream& os, const RibEntry& entry);
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MANAGEMENT_NFD_RIB_ENTRY_HPP
diff --git a/src/transport/stream-transport.hpp b/src/transport/stream-transport.hpp
index 04aeac3..9bb5b7b 100644
--- a/src/transport/stream-transport.hpp
+++ b/src/transport/stream-transport.hpp
@@ -28,17 +28,16 @@
 
 namespace ndn {
 
-const size_t MAX_LENGTH = 9000;
-
 template<class BaseTransport, class Protocol>
 class StreamTransportImpl
 {
 public:
-  typedef BaseTransport base_transport;
-  typedef Protocol      protocol;
-  typedef StreamTransportImpl<BaseTransport,Protocol> impl;
+  typedef StreamTransportImpl<BaseTransport,Protocol> Impl;
 
-  StreamTransportImpl(base_transport& transport, boost::asio::io_service& ioService)
+  typedef std::list<Block> BlockSequence;
+  typedef std::list<BlockSequence> TransmissionQueue;
+
+  StreamTransportImpl(BaseTransport& transport, boost::asio::io_service& ioService)
     : m_transport(transport)
     , m_socket(ioService)
     , m_inputBufferSize(0)
@@ -58,23 +57,11 @@
         resume();
         m_transport.m_isConnected = true;
 
-        for (std::list<Block>::iterator i = m_sendQueue.begin(); i != m_sendQueue.end(); ++i)
-          m_socket.async_send(boost::asio::buffer(i->wire(), i->size()),
-                              bind(&impl::handle_async_send, this, _1, *i));
-
-        for (std::list< std::pair<Block,Block> >::iterator i = m_sendPairQueue.begin();
-             i != m_sendPairQueue.end(); ++i)
-          {
-            std::vector<boost::asio::const_buffer> buffer;
-            buffer.reserve(2);
-            buffer.push_back(boost::asio::buffer(i->first.wire(),  i->first.size()));
-            buffer.push_back(boost::asio::buffer(i->second.wire(), i->second.size()));
-            m_socket.async_send(buffer,
-                                bind(&impl::handle_async_send2, this, _1, i->first, i->second));
-          }
-
-        m_sendQueue.clear();
-        m_sendPairQueue.clear();
+        if (!m_transmissionQueue.empty()) {
+          boost::asio::async_write(m_socket, *m_transmissionQueue.begin(),
+                                   bind(&Impl::handleAsyncWrite, this, _1,
+                                        m_transmissionQueue.begin()));
+        }
       }
     else
       {
@@ -96,19 +83,19 @@
   }
 
   void
-  connect(const typename protocol::endpoint& endpoint)
+  connect(const typename Protocol::endpoint& endpoint)
   {
     if (!m_connectionInProgress) {
       m_connectionInProgress = true;
 
-      // Wait at most 4 time::seconds to connect
+      // Wait at most 4 seconds to connect
       /// @todo Decide whether this number should be configurable
       m_connectTimer.expires_from_now(boost::posix_time::seconds(4));
-      m_connectTimer.async_wait(bind(&impl::connectTimeoutHandler, this, _1));
+      m_connectTimer.async_wait(bind(&Impl::connectTimeoutHandler, this, _1));
 
       m_socket.open();
       m_socket.async_connect(endpoint,
-                             bind(&impl::connectHandler, this, _1));
+                             bind(&Impl::connectHandler, this, _1));
     }
   }
 
@@ -124,8 +111,7 @@
 
     m_transport.m_isConnected = false;
     m_transport.m_isExpectingData = false;
-    m_sendQueue.clear();
-    m_sendPairQueue.clear();
+    m_transmissionQueue.clear();
   }
 
   void
@@ -151,47 +137,77 @@
       {
         m_transport.m_isExpectingData = true;
         m_inputBufferSize = 0;
-        m_socket.async_receive(boost::asio::buffer(m_inputBuffer, MAX_LENGTH), 0,
-                               bind(&impl::handle_async_receive, this, _1, _2));
+        m_socket.async_receive(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE), 0,
+                               bind(&Impl::handleAsyncReceive, this, _1, _2));
       }
   }
 
   void
   send(const Block& wire)
   {
-    if (!m_transport.m_isConnected)
-      m_sendQueue.push_back(wire);
-    else
-      m_socket.async_send(boost::asio::buffer(wire.wire(), wire.size()),
-                         bind(&impl::handle_async_send, this, _1, wire));
+    BlockSequence sequence;
+    sequence.push_back(wire);
+    m_transmissionQueue.push_back(sequence);
+
+    if (m_transport.m_isConnected && m_transmissionQueue.size() == 1) {
+      boost::asio::async_write(m_socket, *m_transmissionQueue.begin(),
+                               bind(&Impl::handleAsyncWrite, this, _1,
+                                    m_transmissionQueue.begin()));
+    }
+
+    // if not connected or there is transmission in progress (m_transmissionQueue.size() > 1),
+    // next write will be scheduled either in connectHandler or in asyncWriteHandler
   }
 
   void
   send(const Block& header, const Block& payload)
   {
-    if (!m_transport.m_isConnected)
-      {
-        m_sendPairQueue.push_back(std::make_pair(header, payload));
-      }
-    else
-      {
-        std::vector<boost::asio::const_buffer> buffers;
-        buffers.reserve(2);
-        buffers.push_back(boost::asio::buffer(header.wire(),  header.size()));
-        buffers.push_back(boost::asio::buffer(payload.wire(), payload.size()));
+    BlockSequence sequence;
+    sequence.push_back(header);
+    sequence.push_back(payload);
+    m_transmissionQueue.push_back(sequence);
 
-        m_socket.async_send(buffers,
-                            bind(&impl::handle_async_send2, this, _1, header, payload));
-      }
+    if (m_transport.m_isConnected && m_transmissionQueue.size() == 1) {
+      boost::asio::async_write(m_socket, *m_transmissionQueue.begin(),
+                               bind(&Impl::handleAsyncWrite, this, _1,
+                                    m_transmissionQueue.begin()));
+    }
+
+    // if not connected or there is transmission in progress (m_transmissionQueue.size() > 1),
+    // next write will be scheduled either in connectHandler or in asyncWriteHandler
   }
 
-  inline bool
-  processAll(uint8_t* buffer, size_t& offset, size_t availableSize)
+  void
+  handleAsyncWrite(const boost::system::error_code& error,
+                   TransmissionQueue::iterator queueItem)
   {
-    Block element;
-    while(offset < availableSize)
+    if (error)
       {
-        bool ok = Block::fromBuffer(buffer + offset, availableSize - offset, element);
+        if (error == boost::system::errc::operation_canceled) {
+          // async receive has been explicitly cancelled (e.g., socket close)
+          return;
+        }
+
+        m_transport.close();
+        throw Transport::Error(error, "error while sending data to socket");
+      }
+
+    m_transmissionQueue.erase(queueItem);
+
+    if (!m_transmissionQueue.empty()) {
+      boost::asio::async_write(m_socket, *m_transmissionQueue.begin(),
+                               bind(&Impl::handleAsyncWrite, this, _1,
+                                    m_transmissionQueue.begin()));
+    }
+  }
+
+  bool
+  processAll(uint8_t* buffer, size_t& offset, size_t nBytesAvailable)
+  {
+    while (offset < nBytesAvailable)
+      {
+        Block element;
+        bool ok = Block::fromBuffer(buffer + offset, nBytesAvailable - offset, element);
         if (!ok)
           return false;
 
@@ -202,7 +218,7 @@
   }
 
   void
-  handle_async_receive(const boost::system::error_code& error, std::size_t bytes_recvd)
+  handleAsyncReceive(const boost::system::error_code& error, std::size_t nBytesRecvd)
   {
     if (error)
       {
@@ -215,12 +231,12 @@
         throw Transport::Error(error, "error while receiving data from socket");
       }
 
-    m_inputBufferSize += bytes_recvd;
+    m_inputBufferSize += nBytesRecvd;
     // do magic
 
     std::size_t offset = 0;
-    bool ok = processAll(m_inputBuffer, offset, m_inputBufferSize);
-    if (!ok && m_inputBufferSize == MAX_LENGTH && offset == 0)
+    bool hasProcessedSome = processAll(m_inputBuffer, offset, m_inputBufferSize);
+    if (!hasProcessedSome && m_inputBufferSize == MAX_NDN_PACKET_SIZE && offset == 0)
       {
         m_transport.close();
         throw Transport::Error(boost::system::error_code(),
@@ -242,32 +258,18 @@
       }
 
     m_socket.async_receive(boost::asio::buffer(m_inputBuffer + m_inputBufferSize,
-                                               MAX_LENGTH - m_inputBufferSize), 0,
-                           bind(&impl::handle_async_receive, this, _1, _2));
-  }
-
-  void
-  handle_async_send(const boost::system::error_code& error, const Block& wire)
-  {
-    // pass (needed to keep data block alive during the send)
-  }
-
-  void
-  handle_async_send2(const boost::system::error_code& error,
-                     const Block& header, const Block& payload)
-  {
-    // pass (needed to keep data blocks alive during the send)
+                                               MAX_NDN_PACKET_SIZE - m_inputBufferSize), 0,
+                           bind(&Impl::handleAsyncReceive, this, _1, _2));
   }
 
 protected:
-  base_transport& m_transport;
+  BaseTransport& m_transport;
 
-  typename protocol::socket m_socket;
-  uint8_t m_inputBuffer[MAX_LENGTH];
+  typename Protocol::socket m_socket;
+  uint8_t m_inputBuffer[MAX_NDN_PACKET_SIZE];
   size_t m_inputBufferSize;
 
-  std::list< Block > m_sendQueue;
-  std::list< std::pair<Block, Block> > m_sendPairQueue;
+  TransmissionQueue m_transmissionQueue;
   bool m_connectionInProgress;
 
   boost::asio::deadline_timer m_connectTimer;
@@ -278,19 +280,17 @@
 class StreamTransportWithResolverImpl : public StreamTransportImpl<BaseTransport, Protocol>
 {
 public:
-  typedef BaseTransport base_transport;
-  typedef Protocol      protocol;
-  typedef StreamTransportWithResolverImpl<BaseTransport,Protocol> impl;
+  typedef StreamTransportWithResolverImpl<BaseTransport,Protocol> Impl;
 
-  StreamTransportWithResolverImpl(base_transport& transport, boost::asio::io_service& ioService)
-    : StreamTransportImpl<base_transport, protocol>(transport, ioService)
+  StreamTransportWithResolverImpl(BaseTransport& transport, boost::asio::io_service& ioService)
+    : StreamTransportImpl<BaseTransport, Protocol>(transport, ioService)
   {
   }
 
   void
   resolveHandler(const boost::system::error_code& error,
-                 typename protocol::resolver::iterator endpoint,
-                 const shared_ptr<typename protocol::resolver>&)
+                 typename Protocol::resolver::iterator endpoint,
+                 const shared_ptr<typename Protocol::resolver>&)
   {
     if (error)
       {
@@ -300,7 +300,7 @@
         throw Transport::Error(error, "Error during resolution of host or port");
       }
 
-    typename protocol::resolver::iterator end;
+    typename Protocol::resolver::iterator end;
     if (endpoint == end)
       {
         this->m_transport.close();
@@ -308,25 +308,25 @@
       }
 
     this->m_socket.async_connect(*endpoint,
-                                 bind(&impl::connectHandler, this, _1));
+                                 bind(&Impl::connectHandler, this, _1));
   }
 
   void
-  connect(const typename protocol::resolver::query& query)
+  connect(const typename Protocol::resolver::query& query)
   {
     if (!this->m_connectionInProgress) {
       this->m_connectionInProgress = true;
 
-      // Wait at most 4 time::seconds to connect
+      // Wait at most 4 seconds to connect
       /// @todo Decide whether this number should be configurable
       this->m_connectTimer.expires_from_now(boost::posix_time::seconds(4));
-      this->m_connectTimer.async_wait(bind(&impl::connectTimeoutHandler, this, _1));
+      this->m_connectTimer.async_wait(bind(&Impl::connectTimeoutHandler, this, _1));
 
-      // typename boost::asio::ip::basic_resolver< protocol > resolver;
-      shared_ptr<typename protocol::resolver> resolver =
-        make_shared<typename protocol::resolver>(ref(this->m_socket.get_io_service()));
+      // typename boost::asio::ip::basic_resolver< Protocol > resolver;
+      shared_ptr<typename Protocol::resolver> resolver =
+        make_shared<typename Protocol::resolver>(ref(this->m_socket.get_io_service()));
 
-      resolver->async_resolve(query, bind(&impl::resolveHandler, this, _1, _2, resolver));
+      resolver->async_resolve(query, bind(&Impl::resolveHandler, this, _1, _2, resolver));
     }
   }
 };
diff --git a/src/transport/tcp-transport.cpp b/src/transport/tcp-transport.cpp
index 6fab8e0..7ffe530 100644
--- a/src/transport/tcp-transport.cpp
+++ b/src/transport/tcp-transport.cpp
@@ -53,18 +53,21 @@
 void
 TcpTransport::send(const Block& wire)
 {
+  BOOST_ASSERT(static_cast<bool>(m_impl));
   m_impl->send(wire);
 }
 
 void
 TcpTransport::send(const Block& header, const Block& payload)
 {
+  BOOST_ASSERT(static_cast<bool>(m_impl));
   m_impl->send(header, payload);
 }
 
 void
 TcpTransport::close()
 {
+  BOOST_ASSERT(static_cast<bool>(m_impl));
   m_impl->close();
   m_impl.reset();
 }
@@ -72,12 +75,15 @@
 void
 TcpTransport::pause()
 {
-  m_impl->pause();
+  if (static_cast<bool>(m_impl)) {
+    m_impl->pause();
+  }
 }
 
 void
 TcpTransport::resume()
 {
+  BOOST_ASSERT(static_cast<bool>(m_impl));
   m_impl->resume();
 }
 
diff --git a/src/transport/unix-transport.cpp b/src/transport/unix-transport.cpp
index cbfada8..03ee8b6 100644
--- a/src/transport/unix-transport.cpp
+++ b/src/transport/unix-transport.cpp
@@ -117,8 +117,9 @@
 void
 UnixTransport::pause()
 {
-  BOOST_ASSERT(static_cast<bool>(m_impl));
-  m_impl->pause();
+  if (static_cast<bool>(m_impl)) {
+    m_impl->pause();
+  }
 }
 
 void
diff --git a/src/util/command-interest-generator.hpp b/src/util/command-interest-generator.hpp
index 8f97b7b..651a19b 100644
--- a/src/util/command-interest-generator.hpp
+++ b/src/util/command-interest-generator.hpp
@@ -32,6 +32,9 @@
 /**
  * @brief Helper class to generate CommandInterests
  *
+ * @deprecated Use SignedInterest (generated by KeyChain) instead.
+ * See KeyChain::sign and KeyChain::signByIdentity methods.
+ *
  * @see http://redmine.named-data.net/projects/nfd/wiki/Command_Interests
  */
 class CommandInterestGenerator
diff --git a/src/util/command-interest-validator.hpp b/src/util/command-interest-validator.hpp
index 7d179a0..d132231 100644
--- a/src/util/command-interest-validator.hpp
+++ b/src/util/command-interest-validator.hpp
@@ -30,6 +30,15 @@
 
 namespace ndn {
 
+/**
+ * @brief Helper class to validate CommandInterests
+ *
+ * @deprecated Use ValidatorConfig instead.
+ * See http://redmine.named-data.net/projects/ndn-cxx/wiki/CommandValidatorConf
+ * for more details about the configuration format of ValidatorConfig.
+ *
+ * @see http://redmine.named-data.net/projects/nfd/wiki/Command_Interests
+ */
 class CommandInterestValidator : public Validator
 {
 public:
diff --git a/tests/integrated/test-faces.cpp b/tests/integrated/test-faces.cpp
index c86cd62..e7ce988 100644
--- a/tests/integrated/test-faces.cpp
+++ b/tests/integrated/test-faces.cpp
@@ -21,6 +21,7 @@
 
 #include "face.hpp"
 #include "util/scheduler.hpp"
+#include "security/key-chain.hpp"
 
 #include "boost-test.hpp"
 
@@ -136,6 +137,20 @@
                        bind(&FacesFixture::onData, this),
                        bind(&FacesFixture::onTimeout, this));
 
+  Name veryLongName;
+  for (size_t i = 0; i <= MAX_NDN_PACKET_SIZE / 10; i++)
+    {
+      veryLongName.append("0123456789");
+    }
+
+  BOOST_CHECK_THROW(face.expressInterest(veryLongName, OnData(), OnTimeout()), Face::Error);
+
+  shared_ptr<Data> data = make_shared<Data>(veryLongName);
+  data->setContent(reinterpret_cast<const uint8_t*>("01234567890"), 10);
+  KeyChain keyChain;
+  keyChain.sign(*data);
+  BOOST_CHECK_THROW(face.put(*data), Face::Error);
+
   BOOST_REQUIRE_NO_THROW(face.processEvents());
 
   BOOST_CHECK_EQUAL(nData, 1);
diff --git a/tests/unit-tests/management/test-nfd-control-command.cpp b/tests/unit-tests/management/test-nfd-control-command.cpp
index 78b2877..045cd68 100644
--- a/tests/unit-tests/management/test-nfd-control-command.cpp
+++ b/tests/unit-tests/management/test-nfd-control-command.cpp
@@ -225,8 +225,7 @@
   BOOST_CHECK_EQUAL(p1.getCost(), 0);
   BOOST_REQUIRE(p1.hasFlags());
   BOOST_CHECK_EQUAL(p1.getFlags(), static_cast<uint64_t>(ROUTE_FLAG_CHILD_INHERIT));
-  BOOST_REQUIRE(p1.hasExpirationPeriod());
-  BOOST_CHECK_GT(p1.getExpirationPeriod(), time::hours(240));
+  BOOST_CHECK_EQUAL(p1.hasExpirationPeriod(), false);
 
   ControlParameters p2;
   p2.setName("ndn:/example")
@@ -234,7 +233,7 @@
     .setCost(6);
   BOOST_CHECK_NO_THROW(command.validateRequest(p2));
   command.applyDefaultsToRequest(p2);
-  BOOST_CHECK_EQUAL(p2.getExpirationPeriod(), time::hours(1));
+  BOOST_CHECK_EQUAL(p2.hasExpirationPeriod(), false);
   BOOST_CHECK_NO_THROW(command.validateResponse(p2));
 }
 
diff --git a/tests/unit-tests/management/test-nfd-face-status.cpp b/tests/unit-tests/management/test-nfd-face-status.cpp
index 2691645..22c7264 100644
--- a/tests/unit-tests/management/test-nfd-face-status.cpp
+++ b/tests/unit-tests/management/test-nfd-face-status.cpp
@@ -39,7 +39,9 @@
          .setNInInterests(10)
          .setNInDatas(200)
          .setNOutInterests(3000)
-         .setNOutDatas(4);
+         .setNOutDatas(4)
+         .setNInBytes(1329719163)
+         .setNOutBytes(999110448);
 
   Block wire;
   BOOST_REQUIRE_NO_THROW(wire = status1.wireEncode());
@@ -50,15 +52,16 @@
   //  printf("0x%02x, ", *it);
   // }
   static const uint8_t expected[] = {
-    0x80, 0x46, 0x69, 0x01, 0x64, 0x72, 0x15, 0x74, 0x63, 0x70, 0x34, 0x3a,
+    0x80, 0x52, 0x69, 0x01, 0x64, 0x72, 0x15, 0x74, 0x63, 0x70, 0x34, 0x3a,
     0x2f, 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x31, 0x3a,
     0x36, 0x33, 0x36, 0x33, 0x81, 0x16, 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f,
     0x2f, 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x32, 0x3a, 0x35,
     0x35, 0x35, 0x35, 0x35, 0x6d, 0x02, 0x27, 0x10, 0xc2, 0x01, 0x02, 0x90,
-    0x01, 0x0a, 0x91, 0x01, 0xc8, 0x92, 0x02, 0x0b, 0xb8, 0x93, 0x01, 0x04
+    0x01, 0x0a, 0x91, 0x01, 0xc8, 0x92, 0x02, 0x0b, 0xb8, 0x93, 0x01, 0x04,
+    0x94, 0x04, 0x4f, 0x41, 0xe7, 0x7b, 0x95, 0x04, 0x3b, 0x8d, 0x37, 0x30
   };
-  BOOST_REQUIRE_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
-                                  wire.begin(), wire.end());
+  BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+                                wire.begin(), wire.end());
 
   BOOST_REQUIRE_NO_THROW(FaceStatus(wire));
   FaceStatus status2(wire);
@@ -70,15 +73,20 @@
   BOOST_CHECK_EQUAL(status1.getNInDatas(), status2.getNInDatas());
   BOOST_CHECK_EQUAL(status1.getNOutInterests(), status2.getNOutInterests());
   BOOST_CHECK_EQUAL(status1.getNOutDatas(), status2.getNOutDatas());
+  BOOST_CHECK_EQUAL(status1.getNInBytes(), status2.getNInBytes());
+  BOOST_CHECK_EQUAL(status1.getNOutBytes(), status2.getNOutBytes());
 
   std::ostringstream os;
   os << status2;
-  BOOST_CHECK_EQUAL(os.str(), "FaceStatus(FaceID: 100, "
-                              "RemoteUri: tcp4://192.0.2.1:6363, "
-                              "LocalUri: tcp4://192.0.2.2:55555, "
-                              "ExpirationPeriod: 10000 milliseconds, "
-                              "Flags: 2, "
-                              "Counters: 10|200|3000|4)");
+  BOOST_CHECK_EQUAL(os.str(), "FaceStatus(FaceID: 100,\n"
+                              "RemoteUri: tcp4://192.0.2.1:6363,\n"
+                              "LocalUri: tcp4://192.0.2.2:55555,\n"
+                              "ExpirationPeriod: 10000 milliseconds,\n"
+                              "Flags: 2,\n"
+                              "Counters: { Interests: {in: 10, out: 3000},\n"
+                              "            Data: {in: 200, out: 4},\n"
+                              "            bytes: {in: 1329719163, out: 999110448} }\n"
+                              ")");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit-tests/management/test-nfd-rib-entry.cpp b/tests/unit-tests/management/test-nfd-rib-entry.cpp
new file mode 100644
index 0000000..858598f
--- /dev/null
+++ b/tests/unit-tests/management/test-nfd-rib-entry.cpp
@@ -0,0 +1,341 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2014 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "management/nfd-rib-entry.hpp"
+#include "management/nfd-control-command.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace nfd {
+
+BOOST_AUTO_TEST_SUITE(ManagementTestNfdRibEntry)
+
+const uint8_t RouteData[] =
+{
+  0x81, 0x10, 0x69, 0x01, 0x01, 0x6f, 0x01, 0x80, 0x6a, 0x01, 0x64, 0x6c, 0x01, 0x02,
+  0x6d, 0x02, 0x27, 0x10
+};
+
+const uint8_t RouteInfiniteExpirationPeriod[] =
+{
+  0x81, 0x0C, 0x69, 0x01, 0x01, 0x6f, 0x01, 0x80, 0x6a, 0x01, 0x64, 0x6c, 0x01, 0x02
+};
+
+const uint8_t RibEntryData[] =
+{
+  // Header + Name
+  0x80, 0x34, 0x07, 0x0e, 0x08, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f,
+  0x08, 0x05, 0x77, 0x6f, 0x72, 0x6c, 0x64,
+  // Route
+  0x81, 0x10, 0x69, 0x01, 0x01, 0x6f, 0x01, 0x80, 0x6a, 0x01, 0x64, 0x6c, 0x01, 0x02,
+  0x6d, 0x02, 0x27, 0x10,
+  // Route
+  0x81, 0x10, 0x69, 0x01, 0x02, 0x6f, 0x01, 0x00, 0x6a, 0x01, 0x20, 0x6c, 0x01, 0x01,
+  0x6d, 0x02, 0x13, 0x88
+};
+
+const uint8_t RibEntryInfiniteExpirationPeriod[] =
+{
+  // Header + Name
+  0x80, 0x30, 0x07, 0x0e, 0x08, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f,
+  0x08, 0x05, 0x77, 0x6f, 0x72, 0x6c, 0x64,
+  // Route
+  0x81, 0x10, 0x69, 0x01, 0x01, 0x6f, 0x01, 0x80, 0x6a, 0x01, 0x64, 0x6c, 0x01, 0x02,
+  0x6d, 0x02, 0x27, 0x10,
+  // Route with no ExpirationPeriod
+  0x81, 0x0C, 0x69, 0x01, 0x02, 0x6f, 0x01, 0x00, 0x6a, 0x01, 0x20, 0x6c, 0x01, 0x01,
+};
+
+BOOST_AUTO_TEST_CASE(RouteEncode)
+{
+  Route route;
+  route.setFaceId(1);
+  route.setOrigin(128);
+  route.setCost(100);
+  route.setFlags(ndn::nfd::ROUTE_FLAG_CAPTURE);
+  route.setExpirationPeriod(time::milliseconds(10000));
+
+  const Block& wire = route.wireEncode();
+
+  BOOST_REQUIRE_EQUAL_COLLECTIONS(RouteData,
+                                  RouteData + sizeof(RouteData),
+                                  wire.begin(), wire.end());
+}
+
+BOOST_AUTO_TEST_CASE(RouteDecode)
+{
+  Route route;
+
+  BOOST_REQUIRE_NO_THROW(route.wireDecode(Block(RouteData, sizeof(RouteData))));
+
+  BOOST_REQUIRE_EQUAL(route.getFaceId(), 1);
+  BOOST_REQUIRE_EQUAL(route.getOrigin(), 128);
+  BOOST_REQUIRE_EQUAL(route.getCost(), 100);
+  BOOST_REQUIRE_EQUAL(route.getFlags(), static_cast<uint64_t>(ndn::nfd::ROUTE_FLAG_CAPTURE));
+  BOOST_REQUIRE_EQUAL(route.getExpirationPeriod(), time::milliseconds(10000));
+  BOOST_REQUIRE_EQUAL(route.hasInfiniteExpirationPeriod(), false);
+}
+
+BOOST_AUTO_TEST_CASE(RouteInfiniteExpirationPeriodEncode)
+{
+  Route route;
+  route.setFaceId(1);
+  route.setOrigin(128);
+  route.setCost(100);
+  route.setFlags(ndn::nfd::ROUTE_FLAG_CAPTURE);
+  route.setExpirationPeriod(Route::INFINITE_EXPIRATION_PERIOD);
+
+  const Block& wire = route.wireEncode();
+
+  BOOST_REQUIRE_EQUAL_COLLECTIONS(RouteInfiniteExpirationPeriod,
+                                  RouteInfiniteExpirationPeriod + sizeof(RouteInfiniteExpirationPeriod),
+                                  wire.begin(), wire.end());
+}
+
+BOOST_AUTO_TEST_CASE(RouteInfiniteExpirationPeriodDecode)
+{
+  Route route;
+
+  BOOST_REQUIRE_NO_THROW(route.wireDecode(Block(RouteInfiniteExpirationPeriod,
+                                                sizeof(RouteInfiniteExpirationPeriod))));
+
+  BOOST_REQUIRE_EQUAL(route.getFaceId(), 1);
+  BOOST_REQUIRE_EQUAL(route.getOrigin(), 128);
+  BOOST_REQUIRE_EQUAL(route.getCost(), 100);
+  BOOST_REQUIRE_EQUAL(route.getFlags(), static_cast<uint64_t>(ndn::nfd::ROUTE_FLAG_CAPTURE));
+  BOOST_REQUIRE_EQUAL(route.getExpirationPeriod(), Route::INFINITE_EXPIRATION_PERIOD);
+  BOOST_REQUIRE_EQUAL(route.hasInfiniteExpirationPeriod(), true);
+}
+
+BOOST_AUTO_TEST_CASE(RouteOutputStream)
+{
+  Route route;
+  route.setFaceId(1);
+  route.setOrigin(128);
+  route.setCost(100);
+  route.setFlags(ndn::nfd::ROUTE_FLAG_CAPTURE);
+  route.setExpirationPeriod(time::milliseconds(10000));
+
+  std::ostringstream os;
+  os << route;
+
+  BOOST_CHECK_EQUAL(os.str(), "Route(FaceId: 1, Origin: 128, Cost: 100, "
+                              "Flags: 2, ExpirationPeriod: 10000 milliseconds)");
+}
+
+BOOST_AUTO_TEST_CASE(RibEntryEncode)
+{
+  RibEntry entry;
+  entry.setName("/hello/world");
+
+  Route route1;
+  route1.setFaceId(1);
+  route1.setOrigin(128);
+  route1.setCost(100);
+  route1.setFlags(ndn::nfd::ROUTE_FLAG_CAPTURE);
+  route1.setExpirationPeriod(time::milliseconds(10000));
+  entry.addRoute(route1);
+
+  Route route2;
+  route2.setFaceId(2);
+  route2.setOrigin(0);
+  route2.setCost(32);
+  route2.setFlags(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  route2.setExpirationPeriod(time::milliseconds(5000));
+  entry.addRoute(route2);
+
+  const Block& wire = entry.wireEncode();
+
+  BOOST_CHECK_EQUAL_COLLECTIONS(RibEntryData,
+                                RibEntryData + sizeof(RibEntryData),
+                                wire.begin(), wire.end());
+}
+
+BOOST_AUTO_TEST_CASE(RibEntryDecode)
+{
+  RibEntry entry;
+  BOOST_REQUIRE_NO_THROW(entry.wireDecode(Block(RibEntryData,
+                                          sizeof(RibEntryData))));
+
+  BOOST_CHECK_EQUAL(entry.getName(), "/hello/world");
+  BOOST_CHECK_EQUAL(entry.getRoutes().size(), 2);
+
+  std::list<Route> routes = entry.getRoutes();
+
+  std::list<Route>::const_iterator it = routes.begin();
+  BOOST_CHECK_EQUAL(it->getFaceId(), 1);
+  BOOST_CHECK_EQUAL(it->getOrigin(), 128);
+  BOOST_CHECK_EQUAL(it->getCost(), 100);
+  BOOST_CHECK_EQUAL(it->getFlags(), static_cast<uint64_t>(ndn::nfd::ROUTE_FLAG_CAPTURE));
+  BOOST_CHECK_EQUAL(it->getExpirationPeriod(), time::milliseconds(10000));
+  BOOST_CHECK_EQUAL(it->hasInfiniteExpirationPeriod(), false);
+
+  ++it;
+  BOOST_CHECK_EQUAL(it->getFaceId(), 2);
+  BOOST_CHECK_EQUAL(it->getOrigin(), 0);
+  BOOST_CHECK_EQUAL(it->getCost(), 32);
+  BOOST_CHECK_EQUAL(it->getFlags(), static_cast<uint64_t>(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT));
+  BOOST_CHECK_EQUAL(it->getExpirationPeriod(), time::milliseconds(5000));
+  BOOST_CHECK_EQUAL(it->hasInfiniteExpirationPeriod(), false);
+}
+
+BOOST_AUTO_TEST_CASE(RibEntryInfiniteExpirationPeriodEncode)
+{
+  RibEntry entry;
+  entry.setName("/hello/world");
+
+  Route route1;
+  route1.setFaceId(1);
+  route1.setOrigin(128);
+  route1.setCost(100);
+  route1.setFlags(ndn::nfd::ROUTE_FLAG_CAPTURE);
+  route1.setExpirationPeriod(time::milliseconds(10000));
+  entry.addRoute(route1);
+
+  Route route2;
+  route2.setFaceId(2);
+  route2.setOrigin(0);
+  route2.setCost(32);
+  route2.setFlags(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  route2.setExpirationPeriod(Route::INFINITE_EXPIRATION_PERIOD);
+  entry.addRoute(route2);
+
+  const Block& wire = entry.wireEncode();
+
+  BOOST_CHECK_EQUAL_COLLECTIONS(RibEntryInfiniteExpirationPeriod,
+                                RibEntryInfiniteExpirationPeriod +
+                                sizeof(RibEntryInfiniteExpirationPeriod),
+                                wire.begin(), wire.end());
+}
+
+BOOST_AUTO_TEST_CASE(RibEntryInfiniteExpirationPeriodDecode)
+{
+  RibEntry entry;
+  BOOST_REQUIRE_NO_THROW(entry.wireDecode(Block(RibEntryInfiniteExpirationPeriod,
+                                          sizeof(RibEntryInfiniteExpirationPeriod))));
+
+  BOOST_CHECK_EQUAL(entry.getName(), "/hello/world");
+  BOOST_CHECK_EQUAL(entry.getRoutes().size(), 2);
+
+  std::list<Route> routes = entry.getRoutes();
+
+  std::list<Route>::const_iterator it = routes.begin();
+  BOOST_CHECK_EQUAL(it->getFaceId(), 1);
+  BOOST_CHECK_EQUAL(it->getOrigin(), 128);
+  BOOST_CHECK_EQUAL(it->getCost(), 100);
+  BOOST_CHECK_EQUAL(it->getFlags(), static_cast<uint64_t>(ndn::nfd::ROUTE_FLAG_CAPTURE));
+  BOOST_CHECK_EQUAL(it->getExpirationPeriod(), time::milliseconds(10000));
+  BOOST_CHECK_EQUAL(it->hasInfiniteExpirationPeriod(), false);
+
+  ++it;
+  BOOST_CHECK_EQUAL(it->getFaceId(), 2);
+  BOOST_CHECK_EQUAL(it->getOrigin(), 0);
+  BOOST_CHECK_EQUAL(it->getCost(), 32);
+  BOOST_CHECK_EQUAL(it->getFlags(), static_cast<uint64_t>(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT));
+  BOOST_CHECK_EQUAL(it->getExpirationPeriod(), Route::INFINITE_EXPIRATION_PERIOD);
+  BOOST_CHECK_EQUAL(it->hasInfiniteExpirationPeriod(), true);
+}
+
+BOOST_AUTO_TEST_CASE(RibEntryClear)
+{
+  RibEntry entry;
+  entry.setName("/hello/world");
+
+  Route route1;
+  route1.setFaceId(1);
+  route1.setOrigin(128);
+  route1.setCost(100);
+  route1.setFlags(ndn::nfd::ROUTE_FLAG_CAPTURE);
+  route1.setExpirationPeriod(time::milliseconds(10000));
+  entry.addRoute(route1);
+  BOOST_REQUIRE_EQUAL(entry.getRoutes().size(), 1);
+
+  std::list<Route> routes = entry.getRoutes();
+
+  std::list<Route>::const_iterator it = routes.begin();
+  BOOST_CHECK_EQUAL(it->getFaceId(), 1);
+  BOOST_CHECK_EQUAL(it->getOrigin(), 128);
+  BOOST_CHECK_EQUAL(it->getCost(), 100);
+  BOOST_CHECK_EQUAL(it->getFlags(), static_cast<uint64_t>(ndn::nfd::ROUTE_FLAG_CAPTURE));
+  BOOST_CHECK_EQUAL(it->getExpirationPeriod(), time::milliseconds(10000));
+  BOOST_CHECK_EQUAL(it->hasInfiniteExpirationPeriod(), false);
+
+  entry.clearRoutes();
+  BOOST_CHECK_EQUAL(entry.getRoutes().size(), 0);
+
+  Route route2;
+  route2.setFaceId(2);
+  route2.setOrigin(0);
+  route2.setCost(32);
+  route2.setFlags(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  route2.setExpirationPeriod(Route::INFINITE_EXPIRATION_PERIOD);
+  entry.addRoute(route2);
+  BOOST_REQUIRE_EQUAL(entry.getRoutes().size(), 1);
+
+  routes = entry.getRoutes();
+
+  it = routes.begin();
+  BOOST_CHECK_EQUAL(it->getFaceId(), 2);
+  BOOST_CHECK_EQUAL(it->getOrigin(), 0);
+  BOOST_CHECK_EQUAL(it->getCost(), 32);
+  BOOST_CHECK_EQUAL(it->getFlags(), static_cast<uint64_t>(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT));
+  BOOST_CHECK_EQUAL(it->getExpirationPeriod(), Route::INFINITE_EXPIRATION_PERIOD);
+  BOOST_CHECK_EQUAL(it->hasInfiniteExpirationPeriod(), true);
+}
+
+BOOST_AUTO_TEST_CASE(RibEntryOutputStream)
+{
+  RibEntry entry;
+  entry.setName("/hello/world");
+
+  Route route1;
+  route1.setFaceId(1);
+  route1.setOrigin(128);
+  route1.setCost(100);
+  route1.setFlags(ndn::nfd::ROUTE_FLAG_CAPTURE);
+  route1.setExpirationPeriod(time::milliseconds(10000));
+  entry.addRoute(route1);
+
+  Route route2;
+  route2.setFaceId(2);
+  route2.setOrigin(0);
+  route2.setCost(32);
+  route2.setFlags(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+  route2.setExpirationPeriod(Route::INFINITE_EXPIRATION_PERIOD);
+  entry.addRoute(route2);
+
+  std::ostringstream os;
+  os << entry;
+
+  BOOST_CHECK_EQUAL(os.str(), "RibEntry{\n"
+                              "  Name: /hello/world\n"
+                              "  Route(FaceId: 1, Origin: 128, Cost: 100, "
+                              "Flags: 2, ExpirationPeriod: 10000 milliseconds)\n"
+                              "  Route(FaceId: 2, Origin: 0, Cost: 32, "
+                              "Flags: 1, ExpirationPeriod: Infinity)\n"
+                              "}");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace nfd
+} // namespace ndn