Serve other routers' certificate and LSA

refs #4448, #4430

Change-Id: I3a9a9ae613770774be06effe26dfb866ea49f05a
diff --git a/src/lsa-segment-storage.cpp b/src/lsa-segment-storage.cpp
new file mode 100644
index 0000000..a567aab
--- /dev/null
+++ b/src/lsa-segment-storage.cpp
@@ -0,0 +1,158 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2018,  The University of Memphis,
+ *                           Regents of the University of California,
+ *                           Arizona Board of Regents.
+ *
+ * This file is part of NLSR (Named-data Link State Routing).
+ * See AUTHORS.md for complete list of NLSR authors and contributors.
+ *
+ * NLSR 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.
+ *
+ * NLSR 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
+ * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "lsa-segment-storage.hpp"
+#include "logger.hpp"
+#include "lsa.hpp"
+#include "utility/name-helper.hpp"
+
+namespace nlsr {
+
+INIT_LOGGER(LsaSegmentStorage);
+
+LsaSegmentStorage::LsaSegmentStorage(ndn::Scheduler& scheduler,
+                                     const ndn::time::seconds lsaDeletionTimepoint)
+  : m_scheduler(scheduler)
+  , m_lsaDeletionTimepoint(lsaDeletionTimepoint)
+{
+}
+
+void
+LsaSegmentStorage::connectToFetcher(ndn::util::SegmentFetcher& fetcher)
+{
+  fetcher.afterSegmentValidated.connect(std::bind(&LsaSegmentStorage::afterFetcherSignalEmitted,
+                                                  this, _1));
+}
+
+const ndn::Data*
+LsaSegmentStorage::getLsaSegment(const ndn::Interest& interest)
+{
+  ndn::Name lsaSegmentsKey = interest.getName();
+
+  // If this the first interest then it does not contain the segment number,
+  // so need to append zero segment component at the end to match with the data
+  if (lsaSegmentsKey.size() > 0) {
+    if (!lsaSegmentsKey.get(-1).isSegment()) {
+      lsaSegmentsKey.appendSegment(0);
+    }
+
+    auto it = m_lsaSegments.find(lsaSegmentsKey);
+    if (it == m_lsaSegments.end()) {
+      NLSR_LOG_TRACE("Data for interest: " << interest.getName() << " cannot be found in the lsa storage");
+
+      return nullptr;
+    }
+    else {
+      NLSR_LOG_TRACE("Data for interest: " << interest.getName() << " is in the storage.");
+      return &(it->second);
+    }
+  }
+  else {
+    NLSR_LOG_ERROR("Received interest has empty name.");
+    return nullptr;
+  }
+}
+
+void
+LsaSegmentStorage::afterFetcherSignalEmitted(const ndn::Data& lsaSegment)
+{
+  NLSR_LOG_TRACE("Received a LSA segment: " << lsaSegment.getName());
+
+  // lsaSegmentName is /<router-prefix>/<LS type>/<sequence no.>/<version no.>/<segment no.>
+  auto lsaSegmentName = lsaSegment.getName();
+
+  if (lsaSegmentName.size() > 0) {
+    // lsaSegmentsKey is /<router-prefix>/<LS type>/<sequence no.>/<segment no.>
+    ndn::Name lsaSegmentsKey(lsaSegmentName.getPrefix(lsaSegmentName.size() - 2));
+    lsaSegmentsKey.append(lsaSegmentName.get(-1));
+
+    // No need to store same LSA multiple time
+    if (m_lsaSegments.find(lsaSegmentsKey) == m_lsaSegments.end()) {
+      NLSR_LOG_TRACE("Received LSA segment is new. Storing it in the storage.\n"
+                      << "                  LSA data name: " << lsaSegmentName);
+
+      // Delete the same LSA with lower sequence number
+      deleteOldLsas(lsaSegmentName);
+
+      m_lsaSegments[lsaSegmentsKey] = lsaSegment;
+    }
+    else {
+      NLSR_LOG_TRACE("The received segment is already in the storage.");
+    }
+
+    // schedule the segment deletion
+    scheduleLsaSegmentDeletion(lsaSegmentsKey);
+  }
+  else {
+    NLSR_LOG_ERROR("The received LSA segment has empty name.");
+  }
+}
+
+void
+LsaSegmentStorage::deleteOldLsas(const ndn::Name& newLsaName)
+{
+  auto newLsaKey = newLsaName.getPrefix(newLsaName.size() - 3);
+  auto newSeqNo = newLsaName.get(-3).toNumber();
+
+  std::vector<decltype(m_lsaSegments)::key_type> lsaToDelete;
+
+  for (auto& segment : m_lsaSegments) {
+    ndn::Name segmentKey = segment.first;
+    auto oldSeqNo = segmentKey.get(-2).toNumber();
+    auto existingLsaKey = segmentKey.getPrefix(segmentKey.size() - 2);
+
+    if (newLsaKey == existingLsaKey) {
+      if (newSeqNo > oldSeqNo) { // in the key the second last component is the sequence number
+        NLSR_LOG_TRACE("Outdated LSA: " << segmentKey << " with seq no: " <<
+                       oldSeqNo << " is deleted.");
+        lsaToDelete.push_back(segmentKey);
+      }
+    }
+  }
+
+  for (auto& segmentKey : lsaToDelete) {
+    m_lsaSegments.erase(segmentKey);
+  }
+}
+
+void
+LsaSegmentStorage::scheduleLsaSegmentDeletion(const ndn::Name& lsaSegmentsKey)
+{
+  m_scheduler.scheduleEvent(m_lsaDeletionTimepoint,
+                            [&, this] {
+                              m_lsaSegments.erase(lsaSegmentsKey);
+                            });
+}
+
+void
+LsaSegmentStorage::insertSegment(const ndn::Name& segmentKey,
+                                 const ndn::Data& segmentValue)
+{
+  m_lsaSegments[segmentKey] = segmentValue;
+}
+
+void
+LsaSegmentStorage::deleteSegment(const ndn::Name& segmentKey)
+{
+  m_lsaSegments.erase(segmentKey);
+}
+
+} // namespace nlsr
\ No newline at end of file
diff --git a/src/lsa-segment-storage.hpp b/src/lsa-segment-storage.hpp
new file mode 100644
index 0000000..bc98f25
--- /dev/null
+++ b/src/lsa-segment-storage.hpp
@@ -0,0 +1,102 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2018,  The University of Memphis,
+ *                           Regents of the University of California,
+ *                           Arizona Board of Regents.
+ *
+ * This file is part of NLSR (Named-data Link State Routing).
+ * See AUTHORS.md for complete list of NLSR authors and contributors.
+ *
+ * NLSR 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.
+ *
+ * NLSR 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
+ * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#ifndef NLSR_LSA_SEGMENT_STORAGE_HPP
+#define NLSR_LSA_SEGMENT_STORAGE_HPP
+
+#include "test-access-control.hpp"
+
+#include <ndn-cxx/util/segment-fetcher.hpp>
+#include <ndn-cxx/util/signal.hpp>
+#include <ndn-cxx/util/time.hpp>
+
+#include <vector>
+#include <tuple>
+
+namespace nlsr {
+
+class LsaSegmentStorage
+{
+public:
+  LsaSegmentStorage(ndn::Scheduler& scheduler,
+                    const ndn::time::seconds lsaDeletionTimepoint);
+
+  /*! \brief Get connected to the signal emitted by SegmentFetcher
+   * \param fetcher The SegmentFetcher to whose signal LsaSegmentStorage will subscribe to.
+   */
+  void
+  connectToFetcher(ndn::util::SegmentFetcher& fetcher);
+
+  /*! \brief Returns an LSA segment for an interest from LsaSegmentStorage
+   * \param interest Interest corresponding to the returned LSA segment.
+   */
+  const ndn::Data*
+  getLsaSegment(const ndn::Interest& interest);
+
+  /*! \brief Inserts an LSA segment into LsaSegmentStorage
+   * \param segmentKey Name of data without the version number.
+   * The format of the key is /router-prefix/LS type/sequence no./segment no.
+   * \param segmentValue The actual data packet.
+   */
+  void
+  insertSegment(const ndn::Name& segmentKey, const ndn::Data& segmentValue);
+
+  /*! \brief Given the key remove the corresponding data packet from LsaSegmentStorage.
+   * \param segmentKey Key of the Data packet that will be deleted.
+   */
+  void
+  deleteSegment(const ndn::Name& segmentKey);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  /*! \brief Callback when SegmentFetcher retrieves a segment.
+   */
+   void
+   afterFetcherSignalEmitted(const ndn::Data& lsaSegment);
+
+private:
+  /*! \brief Given an LSA name check whether Data for the same name exists in the
+   * LsaSegmentStorage. If the matched LSA data are of a lower sequence number,
+   * then remove them from LsaSegmentStorage.
+   * \param newLsaName Name of the LSA that will be matched against
+   */
+  void
+  deleteOldLsas(const ndn::Name& newLsaName);
+
+  /*! \brief Schedules the deletion of a LSA data given the segmentKey
+   */
+  void
+  scheduleLsaSegmentDeletion(const ndn::Name& segmentKey);
+
+
+private:
+  ndn::Scheduler& m_scheduler;
+
+  // Key: /<router-prefix>/<LS type>/<sequence no.>/<segment no.>
+  // Value: corresponding LSA data packet
+  //        Data name: /<router-prefix>/<LS type>/<sequence no.>/<version no.>/<segment no.>
+  std::unordered_map<ndn::Name, ndn::Data> m_lsaSegments;
+
+  const ndn::time::seconds m_lsaDeletionTimepoint;
+};
+
+} // namespace nlsr
+
+#endif // NLSR_LSA_SEGMENT_STORAGE_HPP
\ No newline at end of file
diff --git a/src/lsdb.cpp b/src/lsdb.cpp
index 536d154..5ebef48 100644
--- a/src/lsdb.cpp
+++ b/src/lsdb.cpp
@@ -22,6 +22,7 @@
 #include "lsdb.hpp"
 
 #include "logger.hpp"
