lsdb: rebuild using boost::multi_index to replace 3 LSA lists

refs: #4127

Co-authored-by: Nick Gordon <nmgordon@memphis.edu>

Change-Id: Ic179f90019e472157b0d61c6db02a4afaf4843b6
diff --git a/src/lsdb.cpp b/src/lsdb.cpp
index 9913751..fb4bcb7 100644
--- a/src/lsdb.cpp
+++ b/src/lsdb.cpp
@@ -25,14 +25,10 @@
 #include "nlsr.hpp"
 #include "utility/name-helper.hpp"
 
-#include <ndn-cxx/security/signing-helpers.hpp>
-
 namespace nlsr {
 
 INIT_LOGGER(Lsdb);
 
-const ndn::Name::Component Lsdb::NAME_COMPONENT = ndn::Name::Component("lsdb");
-const ndn::time::seconds Lsdb::GRACE_PERIOD = ndn::time::seconds(10);
 const ndn::time::steady_clock::TimePoint Lsdb::DEFAULT_LSA_RETRIEVAL_DEADLINE =
   ndn::time::steady_clock::TimePoint::min();
 
@@ -49,8 +45,8 @@
              return isLsaNew(routerName, lsaType, sequenceNumber);
            }, m_confParam)
   , m_lsaRefreshTime(ndn::time::seconds(m_confParam.getLsaRefreshTime()))
-  , m_thisRouterPrefix(m_confParam.getRouterPrefix().toUri())
   , m_adjLsaBuildInterval(m_confParam.getAdjLsaBuildInterval())
