route: Fix FIB next hop removal bug

refs: #2018

Change-Id: Id107c04d4cdce9cc756acad5d262e5c1e0cc29a8
diff --git a/src/conf-parameter.hpp b/src/conf-parameter.hpp
index b8b5b72..8f2965b 100644
--- a/src/conf-parameter.hpp
+++ b/src/conf-parameter.hpp
@@ -289,12 +289,12 @@
   }
 
   void
-  setMaxFacesPerPrefix(int32_t mfpp)
+  setMaxFacesPerPrefix(uint32_t mfpp)
   {
     m_maxFacesPerPrefix = mfpp;
   }
 
-  int32_t
+  uint32_t
   getMaxFacesPerPrefix() const
   {
     return m_maxFacesPerPrefix;
@@ -353,7 +353,7 @@
   double m_corR;
   double m_corTheta;
 
-  int32_t m_maxFacesPerPrefix;
+  uint32_t m_maxFacesPerPrefix;
 
   std::string m_logDir;
   std::string m_seqFileDir;
diff --git a/src/nlsr.hpp b/src/nlsr.hpp
index 240fffb..00b7877 100644
--- a/src/nlsr.hpp
+++ b/src/nlsr.hpp
@@ -79,7 +79,7 @@
     , m_isRouteCalculationScheduled(false)
     , m_isRoutingTableCalculating(false)
     , m_routingTable(scheduler)
-    , m_fib(*this, m_nlsrFace, scheduler)
+    , m_fib(m_nlsrFace, scheduler, m_adjacencyList, m_confParam)
     , m_namePrefixTable(*this)
     , m_syncLogicHandler(m_nlsrFace, m_nlsrLsdb, m_confParam)
     , m_helloProtocol(*this, scheduler)
diff --git a/src/route/fib.cpp b/src/route/fib.cpp
index 2010858..1815b27 100644
--- a/src/route/fib.cpp
+++ b/src/route/fib.cpp
@@ -24,14 +24,13 @@
 #include <cmath>
 #include <ndn-cxx/common.hpp>
 
-#include "nlsr.hpp"
+#include "adjacency-list.hpp"
+#include "conf-parameter.hpp"
 #include "nexthop-list.hpp"
 #include "face-map.hpp"
 #include "fib.hpp"
 #include "logger.hpp"
 
-
-
 namespace nlsr {
 
 INIT_LOGGER("Fib");
@@ -44,7 +43,7 @@
 static bool
 fibEntryNameCompare(const FibEntry& fibEntry, const ndn::Name& name)
 {
-  return fibEntry.getName() == name ;
+  return fibEntry.getName() == name;
 }
 
 void
@@ -93,84 +92,102 @@
 }
 
 void
-Fib::addNextHopsToFibEntryAndNfd(FibEntry& entry, NexthopList& nextHopList)
+Fib::addNextHopsToFibEntryAndNfd(FibEntry& entry, NexthopList& hopsToAdd)
 {
   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)
+  for (NexthopList::iterator it = hopsToAdd.begin(); it != hopsToAdd.end(); ++it)
   {
     // Add nexthop to FIB entry
-    entry.getNexthopList().addNextHop(*hopIt);
+    entry.getNexthopList().addNextHop(*it);
 
     if (isPrefixUpdatable(name)) {
       // Add nexthop to NDN-FIB
-      registerPrefix(name, hopIt->getConnectingFaceUri(),
-                     hopIt->getRouteCostAsAdjustedInteger(),
+      registerPrefix(name, it->getConnectingFaceUri(),
+                     it->getRouteCostAsAdjustedInteger(),
                      ndn::time::seconds(m_refreshTime + GRACE_PERIOD),
                      ndn::nfd::ROUTE_FLAG_CAPTURE, 0);
     }
   }
+
+  entry.getNexthopList().sort();
+}
+
+void
+Fib::removeOldNextHopsFromFibEntryAndNfd(FibEntry& entry, const NexthopList& installedHops)
+{
+  _LOG_DEBUG("Fib::removeOldNextHopsFromFibEntryAndNfd Called");
+
+  const ndn::Name& name = entry.getName();
+  NexthopList& entryHopList = entry.getNexthopList();
+
+  for (NexthopList::iterator it = entryHopList.begin(); it != entryHopList.end();) {
+
+    const std::string& faceUri = it->getConnectingFaceUri();
+
+    // See if the nexthop is installed in NFD's FIB
+    NexthopList::const_iterator foundIt = std::find_if(installedHops.cbegin(),
+                                                       installedHops.cend(),
+                                                       bind(&compareFaceUri, _1, faceUri));
+
+    // The next hop is not installed
+    if (foundIt == installedHops.cend()) {
+
+      if (isPrefixUpdatable(name)) {
+        // Remove the nexthop from NDN's FIB
+        unregisterPrefix(name, it->getConnectingFaceUri());
+      }
+
+      // Remove the next hop from the FIB entry
+      _LOG_DEBUG("Removing " << it->getConnectingFaceUri() << " from " << name);
+      // Since the iterator will be invalidated on removal, dereference the original
+      // and increment the copy
+      entryHopList.removeNextHop(*(it++));
+    }
+    else {
+      ++it;
+    }
+  }
 }
 
 void
-Fib::removeOldNextHopsFromFibEntryAndNfd(FibEntry& entry, NexthopList& newHopList)
+Fib::update(const ndn::Name& name, NexthopList& allHops)
 {
-  _LOG_DEBUG("Fib::removeOldNextHopsFromFibEntryAndNfd Called");
-  const ndn::Name& name = entry.getName();
-  NexthopList& entryHopList = entry.getNexthopList();
-  NexthopList itHopList = entryHopList;
+  _LOG_DEBUG("Fib::update called");
 
-  for (NexthopList::iterator it = itHopList.begin(); it != itHopList.end(); ++it) {
+  // Sort all of the next hops so lower cost hops are prioritized
+  allHops.sort();
 
-    // 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));
+  // Get the max possible faces which is the minumum of the configuration setting and
+  // the length of the list of all next hops.
+  unsigned int maxFaces = getNumberOfFacesForName(allHops);
 
-      // 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);
+  NexthopList hopsToAdd;
+  unsigned int nFaces = 0;
 
-        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");
+  // Create a list of next hops to be installed with length == maxFaces
+  for (NexthopList::iterator it = allHops.begin(); it != allHops.end() && nFaces < maxFaces;
+       ++it, ++nFaces)
+  {
+    hopsToAdd.addNextHop(*it);
+  }
 
   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) {
+    if (hopsToAdd.getSize() == 0) {
       return;
     }
 
     FibEntry entry(name);
 
-    addNextHopsToFibEntryAndNfd(entry, nextHopList);
-
-    entry.getNexthopList().sort();
+    addNextHopsToFibEntryAndNfd(entry, hopsToAdd);
 
     // Set entry's expiration time point and sequence number
     entry.setExpirationTimePoint(ndn::time::system_clock::now() +
@@ -189,15 +206,14 @@
     FibEntry& entry = *entryIt;
 
     // Remove empty FIB entry
-    if (nextHopList.getSize() == 0) {
+    if (hopsToAdd.getSize() == 0) {
       remove(name);
       return;
     }
 
-    addNextHopsToFibEntryAndNfd(entry, nextHopList);
-    removeOldNextHopsFromFibEntryAndNfd(entry, nextHopList);
+    addNextHopsToFibEntryAndNfd(entry, hopsToAdd);
 
-    entry.getNexthopList().sort();
+    removeOldNextHopsFromFibEntryAndNfd(entry, hopsToAdd);
 
     // Set entry's expiration time point
     entry.setExpirationTimePoint(ndn::time::system_clock::now() +
@@ -205,7 +221,7 @@
     // Increment sequence number
     entry.setSeqNo(entry.getSeqNo() + 1);
 
-    // Cancel previosuly scheduled event
+    // Cancel previously scheduled event
     m_scheduler.cancelEvent(entry.getExpiringEventId());
 
     // Schedule entry to be refreshed
@@ -234,23 +250,24 @@
   }
 }
 
-int
-Fib::getNumberOfFacesForName(NexthopList& nextHopList,
-                             uint32_t maxFacesPerPrefix)
+unsigned int
+Fib::getNumberOfFacesForName(NexthopList& nextHopList)
 {
-  int endFace = 0;
-  if ((maxFacesPerPrefix == 0) || (nextHopList.getSize() <= maxFacesPerPrefix)) {
-    return nextHopList.getSize();
+  uint32_t nNextHops = static_cast<uint32_t>(nextHopList.getNextHops().size());
+  uint32_t nMaxFaces = m_confParameter.getMaxFacesPerPrefix();
+
+  // Allow all faces
+  if (nMaxFaces == 0) {
+    return nNextHops;
   }
   else {
-    return maxFacesPerPrefix;
+    return std::min(nNextHops, nMaxFaces);
   }
-  return endFace;
 }
 
 bool
 Fib::isPrefixUpdatable(const ndn::Name& name) {
-  if (!m_nlsr.getAdjacencyList().isNeighbor(name)) {
+  if (!m_adjacencyList.isNeighbor(name)) {
     return true;
   }
 
@@ -313,7 +330,7 @@
                     uint64_t faceCost, const ndn::time::milliseconds& timeout,
                     uint64_t flags, uint8_t times)
 {
-  uint64_t faceId = m_nlsr.getAdjacencyList().getFaceId(faceUri);
+  uint64_t faceId = m_adjacencyList.getFaceId(faceUri);
   if (faceId != 0) {
     ndn::nfd::ControlParameters faceParameters;
     faceParameters
diff --git a/src/route/fib.hpp b/src/route/fib.hpp
index 9893ff2..e49727b 100644
--- a/src/route/fib.hpp
+++ b/src/route/fib.hpp
@@ -30,27 +30,30 @@
 #include <ndn-cxx/util/time.hpp>
 #include "face-map.hpp"
 #include "fib-entry.hpp"
+#include "test-access-control.hpp"
 
 namespace nlsr {
 
 typedef ndn::function<void(const ndn::nfd::ControlParameters&)> CommandSucceedCallback;
 typedef ndn::function<void(uint32_t/*code*/,const std::string&/*reason*/)> CommandFailCallback;
 
-class Nlsr;
-
+class AdjacencyList;
+class ConfParameter;
 
 class Fib
 {
 public:
-  Fib(Nlsr& nlsr, ndn::Face& face, ndn::Scheduler& scheduler)
-    : m_nlsr(nlsr)
-    , m_scheduler(scheduler)
+  Fib(ndn::Face& face, ndn::Scheduler& scheduler, AdjacencyList& adjacencyList, ConfParameter& conf)
+    : m_scheduler(scheduler)
     , m_table()
     , m_refreshTime(0)
     , m_controller(face)
     , m_faceMap()
+    , m_adjacencyList(adjacencyList)
+    , m_confParameter(conf)
   {
   }
+
   ~Fib()
   {
   }
@@ -59,7 +62,7 @@
   remove(const ndn::Name& name);
 
   void
-  update(const ndn::Name& name, NexthopList& nextHopList);
+  update(const ndn::Name& name, NexthopList& allHops);
 
   void
   clean();
@@ -75,17 +78,17 @@
   isPrefixUpdatable(const ndn::Name& name);
 
   void
-  addNextHopsToFibEntryAndNfd(FibEntry& entry, NexthopList& nextHopList);
+  addNextHopsToFibEntryAndNfd(FibEntry& entry, NexthopList& hopsToAdd);
 
   void
-  removeOldNextHopsFromFibEntryAndNfd(FibEntry& entry, NexthopList& newHopList);
+  removeOldNextHopsFromFibEntryAndNfd(FibEntry& entry, const NexthopList& installedHops);
 
   void
   removeHop(NexthopList& nl, const std::string& doNotRemoveHopFaceUri,
             const ndn::Name& name);
 
-  int
-  getNumberOfFacesForName(NexthopList& nextHopList, uint32_t maxFacesPerPrefix);
+  unsigned int
+  getNumberOfFacesForName(NexthopList& nextHopList);
 
   ndn::EventId
   scheduleEntryExpiration(const ndn::Name& name, int32_t feSeqNum,
@@ -178,14 +181,19 @@
                        const std::string& message);
 
 private:
-  Nlsr& m_nlsr;
   ndn::Scheduler& m_scheduler;
 
   std::list<FibEntry> m_table;
   int32_t m_refreshTime;
   ndn::nfd::Controller m_controller;
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   FaceMap m_faceMap;
 
+private:
+  AdjacencyList& m_adjacencyList;
+  ConfParameter& m_confParameter;
+
   static const uint64_t GRACE_PERIOD;
 };
 
diff --git a/src/route/nexthop-list.hpp b/src/route/nexthop-list.hpp
index 7c2011e..faf13f0 100644
--- a/src/route/nexthop-list.hpp
+++ b/src/route/nexthop-list.hpp
@@ -73,6 +73,7 @@
   }
 
   typedef std::list<NextHop>::iterator iterator;
+  typedef std::list<NextHop>::const_iterator const_iterator;
 
   iterator
   begin()
@@ -86,6 +87,18 @@
     return m_nexthopList.end();
   }
 
+  const_iterator
+  cbegin() const
+  {
+    return m_nexthopList.begin();
+  }
+
+  const_iterator
+  cend() const
+  {
+    return m_nexthopList.end();
+  }
+
   void
   writeLog();
 
diff --git a/src/test-access-control.hpp b/src/test-access-control.hpp
new file mode 100644
index 0000000..f1667e3
--- /dev/null
+++ b/src/test-access-control.hpp
@@ -0,0 +1,39 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014  University of Memphis,
+ *                     Regents of the University of California
+ *
+ * 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_TEST_ACCESS_CONTROL_HPP
+#define NLSR_TEST_ACCESS_CONTROL_HPP
+
+#include "config.hpp"
+
+#ifdef WITH_TESTS
+#define VIRTUAL_WITH_TESTS virtual
+#define PUBLIC_WITH_TESTS_ELSE_PROTECTED public
+#define PUBLIC_WITH_TESTS_ELSE_PRIVATE public
+#define PROTECTED_WITH_TESTS_ELSE_PRIVATE protected
+#else
+#define VIRTUAL_WITH_TESTS
+#define PUBLIC_WITH_TESTS_ELSE_PROTECTED protected
+#define PUBLIC_WITH_TESTS_ELSE_PRIVATE private
+#define PROTECTED_WITH_TESTS_ELSE_PRIVATE private
+#endif
+
+#endif //NLSR_TEST_ACCESS_CONTROL_HPP