helper: Add LFID route calculation
Loop-Free In-port Dependent (LFID) route calculation provides a set of loop-free paths.
Basically porting the existing code from https://github.com/schneiderklaus/ndnSIM-routing
Refs: #4985
Change-Id: I1ab25e729851cf2233c3b99be715ba0159cca0c7
diff --git a/docs/source/examples.rst b/docs/source/examples.rst
index 9ef32fa..84a25bb 100644
--- a/docs/source/examples.rst
+++ b/docs/source/examples.rst
@@ -508,3 +508,32 @@
-----------------------------------
:ref:`Example of packet drop tracer (L2Tracer)`
+
+
+LFID (Loop-Free Inport-Dependent) Routing for ndnSIM
+----------------------------------------------------
+
+LFID (Loop-Free Inport-Dependent) Routing extends the ndnSIM route calculation to provide loop-free and on average shorter loop-free paths than existing work.
+For details see the `tech report.`_
+
+LFID provides a better trade-off than the existing route calculation algorithms:
+
+1. ``CalculateRoutes():`` Only provides a single shortest path nexthop.
+2. ``CalculateAllPossibleRoutes():`` Provides all possible nexthops, but many of them lead to loops.
+
+LFID, on the other hand, maximizes the nexthop choice while also completely avoiding loops.
+
+
+We provide two example topologies (abilene and grid) to compare against the existing route calculation methods:
+
+.. literalinclude:: ../../examples/lfid.cpp
+ :language: c++
+ :linenos:
+ :lines: 71-148
+
+
+Simply run::
+
+ ./waf --run "lfid [--grid] [--routing={lfid|sp|allroutes}]"
+
+The output will show the nexthops at each node for the given name prefix, and any loops during forwarding.
diff --git a/examples/lfid.cpp b/examples/lfid.cpp
new file mode 100644
index 0000000..e2d6f05
--- /dev/null
+++ b/examples/lfid.cpp
@@ -0,0 +1,154 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2011-2019 Regents of the University of California.
+ *
+ * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and
+ * contributors.
+ *
+ * ndnSIM 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.
+ *
+ * ndnSIM 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
+ * ndnSIM, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "ns3/command-line.h"
+#include "ns3/double.h"
+#include "ns3/names.h"
+#include "ns3/point-to-point-channel.h"
+#include "ns3/uinteger.h"
+
+#include "ns3/ndnSIM/helper/ndn-app-helper.hpp"
+#include "ns3/ndnSIM/helper/ndn-global-routing-helper.hpp"
+#include "ns3/ndnSIM/helper/ndn-stack-helper.hpp"
+#include "ns3/ndnSIM/helper/ndn-strategy-choice-helper.hpp"
+#include "ns3/ndnSIM/model/ndn-l3-protocol.hpp"
+#include "ns3/ndnSIM/model/ndn-net-device-transport.hpp"
+//#include "ns3/ndnSIM/NFD/daemon/fw/random-strategy.hpp"
+#include "ns3/ndnSIM/NFD/daemon/fw/best-route-strategy2.hpp"
+#include "ns3/ndnSIM/utils/topology/annotated-topology-reader.hpp"
+
+namespace ns3 {
+
+void
+displayRoutes(const NodeContainer& allNodes, const std::string& prefix)
+{
+ for (const auto& n : allNodes) {
+ const auto& fib = n->GetObject<ndn::L3Protocol>()->getForwarder()->getFib();
+ const auto& e = fib.findLongestPrefixMatch(prefix);
+
+ std::cout << "Node " << n->GetId() << ", prefix: " << prefix << "\n";
+
+ for (const auto& nh : e.getNextHops()) {
+ // Get remote nodeId from face:
+ const auto transport = dynamic_cast<ndn::NetDeviceTransport*>(nh.getFace().getTransport());
+ if (transport == nullptr)
+ continue;
+
+ const auto nd1 = transport->GetNetDevice()->GetObject<PointToPointNetDevice>();
+ if (nd1 == nullptr)
+ continue;
+
+ const auto ppChannel = DynamicCast<PointToPointChannel>(nd1->GetChannel());
+ if (ppChannel == nullptr)
+ continue;
+
+ auto nd2 = ppChannel->GetDevice(0);
+ if (nd2->GetNode() == n)
+ nd2 = ppChannel->GetDevice(1);
+
+ std::cout << " NextHop: " << nd2->GetNode()->GetId() << ", cost: " << nh.getCost() << "\n";
+ }
+ std::cout << "\n";
+ }
+}
+
+int
+main(int argc, char* argv[])
+{
+ bool grid = false; // Use grid topology?
+ std::string routing = "lfid";
+ CommandLine cmd;
+ cmd.AddValue("grid", "use grid topology (instead of abilene)", grid);
+ cmd.AddValue("routing", "which route computation to use (lfid, sp, allroutes)", routing);
+ cmd.Parse(argc, argv);
+
+ std::string topoName = "abilene";
+ if (grid) {
+ topoName = "grid";
+ }
+
+ std::cout << "Using " << topoName << " topology\n\n";
+
+ AnnotatedTopologyReader topologyReader{};
+ topologyReader.SetFileName("src/ndnSIM/examples/topologies/topo-" + topoName + ".txt");
+ topologyReader.Read();
+
+ ndn::StackHelper stackHelper{};
+ stackHelper.InstallAll();
+
+ // IMPORTANT: Has to be run after StackHelper!
+ topologyReader.ApplyOspfMetric();
+
+ const std::string prefix{"/prefix"};
+
+ Ptr<Node> consumerN = Names::Find<Node>("router0");
+ Ptr<Node> producerN = Names::Find<Node>("producer");
+ NS_ABORT_MSG_UNLESS(consumerN && producerN, "consumer or producer name does not exist in topo file!");
+
+ ndn::GlobalRoutingHelper routingHelper;
+ routingHelper.InstallAll(); // Fills GlobalRouter with incidencies.
+ routingHelper.AddOrigin(prefix, producerN);
+
+ if (routing == "lfid") {
+ routingHelper.CalculateLfidRoutes();
+ }
+ else if (routing == "sp") {
+ routingHelper.CalculateRoutes();
+ }
+ else if (routing == "allroutes") {
+ routingHelper.CalculateAllPossibleRoutes();
+ }
+ else {
+ NS_FATAL_ERROR("Unknown route calculation! Use --routing {lfid|sp|allroutes}");
+ }
+
+ // IMPORTANT: Some strategy needs to be installed for displayRoutes() to work.
+ ndn::StrategyChoiceHelper strategyHelper;
+ strategyHelper.InstallAll<nfd::fw::BestRouteStrategy2>("/");
+
+ // TODO: Needs RandomStrategy for test to work!
+ // Uncomment after NFD version has been updated.
+ // strategyHelper.InstallAll<nfd::fw::RandomStrategy>("/");
+
+ displayRoutes(topologyReader.GetNodes(), prefix);
+
+ // Installing applications
+ ndn::AppHelper consumerHelperX{"ns3::ndn::ConsumerCbr"};
+ consumerHelperX.SetPrefix(prefix);
+ consumerHelperX.SetAttribute("Frequency", DoubleValue(100.0));
+ consumerHelperX.Install(consumerN);
+
+ ndn::AppHelper producerHelper0{"ns3::ndn::Producer"};
+ producerHelper0.SetPrefix(prefix);
+ producerHelper0.Install(producerN);
+
+ Simulator::Stop(Seconds(30));
+ Simulator::Run();
+ Simulator::Destroy();
+
+ return 0;
+}
+
+} // namespace ns3
+
+int
+main(int argc, char* argv[])
+{
+ return ns3::main(argc, argv);
+}
diff --git a/examples/topologies/topo-abilene.txt b/examples/topologies/topo-abilene.txt
new file mode 100644
index 0000000..499929a
--- /dev/null
+++ b/examples/topologies/topo-abilene.txt
@@ -0,0 +1,60 @@
+# any empty lines and lines starting with '#' symbol are ignored
+#
+# The file should contain exactly two sections: router and link, each starting with the corresponding keyword
+#
+# router section defines topology nodes and their relative positions (e.g., to use in visualizer)
+router
+
+# each line in this section represents one router and should have the following data
+# node comment yPos xPos
+
+router0 NA 0 1
+router1 NA 1 1
+router2 NA 1 2
+router3 NA 1 3
+router4 NA 1 4
+router5 NA 1 4
+router6 NA 1 4
+router7 NA 1 4
+router8 NA 2 1
+router9 NA 2 1
+producer NA 2 1
+
+# Note that `node` can be any string. It is possible to access to the node by name using Names::Find, see examples.
+
+# link section defines point-to-point links between nodes and characteristics of these links
+link
+
+# Each line should be in the following format (only first two are required, the rest can be omitted)
+# srcNode dstNode bandwidth metric delay queue
+# bandwidth: link bandwidth
+# metric: routing metric // Set real routing metrics inside simulation file!
+# delay: link delay
+# queue: MaxPackets for transmission queue on the link (both directions)
+
+
+# label LinkType LinkLabel LinkNote cost
+
+router0 router1 100Mbps 71 5ms
+router0 router2 100Mbps 20 5ms
+
+router1 producer 100Mbps 16 5ms
+
+router2 router9 100Mbps 54 5ms
+
+router3 router4 100Mbps 71 5ms
+router3 router6 100Mbps 102 5ms
+
+router4 router5 100Mbps 31 5ms
+router4 router6 100Mbps 93 5ms
+
+router5 router8 10Mbps 137 5ms
+
+router6 router7 100Mbps 55 5ms
+
+router7 router8 10Mbps 65 5ms
+router7 producer 10Mbps 45 5ms
+
+router8 router9 10Mbps 70 5ms
+
+router9 producer 10Mbps 43 5ms
diff --git a/examples/topologies/topo-grid.txt b/examples/topologies/topo-grid.txt
new file mode 100644
index 0000000..f733388
--- /dev/null
+++ b/examples/topologies/topo-grid.txt
@@ -0,0 +1,51 @@
+# any empty lines and lines starting with '#' symbol are ignored
+#
+# The file should contain exactly two sections: router and link, each starting with the corresponding keyword
+#
+# router section defines topology nodes and their relative positions (e.g., to use in visualizer)
+router
+
+# each line in this section represents one router and should have the following data
+# node comment yPos xPos
+
+router0 NA 0 1
+router1 NA 1 1
+router2 NA 1 2
+router3 NA 1 3
+router4 NA 1 4
+router5 NA 1 4
+router6 NA 1 4
+router7 NA 1 4
+producer NA 2 1
+
+
+# Note that `node` can be any string. It is possible to access to the node by name using Names::Find, see examples.
+
+# link section defines point-to-point links between nodes and characteristics of these links
+link
+
+# Each line should be in the following format (only first two are required, the rest can be omitted)
+# srcNode dstNode bandwidth metric delay queue
+# bandwidth: link bandwidth
+# metric: routing metric // Set real routing metrics inside simulation file!
+# delay: link delay
+# queue: MaxPackets for transmission queue on the link (both directions)
+
+router0 router1 100Mbps 1 5ms
+router0 router3 100Mbps 2 5ms
+
+router1 router2 100Mbps 3 5ms
+router1 router4 100Mbps 4 5ms
+
+router2 router5 100Mbps 5 5ms
+
+router3 router4 100Mbps 16 5ms
+router3 router6 100Mbps 1 5ms
+
+router4 router5 100Mbps 8 5ms
+router4 router7 100Mbps 4 5ms
+
+router6 router7 100Mbps 5 5ms
+
+router5 producer 10Mbps 10 5ms
+router7 producer 10Mbps 2 5ms
diff --git a/helper/boost-graph-ndn-global-routing-helper.hpp b/helper/boost-graph-ndn-global-routing-helper.hpp
index 49b724e..588f436 100644
--- a/helper/boost-graph-ndn-global-routing-helper.hpp
+++ b/helper/boost-graph-ndn-global-routing-helper.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2011-2015 Regents of the University of California.
+ * Copyright (c) 2011-2019 Regents of the University of California.
*
* This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and
* contributors.
@@ -30,7 +30,9 @@
#include "ns3/ndnSIM/model/ndn-global-router.hpp"
+#include "ns3/node.h"
#include "ns3/node-list.h"
+#include "ns3/channel.h"
#include "ns3/channel-list.h"
#include <list>
diff --git a/helper/lfid/abstract-fib.cpp b/helper/lfid/abstract-fib.cpp
new file mode 100644
index 0000000..c4d736f
--- /dev/null
+++ b/helper/lfid/abstract-fib.cpp
@@ -0,0 +1,188 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2019 Klaus Schneider, The University of Arizona
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Klaus Schneider <klaus@cs.arizona.edu>
+ */
+
+#include "abstract-fib.hpp"
+
+#include <algorithm>
+
+#include "ns3/names.h"
+#include "ns3/ndnSIM/NFD/daemon/fw/forwarder.hpp"
+#include "ns3/ndnSIM/model/ndn-global-router.hpp"
+#include "ns3/ndnSIM/model/ndn-l3-protocol.hpp"
+#include "ns3/node.h"
+
+namespace ns3 {
+namespace ndn {
+
+using std::set;
+
+AbstractFib::AbstractFib(const Ptr<GlobalRouter> own, int numNodes)
+ : nodeId{static_cast<int>(own->GetObject<ns3::Node>()->GetId())}
+ , nodeName{ns3::Names::FindName(own->GetObject<ns3::Node>())}
+ , numberOfNodes{numNodes}
+ , nodeDegree{static_cast<int>(own->GetIncidencies().size())}
+ , ownRouter{own}
+ , upwardCounter{0}
+ , totalNhCounter{0}
+{
+ checkInputs();
+
+ createEmptyFib();
+}
+
+void
+AbstractFib::checkInputs()
+{
+ if (nodeDegree <= 0) {
+ std::cerr << nodeName << " has a degree of " << nodeDegree << "!\n\n";
+ }
+
+ const auto MAX_SIZE{1e5};
+ NS_ABORT_UNLESS(nodeId >= 0 && nodeId <= MAX_SIZE);
+ NS_ABORT_UNLESS(nodeName.size() > 0 && nodeName.size() <= MAX_SIZE);
+ NS_ABORT_UNLESS(nodeDegree >= 0 && nodeDegree <= MAX_SIZE);
+ NS_ABORT_UNLESS(numberOfNodes > 1 && numberOfNodes <= MAX_SIZE);
+}
+
+void
+AbstractFib::createEmptyFib()
+{
+ // Create empty FIB:
+ for (int dstId = 0; dstId < numberOfNodes; dstId++) {
+ if (dstId == nodeId) {
+ continue;
+ }
+ perDstFib.insert({dstId, {}});
+ upwardPerDstFib.insert({dstId, {}});
+ }
+}
+
+Ptr<GlobalRouter>
+AbstractFib::getGR() const
+{
+ return ownRouter;
+}
+
+// Setters:
+void
+AbstractFib::insert(int dstId, const FibNextHop& nh)
+{
+ NS_ABORT_UNLESS(nh.getType() == NextHopType::DOWNWARD || nh.getType() == NextHopType::UPWARD);
+ NS_ABORT_UNLESS(nh.getNexthopId() != nodeId);
+
+ bool inserted1 = perDstFib.at(dstId).insert(nh).second;
+ BOOST_VERIFY(inserted1); // Check if it didn't exist yet.
+ totalNhCounter++;
+
+ if (nh.getType() == NextHopType::UPWARD) {
+ bool inserted2 = upwardPerDstFib.at(dstId).insert(nh).second;
+ BOOST_VERIFY(inserted2);
+ upwardCounter++;
+ }
+}
+
+size_t
+AbstractFib::erase(int dstId, int nhId)
+{
+ auto& fib{perDstFib.at(dstId)};
+
+ auto fibNh = std::find_if(fib.begin(), fib.end(),
+ [&](const FibNextHop& item) { return item.getNexthopId() == nhId; });
+
+ // Element doesn't exist:
+ if (fibNh == fib.end()) {
+
+ // TODO: Figure out why this happens.
+ return 0;
+ }
+
+ NS_ABORT_UNLESS(fibNh != perDstFib.at(dstId).end());
+ NS_ABORT_UNLESS(fibNh->getType() == NextHopType::UPWARD);
+ totalNhCounter--;
+
+ fib.erase(fibNh);
+ auto numErased2 = upwardPerDstFib.at(dstId).erase(*fibNh);
+ NS_ABORT_UNLESS(numErased2 == 1);
+ upwardCounter--;
+
+ return numErased2;
+}
+
+// O(1)
+const set<FibNextHop>&
+AbstractFib::getNexthops(int dstId) const
+{
+ NS_ABORT_MSG_IF(dstId == nodeId, "Requested destination id is the same as current nodeId!");
+ NS_ABORT_MSG_IF(perDstFib.count(dstId) == 0,
+ "Node " << nodeId << " No nexthops for dst: " << dstId << "!");
+ NS_ABORT_UNLESS(perDstFib.count(dstId) != 0);
+ return perDstFib.at(dstId);
+}
+
+const set<FibNextHop>&
+AbstractFib::getUpwardNexthops(int dstId) const
+{
+ NS_ABORT_MSG_IF(dstId == nodeId, "Requested destination id is the same as current nodeId!");
+ NS_ABORT_MSG_IF(perDstFib.count(dstId) == 0,
+ "Node " << nodeId << " No nexthops for dst: " << dstId << "!");
+ return upwardPerDstFib.at(dstId);
+}
+
+void
+AbstractFib::checkFib() const
+{
+ BOOST_VERIFY(perDstFib.size() > 0);
+
+ for (const auto& fibSet : perDstFib) {
+ const size_t numNhs = fibSet.second.size();
+
+ bool hasDownward{false};
+ std::unordered_set<int> nextHopSet{};
+
+ for (const FibNextHop& nextHop : fibSet.second) {
+ BOOST_VERIFY(nextHop.getCost() > 0 && nextHop.getCost() < FibNextHop::MAX_COST);
+ if (nextHop.getType() == NextHopType::DOWNWARD) {
+ hasDownward = true;
+ }
+
+ // Only one FIB entry per nexthop allowed!
+ BOOST_VERIFY(nextHopSet.count(nextHop.getNexthopId()) == 0);
+ nextHopSet.emplace(nextHop.getNexthopId());
+ }
+ BOOST_VERIFY(hasDownward || numNhs == 0);
+ BOOST_VERIFY(nextHopSet.size() == fibSet.second.size());
+ }
+}
+
+std::ostream&
+operator<<(std::ostream& os, const AbstractFib& fib)
+{
+ for (const auto& entry : fib.perDstFib) {
+ os << "\nFIB node: " << fib.nodeName << fib.nodeId << "\n";
+ os << "Dst: " << entry.first << "\n";
+ for (const auto& nh : entry.second) {
+ os << nh << "\n";
+ }
+ }
+ return os;
+}
+
+} // namespace ndn
+} // namespace ns-3
diff --git a/helper/lfid/abstract-fib.hpp b/helper/lfid/abstract-fib.hpp
new file mode 100644
index 0000000..b5a118f
--- /dev/null
+++ b/helper/lfid/abstract-fib.hpp
@@ -0,0 +1,164 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2019 Klaus Schneider, The University of Arizona
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Klaus Schneider <klaus@cs.arizona.edu>
+ */
+
+#ifndef LFID_ABS_FIB_H
+#define LFID_ABS_FIB_H
+
+#include <set>
+#include <unordered_map>
+
+#include "ns3/abort.h"
+#include "ns3/ndnSIM/helper/lfid/fib-nexthop.hpp"
+#include "ns3/ptr.h"
+
+namespace ns3 {
+namespace ndn {
+
+class GlobalRouter;
+
+/**
+ * An abstract, lightweight representation of the FIB.
+ */
+class AbstractFib {
+public:
+ using AllNodeFib = std::unordered_map<int, AbstractFib>;
+
+ /**
+ * @param own The GlobalRouter object representing the current router
+ * @param numNodes The total number of nodes in the network
+ */
+ AbstractFib(const Ptr<GlobalRouter> own, int numNodes);
+
+public:
+ // Getters:
+ /**
+ * @return Return set of nexthops per destination
+ */
+ const std::set<FibNextHop>&
+ getNexthops(int dstId) const;
+
+ /**
+ * @return Return set of upward nexthops per destination
+ */
+ const std::set<FibNextHop>&
+ getUpwardNexthops(int dstId) const;
+
+ /**
+ * Make sure that FIB is consistent (each destination has at least one downward nexthop)
+ */
+ void
+ checkFib() const;
+
+ /**
+ * @return Return number nexthops per destination
+ * @pre Also assure that the destination is not equal to the current nodeId.
+ */
+ int
+ numEnabledNhPerDst(int dstId) const
+ {
+ NS_ABORT_UNLESS(dstId != nodeId);
+ return static_cast<int>(getNexthops(dstId).size());
+ }
+
+ int
+ getNodeId() const
+ {
+ return nodeId;
+ }
+
+ Ptr<GlobalRouter>
+ getGR() const;
+
+ std::string
+ getName() const
+ {
+ return nodeName;
+ }
+
+ int
+ getDegree() const
+ {
+ return nodeDegree;
+ }
+
+ int
+ getNumDsts() const
+ {
+ return static_cast<int>(perDstFib.size());
+ }
+
+ bool
+ contains(int dstId) const
+ {
+ return perDstFib.count(dstId) > 0;
+ }
+
+ // Functions for range-based loop:
+ auto
+ begin() const
+ {
+ return perDstFib.cbegin();
+ }
+
+ auto
+ end() const
+ {
+ return perDstFib.cend();
+ }
+
+ // Setters:
+ void
+ insert(int dstId, const FibNextHop& nh);
+
+ size_t
+ erase(int dstId, int nhId);
+
+private:
+ void
+ checkInputs();
+
+ void
+ createEmptyFib();
+
+private:
+ const int nodeId; // Own node id
+ const std::string nodeName; // Own node name
+ const int numberOfNodes;
+ const int nodeDegree;
+ const Ptr<GlobalRouter> ownRouter;
+
+ int upwardCounter;
+ int totalNhCounter;
+
+ // DstId -> set<FibNextHop>
+ std::unordered_map<int, std::set<FibNextHop>> perDstFib;
+ std::unordered_map<int, std::set<FibNextHop>> upwardPerDstFib;
+
+ friend std::ostream&
+ operator<<(std::ostream&, const AbstractFib& fib);
+};
+
+std::ostream&
+operator<<(std::ostream& os, const AbstractFib& fib);
+
+} // namespace ndn
+} // namespace ns-3
+
+#endif // LFID_ABS_FIB_H
diff --git a/helper/lfid/fib-nexthop.cpp b/helper/lfid/fib-nexthop.cpp
new file mode 100644
index 0000000..dc3b069
--- /dev/null
+++ b/helper/lfid/fib-nexthop.cpp
@@ -0,0 +1,83 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2019 Klaus Schneider, The University of Arizona
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Klaus Schneider <klaus@cs.arizona.edu>
+ */
+
+#include "fib-nexthop.hpp"
+
+#include <boost/functional/hash.hpp>
+#include <ostream>
+
+namespace ns3 {
+namespace ndn {
+
+constexpr int NODE_ID_LIMIT = 1000;
+
+FibNextHop::FibNextHop(int cost, int nhId, int costDelta, NextHopType type)
+{
+ NS_ABORT_UNLESS(cost > 0 && cost <= MAX_COST);
+ NS_ABORT_UNLESS(nhId >= 0 && nhId <= NODE_ID_LIMIT);
+
+ this->m_nhId = nhId;
+ this->m_cost = cost;
+ this->m_type = type;
+ this->m_costDelta = costDelta;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const NextHopType& type)
+{
+ switch (type) {
+ case NextHopType::DOWNWARD:
+ return os << "DOWNWARD";
+ case NextHopType::UPWARD:
+ return os << "UPWARD";
+ case NextHopType::DISABLED:
+ return os << "DISABLED";
+ }
+ return os << static_cast<int>(type);
+}
+
+std::ostream&
+operator<<(std::ostream& os, const FibNextHop& a)
+{
+ return os << "Id: " << a.getNexthopId() << ", cost: " << a.m_cost << ", type: " << a.m_type;
+}
+
+} // namespace ndn
+} // namespace ns-3
+
+namespace std {
+
+using ns3::ndn::FibNextHop;
+
+template <>
+struct hash<FibNextHop> {
+ size_t
+ operator()(const FibNextHop& k) const
+ {
+ // Combine hash via boost library
+ std::size_t seed = 0;
+ boost::hash_combine(seed, k.getNexthopId());
+ boost::hash_combine(seed, k.getCost());
+
+ return seed;
+ }
+};
+
+}
diff --git a/helper/lfid/fib-nexthop.hpp b/helper/lfid/fib-nexthop.hpp
new file mode 100644
index 0000000..32a5e2a
--- /dev/null
+++ b/helper/lfid/fib-nexthop.hpp
@@ -0,0 +1,145 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2019 Klaus Schneider, The University of Arizona
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Klaus Schneider <klaus@cs.arizona.edu>
+ */
+
+#ifndef LFID_FIB_NH_H
+#define LFID_FIB_NH_H
+
+#include <iosfwd>
+#include <unordered_set>
+
+#include "ns3/abort.h"
+
+namespace ns3 {
+namespace ndn {
+
+enum class NextHopType { DOWNWARD,
+ UPWARD,
+ DISABLED };
+
+class FibNextHop {
+public:
+ static constexpr int MAX_COST = 1 * 1000 * 1000;
+
+ /**
+ * @param nhId The nexthop id.
+ * @param cost The link cost of a nexthop. has to be larger than 0!
+ * @param costDelta The cost difference (relative to the shortest path nexthop)
+ * @param type The nexthop type @sa NextHopType
+ */
+ FibNextHop(int cost, int nhId, int costDelta = -1, NextHopType type = NextHopType::DISABLED);
+
+ // Getters
+ int
+ getNexthopId() const
+ {
+ return m_nhId;
+ }
+
+ int
+ getCost() const
+ {
+ return m_cost;
+ }
+
+ int
+ getCostDelta() const
+ {
+ return m_costDelta;
+ }
+
+ NextHopType
+ getType() const
+ {
+ return m_type;
+ }
+
+ // Setters:
+ void
+ setType(const NextHopType& newType)
+ {
+ NS_ABORT_UNLESS(newType != NextHopType::DISABLED);
+ this->m_type = newType;
+ }
+
+ // Only used in old fillFib:
+ void
+ setCost(int newCost, int newCostDelta)
+ {
+ NS_ABORT_UNLESS(newCost > 0);
+ NS_ABORT_UNLESS(newCostDelta >= 0);
+ this->m_cost = newCost;
+ this->m_costDelta = newCostDelta;
+ }
+
+private:
+ // Order of FibNexthop:
+ friend bool
+ operator<(const FibNextHop& own, const FibNextHop& other)
+ {
+ NS_ABORT_UNLESS(own.m_nhId != -1);
+
+ return std::tie(own.m_costDelta, own.m_cost, own.m_nhId)
+ < std::tie(other.m_costDelta, other.m_cost, other.m_nhId);
+ }
+
+ friend bool
+ operator==(const FibNextHop& own, const FibNextHop& other)
+ {
+ if (other.m_nhId == own.m_nhId) {
+ // Check that there are no FibNextHop with identical id, but different cost.
+ NS_ABORT_UNLESS(other.m_cost == own.m_cost);
+ NS_ABORT_UNLESS(other.m_costDelta == own.m_costDelta);
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ friend bool
+ operator!=(const FibNextHop& own, const FibNextHop& other)
+ {
+ return !(own == other);
+ }
+
+ friend std::ostream&
+ operator<<(std::ostream&, const FibNextHop& fib);
+
+private:
+ int m_cost;
+ int m_nhId;
+ NextHopType m_type;
+ int m_costDelta;
+};
+
+std::ostream&
+operator<<(std::ostream& os, const NextHopType& type);
+std::ostream&
+operator<<(std::ostream& os, const FibNextHop& a);
+
+} // namespace ndn
+} // namespace ns-3
+
+namespace std {
+template <>
+struct hash<ns3::ndn::FibNextHop>;
+}
+
+#endif // LFID_FIB_NH_H
diff --git a/helper/lfid/ndn-global-routing-helper-lfid.cpp b/helper/lfid/ndn-global-routing-helper-lfid.cpp
new file mode 100644
index 0000000..13f86ab
--- /dev/null
+++ b/helper/lfid/ndn-global-routing-helper-lfid.cpp
@@ -0,0 +1,201 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2019 Klaus Schneider, The University of Arizona
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Klaus Schneider <klaus@cs.arizona.edu>
+ */
+
+#include "ns3/ndnSIM/helper/ndn-global-routing-helper.hpp"
+
+#include <boost/graph/dijkstra_shortest_paths.hpp>
+#include <boost/graph/graph_utility.hpp>
+
+#include "ns3/ndnSIM/helper/boost-graph-ndn-global-routing-helper.hpp"
+#include "ns3/ndnSIM/helper/lfid/abstract-fib.hpp"
+#include "ns3/ndnSIM/helper/lfid/remove-loops.hpp"
+#include "ns3/ndnSIM/helper/ndn-fib-helper.hpp"
+#include "ns3/ndnSIM/model/ndn-global-router.hpp"
+
+NS_LOG_COMPONENT_DEFINE("ndn.GlobalRoutingHelperLfid");
+
+namespace ns3 {
+namespace ndn {
+
+using std::get;
+using std::unordered_map;
+
+void
+GlobalRoutingHelper::CalculateLfidRoutes()
+{
+ BOOST_CONCEPT_ASSERT((boost::VertexListGraphConcept<boost::NdnGlobalRouterGraph>));
+ BOOST_CONCEPT_ASSERT((boost::IncidenceGraphConcept<boost::NdnGlobalRouterGraph>));
+
+ // Creates graph from nodeList:
+ boost::NdnGlobalRouterGraph graph{};
+
+ AbstractFib::AllNodeFib allNodeFIB;
+
+ // Store mapping nodeId -> faceId -> Ptr<Face>
+ unordered_map<int, unordered_map<nfd::FaceId, shared_ptr<Face>>> faceMap;
+
+ // For all existing nodes:
+ for (auto node = NodeList::Begin(); node != NodeList::End(); node++) {
+
+ int nodeId = static_cast<int>((*node)->GetId());
+ const Ptr<GlobalRouter>& source = (*node)->GetObject<GlobalRouter>();
+
+ AbstractFib nodeFib = AbstractFib{source, static_cast<int>(NodeList::GetNNodes())};
+
+ if (source == nullptr) {
+ NS_LOG_ERROR("Node " << (*node)->GetId() << " does not export GlobalRouter interface");
+ continue;
+ }
+
+ // map: neighborId -> DistancesMap
+ unordered_map<int, boost::DistancesMap> nbSp;
+
+ // map: Destination (GlobalRouter) -> Distance
+ boost::DistancesMap distMap;
+ unordered_map<int, boost::DistancesMap> neighborSpMap;
+
+ dijkstra_shortest_paths(graph, source,
+ distance_map(boost::ref(distMap))
+ .distance_inf(boost::WeightInf)
+ .distance_zero(boost::WeightZero)
+ .distance_compare(boost::WeightCompare())
+ .distance_combine(boost::WeightCombine()));
+
+ // 1. Get all neighbors of node.
+ unordered_map<nfd::FaceId, uint64_t> originalMetrics;
+ auto& originalFace = faceMap[nodeId];
+
+ const GlobalRouter::IncidencyList& neighbors = source->GetIncidencies();
+ // Set link weight of all neighbors to infinity
+ for (const auto& neighbor : neighbors) {
+ int nbId = get<2>(neighbor)->GetObject<ns3::Node>()->GetId();
+ NS_ABORT_UNLESS(nbId != nodeId);
+
+ auto& face = get<shared_ptr<Face>>(neighbor);
+ NS_ABORT_UNLESS(face != nullptr);
+
+ originalMetrics[nbId] = face->getMetric();
+ originalFace[nbId] = face; // Is only a copy
+ face->setMetric(get<uint16_t>(boost::WeightInf));
+ }
+
+ // 2. Calculate Dijkstra for neighbors
+ for (const auto& neighbor : neighbors) {
+ const auto& nSource = get<0>(neighbor);
+ const auto& target = get<2>(neighbor);
+
+ int nbId = target->GetObject<ns3::Node>()->GetId();
+ Ptr<GlobalRouter> nbRouter = target;
+
+ NS_ABORT_UNLESS(target != source && nbId != nodeId);
+ NS_ABORT_UNLESS(nSource == source);
+
+ dijkstra_shortest_paths(graph, nbRouter,
+ distance_map(boost::ref(neighborSpMap[nbId]))
+ .distance_inf(boost::WeightInf)
+ .distance_zero(boost::WeightZero)
+ .distance_compare(boost::WeightCompare())
+ .distance_combine(boost::WeightCombine()));
+ }
+
+ // 3. Reset link weights
+ for (const auto& neighbor : neighbors) {
+ int nbId = get<2>(neighbor)->GetObject<ns3::Node>()->GetId();
+ NS_ABORT_UNLESS(nbId != nodeId);
+
+ const auto& face = get<shared_ptr<Face>>(neighbor);
+ NS_ABORT_UNLESS(face->getMetric() == get<uint16_t>(boost::WeightInf));
+ face->setMetric(originalMetrics.at(nbId));
+ }
+
+ // 4. Fill Abstract FIB:
+ // For each destination:
+ for (const auto& dstEntry : distMap) {
+ Ptr<GlobalRouter> dstRouter = dstEntry.first;
+ int dstId = dstRouter->GetObject<ns3::Node>()->GetId();
+ if (dstRouter == source)
+ continue; // Skip destination == source.
+
+ int spTotalCost = static_cast<int>(get<uint32_t>(dstEntry.second));
+
+ // For each neighbor:
+ for (const auto& nb : neighborSpMap) {
+ int neighborId = nb.first;
+ const uint32_t nbDist{get<uint32_t>(nb.second.at(dstRouter))};
+
+ int neighborCost = static_cast<int>(nbDist);
+ int neighborTotalCost = neighborCost + static_cast<int>(originalFace.at(neighborId)->getMetric());
+
+ NS_ABORT_UNLESS(neighborTotalCost >= spTotalCost);
+
+ // Skip routers that would loop back
+ if (neighborTotalCost >= static_cast<int>(get<uint16_t>(boost::WeightInf)))
+ continue;
+
+ NextHopType nbType;
+ if (neighborCost < spTotalCost) {
+ nbType = NextHopType::DOWNWARD;
+ }
+ else {
+ nbType = NextHopType::UPWARD;
+ }
+
+ int costDelta = neighborTotalCost - spTotalCost;
+ FibNextHop nh = {neighborTotalCost, neighborId, costDelta, nbType};
+ nodeFib.insert(dstId, nh);
+ }
+
+ } // End for all dsts
+
+ nodeFib.checkFib();
+ allNodeFIB.emplace(nodeId, nodeFib);
+ } // End for all nodes
+
+ /// 4. Remove loops and Deadends ///
+ removeLoops(allNodeFIB, true);
+ removeDeadEnds(allNodeFIB, true);
+
+ // 5. Insert from AbsFIB into real FIB!
+ // For each node in the AbsFIB: Insert into real fib.
+ for (const auto& nodeEntry : allNodeFIB) {
+ int nodeId = nodeEntry.first;
+ const auto& fib = nodeEntry.second;
+
+ // For each destination:
+ for (const auto& dst : fib) {
+ int dstId = dst.first;
+ const auto& dstRouter = allNodeFIB.at(dstId).getGR();
+
+ // Each fibNexthop
+ for (const auto& nh : dst.second) {
+ int neighborId = nh.getNexthopId();
+ int neighborTotalCost = nh.getCost();
+
+ for (const auto& prefix : dstRouter->GetLocalPrefixes()) {
+ Ptr<Node> node = NodeList::GetNode(static_cast<uint32_t>(nodeId));
+ FibHelper::AddRoute(node, *prefix, faceMap.at(nodeId).at(neighborId), neighborTotalCost);
+ }
+ }
+ }
+ }
+}
+
+} // namespace ndn
+} // namespace ns3
diff --git a/helper/lfid/remove-loops.cpp b/helper/lfid/remove-loops.cpp
new file mode 100644
index 0000000..90062d1
--- /dev/null
+++ b/helper/lfid/remove-loops.cpp
@@ -0,0 +1,348 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2019 Klaus Schneider, The University of Arizona
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Klaus Schneider <klaus@cs.arizona.edu>
+ */
+
+#include "remove-loops.hpp"
+
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/dijkstra_shortest_paths.hpp>
+#include <boost/graph/properties.hpp>
+#include <boost/property_map/property_map.hpp>
+#include <queue>
+
+#include "ns3/abort.h"
+#include "ns3/ndnSIM/helper/lfid/abstract-fib.hpp"
+
+namespace ns3 {
+namespace ndn {
+
+using std::set;
+using AllNodeFib = AbstractFib::AllNodeFib;
+
+/**
+ * Fill directed graph only with edges existing in the FIB.
+ */
+void
+getDigraphFromFib(DiGraph& dg, const AllNodeFib& allNodeFIB, const int dstId)
+{
+
+ // 1. Erase All Arcs:
+ dg.clear();
+
+ // 2. Add Arcs from FIB
+ for (const auto& node : allNodeFIB) {
+ int nodeId = node.first;
+ if (dstId == nodeId) {
+ continue;
+ }
+
+ for (const auto& fibNh : node.second.getNexthops(dstId)) {
+ NS_ABORT_UNLESS(fibNh.getType() <= NextHopType::UPWARD);
+ boost::add_edge(static_cast<uint64_t>(nodeId), static_cast<uint64_t>(fibNh.getNexthopId()), 1, dg);
+ }
+ }
+}
+
+class NodePrio {
+public:
+ NodePrio(int nodeId, int remainingNh, set<FibNextHop> nhSet)
+ : m_nodeId{nodeId}
+ , m_remainingNh{remainingNh}
+ , m_uwSet{nhSet}
+ {
+ NS_ABORT_UNLESS(remainingNh > 0 && m_uwSet.size() > 0);
+ NS_ABORT_UNLESS(static_cast<int>(m_uwSet.size()) < remainingNh);
+ }
+
+ int
+ getId() const
+ {
+ return m_nodeId;
+ }
+
+ int
+ getRemainingUw() const
+ {
+ return static_cast<int>(m_uwSet.size());
+ }
+
+ /**
+ * Order by Remamining UW NHs, Highest DeltaCost, and then id.
+ */
+ bool
+ operator<(const NodePrio& other) const
+ {
+ return std::make_tuple(m_remainingNh, getHighestCostUw(), m_nodeId)
+ < std::make_tuple(other.m_remainingNh, other.getHighestCostUw(), other.m_nodeId);
+ }
+
+ // Setters:
+ FibNextHop
+ popHighestCostUw()
+ {
+ const FibNextHop& tmp = getHighestCostUw();
+ eraseUw(tmp);
+ return tmp;
+ }
+
+ void
+ reduceRemainingNh()
+ {
+ m_remainingNh--;
+ // Check that remaining nexthops >= remaining uw nexthops.
+ NS_ABORT_UNLESS(m_remainingNh > 0 && m_remainingNh > getRemainingUw());
+ }
+
+private:
+ void
+ eraseUw(FibNextHop nh)
+ {
+ NS_ABORT_UNLESS(m_uwSet.size() > 0);
+ auto success = m_uwSet.erase(nh);
+ NS_ABORT_UNLESS(success == 1);
+ }
+
+ FibNextHop
+ getHighestCostUw() const
+ {
+ NS_ABORT_UNLESS(m_uwSet.size() > 0);
+ NS_ABORT_UNLESS(std::prev(m_uwSet.end()) != m_uwSet.end());
+ return *(std::prev(m_uwSet.end()));
+ }
+
+private:
+ int m_nodeId;
+ int m_remainingNh;
+ set<FibNextHop> m_uwSet;
+
+ friend std::ostream&
+ operator<<(std::ostream&, const NodePrio& node);
+};
+
+std::ostream&
+operator<<(std::ostream& os, const NodePrio& node)
+{
+ return os << "Id: " << node.m_nodeId << ", remaining NH: " << node.m_remainingNh
+ << ", remaining UW: " << node.getRemainingUw() << " ";
+}
+
+int
+removeLoops(AllNodeFib& allNodeFIB, bool printOutput)
+{
+ int removedLoopCounter = 0;
+ int upwardCounter = 0;
+
+ const int NUM_NODES{static_cast<int>(allNodeFIB.size())};
+
+ // Build graph with boost graph library:
+ DiGraph dg{};
+
+ // Add all Arcs that fit into FIB. // O(n)
+ for (int dstId = 0; dstId < NUM_NODES; dstId++) {
+ // 1. Get DiGraph from Fib //
+ getDigraphFromFib(dg, allNodeFIB, dstId);
+
+ // NodeId -> set<UwNexthops>
+ std::priority_queue<NodePrio> q;
+
+ // 2. Put nodes in the queue, ordered by # remaining nexthops, then CostDelta // O(n^2)
+ for (const auto& node : allNodeFIB) {
+ int nodeId{node.first};
+ const AbstractFib& fib{node.second};
+ if (nodeId == dstId) {
+ continue;
+ }
+
+ const auto& uwNhSet = fib.getUpwardNexthops(dstId);
+ if (!uwNhSet.empty()) {
+ upwardCounter += uwNhSet.size();
+
+ int fibSize{fib.numEnabledNhPerDst(dstId)};
+ // NodePrio tmpNode {nodeId, fibSize, uwNhSet};
+ q.emplace(nodeId, fibSize, uwNhSet);
+ }
+ }
+
+ // 3. Iterate PriorityQueue //
+ while (!q.empty()) {
+ NodePrio node = q.top();
+ q.pop();
+
+ int nodeId = node.getId();
+ int nhId = node.popHighestCostUw().getNexthopId();
+
+ // Remove opposite of Uphill link
+ // int arcId1 {getArcId(arcMap, nhId, nodeId)};
+ auto res = boost::edge(static_cast<uint64_t>(nhId), static_cast<uint64_t>(nodeId), dg);
+
+ auto arc = res.first;
+ bool arcExists = res.second;
+
+ if (arcExists) {
+ boost::remove_edge(arc, dg);
+ }
+
+ // 2. Loop Check: Is the current node still reachable for the uphill nexthop?
+ // Uses BFS:
+ // bool willLoop = bfs(dg).run(dg.nodeFromId(nhId), dg.nodeFromId(nodeId)); // O(m^2n)
+
+ std::vector<int> dists(num_vertices(dg));
+
+ auto weightmap = get(boost::edge_weight, dg);
+
+ const auto& x = boost::edges(dg);
+ for (auto e = x.first; e != x.second; e++) {
+ int weight = get(weightmap, *e);
+ NS_ABORT_UNLESS(weight == 1); // Only use uniform weights.
+ }
+
+ // TODO: Could be replaced by BFS/DFS to improve speed.
+ dijkstra_shortest_paths(dg, static_cast<uint64_t>(nhId),
+ distance_map(
+ boost::make_iterator_property_map(dists.begin(), get(boost::vertex_index, dg))));
+
+ bool willLoop = (dists.at(static_cast<size_t>(nodeId)) < (std::numeric_limits<int>::max() - 1));
+
+ // Uphill nexthop loops back to original node
+ if (willLoop) {
+ node.reduceRemainingNh();
+ removedLoopCounter++;
+
+ // Erase FIB entry
+ allNodeFIB.at(node.getId()).erase(dstId, nhId);
+
+ auto res2 = boost::edge(static_cast<uint64_t>(node.getId()), static_cast<uint64_t>(nhId), dg);
+ auto arc2 = res2.first;
+ NS_ABORT_UNLESS(res.second);
+
+ boost::remove_edge(arc2, dg);
+ }
+
+ // Add opposite of UW link back:
+ if (arcExists) {
+ boost::add_edge(static_cast<uint64_t>(nhId), static_cast<uint64_t>(nodeId), 1, dg);
+ }
+
+ // If not has further UW nexthops: Requeue.
+ if (node.getRemainingUw() > 0) {
+ q.push(node);
+ }
+ }
+ }
+
+ if (printOutput) {
+ std::cout << "Found " << upwardCounter << " UW nexthops, Removed " << removedLoopCounter
+ << " Looping UwNhs, Remaining: " << upwardCounter - removedLoopCounter << " NHs\n";
+ }
+ NS_ABORT_UNLESS((upwardCounter - removedLoopCounter) >= 0);
+
+ return removedLoopCounter;
+}
+
+int
+removeDeadEnds(AllNodeFib& allNodeFIB, bool printOutput)
+{
+ int NUM_NODES{static_cast<int>(allNodeFIB.size())};
+ int checkedUwCounter{0};
+ int uwCounter{0};
+ int totalCounter{0};
+ int removedDeadendCounter{0};
+
+ for (int dstId = 0; dstId < NUM_NODES; dstId++) {
+ // NodeId -> FibNexthops (Order important)
+ set<std::pair<int, FibNextHop>> nhSet;
+
+ // 1. Put all uwNexthops in set<NodeId, FibNexhtop>:
+ for (const auto& node : allNodeFIB) {
+ int nodeId{node.first};
+ if (nodeId == dstId) {
+ continue;
+ }
+
+ totalCounter += node.second.getNexthops(dstId).size();
+
+ const auto& uwNhSet = node.second.getUpwardNexthops(dstId);
+ uwCounter += uwNhSet.size();
+ for (const FibNextHop& fibNh : uwNhSet) {
+ nhSet.emplace(nodeId, fibNh);
+ }
+ }
+
+ // FibNexthops ordered by (costDelta, cost, nhId).
+ // Start with nexthop with highest cost:
+ while (!nhSet.empty()) {
+ checkedUwCounter++;
+
+ // Pop from queue:
+ const auto& nhPair = nhSet.begin();
+ NS_ABORT_UNLESS(nhPair != nhSet.end());
+ nhSet.erase(nhPair);
+
+ int nodeId = nhPair->first;
+ const FibNextHop& nh = nhPair->second;
+ AbstractFib& fib = allNodeFIB.at(nodeId);
+
+ if (nh.getNexthopId() == dstId) {
+ continue;
+ }
+
+ int reverseEntries{allNodeFIB.at(nh.getNexthopId()).numEnabledNhPerDst(dstId)};
+
+ // Must have at least one FIB entry.
+ NS_ABORT_UNLESS(reverseEntries > 0);
+
+ // If it has exactly 1 entry -> Is downward back through the upward nexthop!
+ // Higher O-Complexity below:
+ if (reverseEntries <= 1) {
+ removedDeadendCounter++;
+
+ // Erase NhEntry from FIB:
+ fib.erase(dstId, nh.getNexthopId());
+
+ // Push into Queue: All NhEntries that lead to m_nodeId!
+ const auto& nexthops = fib.getNexthops(dstId);
+
+ for (const auto& ownNhs : nexthops) {
+ if (ownNhs.getType() == NextHopType::DOWNWARD && ownNhs.getNexthopId() != dstId) {
+ const auto& reverseNh = allNodeFIB.at(ownNhs.getNexthopId()).getNexthops(dstId);
+
+ for (const auto& y : reverseNh) {
+ if (y.getNexthopId() == nodeId) {
+ NS_ABORT_UNLESS(y.getType() == NextHopType::UPWARD);
+ nhSet.emplace(ownNhs.getNexthopId(), y);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (printOutput) {
+ std::cout << "Checked " << checkedUwCounter << " Upward NHs, Removed " << removedDeadendCounter
+ << " Deadend UwNhs, Remaining: " << uwCounter - removedDeadendCounter << " UW NHs, "
+ << totalCounter - removedDeadendCounter << " total nexthops\n";
+ }
+
+ return removedDeadendCounter;
+}
+
+} // namespace ndn
+} // namespace ns3
diff --git a/helper/lfid/remove-loops.hpp b/helper/lfid/remove-loops.hpp
new file mode 100644
index 0000000..ec2a89e
--- /dev/null
+++ b/helper/lfid/remove-loops.hpp
@@ -0,0 +1,49 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2019 Klaus Schneider, The University of Arizona
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Klaus Schneider <klaus@cs.arizona.edu>
+ */
+
+#ifndef LFID_REMOVE_LOOPS_H
+#define LFID_REMOVE_LOOPS_H
+
+#include <boost/graph/adjacency_list.hpp>
+
+#include "ns3/ndnSIM/helper/lfid/abstract-fib.hpp"
+
+namespace ns3 {
+namespace ndn {
+
+// No Vertex Property
+// Edge Property: Weight.
+using DiGraph =
+ boost::adjacency_list<boost::listS, boost::vecS, boost::bidirectionalS, boost::no_property,
+ boost::property<boost::edge_weight_t, int>>;
+
+void
+getDigraphFromFib(DiGraph& dg, const AbstractFib::AllNodeFib& allNodeFIB, const int dstId);
+
+int
+removeLoops(AbstractFib::AllNodeFib& allNodeFIB, bool printOutput = true);
+
+int
+removeDeadEnds(AbstractFib::AllNodeFib& allNodeFIB, bool printOutput = true);
+
+} // namespace ndn
+} // namespace ns3
+
+#endif //LFID_REMOVE_LOOPS_H
diff --git a/helper/ndn-global-routing-helper.hpp b/helper/ndn-global-routing-helper.hpp
index b405838..d14b2fe 100644
--- a/helper/ndn-global-routing-helper.hpp
+++ b/helper/ndn-global-routing-helper.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2011-2015 Regents of the University of California.
+ * Copyright (c) 2011-2019 Regents of the University of California.
*
* This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and
* contributors.
@@ -101,6 +101,19 @@
CalculateRoutes();
/**
+ * @brief Calculates a set of loop-free multipath routes.
+ *
+ * For full description please see tech tech report "Hop-by-Hop Multipath Routing:
+ * Choosing the Right Nexthop Set" and the associated Github repository:
+ *
+ * https://github.com/schneiderklaus/ndnSIM-routing
+ *
+ * @sa https://named-data.net/publications/techreports/mp_routing_tech_report/
+ */
+ static void
+ CalculateLfidRoutes();
+
+ /**
* @brief Calculate all possible next-hop independent alternative routes
*
* Refer to the implementation for more details.
diff --git a/tests/unit-tests/helper/lfid-routing-helper.t.cpp b/tests/unit-tests/helper/lfid-routing-helper.t.cpp
new file mode 100644
index 0000000..77eed91
--- /dev/null
+++ b/tests/unit-tests/helper/lfid-routing-helper.t.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2011-2019 Regents of the University of California.
+ *
+ * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and
+ * contributors.
+ *
+ * ndnSIM 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.
+ *
+ * ndnSIM 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
+ * ndnSIM, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "helper/ndn-global-routing-helper.hpp"
+#include "helper/ndn-stack-helper.hpp"
+
+#include "model/ndn-global-router.hpp"
+#include "model/ndn-l3-protocol.hpp"
+#include "model/ndn-net-device-transport.hpp"
+
+#include "NFD/daemon/fw/best-route-strategy2.hpp"
+
+#include "ns3/channel.h"
+#include "ns3/net-device.h"
+#include "ns3/core-module.h"
+#include "ns3/network-module.h"
+#include "ns3/ndnSIM-module.h"
+#include "ns3/point-to-point-net-device.h"
+#include "ns3/point-to-point-module.h"
+#include "ns3/point-to-point-layout-module.h"
+
+#include "../tests-common.hpp"
+
+#include <boost/filesystem.hpp>
+
+namespace ns3 {
+namespace ndn {
+
+BOOST_FIXTURE_TEST_SUITE(HelperLfidRoutingHelper, CleanupFixture)
+
+BOOST_AUTO_TEST_CASE(CalculateRouteAbilene)
+{
+ AnnotatedTopologyReader topologyReader;
+ topologyReader.SetFileName("src/ndnSIM/examples/topologies/topo-abilene.txt");
+ topologyReader.Read();
+
+ // Install NDN stack on all nodes
+ ndn::StackHelper stackHelper{};
+ stackHelper.InstallAll();
+
+ // IMPORTANT: Has to be run after StackHelper!
+ topologyReader.ApplyOspfMetric();
+
+ const std::string prefix{"/prefix"};
+
+ ndn::GlobalRoutingHelper ndnGlobalRoutingHelper;
+ BOOST_CHECK_NO_THROW(ndnGlobalRoutingHelper.InstallAll());
+
+// auto producer = Names::Find<Node>("producer");
+ const NodeContainer allNodes {topologyReader.GetNodes()};
+
+ // Make every node a producer for their prefix:
+ for (int i = 0; i < allNodes.size(); i++) {
+ ndnGlobalRoutingHelper.AddOrigins(prefix + std::to_string(i), allNodes.Get(i));
+ }
+ BOOST_CHECK_NO_THROW(ndn::GlobalRoutingHelper::CalculateLfidRoutes());
+
+ // IMPORTANT: Some strategy needs to be installed for test to work.
+ ndn::StrategyChoiceHelper str;
+ str.InstallAll<nfd::fw::BestRouteStrategy2>("/");
+
+ int numNexthops = 0;
+
+ // For all nodes
+ for (const auto& n : allNodes) {
+
+ // For all producer prefixes i
+ for (int i = 0; i < allNodes.size(); i++) {
+ if (n->GetId() == i) continue;
+
+ std::string prodPrefix = prefix + std::to_string(i);
+
+ const auto& fib = n->GetObject<ndn::L3Protocol>()->getForwarder()->getFib();
+ auto& e = fib.findLongestPrefixMatch(prodPrefix);
+
+ // Check that each node has at least 1 nexthop
+ BOOST_CHECK_GE(e.getNextHops().size(), 1);
+
+ for (const auto& nh : e.getNextHops()) {
+ // Get remote nodeId from face:
+ const auto& transport =
+ dynamic_cast<ndn::NetDeviceTransport*>(nh.getFace().getTransport());
+ BOOST_ASSERT(transport);
+
+ const auto& nd1 = transport->GetNetDevice()->GetObject<PointToPointNetDevice>();
+ BOOST_ASSERT(nd1);
+
+ const auto& ppChannel = DynamicCast<PointToPointChannel>(nd1->GetChannel());
+ BOOST_ASSERT(ppChannel);
+
+ auto nd2 = ppChannel->GetDevice(0);
+ // If node in channel is own node -> Switch to other node.
+ if (nd2->GetNode() == n) {
+ nd2 = ppChannel->GetDevice(1);
+ }
+
+ // Cost must be greater than 0.
+ BOOST_CHECK_GE(nh.getCost(), 1);
+ }
+
+ numNexthops += e.getNextHops().size();
+ }
+ }
+
+ BOOST_CHECK_EQUAL(numNexthops, 226);
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace ndn
+} // namespace ns3