+  , m_thisRouterPrefix(m_confParam.getRouterPrefix())
   , m_sequencingManager(m_confParam.getStateFileDir(), m_confParam.getHyperbolicState())
   , m_onNewLsaConnection(m_sync.onNewLsa->connect(
       [this] (const ndn::Name& updateName, uint64_t sequenceNumber,
@@ -65,521 +61,33 @@
 {
 }
 
-Lsdb::~Lsdb()
-{
-  for (const auto& sp : m_fetchers) {
-    sp->stop();
-  }
-}
-
 void
-Lsdb::onFetchLsaError(uint32_t errorCode,
-                      const std::string& msg,
-                      const ndn::Name& interestName,
-                      uint32_t retransmitNo,
-                      const ndn::time::steady_clock::TimePoint& deadline,
-                      ndn::Name lsaName,
-                      uint64_t seqNo)
-{
-  NLSR_LOG_DEBUG("Failed to fetch LSA: " << lsaName << ", Error code: " << errorCode
-                 << ", Message: " << msg);
-
-  if (ndn::time::steady_clock::now() < deadline) {
-    auto it = m_highestSeqNo.find(lsaName);
-    if (it != m_highestSeqNo.end() && it->second == seqNo) {
-      // If the SegmentFetcher failed due to an Interest timeout, it is safe to re-express
-      // immediately since at the least the LSA Interest lifetime has elapsed.
-      // Otherwise, it is necessary to delay the Interest re-expression to prevent
-      // the potential for constant Interest flooding.
-      ndn::time::seconds delay = m_confParam.getLsaInterestLifetime();
-
-      if (errorCode == ndn::util::SegmentFetcher::ErrorCode::INTEREST_TIMEOUT) {
-        delay = ndn::time::seconds(0);
-      }
-
-      m_scheduler.schedule(delay, std::bind(&Lsdb::expressInterest, this,
-                                            interestName, retransmitNo + 1, deadline));
-    }
-  }
-}
-
-void
-Lsdb::afterFetchLsa(const ndn::ConstBufferPtr& bufferPtr, const ndn::Name& interestName)
-{
-  NLSR_LOG_DEBUG("Received data for LSA interest: " << interestName);
-
-  ndn::Name lsaName = interestName.getSubName(0, interestName.size()-1);
-  uint64_t seqNo = interestName[-1].toNumber();
-
-  if (m_highestSeqNo.find(lsaName) == m_highestSeqNo.end()) {
-    m_highestSeqNo[lsaName] = seqNo;
-  }
-  else if (seqNo > m_highestSeqNo[lsaName]) {
-    m_highestSeqNo[lsaName] = seqNo;
-    NLSR_LOG_TRACE("SeqNo for LSA(name): " << interestName << "  updated");
-  }
-  else if (seqNo < m_highestSeqNo[lsaName]) {
-    return;
-  }
-
-  std::string chkString("LSA");
-  int32_t lsaPosition = util::getNameComponentPosition(interestName, chkString);
-
-  if (lsaPosition >= 0) {
-    // Extracts the prefix of the originating router from the data.
-    ndn::Name originRouter = m_confParam.getNetwork();
-    originRouter.append(interestName.getSubName(lsaPosition + 1,
-                                                interestName.size() - lsaPosition - 3));
-
-    try {
-      ndn::Block block(bufferPtr);
-      Lsa::Type interestedLsType;
-      std::istringstream(interestName[-2].toUri()) >> interestedLsType;
-
-      if (interestedLsType == Lsa::Type::NAME) {
-        processContentNameLsa(originRouter.append(boost::lexical_cast<std::string>(interestedLsType)),
-                              seqNo,
-                              block);
-      }
-      else if (interestedLsType == Lsa::Type::ADJACENCY) {
-        processContentAdjacencyLsa(originRouter.append(boost::lexical_cast<std::string>(interestedLsType)),
-                                   seqNo,
-                                   block);
-      }
-      else if (interestedLsType == Lsa::Type::COORDINATE) {
-        processContentCoordinateLsa(originRouter.append(boost::lexical_cast<std::string>(interestedLsType)),
-                                    seqNo,
-                                    block);
-      }
-      else {
-        NLSR_LOG_WARN("Received unrecognized LSA Type: " << interestedLsType);
-      }
-    }
-    catch (const std::exception& e) {
-      NLSR_LOG_TRACE("LSA data decoding error :( " << e.what());
-      return;
-    }
-
-    lsaIncrementSignal(Statistics::PacketType::RCV_LSA_DATA);
-  }
-}
-
-  /*! \brief Compares if a name LSA is the same as the one specified by key
-
-    \param nlsa1 A name LSA object
-    \param key A key of an originating router to compare to nlsa1
-   */
-static bool
-nameLsaCompareByKey(const NameLsa& nlsa1, const ndn::Name& key)
-{
-  return nlsa1.getKey() == key;
-}
-
-bool
 Lsdb::buildAndInstallOwnNameLsa()
 {
-  NameLsa nameLsa(m_confParam.getRouterPrefix(),
-                  m_sequencingManager.getNameLsaSeq() + 1,
-                  getLsaExpirationTimePoint(),
-                  m_confParam.getNamePrefixList());
+  NameLsa nameLsa(m_thisRouterPrefix, m_sequencingManager.getNameLsaSeq() + 1,
+                  getLsaExpirationTimePoint(), m_confParam.getNamePrefixList());
   m_sequencingManager.increaseNameLsaSeq();
-
   m_sequencingManager.writeSeqNoToFile();
   m_sync.publishRoutingUpdate(Lsa::Type::NAME, m_sequencingManager.getNameLsaSeq());
 
-  return installNameLsa(nameLsa);
-}
-
-NameLsa*
-Lsdb::findNameLsa(const ndn::Name& key)
-{
-  auto it = std::find_if(m_nameLsdb.begin(), m_nameLsdb.end(),
-                         std::bind(nameLsaCompareByKey, _1, key));
-  if (it != m_nameLsdb.end()) {
-    return &*it;
-  }
-  return nullptr;
-}
-
-bool
-Lsdb::isNameLsaNew(const ndn::Name& key, uint64_t seqNo)
-{
-  NameLsa* nameLsaCheck = findNameLsa(key);
-  // Is the name in the LSDB
-  if (nameLsaCheck != nullptr) {
-    // And the supplied seq no is the highest so far
-    if (nameLsaCheck->getSeqNo() < seqNo) {
-      return true;
-    }
-    else {
-      return false;
-    }
-  }
-  return true;
-}
-
-ndn::scheduler::EventId
-Lsdb::scheduleNameLsaExpiration(const ndn::Name& key, int seqNo,
-                                const ndn::time::seconds& expTime)
-{
-  return m_scheduler.schedule(expTime + GRACE_PERIOD,
-                              std::bind(&Lsdb::expireOrRefreshNameLsa, this, key, seqNo));
-}
-
-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.
-  if (chkNameLsa == nullptr) {
-    addNameLsa(nlsa);
-    NLSR_LOG_DEBUG("New Name LSA");
-    NLSR_LOG_DEBUG("Adding Name Lsa");
-    NLSR_LOG_DEBUG(nlsa);
-
-    NLSR_LOG_TRACE("nlsa.getOriginRouter(): " << nlsa.getOriginRouter());
-    NLSR_LOG_TRACE("m_confParam.getRouterPrefix(): " << m_confParam.getRouterPrefix());
-
-    if (nlsa.getOriginRouter() != m_confParam.getRouterPrefix()) {
-      // If this name LSA is from another router, add the advertised
-      // prefixes to the NPT.
-      m_namePrefixTable.addEntry(nlsa.getOriginRouter(), nlsa.getOriginRouter());
-
-      for (const auto& name : nlsa.getNpl().getNames()) {
-        if (name != m_confParam.getRouterPrefix()) {
-          m_namePrefixTable.addEntry(name, nlsa.getOriginRouter());
-        }
-      }
-      auto duration = nlsa.getExpirationTimePoint() - ndn::time::system_clock::now();
-      timeToExpire = ndn::time::duration_cast<ndn::time::seconds>(duration);
-    }
-
-    nlsa.setExpiringEventId(scheduleNameLsaExpiration(nlsa.getKey(),
-                                                      nlsa.getSeqNo(),
-                                                      timeToExpire));
-  }
-  // Else this is a known name LSA, so we are updating it.
-  else {
-    NLSR_LOG_TRACE("Known name lsa");
-    NLSR_LOG_TRACE("chkNameLsa->getSeqNo(): " << chkNameLsa->getSeqNo());
-    NLSR_LOG_TRACE("nlsa.getSeqNo(): " << nlsa.getSeqNo());
-    if (chkNameLsa->getSeqNo() < nlsa.getSeqNo()) {
-      NLSR_LOG_DEBUG("Updated Name LSA. Updating LSDB");
-      NLSR_LOG_DEBUG("Deleting Name Lsa");
-      NLSR_LOG_DEBUG(chkNameLsa);
-      chkNameLsa->setSeqNo(nlsa.getSeqNo());
-      chkNameLsa->setExpirationTimePoint(nlsa.getExpirationTimePoint());
-      chkNameLsa->getNpl().sort();
-      nlsa.getNpl().sort();
-      // Obtain the set difference of the current and the incoming
-      // name prefix sets, and add those.
-      std::list<ndn::Name> newNames = nlsa.getNpl().getNames();
-      std::list<ndn::Name> oldNames = chkNameLsa->getNpl().getNames();
-      std::list<ndn::Name> namesToAdd;
-      std::set_difference(newNames.begin(), newNames.end(), oldNames.begin(), oldNames.end(),
-                          std::inserter(namesToAdd, namesToAdd.begin()));
-      for (const auto& name : namesToAdd) {
-        chkNameLsa->addName(name);
-        if (nlsa.getOriginRouter() != m_confParam.getRouterPrefix()) {
-          if (name != m_confParam.getRouterPrefix()) {
-            m_namePrefixTable.addEntry(name, nlsa.getOriginRouter());
-          }
-        }
-      }
-
-      chkNameLsa->getNpl().sort();
-
-      // Also remove any names that are no longer being advertised.
-      std::list<ndn::Name> namesToRemove;
-      std::set_difference(oldNames.begin(), oldNames.end(), newNames.begin(), newNames.end(),
-                          std::inserter(namesToRemove, namesToRemove.begin()));
-      for (const auto& name : namesToRemove) {
-        NLSR_LOG_DEBUG("Removing name LSA no longer advertised: " << name);
-        chkNameLsa->removeName(name);
-        if (nlsa.getOriginRouter() != m_confParam.getRouterPrefix()) {
-          if (name != m_confParam.getRouterPrefix()) {
-            m_namePrefixTable.removeEntry(name, nlsa.getOriginRouter());
-          }
-        }
-      }
-
-      if (nlsa.getOriginRouter() != m_confParam.getRouterPrefix()) {
-        auto duration = nlsa.getExpirationTimePoint() - ndn::time::system_clock::now();
-        timeToExpire = ndn::time::duration_cast<ndn::time::seconds>(duration);
-      }
-      chkNameLsa->getExpiringEventId().cancel();
-      chkNameLsa->setExpiringEventId(scheduleNameLsaExpiration(nlsa.getKey(),
-                                                               nlsa.getSeqNo(),
-                                                               timeToExpire));
-      NLSR_LOG_DEBUG("Adding Name Lsa");
-      NLSR_LOG_DEBUG(chkNameLsa);
-    }
-  }
-  return true;
-}
-
-bool
-Lsdb::addNameLsa(NameLsa& nlsa)
-{
-  auto it = std::find_if(m_nameLsdb.begin(), m_nameLsdb.end(),
-                         std::bind(nameLsaCompareByKey, _1, nlsa.getKey()));
-  if (it == m_nameLsdb.end()) {
-    m_nameLsdb.push_back(nlsa);
-    return true;
-  }
-  return false;
-}
-
-bool
-Lsdb::removeNameLsa(const ndn::Name& key)
-{
-  auto it = std::find_if(m_nameLsdb.begin(), m_nameLsdb.end(),
-                         std::bind(nameLsaCompareByKey, _1, key));
-  if (it != m_nameLsdb.end()) {
-    NLSR_LOG_DEBUG("Deleting Name Lsa");
-    NLSR_LOG_DEBUG(*it);
-    // If the requested name LSA is not ours, we also need to remove
-    // its entries from the NPT.
-    if (it->getOriginRouter() != m_confParam.getRouterPrefix()) {
-      m_namePrefixTable.removeEntry(it->getOriginRouter(), it->getOriginRouter());
-
-      for (const auto& name : it->getNpl().getNames()) {
-        if (name != m_confParam.getRouterPrefix()) {
-          m_namePrefixTable.removeEntry(name, it->getOriginRouter());
-        }
-      }
-    }
-    m_nameLsdb.erase(it);
-    return true;
-  }
-  return false;
-}
-
-bool
-Lsdb::doesNameLsaExist(const ndn::Name& key)
-{
-  auto it = std::find_if(m_nameLsdb.begin(), m_nameLsdb.end(),
-                         std::bind(nameLsaCompareByKey, _1, key));
-  return it != m_nameLsdb.end();
+  installLsa(std::make_shared<NameLsa>(nameLsa));
 }
 
 void
-Lsdb::writeNameLsdbLog()
-{
-  NLSR_LOG_TRACE("---------------Name LSDB-------------------");
-  for (const auto& nlsa : m_nameLsdb) {
-    NLSR_LOG_TRACE(nlsa);
-  }
-}
-
-const std::list<NameLsa>&
-Lsdb::getNameLsdb() const
-{
-  return m_nameLsdb;
-}
-
-// Cor LSA and LSDB related Functions start here
-
-/*! \brief Compares whether an LSA object is the same as a key.
-  \param clsa The cor. LSA to check the identity of.
-  \param key The key of the publishing router to check against.
-*/
-static bool
-corLsaCompareByKey(const CoordinateLsa& clsa, const ndn::Name& key)
-{
-  return clsa.getKey() == key;
-}
-
-bool
 Lsdb::buildAndInstallOwnCoordinateLsa()
 {
-  CoordinateLsa corLsa(m_confParam.getRouterPrefix(),
-                       m_sequencingManager.getCorLsaSeq() + 1,
-                       getLsaExpirationTimePoint(),
-                       m_confParam.getCorR(),
+  CoordinateLsa corLsa(m_thisRouterPrefix, m_sequencingManager.getCorLsaSeq() + 1,
+                       getLsaExpirationTimePoint(), m_confParam.getCorR(),
                        m_confParam.getCorTheta());
+  m_sequencingManager.increaseCorLsaSeq();
+  m_sequencingManager.writeSeqNoToFile();
 
   // Sync coordinate LSAs if using HR or HR dry run.
   if (m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_OFF) {
-    m_sequencingManager.increaseCorLsaSeq();
-    m_sequencingManager.writeSeqNoToFile();
     m_sync.publishRoutingUpdate(Lsa::Type::COORDINATE, m_sequencingManager.getCorLsaSeq());
   }
 
-  installCoordinateLsa(corLsa);
-
-  return true;
-}
-
-CoordinateLsa*
-Lsdb::findCoordinateLsa(const ndn::Name& key)
-{
-  auto it = std::find_if(m_corLsdb.begin(), m_corLsdb.end(),
-                         std::bind(corLsaCompareByKey, _1, key));
-  if (it != m_corLsdb.end()) {
-    return &*it;
-  }
-  return nullptr;
-}
-
-bool
-Lsdb::isCoordinateLsaNew(const ndn::Name& key, uint64_t seqNo)
-{
-  CoordinateLsa* clsa = findCoordinateLsa(key);
-  // Is the coordinate LSA in the LSDB already
-  if (clsa != nullptr) {
-    // And the seq no is newer (higher) than the current one
-    if (clsa->getSeqNo() < seqNo) {
-      return true;
-    }
-    else {
-      return false;
-    }
-  }
-  return true;
-}
-
-  // Schedules a refresh/expire event in the scheduler.
-  // \param key The name of the router that published the LSA.
-  // \param seqNo the seq. no. associated with the LSA to check.
-  // \param expTime How long to wait before triggering the event.
-ndn::scheduler::EventId
-Lsdb::scheduleCoordinateLsaExpiration(const ndn::Name& key, int seqNo,
-                                      const ndn::time::seconds& expTime)
-{
-  return m_scheduler.schedule(expTime + GRACE_PERIOD,
-                              std::bind(&Lsdb::expireOrRefreshCoordinateLsa, this, key, seqNo));
-}
-
-bool
-Lsdb::installCoordinateLsa(CoordinateLsa& clsa)
-{
-  ndn::time::seconds timeToExpire = m_lsaRefreshTime;
-  CoordinateLsa* chkCorLsa = findCoordinateLsa(clsa.getKey());
-  // Checking whether the LSA is new or not.
-  if (chkCorLsa == nullptr) {
-    NLSR_LOG_DEBUG("New Coordinate LSA. Adding to LSDB");
-    NLSR_LOG_DEBUG("Adding Coordinate Lsa");
-    NLSR_LOG_DEBUG(clsa);
-    addCoordinateLsa(clsa);
-
-    // Register the LSA's origin router prefix
-    if (clsa.getOriginRouter() != m_confParam.getRouterPrefix()) {
-      m_namePrefixTable.addEntry(clsa.getOriginRouter(), clsa.getOriginRouter());
-    }
-    if (m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_OFF) {
-      m_routingTable.scheduleRoutingTableCalculation();
-    }
-    // Set the expiration time for the new LSA.
-    if (clsa.getOriginRouter() != m_confParam.getRouterPrefix()) {
-      auto duration = clsa.getExpirationTimePoint() - ndn::time::system_clock::now();
-      timeToExpire = ndn::time::duration_cast<ndn::time::seconds>(duration);
-    }
-    scheduleCoordinateLsaExpiration(clsa.getKey(), clsa.getSeqNo(), timeToExpire);
-  }
-  // We are just updating this LSA.
-  else {
-    if (chkCorLsa->getSeqNo() < clsa.getSeqNo()) {
-      NLSR_LOG_DEBUG("Updated Coordinate LSA. Updating LSDB");
-      NLSR_LOG_DEBUG("Deleting Coordinate Lsa");
-      NLSR_LOG_DEBUG(chkCorLsa);
-      chkCorLsa->setSeqNo(clsa.getSeqNo());
-      chkCorLsa->setExpirationTimePoint(clsa.getExpirationTimePoint());
-      // If the new LSA contains new routing information, update the LSDB with it.
-      if (!chkCorLsa->isEqualContent(clsa)) {
-        chkCorLsa->setCorRadius(clsa.getCorRadius());
-        chkCorLsa->setCorTheta(clsa.getCorTheta());
-        if (m_confParam.getHyperbolicState() >= HYPERBOLIC_STATE_ON) {
-          m_routingTable.scheduleRoutingTableCalculation();
-        }
-      }
-      // If this is an LSA from another router, refresh its expiration time.
-      if (clsa.getOriginRouter() != m_confParam.getRouterPrefix()) {
-        auto duration = clsa.getExpirationTimePoint() - ndn::time::system_clock::now();
-        timeToExpire = ndn::time::duration_cast<ndn::time::seconds>(duration);
-      }
-      chkCorLsa->getExpiringEventId().cancel();
-      chkCorLsa->setExpiringEventId(scheduleCoordinateLsaExpiration(clsa.getKey(),
-                                                                    clsa.getSeqNo(),
-                                                                    timeToExpire));
-      NLSR_LOG_DEBUG("Adding Coordinate Lsa");
-      NLSR_LOG_DEBUG(chkCorLsa);
-    }
-  }
-  return true;
-}
-
-bool
-Lsdb::addCoordinateLsa(CoordinateLsa& clsa)
-{
-  auto it = std::find_if(m_corLsdb.begin(), m_corLsdb.end(),
-                         std::bind(corLsaCompareByKey, _1, clsa.getKey()));
-  if (it == m_corLsdb.end()) {
-    m_corLsdb.push_back(clsa);
-    return true;
-  }
-  return false;
-}
-
-bool
-Lsdb::removeCoordinateLsa(const ndn::Name& key)
-{
-  auto it = std::find_if(m_corLsdb.begin(), m_corLsdb.end(),
-                         std::bind(corLsaCompareByKey, _1, key));
-
-  if (it != m_corLsdb.end()) {
-    NLSR_LOG_DEBUG("Deleting Coordinate Lsa");
-    NLSR_LOG_DEBUG(*it);
-
-    if (it->getOriginRouter() != m_confParam.getRouterPrefix()) {
-      m_namePrefixTable.removeEntry(it->getOriginRouter(), it->getOriginRouter());
-    }
-
-    m_corLsdb.erase(it);
-    return true;
-  }
-  return false;
-}
-
-bool
-Lsdb::doesCoordinateLsaExist(const ndn::Name& key)
-{
-  auto it = std::find_if(m_corLsdb.begin(), m_corLsdb.end(),
-                         std::bind(corLsaCompareByKey, _1, key));
-  return it != m_corLsdb.end();
-}
-
-void
-Lsdb::writeCorLsdbLog()
-{
-  if (m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_OFF) {
-    return;
-  }
-
-  NLSR_LOG_DEBUG("---------------Cor LSDB-------------------");
-  for (const auto& corLsa : m_corLsdb) {
-    NLSR_LOG_DEBUG(corLsa);
-  }
-}
-
-const std::list<CoordinateLsa>&
-Lsdb::getCoordinateLsdb() const
-{
-  return m_corLsdb;
-}
-
-// Adj LSA and LSDB related function starts here
-
-  /*! \brief Returns whether an adj. LSA object is from some router.
-    \param alsa The adj. LSA object.
-    \param key The router name that you want to compare the LSA with.
-   */
-static bool
-adjLsaCompareByKey(AdjLsa& alsa, const ndn::Name& key)
-{
-  return alsa.getKey() == key;
+  installLsa(std::make_shared<CoordinateLsa>(corLsa));
 }
 
 void
@@ -603,10 +111,258 @@
   m_scheduledAdjLsaBuild = m_scheduler.schedule(m_adjLsaBuildInterval, [this] { buildAdjLsa(); });
 }
 
+template<typename T>
+void
+Lsdb::writeLog() const
+{
+  if ((T::type() == Lsa::Type::COORDINATE &&
+      m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_OFF) ||
+      (T::type() == Lsa::Type::ADJACENCY &&
+      m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_ON)) {
+    return;
+  }
+
+  NLSR_LOG_DEBUG("---------------" << T::type() << " LSDB-------------------");
+  auto lsaRange = m_lsdb.get<byType>().equal_range(T::type());
+  for (auto lsaIt = lsaRange.first; lsaIt != lsaRange.second; ++lsaIt) {
+    auto lsaPtr = std::static_pointer_cast<T>(*lsaIt);
+    NLSR_LOG_DEBUG(lsaPtr->toString());
+  }
+}
+
+void
+Lsdb::writeLog() const
+{
+  writeLog<CoordinateLsa>();
+  writeLog<NameLsa>();
+  writeLog<AdjLsa>();
+}
+
+void
+Lsdb::processInterest(const ndn::Name& name, const ndn::Interest& interest)
+{
+  ndn::Name interestName(interest.getName());
+  NLSR_LOG_DEBUG("Interest received for LSA: " << interestName);
+
+  if (interestName[-2].isVersion()) {
+    // Interest for particular segment
+    if (m_segmentPublisher.replyFromStore(interestName)) {
+      NLSR_LOG_TRACE("Reply from SegmentPublisher storage");
+      return;
+    }
+    // Remove version and segment
+    interestName = interestName.getSubName(0, interestName.size() - 2);
+    NLSR_LOG_TRACE("Interest w/o segment and version: " << interestName);
+  }
+
+  // increment RCV_LSA_INTEREST
+  lsaIncrementSignal(Statistics::PacketType::RCV_LSA_INTEREST);
+
+  std::string chkString("LSA");
+  int32_t lsaPosition = util::getNameComponentPosition(interestName, chkString);
+
+  // Forms the name of the router that the Interest packet came from.
+  ndn::Name originRouter = m_confParam.getNetwork();
+  originRouter.append(interestName.getSubName(lsaPosition + 1,
+                                              interestName.size() - lsaPosition - 3));
+
+  // if the interest is for this router's LSA
+  if (originRouter == m_thisRouterPrefix && lsaPosition >= 0) {
+    uint64_t seqNo = interestName[-1].toNumber();
+    NLSR_LOG_DEBUG("LSA sequence number from interest: " << seqNo);
+
+    std::string lsaType = interestName[-2].toUri();
+    Lsa::Type interestedLsType;
+    std::istringstream(lsaType) >> interestedLsType;
+    if (interestedLsType == Lsa::Type::BASE) {
+      NLSR_LOG_WARN("Received unrecognized LSA type: " << lsaType);
+      return;
+    }
+
+    incrementInterestRcvdStats(interestedLsType);
+    if (processInterestForLsa(interest, originRouter, interestedLsType, seqNo)) {
+      lsaIncrementSignal(Statistics::PacketType::SENT_LSA_DATA);
+    }
+  }
+  // else the interest is for other router's LSA, serve signed data from LsaSegmentStorage
+  else if (auto lsaSegment = m_lsaStorage.find(interest)) {
+    NLSR_LOG_TRACE("Found data in lsa storage. Sending the data for " << interest.getName());
+    m_face.put(*lsaSegment);
+  }
+}
+
+bool
+Lsdb::processInterestForLsa(const ndn::Interest& interest, const ndn::Name& originRouter,
+                            Lsa::Type lsaType, uint64_t seqNo)
+{
+  NLSR_LOG_DEBUG(interest << " received for " << lsaType);
+  if (auto lsaPtr = findLsa(originRouter, lsaType)) {
+    NLSR_LOG_TRACE("Verifying SeqNo for " << lsaType << " is same as requested.");
+    if (lsaPtr->getSeqNo() == seqNo) {
+      m_segmentPublisher.publish(interest.getName(), interest.getName(),
+                                 lsaPtr->wireEncode(),
+                                 m_lsaRefreshTime, m_confParam.getSigningInfo());
+      incrementDataSentStats(lsaType);
+      return true;
+    }
+  }
+  else {
+    NLSR_LOG_TRACE(interest << "  was not found in our LSDB");
+  }
+  return false;
+}
+
+void
+Lsdb::installLsa(shared_ptr<Lsa> lsa)
+{
+  auto timeToExpire = m_lsaRefreshTime;
+
+  auto chkLsa = findLsa(lsa->getOriginRouter(), lsa->getType());
+  if (chkLsa == nullptr) {
+    NLSR_LOG_DEBUG("Adding " << lsa->getType() << " LSA");
+    NLSR_LOG_DEBUG(lsa->toString());
+    ndn::time::seconds timeToExpire = m_lsaRefreshTime;
+
+    m_lsdb.emplace(lsa);
+
+    // Add any new name prefixes to the NPT if from another router
+    if (lsa->getOriginRouter() != m_thisRouterPrefix) {
+      // Pass the origin router as both the name to register and where it came from.
+      m_namePrefixTable.addEntry(lsa->getOriginRouter(), lsa->getOriginRouter());
+
+      if (lsa->getType() == Lsa::Type::NAME) {
+        auto nlsa = std::static_pointer_cast<NameLsa>(lsa);
+        for (const auto& name : nlsa->getNpl().getNames()) {
+          if (name != m_thisRouterPrefix) {
+            m_namePrefixTable.addEntry(name, nlsa->getOriginRouter());
+          }
+        }
+      }
+
+      auto duration = lsa->getExpirationTimePoint() - ndn::time::system_clock::now();
+      if (duration > ndn::time::seconds(0)) {
+        timeToExpire = ndn::time::duration_cast<ndn::time::seconds>(duration);
+      }
+    }
+
+    if ((lsa->getType() == Lsa::Type::ADJACENCY && m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_ON)||
+        (lsa->getType() == Lsa::Type::COORDINATE && m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_OFF)) {
+      m_routingTable.scheduleRoutingTableCalculation();
+    }
+
+    lsa->setExpiringEventId(scheduleLsaExpiration(lsa, timeToExpire));
+  }
+  // Else this is a known name LSA, so we are updating it.
+  else if (chkLsa->getSeqNo() < lsa->getSeqNo()) {
+    NLSR_LOG_DEBUG("Updating " << lsa->getType() << " LSA:");
+    NLSR_LOG_DEBUG(chkLsa->toString());
+    chkLsa->setSeqNo(lsa->getSeqNo());
+    chkLsa->setExpirationTimePoint(lsa->getExpirationTimePoint());
+
+    if (lsa->getType() == Lsa::Type::NAME) {
+      auto chkNameLsa = std::static_pointer_cast<NameLsa>(chkLsa);
+      auto nlsa = std::static_pointer_cast<NameLsa>(lsa);
+      chkNameLsa->getNpl().sort();
+      nlsa->getNpl().sort();
+      if (!chkNameLsa->isEqualContent(*nlsa)) {
+        // Obtain the set difference of the current and the incoming
+        // name prefix sets, and add those.
+        std::list<ndn::Name> newNames = nlsa->getNpl().getNames();
+        std::list<ndn::Name> oldNames = chkNameLsa->getNpl().getNames();
+        std::list<ndn::Name> namesToAdd;
+        std::set_difference(newNames.begin(), newNames.end(), oldNames.begin(), oldNames.end(),
+                            std::inserter(namesToAdd, namesToAdd.begin()));
+        for (const auto& name : namesToAdd) {
+          chkNameLsa->addName(name);
+          if (nlsa->getOriginRouter() != m_thisRouterPrefix && name != m_thisRouterPrefix) {
+            m_namePrefixTable.addEntry(name, nlsa->getOriginRouter());
+          }
+        }
+
+        chkNameLsa->getNpl().sort();
+
+        // Also remove any names that are no longer being advertised.
+        std::list<ndn::Name> namesToRemove;
+        std::set_difference(oldNames.begin(), oldNames.end(), newNames.begin(), newNames.end(),
+                            std::inserter(namesToRemove, namesToRemove.begin()));
+        for (const auto& name : namesToRemove) {
+          NLSR_LOG_DEBUG("Removing name" << name << " from Name LSA no longer advertised.");
+          chkNameLsa->removeName(name);
+          if (nlsa->getOriginRouter() != m_thisRouterPrefix && name != m_thisRouterPrefix) {
+            m_namePrefixTable.removeEntry(name, nlsa->getOriginRouter());
+          }
+        }
+      }
+    }
+    else if (lsa->getType() == Lsa::Type::ADJACENCY) {
+      auto chkAdjLsa = std::static_pointer_cast<AdjLsa>(chkLsa);
+      auto alsa = std::static_pointer_cast<AdjLsa>(lsa);
+      if (!chkAdjLsa->isEqualContent(*alsa)) {
+        chkAdjLsa->resetAdl();
+        for (const auto& adjacent : alsa->getAdl()) {
+          chkAdjLsa->addAdjacent(adjacent);
+        }
+        m_routingTable.scheduleRoutingTableCalculation();
+      }
+    }
+    else {
+      auto chkCorLsa = std::static_pointer_cast<CoordinateLsa>(chkLsa);
+      auto clsa = std::static_pointer_cast<CoordinateLsa>(lsa);
+      if (!chkCorLsa->isEqualContent(*clsa)) {
+        chkCorLsa->setCorRadius(clsa->getCorRadius());
+        chkCorLsa->setCorTheta(clsa->getCorTheta());
+        if (m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_OFF) {
+          m_routingTable.scheduleRoutingTableCalculation();
+        }
+      }
+    }
+
+    if (chkLsa->getOriginRouter() != m_thisRouterPrefix) {
+      auto duration = lsa->getExpirationTimePoint() - ndn::time::system_clock::now();
+      if (duration > ndn::time::seconds(0)) {
+        timeToExpire = ndn::time::duration_cast<ndn::time::seconds>(duration);
+      }
+    }
+    chkLsa->getExpiringEventId().cancel();
+    chkLsa->setExpiringEventId(scheduleLsaExpiration(chkLsa, timeToExpire));
+    NLSR_LOG_DEBUG("Updated " << lsa->getType() << " LSA:");
+    NLSR_LOG_DEBUG(chkLsa->toString());
+  }
+}
+
+bool
+Lsdb::removeLsa(const ndn::Name& router, Lsa::Type lsaType)
+{
+  auto lsaIt = m_lsdb.get<byName>().find(std::make_tuple(router, lsaType));
+
+  if (lsaIt != m_lsdb.end()) {
+    auto lsaPtr = *lsaIt;
+    NLSR_LOG_DEBUG("Removing " << lsaType << " LSA:");
+    NLSR_LOG_DEBUG(lsaPtr->toString());
+    // If the requested name LSA is not ours, we also need to remove
+    // its entries from the NPT.
+    if (lsaPtr->getOriginRouter() != m_thisRouterPrefix) {
+      m_namePrefixTable.removeEntry(lsaPtr->getOriginRouter(), lsaPtr->getOriginRouter());
+
+      if (lsaType == Lsa::Type::NAME) {
+        auto nlsaPtr = std::static_pointer_cast<NameLsa>(lsaPtr);
+        for (const auto& name : nlsaPtr->getNpl().getNames()) {
+          if (name != m_thisRouterPrefix) {
+            m_namePrefixTable.removeEntry(name, nlsaPtr->getOriginRouter());
+          }
+        }
+      }
+    }
+    m_lsdb.erase(lsaIt);
+    return true;
+  }
+  return false;
+}
+
 void
 Lsdb::buildAdjLsa()
 {
-  NLSR_LOG_TRACE("Lsdb::buildAdjLsa called");
+  NLSR_LOG_TRACE("buildAdjLsa called");
 
   m_isBuildAdjLsaSheduled = false;
 
@@ -626,11 +382,8 @@
       // routers to delete it, too.
       else {
         NLSR_LOG_DEBUG("Removing own Adj LSA; no ACTIVE neighbors");
-        // Get this router's key
-        ndn::Name key = m_confParam.getRouterPrefix();
-        key.append(boost::lexical_cast<std::string>(Lsa::Type::ADJACENCY));
 
-        removeAdjLsa(key);
+        removeLsa(m_thisRouterPrefix, Lsa::Type::ADJACENCY);
         // Recompute routing table after removal
         m_routingTable.scheduleRoutingTableCalculation();
       }
@@ -651,303 +404,57 @@
   }
 }
 
-bool
-Lsdb::addAdjLsa(AdjLsa& alsa)
-{
-  auto it = std::find_if(m_adjLsdb.begin(), m_adjLsdb.end(),
-                         std::bind(adjLsaCompareByKey, _1, alsa.getKey()));
-  if (it == m_adjLsdb.end()) {
-    m_adjLsdb.push_back(alsa);
-    // Add any new name prefixes to the NPT
-    // Only add NPT entries if this is an adj LSA from another router.
-    if (alsa.getOriginRouter() != m_confParam.getRouterPrefix()) {
-      // Pass the originating router as both the name to register and
-      // where it came from.
-      m_namePrefixTable.addEntry(alsa.getOriginRouter(), alsa.getOriginRouter());
-    }
-    return true;
-  }
-  return false;
-}
-
-AdjLsa*
-Lsdb::findAdjLsa(const ndn::Name& key)
-{
-  auto it = std::find_if(m_adjLsdb.begin(), m_adjLsdb.end(),
-                         std::bind(adjLsaCompareByKey, _1, key));
-  if (it != m_adjLsdb.end()) {
-    return &*it;
-  }
-  return nullptr;
-}
-
-bool
-Lsdb::isAdjLsaNew(const ndn::Name& key, uint64_t seqNo)
-{
-  AdjLsa* adjLsaCheck = findAdjLsa(key);
-  // If it is in the LSDB
-  if (adjLsaCheck != nullptr) {
-    // And the supplied seq no is newer (higher) than the current one.
-    if (adjLsaCheck->getSeqNo() < seqNo) {
-      return true;
-    }
-    else {
-      return false;
-    }
-  }
-  return true;
-}
-
-ndn::scheduler::EventId
-Lsdb::scheduleAdjLsaExpiration(const ndn::Name& key, int seqNo,
-                               const ndn::time::seconds& expTime)
-{
-  return m_scheduler.schedule(expTime + GRACE_PERIOD,
-                              std::bind(&Lsdb::expireOrRefreshAdjLsa, this, key, seqNo));
-}
-
-bool
-Lsdb::installAdjLsa(AdjLsa& alsa)
-{
-  ndn::time::seconds timeToExpire = m_lsaRefreshTime;
-  AdjLsa* chkAdjLsa = findAdjLsa(alsa.getKey());
-  // If this adj. LSA is not in the LSDB already
-  if (chkAdjLsa == nullptr) {
-    NLSR_LOG_DEBUG("New Adj LSA. Adding to LSDB");
-    NLSR_LOG_DEBUG("Adding Adj Lsa");
-    NLSR_LOG_DEBUG(alsa);
-    addAdjLsa(alsa);
-
-    m_routingTable.scheduleRoutingTableCalculation();
-    if (alsa.getOriginRouter() != m_confParam.getRouterPrefix()) {
-      ndn::time::system_clock::Duration duration = alsa.getExpirationTimePoint() -
-                                                   ndn::time::system_clock::now();
-      timeToExpire = ndn::time::duration_cast<ndn::time::seconds>(duration);
-    }
-    scheduleAdjLsaExpiration(alsa.getKey(), alsa.getSeqNo(), timeToExpire);
-  }
-  else {
-    if (chkAdjLsa->getSeqNo() < alsa.getSeqNo()) {
-      NLSR_LOG_DEBUG("Updated Adj LSA. Updating LSDB");
-      NLSR_LOG_DEBUG("Deleting Adj Lsa");
-      NLSR_LOG_DEBUG(chkAdjLsa);
-      chkAdjLsa->setSeqNo(alsa.getSeqNo());
-      chkAdjLsa->setExpirationTimePoint(alsa.getExpirationTimePoint());
-      // If the new adj LSA has new content, update the contents of
-      // the LSDB entry. Additionally, since we've changed the
-      // contents of the LSDB, we have to schedule a routing
-      // calculation.
-      if (!chkAdjLsa->isEqualContent(alsa)) {
-        chkAdjLsa->resetAdl();
-        for (const auto& adjacent : alsa.getAdl()) {
-          chkAdjLsa->addAdjacent(adjacent);
-        }
-        m_routingTable.scheduleRoutingTableCalculation();
-      }
-      if (alsa.getOriginRouter() != m_confParam.getRouterPrefix()) {
-        auto duration = alsa.getExpirationTimePoint() - ndn::time::system_clock::now();
-        timeToExpire = ndn::time::duration_cast<ndn::time::seconds>(duration);
-      }
-      chkAdjLsa->getExpiringEventId().cancel();
-      chkAdjLsa->setExpiringEventId(scheduleAdjLsaExpiration(alsa.getKey(),
-                                                             alsa.getSeqNo(),
-                                                             timeToExpire));
-      NLSR_LOG_DEBUG("Adding Adj Lsa");
-      NLSR_LOG_DEBUG(chkAdjLsa);
-    }
-  }
-  return true;
-}
-
-bool
+void
 Lsdb::buildAndInstallOwnAdjLsa()
 {
-  AdjLsa adjLsa(m_confParam.getRouterPrefix(),
-                m_sequencingManager.getAdjLsaSeq() + 1,
+  AdjLsa adjLsa(m_thisRouterPrefix, m_sequencingManager.getAdjLsaSeq() + 1,
                 getLsaExpirationTimePoint(),
                 m_confParam.getAdjacencyList().getNumOfActiveNeighbor(),
                 m_confParam.getAdjacencyList());
+  m_sequencingManager.increaseAdjLsaSeq();
+  m_sequencingManager.writeSeqNoToFile();
 
   //Sync adjacency LSAs if link-state or dry-run HR is enabled.
   if (m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_ON) {
-    m_sequencingManager.increaseAdjLsaSeq();
-    m_sequencingManager.writeSeqNoToFile();
     m_sync.publishRoutingUpdate(Lsa::Type::ADJACENCY, m_sequencingManager.getAdjLsaSeq());
   }
 
-  return installAdjLsa(adjLsa);
+  installLsa(std::make_shared<AdjLsa>(adjLsa));
 }
 
-bool
-Lsdb::removeAdjLsa(const ndn::Name& key)
-{
-  auto it = std::find_if(m_adjLsdb.begin(), m_adjLsdb.end(),
-                         std::bind(adjLsaCompareByKey, _1, key));
-  if (it != m_adjLsdb.end()) {
-    NLSR_LOG_DEBUG("Deleting Adj Lsa");
-    NLSR_LOG_DEBUG(*it);
-    if (it->getOriginRouter() != m_confParam.getRouterPrefix()) {
-      m_namePrefixTable.removeEntry(it->getOriginRouter(), it->getOriginRouter());
-    }
-    m_adjLsdb.erase(it);
-    return true;
-  }
-  return false;
-}
-
-bool
-Lsdb::doesAdjLsaExist(const ndn::Name& key)
-{
-  auto it = std::find_if(m_adjLsdb.begin(), m_adjLsdb.end(),
-                         std::bind(adjLsaCompareByKey, _1, key));
-  return it != m_adjLsdb.end();
-}
-
-const std::list<AdjLsa>&
-Lsdb::getAdjLsdb() const
-{
-  return m_adjLsdb;
-}
-
-  // This function determines whether a name LSA should be refreshed
-  // or expired. The conditions for getting refreshed are: it is still
-  // in the LSDB, it hasn't been updated by something else already (as
-  // evidenced by its seq. no.), and this is the originating router for
-  // the LSA. Is it let expire in all other cases.
-  // lsaKey is the key of the LSA's publishing router.
-  // seqNo is the seq. no. of the candidate LSA.
 void
-Lsdb::expireOrRefreshNameLsa(const ndn::Name& lsaKey, uint64_t seqNo)
+Lsdb::expireOrRefreshLsa(std::shared_ptr<Lsa> lsa)
 {
-  NLSR_LOG_DEBUG("Lsdb::expireOrRefreshNameLsa Called");
-  NLSR_LOG_DEBUG("LSA Key : " << lsaKey << " Seq No: " << seqNo);
-  NameLsa* chkNameLsa = findNameLsa(lsaKey);
+  NLSR_LOG_DEBUG("ExpireOrRefreshLsa called for " << lsa->getType());
+  NLSR_LOG_DEBUG("OriginRouter: " << lsa->getOriginRouter() << " Seq No: " << lsa->getSeqNo());
+
+  auto lsaIt = m_lsdb.get<byName>().find(std::make_tuple(lsa->getOriginRouter(), lsa->getType()));
+
   // If this name LSA exists in the LSDB
-  if (chkNameLsa != nullptr) {
-    NLSR_LOG_DEBUG("LSA Exists with seq no: " << chkNameLsa->getSeqNo());
+  if (lsaIt != m_lsdb.end()) {
+    auto lsaPtr = *lsaIt;
+    NLSR_LOG_DEBUG(lsaPtr->toString());
+    NLSR_LOG_DEBUG("LSA Exists with seq no: " << lsaPtr->getSeqNo());
     // If its seq no is the one we are expecting.
-    if (chkNameLsa->getSeqNo() == seqNo) {
-      if (chkNameLsa->getOriginRouter() == m_thisRouterPrefix) {
-        NLSR_LOG_DEBUG("Own Name LSA, so refreshing it");
-        NLSR_LOG_DEBUG("Deleting Name Lsa");
-        NLSR_LOG_DEBUG(chkNameLsa);
-        chkNameLsa->setSeqNo(chkNameLsa->getSeqNo() + 1);
-        m_sequencingManager.setNameLsaSeq(chkNameLsa->getSeqNo());
-        chkNameLsa->setExpirationTimePoint(getLsaExpirationTimePoint());
-        NLSR_LOG_DEBUG("Adding Name Lsa");
-        NLSR_LOG_DEBUG(chkNameLsa);
+    if (lsaPtr->getSeqNo() == lsa->getSeqNo()) {
+      if (lsaPtr->getOriginRouter() == m_thisRouterPrefix) {
+        NLSR_LOG_DEBUG("Own " << lsaPtr->getType() << " LSA, so refreshing it.");
+        NLSR_LOG_DEBUG("Current LSA:");
+        NLSR_LOG_DEBUG(lsaPtr->toString());
+        lsaPtr->setSeqNo(lsaPtr->getSeqNo() + 1);
+        m_sequencingManager.setLsaSeq(lsaPtr->getSeqNo(), lsaPtr->getType());
+        lsaPtr->setExpirationTimePoint(getLsaExpirationTimePoint());
+        NLSR_LOG_DEBUG("Updated LSA:");
+        NLSR_LOG_DEBUG(lsaPtr->toString());
         // schedule refreshing event again
-        chkNameLsa->setExpiringEventId(scheduleNameLsaExpiration(chkNameLsa->getKey(),
-                                                                 chkNameLsa->getSeqNo(),
-                                                                 m_lsaRefreshTime));
+        lsaPtr->setExpiringEventId(scheduleLsaExpiration(lsaPtr, m_lsaRefreshTime));
         m_sequencingManager.writeSeqNoToFile();
-        m_sync.publishRoutingUpdate(Lsa::Type::NAME, m_sequencingManager.getNameLsaSeq());
+        m_sync.publishRoutingUpdate(lsaPtr->getType(), m_sequencingManager.getLsaSeq(lsaPtr->getType()));
       }
       // Since we cannot refresh other router's LSAs, our only choice is to expire.
       else {
-        NLSR_LOG_DEBUG("Other's Name LSA, so removing from LSDB");
-        removeNameLsa(lsaKey);
-      }
-    }
-  }
-}
-
-  // This function determines whether an adj. LSA should be refreshed
-  // or expired. The conditions for getting refreshed are: it is still
-  // in the LSDB, it hasn't been updated by something else already (as
-  // evidenced by its seq. no.), and this is the originating router for
-  // the LSA. Is it let expire in all other cases.
-  // lsaKey is the key of the LSA's publishing router.
-  // seqNo is the seq. no. of the candidate LSA.
-void
-Lsdb::expireOrRefreshAdjLsa(const ndn::Name& lsaKey, uint64_t seqNo)
-{
-  NLSR_LOG_DEBUG("Lsdb::expireOrRefreshAdjLsa Called");
-  NLSR_LOG_DEBUG("LSA Key: " << lsaKey << " Seq No: " << seqNo);
-  AdjLsa* chkAdjLsa = findAdjLsa(lsaKey);
-  // If this is a valid LSA
-  if (chkAdjLsa != nullptr) {
-    NLSR_LOG_DEBUG("LSA Exists with seq no: " << chkAdjLsa->getSeqNo());
-    // And if it hasn't been updated for some other reason
-    if (chkAdjLsa->getSeqNo() == seqNo) {
-      // If it is our own LSA
-      if (chkAdjLsa->getOriginRouter() == m_thisRouterPrefix) {
-        NLSR_LOG_DEBUG("Own Adj LSA, so refreshing it");
-        NLSR_LOG_DEBUG("Deleting Adj Lsa");
-        NLSR_LOG_DEBUG(chkAdjLsa);
-        chkAdjLsa->setSeqNo(chkAdjLsa->getSeqNo() + 1);
-        m_sequencingManager.setAdjLsaSeq(chkAdjLsa->getSeqNo());
-        chkAdjLsa->setExpirationTimePoint(getLsaExpirationTimePoint());
-        NLSR_LOG_DEBUG("Adding Adj Lsa");
-        NLSR_LOG_DEBUG(chkAdjLsa);
-        // schedule refreshing event again
-        chkAdjLsa->setExpiringEventId(scheduleAdjLsaExpiration(chkAdjLsa->getKey(),
-                                                               chkAdjLsa->getSeqNo(),
-                                                               m_lsaRefreshTime));
-        m_sequencingManager.writeSeqNoToFile();
-        m_sync.publishRoutingUpdate(Lsa::Type::ADJACENCY, m_sequencingManager.getAdjLsaSeq());
-      }
-      // An LSA from another router is expiring
-      else {
-        NLSR_LOG_DEBUG("Other's Adj LSA, so removing from LSDB");
-        removeAdjLsa(lsaKey);
-      }
-      // We have changed the contents of the LSDB, so we have to
-      // schedule a routing calculation
-      m_routingTable.scheduleRoutingTableCalculation();
-    }
-  }
-}
-
-  // This function determines whether an adj. LSA should be refreshed
-  // or expired. The conditions for getting refreshed are: it is still
-  // in the LSDB, it hasn't been updated by something else already (as
-  // evidenced by its seq. no.), and this is the originating router for
-  // the LSA. It is let expire in all other cases.
-  // lsaKey is the key of the LSA's publishing router.
-  // seqNo is the seq. no. of the candidate LSA.
-void
-Lsdb::expireOrRefreshCoordinateLsa(const ndn::Name& lsaKey,
-                                    uint64_t seqNo)
-{
-  NLSR_LOG_DEBUG("Lsdb::expireOrRefreshCorLsa Called ");
-  NLSR_LOG_DEBUG("LSA Key : " << lsaKey << " Seq No: " << seqNo);
-  CoordinateLsa* chkCorLsa = findCoordinateLsa(lsaKey);
-  // Whether the LSA is in the LSDB or not.
-  if (chkCorLsa != nullptr) {
-    NLSR_LOG_DEBUG("LSA Exists with seq no: " << chkCorLsa->getSeqNo());
-    // Whether the LSA has been updated without our knowledge.
-    if (chkCorLsa->getSeqNo() == seqNo) {
-      if (chkCorLsa->getOriginRouter() == m_thisRouterPrefix) {
-        NLSR_LOG_DEBUG("Own Cor LSA, so refreshing it");
-        NLSR_LOG_DEBUG("Deleting Coordinate Lsa");
-        NLSR_LOG_DEBUG(chkCorLsa);
-        chkCorLsa->setSeqNo(chkCorLsa->getSeqNo() + 1);
-        if (m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_OFF) {
-          m_sequencingManager.setCorLsaSeq(chkCorLsa->getSeqNo());
-        }
-
-        chkCorLsa->setExpirationTimePoint(getLsaExpirationTimePoint());
-        NLSR_LOG_DEBUG("Adding Coordinate Lsa");
-        NLSR_LOG_DEBUG(chkCorLsa);
-        // schedule refreshing event again
-        chkCorLsa->setExpiringEventId(scheduleCoordinateLsaExpiration(
-                                        chkCorLsa->getKey(),
-                                        chkCorLsa->getSeqNo(),
-                                        m_lsaRefreshTime));
-        // Only sync coordinate LSAs if link-state routing is disabled
-        if (m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_OFF) {
-          m_sequencingManager.writeSeqNoToFile();
-          m_sync.publishRoutingUpdate(Lsa::Type::COORDINATE, m_sequencingManager.getCorLsaSeq());
-        }
-      }
-      // We can't refresh other router's LSAs, so we remove it.
-      else {
-        NLSR_LOG_DEBUG("Other's Cor LSA, so removing from LSDB");
-        removeCoordinateLsa(lsaKey);
-      }
-      if (m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_ON) {
-        m_routingTable.scheduleRoutingTableCalculation();
+        NLSR_LOG_DEBUG("Other's " << lsaPtr->getType() << " LSA, so removing from LSDB");
+        removeLsa(lsaPtr->getOriginRouter(), lsaPtr->getType());
       }
     }
   }
@@ -1015,281 +522,99 @@
     m_fetchers.erase(it);
   });
 
-  // increment a specific SENT_LSA_INTEREST
   Lsa::Type lsaType;
   std::istringstream(interestName[-2].toUri()) >> lsaType;
-  switch (lsaType) {
-  case Lsa::Type::ADJACENCY:
-    lsaIncrementSignal(Statistics::PacketType::SENT_ADJ_LSA_INTEREST);
-    break;
-  case Lsa::Type::COORDINATE:
-    lsaIncrementSignal(Statistics::PacketType::SENT_COORD_LSA_INTEREST);
-    break;
-  case Lsa::Type::NAME:
-    lsaIncrementSignal(Statistics::PacketType::SENT_NAME_LSA_INTEREST);
-    break;
-  default:
-    NLSR_LOG_ERROR("lsaType " << lsaType << " not recognized; failed Statistics::PacketType conversion");
+  incrementInterestSentStats(lsaType);
+}
+
+void
+Lsdb::onFetchLsaError(uint32_t errorCode, const std::string& msg, const ndn::Name& interestName,
+                      uint32_t retransmitNo, const ndn::time::steady_clock::TimePoint& deadline,
+                      ndn::Name lsaName, uint64_t seqNo)
+{
+  NLSR_LOG_DEBUG("Failed to fetch LSA: " << lsaName << ", Error code: " << errorCode
+                 << ", Message: " << msg);
+
+  if (ndn::time::steady_clock::now() < deadline) {
+    auto it = m_highestSeqNo.find(lsaName);
+    if (it != m_highestSeqNo.end() && it->second == seqNo) {
+      // If the SegmentFetcher failed due to an Interest timeout, it is safe to re-express
+      // immediately since at the least the LSA Interest lifetime has elapsed.
+      // Otherwise, it is necessary to delay the Interest re-expression to prevent
+      // the potential for constant Interest flooding.
+      ndn::time::seconds delay = m_confParam.getLsaInterestLifetime();
+
+      if (errorCode == ndn::util::SegmentFetcher::ErrorCode::INTEREST_TIMEOUT) {
+        delay = ndn::time::seconds(0);
+      }
+      m_scheduler.schedule(delay, std::bind(&Lsdb::expressInterest, this,
+                                            interestName, retransmitNo + 1, deadline));
+    }
   }
 }
 
 void
-Lsdb::processInterest(const ndn::Name& name, const ndn::Interest& interest)
+Lsdb::afterFetchLsa(const ndn::ConstBufferPtr& bufferPtr, const ndn::Name& interestName)
 {
-  ndn::Name interestName(interest.getName());
-  NLSR_LOG_DEBUG("Interest received for LSA: " << interestName);
+  NLSR_LOG_DEBUG("Received data for LSA interest: " << interestName);
+  lsaIncrementSignal(Statistics::PacketType::RCV_LSA_DATA);
 
-  if (interestName[-2].isVersion()) {
-    // Interest for particular segment
-    if (m_segmentPublisher.replyFromStore(interestName)) {
-      NLSR_LOG_TRACE("Reply from SegmentPublisher storage");
-      return;
-    }
-    // Remove version and segment
-    interestName = interestName.getSubName(0, interestName.size() - 2);
-    NLSR_LOG_TRACE("Interest w/o segment and version: " << interestName);
+  ndn::Name lsaName = interestName.getSubName(0, interestName.size()-1);
+  uint64_t seqNo = interestName[-1].toNumber();
+
+  if (m_highestSeqNo.find(lsaName) == m_highestSeqNo.end()) {
+    m_highestSeqNo[lsaName] = seqNo;
   }
-
-  // increment RCV_LSA_INTEREST
-  lsaIncrementSignal(Statistics::PacketType::RCV_LSA_INTEREST);
+  else if (seqNo > m_highestSeqNo[lsaName]) {
+    m_highestSeqNo[lsaName] = seqNo;
+    NLSR_LOG_TRACE("SeqNo for LSA(name): " << interestName << "  updated");
+  }
+  else if (seqNo < m_highestSeqNo[lsaName]) {
+    return;
+  }
 
   std::string chkString("LSA");
   int32_t lsaPosition = util::getNameComponentPosition(interestName, chkString);
 
-  // Forms the name of the router that the Interest packet came from.
-  ndn::Name originRouter = m_confParam.getNetwork();
-  originRouter.append(interestName.getSubName(lsaPosition + 1,
-                                              interestName.size() - lsaPosition - 3));
+  if (lsaPosition >= 0) {
+    // Extracts the prefix of the originating router from the data.
+    ndn::Name originRouter = m_confParam.getNetwork();
+    originRouter.append(interestName.getSubName(lsaPosition + 1,
+                                                interestName.size() - lsaPosition - 3));
 
-  // if the interest is for this router's LSA
-  if (originRouter == m_confParam.getRouterPrefix() && lsaPosition >= 0) {
-    uint64_t seqNo = interestName[-1].toNumber();
-    NLSR_LOG_DEBUG("LSA sequence number from interest: " << seqNo);
+    try {
+      Lsa::Type interestedLsType;
+      std::istringstream(interestName[-2].toUri()) >> interestedLsType;
 
-    std::string lsaType = interestName[-2].toUri();
-    Lsa::Type interestedLsType;
-    std::istringstream(lsaType) >> interestedLsType;
+      if (interestedLsType == Lsa::Type::BASE) {
+        NLSR_LOG_WARN("Received unrecognized LSA Type: " << interestName[-2].toUri());
+        return;
+      }
 
-    if (interestedLsType == Lsa::Type::NAME) {
-      processInterestForNameLsa(interest, originRouter.append(lsaType), seqNo);
+      ndn::Block block(bufferPtr);
+      if (interestedLsType == Lsa::Type::NAME) {
+        lsaIncrementSignal(Statistics::PacketType::RCV_NAME_LSA_DATA);
+        if (isLsaNew(originRouter, interestedLsType, seqNo)) {
+          installLsa(std::make_shared<NameLsa>(block));
+        }
+      }
+      else if (interestedLsType == Lsa::Type::ADJACENCY) {
+        lsaIncrementSignal(Statistics::PacketType::RCV_ADJ_LSA_DATA);
+        if (isLsaNew(originRouter, interestedLsType, seqNo)) {
+          installLsa(std::make_shared<AdjLsa>(block));
+        }
+      }
+      else if (interestedLsType == Lsa::Type::COORDINATE) {
+        lsaIncrementSignal(Statistics::PacketType::RCV_COORD_LSA_DATA);
+        if (isLsaNew(originRouter, interestedLsType, seqNo)) {
+          installLsa(std::make_shared<CoordinateLsa>(block));
+        }
+      }
     }
-    else if (interestedLsType == Lsa::Type::ADJACENCY) {
-      processInterestForAdjacencyLsa(interest, originRouter.append(lsaType), seqNo);
+    catch (const std::exception& e) {
+      NLSR_LOG_TRACE("LSA data decoding error :( " << e.what());
+      return;
     }
-    else if (interestedLsType == Lsa::Type::COORDINATE) {
-      processInterestForCoordinateLsa(interest, originRouter.append(lsaType), seqNo);
-    }
-    else {
-      NLSR_LOG_WARN("Received unrecognized LSA type: " << interestedLsType);
-    }
-    lsaIncrementSignal(Statistics::PacketType::SENT_LSA_DATA);
-  }
-  else { // else the interest is for other router's lsa, serve from LsaSegmentStorage
-    std::shared_ptr<const ndn::Data> lsaSegment = m_lsaStorage.find(interest);
-    if (lsaSegment) {
-      NLSR_LOG_TRACE("Found data in lsa storage. Sending the data for " << interest.getName());
-      m_face.put(*lsaSegment);
-    }
-    else {
-      NLSR_LOG_TRACE(interest << " was not found in this lsa storage.");
-    }
-  }
-}
-
-  // \brief Finds and sends a requested name LSA.
-  // \param interest The interest that seeks the name LSA.
-  // \param lsaKey The LSA that the Interest is seeking.
-  // \param seqNo A sequence number to ensure that we are sending the
-  // version that was requested.
-void
-Lsdb::processInterestForNameLsa(const ndn::Interest& interest,
-                                const ndn::Name& lsaKey,
-                                uint64_t seqNo)
-{
-  // increment RCV_NAME_LSA_INTEREST
-  lsaIncrementSignal(Statistics::PacketType::RCV_NAME_LSA_INTEREST);
-  NLSR_LOG_DEBUG("nameLsa interest " << interest << " received");
-  NameLsa* nameLsa = findNameLsa(lsaKey);
-  if (nameLsa != nullptr) {
-    NLSR_LOG_TRACE("Verifying SeqNo for NameLsa is same as requested.");
-    if (nameLsa->getSeqNo() == seqNo) {
-      m_segmentPublisher.publish(interest.getName(), interest.getName(),
-                                 nameLsa->wireEncode(),
-                                 m_lsaRefreshTime, m_confParam.getSigningInfo());
-
-      lsaIncrementSignal(Statistics::PacketType::SENT_NAME_LSA_DATA);
-    }
-    else {
-      NLSR_LOG_TRACE("SeqNo for nameLsa does not match");
-    }
-  }
-  else {
-    NLSR_LOG_TRACE(interest << "  was not found in this lsdb");
-  }
-}
-
-  // \brief Finds and sends a requested adj. LSA.
-  // \param interest The interest that seeks the adj. LSA.
-  // \param lsaKey The LSA that the Interest is seeking.
-  // \param seqNo A sequence number to ensure that we are sending the
-  // version that was requested.
-void
-Lsdb::processInterestForAdjacencyLsa(const ndn::Interest& interest,
-                                     const ndn::Name& lsaKey,
-                                     uint64_t seqNo)
-{
-  if (m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_ON) {
-    NLSR_LOG_ERROR("Received interest for an adjacency LSA when hyperbolic routing is enabled");
-  }
-
-  lsaIncrementSignal(Statistics::PacketType::RCV_ADJ_LSA_INTEREST);
-  NLSR_LOG_DEBUG("AdjLsa interest " << interest << " received");
-  AdjLsa* adjLsa = findAdjLsa(lsaKey);
-  if (adjLsa != nullptr) {
-    NLSR_LOG_TRACE("Verifying SeqNo for AdjLsa is same as requested.");
-    if (adjLsa->getSeqNo() == seqNo) {
-      m_segmentPublisher.publish(interest.getName(), interest.getName(),
-                                 adjLsa->wireEncode(),
-                                 m_lsaRefreshTime, m_confParam.getSigningInfo());
-
-      lsaIncrementSignal(Statistics::PacketType::SENT_ADJ_LSA_DATA);
-    }
-    else {
-      NLSR_LOG_TRACE("SeqNo for AdjLsa does not match");
-    }
-  }
-  else {
-    NLSR_LOG_TRACE(interest << "  was not found in this lsdb");
-  }
-}
-
-  // \brief Finds and sends a requested cor. LSA.
-  // \param interest The interest that seeks the cor. LSA.
-  // \param lsaKey The LSA that the Interest is seeking.
-  // \param seqNo A sequence number to ensure that we are sending the
-  // version that was requested.
-void
-Lsdb::processInterestForCoordinateLsa(const ndn::Interest& interest,
-                                      const ndn::Name& lsaKey,
-                                      uint64_t seqNo)
-{
-  if (m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_OFF) {
-    NLSR_LOG_ERROR("Received Interest for a coordinate LSA when link-state routing is enabled");
-  }
-
-  lsaIncrementSignal(Statistics::PacketType::RCV_COORD_LSA_INTEREST);
-  NLSR_LOG_DEBUG("CoordinateLsa interest " << interest << " received");
-  CoordinateLsa* corLsa = findCoordinateLsa(lsaKey);
-  if (corLsa != nullptr) {
-    NLSR_LOG_TRACE("Verifying SeqNo for CoordinateLsa is same as requested.");
-    if (corLsa->getSeqNo() == seqNo) {
-      m_segmentPublisher.publish(interest.getName(), interest.getName(),
-                                 corLsa->wireEncode(),
-                                 m_lsaRefreshTime, m_confParam.getSigningInfo());
-
-      lsaIncrementSignal(Statistics::PacketType::SENT_COORD_LSA_DATA);
-    }
-    else {
-      NLSR_LOG_TRACE("SeqNo for CoordinateLsa does not match");
-    }
-  }
-  else {
-    NLSR_LOG_TRACE(interest << "  was not found in this lsdb");
-  }
-}
-
-void
-Lsdb::processContentNameLsa(const ndn::Name& lsaKey,
-                            uint64_t lsSeqNo, const ndn::Block& block)
-{
-  lsaIncrementSignal(Statistics::PacketType::RCV_NAME_LSA_DATA);
-  if (isNameLsaNew(lsaKey, lsSeqNo)) {
-    NameLsa nameLsa(block);
-    installNameLsa(nameLsa);
-  }
-}
-
-void
-Lsdb::processContentAdjacencyLsa(const ndn::Name& lsaKey,
-                                 uint64_t lsSeqNo, const ndn::Block& block)
-{
-  lsaIncrementSignal(Statistics::PacketType::RCV_ADJ_LSA_DATA);
-  if (isAdjLsaNew(lsaKey, lsSeqNo)) {
-    AdjLsa adjLsa(block);
-    installAdjLsa(adjLsa);
-  }
-}
-
-void
-Lsdb::processContentCoordinateLsa(const ndn::Name& lsaKey,
-                                  uint64_t lsSeqNo, const ndn::Block& block)
-{
-  lsaIncrementSignal(Statistics::PacketType::RCV_COORD_LSA_DATA);
-  if (isCoordinateLsaNew(lsaKey, lsSeqNo)) {
-    CoordinateLsa corLsa(block);
-    installCoordinateLsa(corLsa);
-  }
-}
-
-ndn::time::system_clock::TimePoint
-Lsdb::getLsaExpirationTimePoint()
-{
-  ndn::time::system_clock::TimePoint expirationTimePoint = ndn::time::system_clock::now();
-  expirationTimePoint = expirationTimePoint +
-                        ndn::time::seconds(m_confParam.getRouterDeadInterval());
-  return expirationTimePoint;
-}
-
-void
-Lsdb::writeAdjLsdbLog()
-{
-  if (m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_ON) {
-    return;
-  }
-
-  NLSR_LOG_TRACE("---------------Adj LSDB-------------------");
-  for (const auto& adj : m_adjLsdb) {
-    NLSR_LOG_TRACE(adj);
-  }
-}
-
-//-----utility function -----
-bool
-Lsdb::doesLsaExist(const ndn::Name& key, const Lsa::Type& lsType)
-{
-  switch (lsType) {
-  case Lsa::Type::ADJACENCY:
-    return doesAdjLsaExist(key);
-
-  case Lsa::Type::COORDINATE:
-    return doesCoordinateLsaExist(key);
-
-  case Lsa::Type::NAME:
-    return doesNameLsaExist(key);
-
-  default:
-    return false;
-  }
-}
-
-bool
-Lsdb::isLsaNew(const ndn::Name& routerName, const Lsa::Type& lsaType,
-               const uint64_t& sequenceNumber) {
-  ndn::Name lsaKey = routerName;
-  lsaKey.append(boost::lexical_cast<std::string>(lsaType));
-
-  switch (lsaType) {
-  case Lsa::Type::ADJACENCY:
-    return isAdjLsaNew(lsaKey, sequenceNumber);
-
-  case Lsa::Type::COORDINATE:
-    return isCoordinateLsaNew(lsaKey, sequenceNumber);
-
-  case Lsa::Type::NAME:
-    return isNameLsaNew(lsaKey, sequenceNumber);
-
-  default:
-    return false;
   }
 }