route: Ensure nexthops are reregistered correctly

Reregister prefixes if nexthops don't change
Remove invalid nexthops instead of all of them
Cancel FibEntry expiration events before scheduling new ones
Remove FibEntry refreshing
Add grace period to LSA expiration

refs: #1892

Change-Id: I5136b6b0f52a47b6ba72e04b0cea81b85ebccd45
diff --git a/src/lsdb.cpp b/src/lsdb.cpp
index dc7882d..74daed4 100644
--- a/src/lsdb.cpp
+++ b/src/lsdb.cpp
@@ -33,6 +33,8 @@
 
 INIT_LOGGER("Lsdb");
 
+const ndn::time::seconds Lsdb::GRACE_PERIOD = ndn::time::seconds(10);
+
 using namespace std;
 
 void
@@ -91,7 +93,7 @@
 Lsdb::scheduleNameLsaExpiration(const ndn::Name& key, int seqNo,
                                 const ndn::time::seconds& expTime)
 {
-  return m_nlsr.getScheduler().scheduleEvent(expTime,
+  return m_nlsr.getScheduler().scheduleEvent(expTime + GRACE_PERIOD,
                                              ndn::bind(&Lsdb::exprireOrRefreshNameLsa,
                                                        this, key, seqNo));
 }
@@ -297,7 +299,7 @@
 Lsdb::scheduleCoordinateLsaExpiration(const ndn::Name& key, int seqNo,
                                       const ndn::time::seconds& expTime)
 {
-  return m_nlsr.getScheduler().scheduleEvent(expTime,
+  return m_nlsr.getScheduler().scheduleEvent(expTime + GRACE_PERIOD,
                                              ndn::bind(&Lsdb::exprireOrRefreshCoordinateLsa,
                                                        this, key, seqNo));
 }
@@ -503,7 +505,7 @@
 Lsdb::scheduleAdjLsaExpiration(const ndn::Name& key, int seqNo,
                                const ndn::time::seconds& expTime)
 {
-  return m_nlsr.getScheduler().scheduleEvent(expTime,
+  return m_nlsr.getScheduler().scheduleEvent(expTime + GRACE_PERIOD,
                                              ndn::bind(&Lsdb::exprireOrRefreshAdjLsa,
                                                        this, key, seqNo));
 }
diff --git a/src/lsdb.hpp b/src/lsdb.hpp
index ef45cea..c5dcbed 100644
--- a/src/lsdb.hpp
+++ b/src/lsdb.hpp
@@ -239,6 +239,7 @@
   seconds m_lsaRefreshTime;
   std::string m_thisRouterPrefix;
 
+  static const ndn::time::seconds GRACE_PERIOD;
 };
 
 }//namespace nlsr
diff --git a/src/route/fib.cpp b/src/route/fib.cpp
index 7a8e98b..de9d365 100644
--- a/src/route/fib.cpp
+++ b/src/route/fib.cpp
@@ -55,43 +55,13 @@
 
 
 ndn::EventId
-Fib::scheduleEntryRefreshing(const ndn::Name& name, int32_t feSeqNum,
+Fib::scheduleEntryExpiration(const ndn::Name& name, int32_t feSeqNum,
                              const ndn::time::seconds& expTime)
 {
-  _LOG_DEBUG("Fib::scheduleEntryRefreshing Called");
-  _LOG_DEBUG("Name: " << name << " Seq Num: " << feSeqNum);
+  _LOG_DEBUG("Fib::scheduleEntryExpiration Called");
+  _LOG_INFO("Name: " << name << " Seq Num: " << feSeqNum);
   return m_nlsr.getScheduler().scheduleEvent(expTime,
-                                             ndn::bind(&Fib::refreshEntry, this,
-                                                       name, feSeqNum));
-}
-
-void
-Fib::refreshEntry(const ndn::Name& name, int32_t feSeqNum)
-{
-  _LOG_DEBUG("Fib::refreshEntry Called");
-  _LOG_DEBUG("Name: " << name << " Seq Num: " << feSeqNum);
-  std::list<FibEntry>::iterator it = std::find_if(m_table.begin(),
-                                                  m_table.end(),
-                                                  bind(&fibEntryNameCompare, _1, name));
-  if (it != m_table.end()) {
-    _LOG_DEBUG("Refreshing the FIB entry. Name: " <<  name);
-    for (std::list<NextHop>::iterator nhit =
-           (*it).getNexthopList().getNextHops().begin();
-           nhit != (*it).getNexthopList().getNextHops().end(); nhit++) {
-      // add entry to NDN-FIB
-      if (isPrefixUpdatable(it->getName())) {
-        registerPrefix(it->getName(), nhit->getConnectingFaceUri(),
-                       std::ceil(nhit->getRouteCost()),
-                       ndn::time::seconds(m_refreshTime + GRACE_PERIOD),
-                       ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
-      }
-    }
-    // increase sequence number and schedule refresh again
-    it->setSeqNo(feSeqNum + 1);
-    it->setExpiringEventId(scheduleEntryRefreshing(it->getName() ,
-                                                   it->getSeqNo(),
-                                                   ndn::time::seconds(m_refreshTime)));
-  }
+                                             ndn::bind(&Fib::remove, this, name));
 }
 
 void
@@ -116,89 +86,134 @@
   }
 }
 
