blob: 90062d17ea8caa00c08d72df0c66d48e07315d9c [file] [log] [blame]
/* -*- 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