+#include "lsa-segment-storage.hpp"
 #include "nlsr.hpp"
 #include "publisher/segment-publisher.hpp"
 #include "utility/name-helper.hpp"
@@ -71,6 +72,8 @@
                    const uint64_t& sequenceNumber) {
              return isLsaNew(routerName, lsaType, sequenceNumber);
            }, m_nlsr.getConfParameter())
+  , m_lsaStorage(scheduler,
+                 ndn::time::seconds(m_nlsr.getConfParameter().getLsaRefreshTime()))
   , m_lsaRefreshTime(0)
   , m_adjLsaBuildInterval(ADJ_LSA_BUILD_INTERVAL_DEFAULT)
   , m_sequencingManager()
@@ -212,6 +215,7 @@
 bool
 Lsdb::installNameLsa(NameLsa& nlsa)
 {
+  NLSR_LOG_TRACE("installNameLsa");
   ndn::time::seconds timeToExpire = m_lsaRefreshTime;
   NameLsa* chkNameLsa = findNameLsa(nlsa.getKey());
   // Determines if the name LSA is new or not.
@@ -221,6 +225,9 @@
     NLSR_LOG_DEBUG("Adding Name Lsa");
     nlsa.writeLog();
 
+    NLSR_LOG_TRACE("nlsa.getOrigRouter(): " << nlsa.getOrigRouter());
+    NLSR_LOG_TRACE("m_nlsr.getConfParameter().getRouterPrefix(): " << m_nlsr.getConfParameter().getRouterPrefix());
+
     if (nlsa.getOrigRouter() != m_nlsr.getConfParameter().getRouterPrefix()) {
       // If this name LSA is from another router, add the advertised
       // prefixes to the NPT.
@@ -245,6 +252,9 @@
   }
   // Else this is a known name LSA, so we are updating it.
   else {
+    NLSR_LOG_TRACE("Known name lsa");
+    NLSR_LOG_TRACE("chkNameLsa->getLsSeqNo(): " << chkNameLsa->getLsSeqNo());
+    NLSR_LOG_TRACE("nlsa.getLsSeqNo(): " << nlsa.getLsSeqNo());
     if (chkNameLsa->getLsSeqNo() < nlsa.getLsSeqNo()) {
       NLSR_LOG_DEBUG("Updated Name LSA. Updating LSDB");
       NLSR_LOG_DEBUG("Deleting Name Lsa");
@@ -999,11 +1009,16 @@
   interest.setInterestLifetime(m_nlsr.getConfParameter().getLsaInterestLifetime());
 
   NLSR_LOG_DEBUG("Fetching Data for LSA: " << interestName << " Seq number: " << seqNo);
-  ndn::util::SegmentFetcher::fetch(m_nlsr.getNlsrFace(), interest,
-                                   m_nlsr.getValidator(),
-                                   std::bind(&Lsdb::afterFetchLsa, this, _1, interestName),
-                                   std::bind(&Lsdb::onFetchLsaError, this, _1, _2, interestName,
-                                             timeoutCount, deadline, lsaName, seqNo));
+  shared_ptr<ndn::util::SegmentFetcher> fetcher =
+    ndn::util::SegmentFetcher::fetch(m_nlsr.getNlsrFace(), interest,
+                                     m_nlsr.getValidator(),
+                                     std::bind(&Lsdb::afterFetchLsa, this, _1, interestName),
+                                     std::bind(&Lsdb::onFetchLsaError, this, _1, _2, interestName,
+                                               timeoutCount, deadline, lsaName, seqNo));
+
+  m_lsaStorage.connectToFetcher(*fetcher);
+  m_nlsr.connectToFetcher(*fetcher);
+
   // increment a specific SENT_LSA_INTEREST
   Lsa::Type lsaType;
   std::istringstream(interestName[-2].toUri()) >> lsaType;
@@ -1034,35 +1049,49 @@
   std::string chkString("LSA");
   int32_t lsaPosition = util::getNameComponentPosition(interest.getName(), chkString);
 
-  if (lsaPosition >= 0) {
+  // Forms the name of the router that the Interest packet came from.
+  ndn::Name originRouter = m_nlsr.getConfParameter().getNetwork();
+  originRouter.append(interestName.getSubName(lsaPosition + 1,
+                                              interest.getName().size() - lsaPosition - 3));
 
-    // Forms the name of the router that the Interest packet came from.
-    ndn::Name originRouter = m_nlsr.getConfParameter().getNetwork();
-    originRouter.append(interestName.getSubName(lsaPosition + 1,
-                                                interest.getName().size() - lsaPosition - 3));
+  // if the interest is for this router's LSA
+  if (originRouter == m_nlsr.getConfParameter().getRouterPrefix()) {
 
-    uint64_t seqNo = interestName[-1].toNumber();
-    NLSR_LOG_DEBUG("LSA sequence number from interest: " << seqNo);
+    if (lsaPosition >= 0) {
 
-    Lsa::Type interestedLsType;
-    std::istringstream(interestName[-2].toUri()) >> interestedLsType;
+      uint64_t seqNo = interestName[-1].toNumber();
+      NLSR_LOG_DEBUG("LSA sequence number from interest: " << seqNo);
 
-    if (interestedLsType == Lsa::Type::NAME) {
-      processInterestForNameLsa(interest, originRouter.append(std::to_string(interestedLsType)),
-                                seqNo);
+      Lsa::Type interestedLsType;
+      std::istringstream(interestName[-2].toUri()) >> interestedLsType;
+
+      if (interestedLsType == Lsa::Type::NAME) {
+        processInterestForNameLsa(interest, originRouter.append(std::to_string(interestedLsType)),
+                                  seqNo);
+      }
+      else if (interestedLsType == Lsa::Type::ADJACENCY) {
+        processInterestForAdjacencyLsa(interest, originRouter.append(std::to_string(interestedLsType)),
+                                       seqNo);
+      }
+      else if (interestedLsType == Lsa::Type::COORDINATE) {
+        processInterestForCoordinateLsa(interest, originRouter.append(std::to_string(interestedLsType)),
+                                        seqNo);
+      }
+      else {
+        NLSR_LOG_WARN("Received unrecognized LSA type: " << interestedLsType);
+      }
+      lsaIncrementSignal(Statistics::PacketType::SENT_LSA_DATA);
     }
-    else if (interestedLsType == Lsa::Type::ADJACENCY) {
-      processInterestForAdjacencyLsa(interest, originRouter.append(std::to_string(interestedLsType)),
-                                     seqNo);
-    }
-    else if (interestedLsType == Lsa::Type::COORDINATE) {
-      processInterestForCoordinateLsa(interest, originRouter.append(std::to_string(interestedLsType)),
-                                      seqNo);
+  }
+  else { // else the interest is for other router's lsa, serve from LsaSegmentStorage
+    const ndn::Data* lsaSegment = m_lsaStorage.getLsaSegment(interest);
+    if (lsaSegment != nullptr) {
+      NLSR_LOG_TRACE("Found data in lsa storage. Sending the data for " << interest.getName());
+      m_nlsr.getNlsrFace().put(*lsaSegment);
     }
     else {
-      NLSR_LOG_WARN("Received unrecognized LSA type: " << interestedLsType);
+      NLSR_LOG_TRACE(interest << "  was not found in this lsa storage.");
     }
-    lsaIncrementSignal(Statistics::PacketType::SENT_LSA_DATA);
   }
 }
 
@@ -1099,8 +1128,12 @@
   if (nameLsa != nullptr) {
     NLSR_LOG_TRACE("Verifying SeqNo for NameLsa is same as requested.");
     if (nameLsa->getLsSeqNo() == seqNo) {
+      // if requested lsa belongs to this router then sign it and serve it
       std::string content = nameLsa->serialize();
       putLsaData(interest,content);
+      // else the requested belongs to neighboring routers, so serve the
+      // original data packet corresponding to the lsa
+
       // increment SENT_NAME_LSA_DATA
       lsaIncrementSignal(Statistics::PacketType::SENT_NAME_LSA_DATA);
     }
diff --git a/src/lsdb.hpp b/src/lsdb.hpp
index 9fd76b7..14c8842 100644
--- a/src/lsdb.hpp
+++ b/src/lsdb.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2017,  The University of Memphis,
+ * Copyright (c) 2014-2018,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -24,6 +24,7 @@
 
 #include "conf-parameter.hpp"
 #include "lsa.hpp"
+#include "lsa-segment-storage.hpp"
 #include "sequencing-manager.hpp"
 #include "test-access-control.hpp"
 #include "communication/sync-logic-handler.hpp"
@@ -195,6 +196,11 @@
     return m_sequencingManager;
   }
 
+  LsaSegmentStorage&
+  getLsaStorage() {
+    return m_lsaStorage;
+  }
+
   void
   writeAdjLsdbLog();
 
@@ -381,6 +387,7 @@
   Nlsr& m_nlsr;
   ndn::Scheduler& m_scheduler;
   SyncLogicHandler m_sync;
+  LsaSegmentStorage m_lsaStorage;
 
   std::list<NameLsa> m_nameLsdb;
   std::list<AdjLsa> m_adjLsdb;
@@ -403,7 +410,6 @@
   SequencingManager m_sequencingManager;
 
   ndn::util::signal::ScopedConnection m_onNewLsaConnection;
-
 };
 
 } // namespace nlsr
diff --git a/src/nlsr.cpp b/src/nlsr.cpp
index 605c563..120092b 100644
--- a/src/nlsr.cpp
+++ b/src/nlsr.cpp
@@ -28,6 +28,7 @@
 #include <sstream>
 #include <cstdio>
 #include <unistd.h>
+#include <vector>
 
 #include <ndn-cxx/net/face-uri.hpp>
 #include <ndn-cxx/signature.hpp>
@@ -141,6 +142,7 @@
 void
 Nlsr::setStrategies()
 {
+  NLSR_LOG_TRACE("in setStrategies");
   const std::string strategy("ndn:/localhost/nfd/strategy/multicast");
 
   m_fib.setStrategy(m_confParam.getLsaPrefix(), strategy, 0);
@@ -184,10 +186,10 @@
   }
 }
 
-
 void
 Nlsr::loadCertToPublish(const ndn::security::v2::Certificate& certificate)
 {
+  NLSR_LOG_TRACE("Loading cert to publish.");
   m_certStore.insert(certificate);
   m_validator.loadAnchor("Authoritative-Certificate",
                           ndn::security::v2::Certificate(certificate));
@@ -197,6 +199,55 @@
 }
 
 void
+Nlsr::connectToFetcher(ndn::util::SegmentFetcher& fetcher)
+{
+  NLSR_LOG_TRACE("NLSR: Connect to SegmentFetcher.");
+
+  fetcher.afterSegmentValidated.connect(std::bind(&Nlsr::afterFetcherSignalEmitted,
+                                                  this, _1));
+}
+
+void
+Nlsr::afterFetcherSignalEmitted(const ndn::Data& lsaSegment)
+{
+  NLSR_LOG_TRACE("SegmentFetcher fetched a data segment. Start inserting cert to own cert store.");
+  ndn::Name keyName = lsaSegment.getSignature().getKeyLocator().getName();
+  if (getCertificate(keyName) == nullptr) {
+    publishCertFromCache(keyName);
+  }
+  else {
+    NLSR_LOG_TRACE("Certificate is already in the store: " << keyName);
+  }
+}
+
+void
+Nlsr::publishCertFromCache(const ndn::Name& keyName)
+{
+  const ndn::security::v2::Certificate* cert = m_validator.getUnverifiedCertCache()
+                                                          .find(keyName);
+  if (cert != nullptr) {
+    m_certStore.insert(*cert);
+    NLSR_LOG_TRACE(*cert);
+    NLSR_LOG_TRACE("Setting interest filter for: "
+                   << ndn::security::v2::extractKeyNameFromCertName(cert->getName()));
+    m_nlsrFace.setInterestFilter(ndn::security::v2::extractKeyNameFromCertName(cert->getName()),
+                                 std::bind(&Nlsr::onKeyInterest,
+                                           this, _1, _2),
+                                 std::bind(&Nlsr::onKeyPrefixRegSuccess, this, _1),
+                                 std::bind(&Nlsr::registrationFailed, this, _1),
+                                 m_signingInfo,
+                                 ndn::nfd::ROUTE_FLAG_CAPTURE);
+
+    if (!cert->getKeyName().equals(cert->getSignature().getKeyLocator().getName())) {
+      publishCertFromCache(cert->getSignature().getKeyLocator().getName());
+    }
+  }
+  else {
+    NLSR_LOG_TRACE("Cert for " << keyName << " was not found in the Validator's cache. ");
+  }
+}
+
+void
 Nlsr::initialize()
 {
   NLSR_LOG_DEBUG("Initializing Nlsr");
@@ -308,7 +359,7 @@
                   << "NLSR is running without security."
                   << " If security is enabled NLSR will not converge.");
 
-    std::cerr << "Router's " << e.what() << "NLSR is running without security "
+    std::cerr << "Router's " << e.what() << ". NLSR is running without security "
               << "(Only for testing, should not be used in production.)"
               << " If security is enabled NLSR will not converge." << std::endl;
   }
diff --git a/src/nlsr.hpp b/src/nlsr.hpp
index d3b8737..018febf 100644
--- a/src/nlsr.hpp
+++ b/src/nlsr.hpp
@@ -299,6 +299,21 @@
   loadCertToPublish(const ndn::security::v2::Certificate& certificate);
 
   void
+  connectToFetcher(ndn::util::SegmentFetcher& fetcher);
+
+  /*! \brief Callback when SegmentFetcher retrieves a segment.
+   */
+  void
+  afterFetcherSignalEmitted(const ndn::Data& lsaSegment);
+
+  /*! \brief Retrieves the chain of certificates from Validator's cache and
+   *   store them in Nlsr's own CertificateStore.
+   * \param keyName Name of the first key in the certificate chain.
+   */
+  void
+  publishCertFromCache(const ndn::Name& keyName);
+
+  void
   initializeKey();
 
   void
diff --git a/tests/test-lsa-segment-storage.cpp b/tests/test-lsa-segment-storage.cpp
new file mode 100644
index 0000000..de0ee13
--- /dev/null
+++ b/tests/test-lsa-segment-storage.cpp
@@ -0,0 +1,154 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2018,  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 "lsa-segment-storage.hpp"
+#include "test-common.hpp"
+#include "nlsr.hpp"
+#include "name-prefix-list.hpp"
+
+#include <boost/test/unit_test.hpp>
+
+namespace nlsr {
+namespace test {
+
+class LsaSegmentStorageFixture : public UnitTestTimeFixture
+{
+public:
+  LsaSegmentStorageFixture()
+    : face(m_ioService, m_keyChain)
+    , nlsr(m_ioService, m_scheduler, face, m_keyChain)
+    , lsdb(nlsr.getLsdb())
+    , lsaStorage(lsdb.getLsaStorage())
+  {
+  }
+
+  static shared_ptr<ndn::Data>
+  makeLsaSegment(const ndn::Name& baseName, uint64_t segmentNo, bool isFinal)
+  {
+    const uint8_t buffer[] = "Hello, world!";
+
+    ndn::Name lsaDataName(baseName);
+    lsaDataName.appendSegment(segmentNo);
+    auto lsaSegment = make_shared<ndn::Data>(ndn::Name(lsaDataName));
+    lsaSegment->setContent(buffer, sizeof(buffer));
+    if (isFinal) {
+      lsaSegment->setFinalBlockId(lsaSegment->getName()[-1]);
+    }
+
+    return signData(lsaSegment);
+  }
+
+  void
+  receiveLsaInterest(const ndn::Name& baseInterestName, uint64_t segmentNo,
+                     bool isSegmentZero)
+  {
+    if (isSegmentZero) {
+      lsdb.processInterest(ndn::Name(), ndn::Interest(baseInterestName));
+    }
+    else {
+      ndn::Name nextInterestName(baseInterestName);
+      nextInterestName.appendSegment(segmentNo);
+      lsdb.processInterest(ndn::Name(), ndn::Interest(nextInterestName));
+      advanceClocks(ndn::time::milliseconds(1), 10);
+    }
+  }
+
+public:
+  ndn::util::DummyClientFace face;
+  Nlsr nlsr;
+  Lsdb& lsdb;
+  LsaSegmentStorage& lsaStorage;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestLsaSegmentStorage, LsaSegmentStorageFixture)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  ndn::Name lsaInterestName("/ndn/NLSR/LSA/other-site/%C1.Router/other-router/NAME");
+  lsaInterestName.appendNumber(12);
+
+  ndn::Name lsaDataName(lsaInterestName);
+  lsaDataName.appendVersion();
+
+  for (uint64_t segmentNo = 0; segmentNo < 4; ++segmentNo) {
+    auto lsaData = makeLsaSegment(lsaDataName, segmentNo, segmentNo == 4);
+    lsaStorage.afterFetcherSignalEmitted(*lsaData);
+  }
+
+  // receive interest for other-router's LSA that is stored in this router's storage
+  for (uint64_t segmentNo = 0; segmentNo < 4; ++segmentNo) {
+    receiveLsaInterest(lsaInterestName, segmentNo, segmentNo == 0);
+  }
+
+  // 4 data segments should be sent in response to 4 interests
+  BOOST_CHECK_EQUAL(face.sentData.size(), 4);
+}
+
+BOOST_AUTO_TEST_CASE(DeleteOldLsa)
+{
+  ndn::Name lsaDataName("/ndn/NLSR/LSA/other-site/%C1.Router/other-router/NAME");
+  uint64_t segmentNo = 0;
+
+  uint64_t oldSeqNo = 12;
+  ndn::Name oldLsaDataName(lsaDataName);
+  oldLsaDataName.appendNumber(oldSeqNo);
+  oldLsaDataName.appendVersion();
+
+  auto oldLsaData = makeLsaSegment(oldLsaDataName, segmentNo, true);
+  lsaStorage.afterFetcherSignalEmitted(*oldLsaData);
+  advanceClocks(ndn::time::milliseconds(1), 10);
+
+  uint64_t newSeqNo = 13;
+  ndn::Name newLsaDataName(lsaDataName);
+  newLsaDataName.appendNumber(newSeqNo);
+  newLsaDataName.appendVersion();
+
+  auto newLsaData = makeLsaSegment(newLsaDataName, segmentNo, true);
+  lsaStorage.afterFetcherSignalEmitted(*newLsaData);
+  advanceClocks(ndn::time::milliseconds(1), 10);
+
+  ndn::Name lsaInterestName(lsaDataName);
+
+  ndn::Name oldLsaInterestName(lsaInterestName);
+  oldLsaInterestName.appendNumber(oldSeqNo);
+  receiveLsaInterest(oldLsaInterestName, segmentNo, true);
+
+  advanceClocks(ndn::time::milliseconds(1), 10);
+
+  BOOST_CHECK_EQUAL(face.sentData.size(), 0);
+
+  ndn::Name newLsaInterestName(lsaInterestName);
+  newLsaInterestName.appendNumber(newSeqNo);
+  receiveLsaInterest(newLsaInterestName, segmentNo, true);
+
+  advanceClocks(ndn::time::milliseconds(1), 10);
+
+  BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestLsaSegmentStorage
+
+} // namespace test
+} // namespace nlsr
diff --git a/tests/test-lsdb.cpp b/tests/test-lsdb.cpp
index 197d5cd..8a36113 100644
--- a/tests/test-lsdb.cpp
+++ b/tests/test-lsdb.cpp
@@ -20,6 +20,7 @@
  **/
 
 #include "lsdb.hpp"
+
 #include "test-common.hpp"
 #include "nlsr.hpp"
 #include "lsa.hpp"
@@ -173,23 +174,22 @@
 
 BOOST_AUTO_TEST_CASE(SegmentLsaData)
 {
-  ndn::Name router("/ndn/cs/%C1.Router/router1");
-  uint64_t seqNo = 12;
-  NamePrefixList prefixList;
+  ndn::Name lsaKey("/ndn/site/%C1.Router/this-router/NAME");
 
-  NameLsa lsa(router, seqNo, ndn::time::system_clock::now(), prefixList);
+  NameLsa* lsa = lsdb.findNameLsa(lsaKey);
+  uint64_t seqNo = lsa->getLsSeqNo();
 
   ndn::Name prefix("/ndn/edu/memphis/netlab/research/nlsr/test/prefix/");
 
   int nPrefixes = 0;
-  while (lsa.serialize().size() < ndn::MAX_NDN_PACKET_SIZE) {
-    lsa.addName(ndn::Name(prefix).appendNumber(++nPrefixes));
+  while (lsa->serialize().size() < ndn::MAX_NDN_PACKET_SIZE) {
+    lsa->addName(ndn::Name(prefix).appendNumber(++nPrefixes));
   }
+  lsdb.installNameLsa(*lsa);
 
-  std::string expectedDataContent = lsa.serialize();
-  lsdb.installNameLsa(lsa);
+  std::string expectedDataContent = lsa->serialize();
 
-  ndn::Name interestName("/ndn/NLSR/LSA/cs/%C1.Router/router1/NAME/");
+  ndn::Name interestName("/ndn/NLSR/LSA/site/%C1.Router/this-router/NAME/");
   interestName.appendNumber(seqNo);
 
   ndn::Interest interest(interestName);
diff --git a/tests/test-statistics.cpp b/tests/test-statistics.cpp
index 90a2a67..c74ab38 100644
--- a/tests/test-statistics.cpp
+++ b/tests/test-statistics.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2017,  The University of Memphis,
+ * Copyright (c) 2014-2018,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -222,21 +222,23 @@
  */
 BOOST_AUTO_TEST_CASE(LsdbReceiveInterestSendData)
 {
-  std::string routerName("/ndn/site/%C1.Router/router");
-  ndn::time::system_clock::TimePoint MAX_TIME = ndn::time::system_clock::TimePoint::max();
-  uint32_t seqNo = 1;
-
   // Adjacency LSA
+  lsdb.buildAndInstallOwnAdjLsa();
+
+  ndn::Name adjLsaKey = conf.getRouterPrefix();
+  adjLsaKey.append(std::to_string(Lsa::Type::ADJACENCY));
+
+  AdjLsa* adjLsa = lsdb.findAdjLsa(adjLsaKey);
+  uint32_t seqNo = adjLsa->getLsSeqNo();
+
   Adjacent adjacency("adjacency");
   adjacency.setStatus(Adjacent::STATUS_ACTIVE);
 
-  AdjacencyList adjacencies;
-  adjacencies.insert(adjacency);
+  adjLsa->addAdjacent(adjacency);
 
-  AdjLsa adjLsa(routerName, seqNo, MAX_TIME, 1, adjacencies);
-  lsdb.installAdjLsa(adjLsa);
+  lsdb.installAdjLsa(*adjLsa);
 
-  const std::string interestPrefix("/ndn/NLSR/LSA/site/%C1.Router/router/");
+  const std::string interestPrefix("/ndn/NLSR/LSA/site/%C1.Router/this-router/");
 
   // Receive Adjacency LSA Interest
   receiveInterestAndCheckSentStats(interestPrefix,
@@ -246,10 +248,15 @@
                                    Statistics::PacketType::SENT_ADJ_LSA_DATA);
 
   // Name LSA
-  NamePrefixList prefixes{ndn::Name{"/ndn/name"}};
+  ndn::Name nameLsaKey = conf.getRouterPrefix();
+  nameLsaKey.append(std::to_string(Lsa::Type::NAME));
 
-  NameLsa nameLsa(routerName, seqNo, MAX_TIME, prefixes);
-  lsdb.installNameLsa(nameLsa);
+  NameLsa* nameLsa = lsdb.findNameLsa(nameLsaKey);
+
+  seqNo = nameLsa->getLsSeqNo();
+
+  nameLsa->addName(ndn::Name("/ndn/name"));
+  lsdb.installNameLsa(*nameLsa);
 
   // Receive Name LSA Interest
   receiveInterestAndCheckSentStats(interestPrefix,
@@ -258,10 +265,15 @@
                                    Statistics::PacketType::RCV_NAME_LSA_INTEREST,
                                    Statistics::PacketType::SENT_NAME_LSA_DATA);
 
-  // Coordinate LSA
-  std::vector<double> angles = {20.0, 30.0};
-  CoordinateLsa coordLsa(routerName, seqNo, MAX_TIME, 2.5, angles);
-  lsdb.installCoordinateLsa(coordLsa);
+  // // Coordinate LSA
+  lsdb.buildAndInstallOwnCoordinateLsa();
+  ndn::Name coorLsaKey = conf.getRouterPrefix();
+  coorLsaKey.append(std::to_string(Lsa::Type::COORDINATE));
+
+  CoordinateLsa* coorLsa = lsdb.findCoordinateLsa(coorLsaKey);
+  seqNo = coorLsa->getLsSeqNo();
+  coorLsa->setCorTheta({20.0, 30.0});
+  lsdb.installCoordinateLsa(*coorLsa);
 
   // Receive Adjacency LSA Interest
   receiveInterestAndCheckSentStats(interestPrefix,