+bool
+compareFaceUri(const NextHop& hop, const std::string& faceUri)
+{
+  return hop.getConnectingFaceUri() == faceUri;
+}
+
+void
+Fib::addNextHopsToFibEntryAndNfd(FibEntry& entry, NexthopList& nextHopList)
+{
+  const ndn::Name& name = entry.getName();
+
+  nextHopList.sort();
+
+  int numFaces = 0;
+  int maxFaces = getNumberOfFacesForName(nextHopList,
+                                         m_nlsr.getConfParameter().getMaxFacesPerPrefix());
+
+  for (NexthopList::iterator hopIt = nextHopList.begin();
+       hopIt != nextHopList.end() && numFaces < maxFaces; ++hopIt, ++numFaces)
+  {
+    // Add nexthop to FIB entry
+    entry.getNexthopList().addNextHop(*hopIt);
+
+    if (isPrefixUpdatable(name)) {
+      // Add nexthop to NDN-FIB
+      registerPrefix(name, hopIt->getConnectingFaceUri(),
+                     std::ceil(hopIt->getRouteCost()),
+                     ndn::time::seconds(m_refreshTime + GRACE_PERIOD),
+                     ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
+    }
+  }
+}
+
+void
+Fib::removeOldNextHopsFromFibEntryAndNfd(FibEntry& entry, NexthopList& newHopList)
+{
+  _LOG_DEBUG("Fib::removeOldNextHopsFromFibEntryAndNfd Called");
+  const ndn::Name& name = entry.getName();
+  NexthopList& entryHopList = entry.getNexthopList();
+  NexthopList itHopList = entryHopList;
+
+  for (NexthopList::iterator it = itHopList.begin(); it != itHopList.end(); ++it) {
+
+    // See if the nexthop is in the new nexthop list
+    const std::string& faceUri = it->getConnectingFaceUri();
+    NexthopList::iterator foundIt = std::find_if(newHopList.begin(),
+                                                 newHopList.end(),
+                                                 bind(&compareFaceUri, _1, faceUri));
+
+      // The next hop is not in the new nexthop list
+      if (foundIt == newHopList.end()) {
+        // Remove the next hop from the FIB entry
+        _LOG_DEBUG("Removing " << it->getConnectingFaceUri() << " from " << name);
+        entryHopList.removeNextHop(*it);
+
+        if (isPrefixUpdatable(name)) {
+          // Remove the nexthop from NDN-FIB
+          unregisterPrefix(name, it->getConnectingFaceUri());
+        }
+      }
+    }
+}
 
 void
 Fib::update(const ndn::Name& name, NexthopList& nextHopList)
 {
   _LOG_DEBUG("Fib::updateFib Called");
-  int startFace = 0;
-  int endFace = getNumberOfFacesForName(nextHopList,
-                                        m_nlsr.getConfParameter().getMaxFacesPerPrefix());
-  std::list<FibEntry>::iterator it = std::find_if(m_table.begin(),
-                                                  m_table.end(),
-                                                  bind(&fibEntryNameCompare, _1, name));
-  if (it == m_table.end()) {
-    if (nextHopList.getSize() > 0) {
-      nextHopList.sort();
-      FibEntry newEntry(name);
-      std::list<NextHop> nhl = nextHopList.getNextHops();
-      std::list<NextHop>::iterator nhit = nhl.begin();
-      for (int i = startFace; i < endFace && nhit != nhl.end(); ++nhit, i++) {
-        newEntry.getNexthopList().addNextHop((*nhit));
-        //Add entry to NDN-FIB
-        if (isPrefixUpdatable(name)) {
-          registerPrefix(name, nhit->getConnectingFaceUri(),
-                         std::ceil(nhit->getRouteCost()),
-                         ndn::time::seconds(m_refreshTime + GRACE_PERIOD),
-                         ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
-        }
-      }
-      newEntry.getNexthopList().sort();
-      ndn::time::system_clock::TimePoint expirationTimePoint = ndn::time::system_clock::now();
-      expirationTimePoint = expirationTimePoint + ndn::time::seconds(m_refreshTime);
-      newEntry.setExpirationTimePoint(expirationTimePoint);
-      newEntry.setSeqNo(1);
-      newEntry.setExpiringEventId(scheduleEntryRefreshing(name , 1,
-                                                          ndn::time::seconds(m_refreshTime)));
-      m_table.push_back(newEntry);
+
+  std::list<FibEntry>::iterator entryIt = std::find_if(m_table.begin(),
+                                                       m_table.end(),
+                                                       bind(&fibEntryNameCompare, _1, name));
+  // New FIB entry
+  if (entryIt == m_table.end()) {
+    _LOG_DEBUG("New FIB Entry");
+
+    // Don't create an entry for a name with no nexthops
+    if (nextHopList.getSize() == 0) {
+      return;
     }
+
+    FibEntry entry(name);
+
+    addNextHopsToFibEntryAndNfd(entry, nextHopList);
+
+    entry.getNexthopList().sort();
+
+    // Set entry's expiration time point and sequence number
+    entry.setExpirationTimePoint(ndn::time::system_clock::now() +
+                                  ndn::time::seconds(m_refreshTime));
+    entry.setSeqNo(1);
+
+    // Schedule entry to be refreshed
+    entry.setExpiringEventId(scheduleEntryExpiration(name , entry.getSeqNo(),
+                                                     ndn::time::seconds(m_refreshTime)));
+    m_table.push_back(entry);
   }
   else {
-    _LOG_DEBUG("Old FIB Entry");
-    if (nextHopList.getSize() > 0) {
-      nextHopList.sort();
-      if (!it->isEqualNextHops(nextHopList)) {
-        std::list<NextHop> nhl = nextHopList.getNextHops();
-        std::list<NextHop>::iterator nhit = nhl.begin();
-        // Add first Entry to NDN-FIB
-        if (isPrefixUpdatable(name)) {
-          registerPrefix(name, nhit->getConnectingFaceUri(),
-                         std::ceil(nhit->getRouteCost()),
-                         ndn::time::seconds(m_refreshTime + GRACE_PERIOD),
-                         ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
-        }
-        removeHop(it->getNexthopList(), nhit->getConnectingFaceUri(), name);
-        it->getNexthopList().reset();
-        it->getNexthopList().addNextHop((*nhit));
-        ++startFace;
-        ++nhit;
-        for (int i = startFace; i < endFace && nhit != nhl.end(); ++nhit, i++) {
-          it->getNexthopList().addNextHop((*nhit));
-          //Add Entry to NDN_FIB
-          if (isPrefixUpdatable(name)) {
-            registerPrefix(name, nhit->getConnectingFaceUri(),
-                           std::ceil(nhit->getRouteCost()),
-                           ndn::time::seconds(m_refreshTime + GRACE_PERIOD),
-                           ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
-          }
-        }
-      }
-      ndn::time::system_clock::TimePoint expirationTimePoint = ndn::time::system_clock::now();
-      expirationTimePoint = expirationTimePoint + ndn::time::seconds(m_refreshTime);
-      it->setExpirationTimePoint(expirationTimePoint);
-      it->setSeqNo(it->getSeqNo() + 1);
-      (*it).setExpiringEventId(scheduleEntryRefreshing(it->getName() ,
-                                                       it->getSeqNo(),
-                                                       ndn::time::seconds(m_refreshTime)));
-    }
-    else {
+    // Existing FIB entry
+    _LOG_DEBUG("Existing FIB Entry");
+
+    FibEntry& entry = *entryIt;
+
+    // Remove empty FIB entry
+    if (nextHopList.getSize() == 0) {
       remove(name);
+      return;
     }
+
+    addNextHopsToFibEntryAndNfd(entry, nextHopList);
+    removeOldNextHopsFromFibEntryAndNfd(entry, nextHopList);
+
+    entry.getNexthopList().sort();
+
+    // Set entry's expiration time point
+    entry.setExpirationTimePoint(ndn::time::system_clock::now() +
+                                  ndn::time::seconds(m_refreshTime));
+    // Increment sequence number
+    entry.setSeqNo(entry.getSeqNo() + 1);
+
+    // Cancel previosuly scheduled event
+    m_nlsr.getScheduler().cancelEvent(entry.getExpiringEventId());
+
+    // Schedule entry to be refreshed
+    entry.setExpiringEventId(scheduleEntryExpiration(name , entry.getSeqNo(),
+                                                     ndn::time::seconds(m_refreshTime)));
   }
 }
 
-
-
 void
 Fib::clean()
 {
diff --git a/src/route/fib.hpp b/src/route/fib.hpp
index 2522928..6b448fb 100644
--- a/src/route/fib.hpp
+++ b/src/route/fib.hpp
@@ -74,6 +74,12 @@
   isPrefixUpdatable(const ndn::Name& name);
 
   void
+  addNextHopsToFibEntryAndNfd(FibEntry& entry, NexthopList& nextHopList);
+
+  void
+  removeOldNextHopsFromFibEntryAndNfd(FibEntry& entry, NexthopList& newHopList);
+
+  void
   removeHop(NexthopList& nl, const std::string& doNotRemoveHopFaceUri,
             const ndn::Name& name);
 
@@ -81,15 +87,12 @@
   getNumberOfFacesForName(NexthopList& nextHopList, uint32_t maxFacesPerPrefix);
 
   ndn::EventId
-  scheduleEntryRefreshing(const ndn::Name& name, int32_t feSeqNum,
+  scheduleEntryExpiration(const ndn::Name& name, int32_t feSeqNum,
                           const ndn::time::seconds& expTime);
 
   void
   cancelScheduledExpiringEvent(ndn::EventId eid);
 
-  void
-  refreshEntry(const ndn::Name& name, int32_t feSeqNum);
-
 public:
   void
   registerPrefix(const ndn::Name& namePrefix, const std::string& faceUri,
diff --git a/src/route/nexthop-list.hpp b/src/route/nexthop-list.hpp
index c34400d..7c2011e 100644
--- a/src/route/nexthop-list.hpp
+++ b/src/route/nexthop-list.hpp
@@ -72,6 +72,20 @@
     return m_nexthopList;
   }
 
+  typedef std::list<NextHop>::iterator iterator;
+
+  iterator
+  begin()
+  {
+    return m_nexthopList.begin();
+  }
+
+  iterator
+  end()
+  {
+    return m_nexthopList.end();
+  }
+
   void
   writeLog();