lsdb: emit signals when modified

RoutingTable and NamePrefixTable consume the signal
and behave accordingly.

refs: #4127

Change-Id: I6540f30f0222f804b01dc7d9640831c84e5264cc
diff --git a/src/route/name-prefix-table.cpp b/src/route/name-prefix-table.cpp
index 91e6210..deb9af7 100644
--- a/src/route/name-prefix-table.cpp
+++ b/src/route/name-prefix-table.cpp
@@ -33,26 +33,88 @@
 
 INIT_LOGGER(route.NamePrefixTable);
 
-NamePrefixTable::NamePrefixTable(Fib& fib, RoutingTable& routingTable,
-                                 std::unique_ptr<AfterRoutingChange>& afterRoutingChangeSignal)
-  : m_fib(fib)
+NamePrefixTable::NamePrefixTable(const ndn::Name& ownRouterName, Fib& fib,
+                                 RoutingTable& routingTable,
+                                 AfterRoutingChange& afterRoutingChangeSignal,
+                                 Lsdb::AfterLsdbModified& afterLsdbModifiedSignal)
+  : m_ownRouterName(ownRouterName)
+  , m_fib(fib)
   , m_routingTable(routingTable)
 {
-  m_afterRoutingChangeConnection = afterRoutingChangeSignal->connect(
+  m_afterRoutingChangeConnection = afterRoutingChangeSignal.connect(
     [this] (const std::list<RoutingTableEntry>& entries) {
       updateWithNewRoute(entries);
     });
+
+  m_afterLsdbModified = afterLsdbModifiedSignal.connect(
+    [this] (std::shared_ptr<Lsa> lsa, LsdbUpdate updateType,
+            const auto& namesToAdd, const auto& namesToRemove) {
+      updateFromLsdb(lsa, updateType, namesToAdd, namesToRemove);
+    }
+  );
 }
 
 NamePrefixTable::~NamePrefixTable()
 {
   m_afterRoutingChangeConnection.disconnect();
+  m_afterLsdbModified.disconnect();
+}
+
+void
+NamePrefixTable::updateFromLsdb(std::shared_ptr<Lsa> lsa, LsdbUpdate updateType,
+                                const std::list<ndn::Name>& namesToAdd,
+                                const std::list<ndn::Name>& namesToRemove)
+{
+  if (m_ownRouterName == lsa->getOriginRouter()) {
+    return;
+  }
+  NLSR_LOG_TRACE("Got update from Lsdb for router: " << lsa->getOriginRouter());
+
+  if (updateType == LsdbUpdate::INSTALLED) {
+    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_ownRouterName) {
+          addEntry(name, lsa->getOriginRouter());
+        }
+      }
+    }
+  }
+  else if (updateType == LsdbUpdate::UPDATED) {
+    if (lsa->getType() != Lsa::Type::NAME) {
+      return;
+    }
+
+    for (const auto& name : namesToAdd) {
+      if (name != m_ownRouterName) {
+        addEntry(name, lsa->getOriginRouter());
+      }
+    }
+
+    for (const auto& name : namesToRemove) {
+      if (name != m_ownRouterName) {
+        removeEntry(name, lsa->getOriginRouter());
+      }
+    }
+  }
+  else {
+    removeEntry(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_ownRouterName) {
+          removeEntry(name, lsa->getOriginRouter());
+        }
+      }
+    }
+  }
 }
 
 void
 NamePrefixTable::addEntry(const ndn::Name& name, const ndn::Name& destRouter)
 {
-
   // Check if the advertised name prefix is in the table already.
   NptEntryList::iterator nameItr =
     std::find_if(m_table.begin(),
diff --git a/src/route/name-prefix-table.hpp b/src/route/name-prefix-table.hpp
index 370e515..449b928 100644
--- a/src/route/name-prefix-table.hpp
+++ b/src/route/name-prefix-table.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2020,  The University of Memphis,
+/*
+ * Copyright (c) 2014-2021,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -17,7 +17,7 @@
  *
  * 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_NAME_PREFIX_TABLE_HPP
 #define NLSR_NAME_PREFIX_TABLE_HPP
@@ -27,6 +27,7 @@
 #include "signals.hpp"
 #include "test-access-control.hpp"
 #include "route/fib.hpp"
+#include "lsdb.hpp"
 
 #include <list>
 #include <unordered_map>
@@ -41,11 +42,23 @@
   using NptEntryList = std::list<std::shared_ptr<NamePrefixTableEntry>>;
   using const_iterator = NptEntryList::const_iterator;
 
-  NamePrefixTable(Fib& fib, RoutingTable& routingTable,
-                  std::unique_ptr<AfterRoutingChange>& afterRoutingChangeSignal);
+  NamePrefixTable(const ndn::Name& ownRouterName, Fib& fib, RoutingTable& routingTable,
+                  AfterRoutingChange& afterRoutingChangeSignal,
+                  Lsdb::AfterLsdbModified& afterLsdbModifiedSignal);
 
   ~NamePrefixTable();
 
+  /*! \brief Add, update, or remove Names according to the Lsdb update
+    \param lsa The LSA class pointer
+    \param updateType Update type from Lsdb (INSTALLED, UPDATED, REMOVED)
+    \param namesToAdd If LSA is UPDATED, then these are the names to add
+    \param namesToRemove If LSA is UPDATED, then these are the names to remove
+   */
+  void
+  updateFromLsdb(std::shared_ptr<Lsa> lsa, LsdbUpdate updateType,
+                 const std::list<ndn::Name>& namesToAdd,
+                 const std::list<ndn::Name>& namesToRemove);
+
   /*! \brief Adds a destination to the specified name prefix.
     \param name The name prefix
     \param destRouter The destination router prefix
@@ -125,9 +138,11 @@
   NptEntryList m_table;
 
 private:
+  const ndn::Name& m_ownRouterName;
   Fib& m_fib;
   RoutingTable& m_routingTable;
   ndn::util::signal::Connection m_afterRoutingChangeConnection;
+  ndn::util::signal::Connection m_afterLsdbModified;
 };
 
 inline NamePrefixTable::const_iterator
diff --git a/src/route/routing-table.cpp b/src/route/routing-table.cpp
index b600882..c06b429 100644
--- a/src/route/routing-table.cpp
+++ b/src/route/routing-table.cpp
@@ -24,93 +24,85 @@
 #include "conf-parameter.hpp"
 #include "routing-table-calculator.hpp"
 #include "routing-table-entry.hpp"
-#include "name-prefix-table.hpp"
 #include "logger.hpp"
 #include "tlv-nlsr.hpp"
 
-#include <list>
-#include <string>
-
 namespace nlsr {
 
 INIT_LOGGER(route.RoutingTable);
 
-RoutingTable::RoutingTable(ndn::Scheduler& scheduler, Fib& fib, Lsdb& lsdb,
-                           NamePrefixTable& namePrefixTable, ConfParameter& confParam)
-  : afterRoutingChange{std::make_unique<AfterRoutingChange>()}
-  , m_scheduler(scheduler)
-  , m_fib(fib)
+RoutingTable::RoutingTable(ndn::Scheduler& scheduler, Lsdb& lsdb, ConfParameter& confParam)
+  : m_scheduler(scheduler)
   , m_lsdb(lsdb)
-  , m_namePrefixTable(namePrefixTable)
   , m_routingCalcInterval{confParam.getRoutingCalcInterval()}
   , m_isRoutingTableCalculating(false)
   , m_isRouteCalculationScheduled(false)
   , m_confParam(confParam)
+  , m_hyperbolicState(m_confParam.getHyperbolicState())
 {
+  m_afterLsdbModified = lsdb.onLsdbModified.connect(
+    [this] (std::shared_ptr<Lsa> lsa, LsdbUpdate updateType,
+            const auto& namesToAdd, const auto& namesToRemove) {
+      auto type = lsa->getType();
+      bool updateForOwnAdjacencyLsa = lsa->getOriginRouter() == m_confParam.getRouterPrefix() &&
+                                      type == Lsa::Type::ADJACENCY;
+      bool scheduleCalculation = false;
+
+      if (updateType == LsdbUpdate::REMOVED && updateForOwnAdjacencyLsa) {
+        // If own Adjacency LSA is removed then we have no ACTIVE neighbors.
+        // (Own Coordinate LSA is never removed. But routing table calculation is scheduled
+        // in HelloProtocol. The routing table calculator for HR takes into account
+        // the INACTIVE status of the link).
+        NLSR_LOG_DEBUG("No Adj LSA of router itself, routing table can not be calculated :(");
+        clearRoutingTable();
+        clearDryRoutingTable();
+        NLSR_LOG_DEBUG("Calling Update NPT With new Route");
+        afterRoutingChange(m_rTable);
+        NLSR_LOG_DEBUG(*this);
+        m_ownAdjLsaExist = false;
+      }
+
+      if (updateType == LsdbUpdate::INSTALLED && updateForOwnAdjacencyLsa) {
+        m_ownAdjLsaExist = true;
+      }
+
+      // Don;t do anything on removal, wait for HelloProtocol to confirm and then react
+      if (updateType == LsdbUpdate::INSTALLED || updateType == LsdbUpdate::UPDATED) {
+        if ((type == Lsa::Type::ADJACENCY  && m_hyperbolicState != HYPERBOLIC_STATE_ON) ||
+            (type == Lsa::Type::COORDINATE && m_hyperbolicState != HYPERBOLIC_STATE_OFF)) {
+          scheduleCalculation = true;
+        }
+      }
+
+      if (scheduleCalculation) {
+        scheduleRoutingTableCalculation();
+      }
+    }
+  );
 }
 
 void
 RoutingTable::calculate()
 {
   m_lsdb.writeLog();
-  m_namePrefixTable.writeLog();
+  NLSR_LOG_TRACE("Calculating routing table");
+
   if (m_isRoutingTableCalculating == false) {
-    // setting routing table calculation
     m_isRoutingTableCalculating = true;
 
-    bool isHrEnabled = m_confParam.getHyperbolicState() != HYPERBOLIC_STATE_OFF;
-
-    if ((!isHrEnabled &&
-         m_lsdb.doesLsaExist(m_confParam.getRouterPrefix(), Lsa::Type::ADJACENCY))
-        ||
-        (isHrEnabled &&
-         m_lsdb.doesLsaExist(m_confParam.getRouterPrefix(), Lsa::Type::COORDINATE))) {
-      if (m_lsdb.getIsBuildAdjLsaSheduled() != 1) {
-        NLSR_LOG_TRACE("Clearing old routing table");
-        clearRoutingTable();
-        // for dry run options
-        clearDryRoutingTable();
-
-        NLSR_LOG_DEBUG("Calculating routing table");
-
-        // calculate Link State routing
-        if ((m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_OFF) ||
-            (m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_DRY_RUN)) {
-          calculateLsRoutingTable();
-        }
-        // calculate hyperbolic routing
-        if (m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_ON) {
-          calculateHypRoutingTable(false);
-        }
-        // calculate dry hyperbolic routing
-        if (m_confParam.getHyperbolicState() == HYPERBOLIC_STATE_DRY_RUN) {
-          calculateHypRoutingTable(true);
-        }
-        // Inform the NPT that updates have been made
-        NLSR_LOG_DEBUG("Calling Update NPT With new Route");
-        (*afterRoutingChange)(m_rTable);
-        NLSR_LOG_DEBUG(*this);
-        m_namePrefixTable.writeLog();
-        m_fib.writeLog();
-      }
-      else {
-        NLSR_LOG_DEBUG("Adjacency building is scheduled, so routing table can not be calculated :(");
-      }
+    if (m_hyperbolicState == HYPERBOLIC_STATE_OFF) {
+      calculateLsRoutingTable();
     }
-    else {
-      NLSR_LOG_DEBUG("No Adj LSA of router itself, so Routing table can not be calculated :(");
-      clearRoutingTable();
-      clearDryRoutingTable(); // for dry run options
-      // need to update NPT here
-      NLSR_LOG_DEBUG("Calling Update NPT With new Route");
-      (*afterRoutingChange)(m_rTable);
-      NLSR_LOG_DEBUG(*this);
-      m_namePrefixTable.writeLog();
-      m_fib.writeLog();
-      // debugging purpose end
+    else if (m_hyperbolicState == HYPERBOLIC_STATE_DRY_RUN) {
+      calculateLsRoutingTable();
+      calculateHypRoutingTable(true);
     }
-    m_isRouteCalculationScheduled = false; // clear scheduled flag
-    m_isRoutingTableCalculating = false; // unsetting routing table calculation
+    else if (m_hyperbolicState == HYPERBOLIC_STATE_ON) {
+      calculateHypRoutingTable(false);
+    }
+
+    m_isRouteCalculationScheduled = false;
+    m_isRoutingTableCalculating = false;
   }
   else {
     scheduleRoutingTableCalculation();
@@ -122,6 +114,19 @@
 {
   NLSR_LOG_TRACE("CalculateLsRoutingTable Called");
 
+  if (m_lsdb.getIsBuildAdjLsaScheduled()) {
+    NLSR_LOG_DEBUG("Adjacency build is scheduled, routing table can not be calculated :(");
+    return;
+  }
+
+  // We only check this in LS since we never remove our own Coordinate LSA,
+  // whereas we remove our own Adjacency LSA if we don't have any neighbors
+  if (!m_ownAdjLsaExist) {
+    return;
+  }
+
+  clearRoutingTable();
+
   Map map;
   auto lsaRange = m_lsdb.getLsdbIterator<AdjLsa>();
   map.createFromAdjLsdb(lsaRange.first, lsaRange.second);
@@ -132,11 +137,22 @@
   LinkStateRoutingTableCalculator calculator(nRouters);
 
   calculator.calculatePath(map, *this, m_confParam, m_lsdb);
+
+  NLSR_LOG_DEBUG("Calling Update NPT With new Route");
+  afterRoutingChange(m_rTable);
+  NLSR_LOG_DEBUG(*this);
 }
 
 void
 RoutingTable::calculateHypRoutingTable(bool isDryRun)
 {
+  if (isDryRun) {
+    clearDryRoutingTable();
+  }
+  else {
+    clearRoutingTable();
+  }
+
   Map map;
   auto lsaRange = m_lsdb.getLsdbIterator<CoordinateLsa>();
   map.createFromCoordinateLsdb(lsaRange.first, lsaRange.second);
@@ -147,6 +163,12 @@
   HyperbolicRoutingCalculator calculator(nRouters, isDryRun, m_confParam.getRouterPrefix());
 
   calculator.calculatePath(map, *this, m_lsdb, m_confParam.getAdjacencyList());
+
+  if (!isDryRun) {
+    NLSR_LOG_DEBUG("Calling Update NPT With new Route");
+    afterRoutingChange(m_rTable);
+    NLSR_LOG_DEBUG(*this);
+  }
 }
 
 void
@@ -179,6 +201,7 @@
   else {
     rteChk->getNexthopList().addNextHop(nh);
   }
+  m_wire.reset();
 }
 
 RoutingTableEntry*
@@ -207,22 +230,21 @@
   else {
     it->getNexthopList().addNextHop(nh);
   }
+  m_wire.reset();
 }
 
 void
 RoutingTable::clearRoutingTable()
 {
-  if (m_rTable.size() > 0) {
-    m_rTable.clear();
-  }
+  m_rTable.clear();
+  m_wire.reset();
 }
 
 void
 RoutingTable::clearDryRoutingTable()
 {
-  if (m_dryTable.size() > 0) {
-    m_dryTable.clear();
-  }
+  m_dryTable.clear();
+  m_wire.reset();
 }
 
 template<ndn::encoding::Tag TAG>
diff --git a/src/route/routing-table.hpp b/src/route/routing-table.hpp
index 4a0ebd2..f7e0525 100644
--- a/src/route/routing-table.hpp
+++ b/src/route/routing-table.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2020,  The University of Memphis,
+ * Copyright (c) 2014-2021,  The University of Memphis,
  *                           Regents of the University of California
  *
  * This file is part of NLSR (Named-data Link State Routing).
@@ -27,6 +27,7 @@
 #include "lsdb.hpp"
 #include "route/fib.hpp"
 #include "test-access-control.hpp"
+#include "route/name-prefix-table.hpp"
 
 #include <ndn-cxx/util/scheduler.hpp>
 
@@ -89,8 +90,12 @@
 {
 public:
   explicit
-  RoutingTable(ndn::Scheduler& scheduler, Fib& fib, Lsdb& lsdb,
-               NamePrefixTable& namePrefixTable, ConfParameter& confParam);
+  RoutingTable(ndn::Scheduler& scheduler, Lsdb& lsdb, ConfParameter& confParam);
+
+  ~RoutingTable()
+  {
+    m_afterLsdbModified.disconnect();
+  }
 
   /*! \brief Calculates a list of next hops for each router in the network.
    *
@@ -122,24 +127,6 @@
   void
   scheduleRoutingTableCalculation();
 
-  void
-  setRoutingCalcInterval(uint32_t interval)
-  {
-    m_routingCalcInterval = ndn::time::seconds(interval);
-  }
-
-  const ndn::time::seconds&
-  getRoutingCalcInterval() const
-  {
-    return m_routingCalcInterval;
-  }
-
-  uint64_t
-  getRtSize()
-  {
-    return m_rTable.size();
-  }
-
 private:
   /*! \brief Calculates a link-state routing table. */
   void
@@ -156,21 +143,21 @@
   clearDryRoutingTable();
 
 public:
-  std::unique_ptr<AfterRoutingChange> afterRoutingChange;
+  AfterRoutingChange afterRoutingChange;
 
 private:
   ndn::Scheduler& m_scheduler;
-  Fib& m_fib;
   Lsdb& m_lsdb;
-  NamePrefixTable& m_namePrefixTable;
-
-  ndn::time::seconds m_routingCalcInterval;
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  ndn::time::seconds m_routingCalcInterval;
   bool m_isRoutingTableCalculating;
   bool m_isRouteCalculationScheduled;
 
   ConfParameter& m_confParam;
+  ndn::util::signal::Connection m_afterLsdbModified;
+  int32_t m_hyperbolicState;
+  bool m_ownAdjLsaExist = false;
 };
 
 } // namespace nlsr