blob: 90062d17ea8caa00c08d72df0c66d48e07315d9c [file] [log] [blame]
Klaus Schneider38784302019-08-31 18:26:36 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2019 Klaus Schneider, The University of Arizona
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation;
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Author: Klaus Schneider <klaus@cs.arizona.edu>
19 */
20
21#include "remove-loops.hpp"
22
23#include <boost/graph/adjacency_list.hpp>
24#include <boost/graph/dijkstra_shortest_paths.hpp>
25#include <boost/graph/properties.hpp>
26#include <boost/property_map/property_map.hpp>
27#include <queue>
28
29#include "ns3/abort.h"
30#include "ns3/ndnSIM/helper/lfid/abstract-fib.hpp"
31
32namespace ns3 {
33namespace ndn {
34
35using std::set;
36using AllNodeFib = AbstractFib::AllNodeFib;
37
38/**
39 * Fill directed graph only with edges existing in the FIB.
40 */
41void
42getDigraphFromFib(DiGraph& dg, const AllNodeFib& allNodeFIB, const int dstId)
43{
44
45 // 1. Erase All Arcs:
46 dg.clear();
47
48 // 2. Add Arcs from FIB
49 for (const auto& node : allNodeFIB) {
50 int nodeId = node.first;
51 if (dstId == nodeId) {
52 continue;
53 }
54
55 for (const auto& fibNh : node.second.getNexthops(dstId)) {
56 NS_ABORT_UNLESS(fibNh.getType() <= NextHopType::UPWARD);
57 boost::add_edge(static_cast<uint64_t>(nodeId), static_cast<uint64_t>(fibNh.getNexthopId()), 1, dg);
58 }
59 }
60}
61
62class NodePrio {
63public:
64 NodePrio(int nodeId, int remainingNh, set<FibNextHop> nhSet)
65 : m_nodeId{nodeId}
66 , m_remainingNh{remainingNh}
67 , m_uwSet{nhSet}
68 {
69 NS_ABORT_UNLESS(remainingNh > 0 && m_uwSet.size() > 0);
70 NS_ABORT_UNLESS(static_cast<int>(m_uwSet.size()) < remainingNh);
71 }
72
73 int
74 getId() const
75 {
76 return m_nodeId;
77 }
78
79 int
80 getRemainingUw() const
81 {
82 return static_cast<int>(m_uwSet.size());
83 }
84
85 /**
86 * Order by Remamining UW NHs, Highest DeltaCost, and then id.
87 */
88 bool
89 operator<(const NodePrio& other) const
90 {
91 return std::make_tuple(m_remainingNh, getHighestCostUw(), m_nodeId)
92 < std::make_tuple(other.m_remainingNh, other.getHighestCostUw(), other.m_nodeId);
93 }
94
95 // Setters:
96 FibNextHop
97 popHighestCostUw()
98 {
99 const FibNextHop& tmp = getHighestCostUw();
100 eraseUw(tmp);
101 return tmp;
102 }
103
104 void
105 reduceRemainingNh()
106 {
107 m_remainingNh--;
108 // Check that remaining nexthops >= remaining uw nexthops.
109 NS_ABORT_UNLESS(m_remainingNh > 0 && m_remainingNh > getRemainingUw());
110 }
111
112private:
113 void
114 eraseUw(FibNextHop nh)
115 {
116 NS_ABORT_UNLESS(m_uwSet.size() > 0);
117 auto success = m_uwSet.erase(nh);
118 NS_ABORT_UNLESS(success == 1);
119 }
120
121 FibNextHop
122 getHighestCostUw() const
123 {
124 NS_ABORT_UNLESS(m_uwSet.size() > 0);
125 NS_ABORT_UNLESS(std::prev(m_uwSet.end()) != m_uwSet.end());
126 return *(std::prev(m_uwSet.end()));
127 }
128
129private:
130 int m_nodeId;
131 int m_remainingNh;
132 set<FibNextHop> m_uwSet;
133
134 friend std::ostream&
135 operator<<(std::ostream&, const NodePrio& node);
136};
137
138std::ostream&
139operator<<(std::ostream& os, const NodePrio& node)
140{
141 return os << "Id: " << node.m_nodeId << ", remaining NH: " << node.m_remainingNh
142 << ", remaining UW: " << node.getRemainingUw() << " ";
143}
144
145int
146removeLoops(AllNodeFib& allNodeFIB, bool printOutput)
147{
148 int removedLoopCounter = 0;
149 int upwardCounter = 0;
150
151 const int NUM_NODES{static_cast<int>(allNodeFIB.size())};
152
153 // Build graph with boost graph library:
154 DiGraph dg{};
155
156 // Add all Arcs that fit into FIB. // O(n)
157 for (int dstId = 0; dstId < NUM_NODES; dstId++) {
158 // 1. Get DiGraph from Fib //
159 getDigraphFromFib(dg, allNodeFIB, dstId);
160
161 // NodeId -> set<UwNexthops>
162 std::priority_queue<NodePrio> q;
163
164 // 2. Put nodes in the queue, ordered by # remaining nexthops, then CostDelta // O(n^2)
165 for (const auto& node : allNodeFIB) {
166 int nodeId{node.first};
167 const AbstractFib& fib{node.second};
168 if (nodeId == dstId) {
169 continue;
170 }
171
172 const auto& uwNhSet = fib.getUpwardNexthops(dstId);
173 if (!uwNhSet.empty()) {
174 upwardCounter += uwNhSet.size();
175
176 int fibSize{fib.numEnabledNhPerDst(dstId)};
177 // NodePrio tmpNode {nodeId, fibSize, uwNhSet};
178 q.emplace(nodeId, fibSize, uwNhSet);
179 }
180 }
181
182 // 3. Iterate PriorityQueue //
183 while (!q.empty()) {
184 NodePrio node = q.top();
185 q.pop();
186
187 int nodeId = node.getId();
188 int nhId = node.popHighestCostUw().getNexthopId();
189
190 // Remove opposite of Uphill link
191 // int arcId1 {getArcId(arcMap, nhId, nodeId)};
192 auto res = boost::edge(static_cast<uint64_t>(nhId), static_cast<uint64_t>(nodeId), dg);
193
194 auto arc = res.first;
195 bool arcExists = res.second;
196
197 if (arcExists) {
198 boost::remove_edge(arc, dg);
199 }
200
201 // 2. Loop Check: Is the current node still reachable for the uphill nexthop?
202 // Uses BFS:
203 // bool willLoop = bfs(dg).run(dg.nodeFromId(nhId), dg.nodeFromId(nodeId)); // O(m^2n)
204
205 std::vector<int> dists(num_vertices(dg));
206
207 auto weightmap = get(boost::edge_weight, dg);
208
209 const auto& x = boost::edges(dg);
210 for (auto e = x.first; e != x.second; e++) {
211 int weight = get(weightmap, *e);
212 NS_ABORT_UNLESS(weight == 1); // Only use uniform weights.
213 }
214
215 // TODO: Could be replaced by BFS/DFS to improve speed.
216 dijkstra_shortest_paths(dg, static_cast<uint64_t>(nhId),
217 distance_map(
218 boost::make_iterator_property_map(dists.begin(), get(boost::vertex_index, dg))));
219
220 bool willLoop = (dists.at(static_cast<size_t>(nodeId)) < (std::numeric_limits<int>::max() - 1));
221
222 // Uphill nexthop loops back to original node
223 if (willLoop) {
224 node.reduceRemainingNh();
225 removedLoopCounter++;
226
227 // Erase FIB entry
228 allNodeFIB.at(node.getId()).erase(dstId, nhId);
229
230 auto res2 = boost::edge(static_cast<uint64_t>(node.getId()), static_cast<uint64_t>(nhId), dg);
231 auto arc2 = res2.first;
232 NS_ABORT_UNLESS(res.second);
233
234 boost::remove_edge(arc2, dg);
235 }
236
237 // Add opposite of UW link back:
238 if (arcExists) {
239 boost::add_edge(static_cast<uint64_t>(nhId), static_cast<uint64_t>(nodeId), 1, dg);
240 }
241
242 // If not has further UW nexthops: Requeue.
243 if (node.getRemainingUw() > 0) {
244 q.push(node);
245 }
246 }
247 }
248
249 if (printOutput) {
250 std::cout << "Found " << upwardCounter << " UW nexthops, Removed " << removedLoopCounter
251 << " Looping UwNhs, Remaining: " << upwardCounter - removedLoopCounter << " NHs\n";
252 }
253 NS_ABORT_UNLESS((upwardCounter - removedLoopCounter) >= 0);
254
255 return removedLoopCounter;
256}
257
258int
259removeDeadEnds(AllNodeFib& allNodeFIB, bool printOutput)
260{
261 int NUM_NODES{static_cast<int>(allNodeFIB.size())};
262 int checkedUwCounter{0};
263 int uwCounter{0};
264 int totalCounter{0};
265 int removedDeadendCounter{0};
266
267 for (int dstId = 0; dstId < NUM_NODES; dstId++) {
268 // NodeId -> FibNexthops (Order important)
269 set<std::pair<int, FibNextHop>> nhSet;
270
271 // 1. Put all uwNexthops in set<NodeId, FibNexhtop>:
272 for (const auto& node : allNodeFIB) {
273 int nodeId{node.first};
274 if (nodeId == dstId) {
275 continue;
276 }
277
278 totalCounter += node.second.getNexthops(dstId).size();
279
280 const auto& uwNhSet = node.second.getUpwardNexthops(dstId);
281 uwCounter += uwNhSet.size();
282 for (const FibNextHop& fibNh : uwNhSet) {
283 nhSet.emplace(nodeId, fibNh);
284 }
285 }
286
287 // FibNexthops ordered by (costDelta, cost, nhId).
288 // Start with nexthop with highest cost:
289 while (!nhSet.empty()) {
290 checkedUwCounter++;
291
292 // Pop from queue:
293 const auto& nhPair = nhSet.begin();
294 NS_ABORT_UNLESS(nhPair != nhSet.end());
295 nhSet.erase(nhPair);
296
297 int nodeId = nhPair->first;
298 const FibNextHop& nh = nhPair->second;
299 AbstractFib& fib = allNodeFIB.at(nodeId);
300
301 if (nh.getNexthopId() == dstId) {
302 continue;
303 }
304
305 int reverseEntries{allNodeFIB.at(nh.getNexthopId()).numEnabledNhPerDst(dstId)};
306
307 // Must have at least one FIB entry.
308 NS_ABORT_UNLESS(reverseEntries > 0);
309
310 // If it has exactly 1 entry -> Is downward back through the upward nexthop!
311 // Higher O-Complexity below:
312 if (reverseEntries <= 1) {
313 removedDeadendCounter++;
314
315 // Erase NhEntry from FIB:
316 fib.erase(dstId, nh.getNexthopId());
317
318 // Push into Queue: All NhEntries that lead to m_nodeId!
319 const auto& nexthops = fib.getNexthops(dstId);
320
321 for (const auto& ownNhs : nexthops) {
322 if (ownNhs.getType() == NextHopType::DOWNWARD && ownNhs.getNexthopId() != dstId) {
323 const auto& reverseNh = allNodeFIB.at(ownNhs.getNexthopId()).getNexthops(dstId);
324
325 for (const auto& y : reverseNh) {
326 if (y.getNexthopId() == nodeId) {
327 NS_ABORT_UNLESS(y.getType() == NextHopType::UPWARD);
328 nhSet.emplace(ownNhs.getNexthopId(), y);
329 break;
330 }
331 }
332 }
333 }
334 }
335 }
336 }
337
338 if (printOutput) {
339 std::cout << "Checked " << checkedUwCounter << " Upward NHs, Removed " << removedDeadendCounter
340 << " Deadend UwNhs, Remaining: " << uwCounter - removedDeadendCounter << " UW NHs, "
341 << totalCounter - removedDeadendCounter << " total nexthops\n";
342 }
343
344 return removedDeadendCounter;
345}
346
347} // namespace ndn
348} // namespace ns3