routing-calculator-link-state.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2014-2024, The University of Memphis,
4  * Regents of the University of California,
5  * Arizona Board of Regents.
6  *
7  * This file is part of NLSR (Named-data Link State Routing).
8  * See AUTHORS.md for complete list of NLSR authors and contributors.
9  *
10  * NLSR is free software: you can redistribute it and/or modify it under the terms
11  * of the GNU General Public License as published by the Free Software Foundation,
12  * either version 3 of the License, or (at your option) any later version.
13  *
14  * NLSR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
15  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16  * PURPOSE. See the GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along with
19  * NLSR, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "routing-calculator.hpp"
23 #include "name-map.hpp"
24 #include "nexthop.hpp"
25 
26 #include "adjacent.hpp"
27 #include "logger.hpp"
28 #include "nlsr.hpp"
29 
30 #include <boost/multi_array.hpp>
31 
32 namespace nlsr {
33 namespace {
34 
35 INIT_LOGGER(route.RoutingCalculatorLinkState);
36 
37 constexpr int EMPTY_PARENT = -12345;
38 constexpr double INF_DISTANCE = 2147483647;
39 constexpr int NO_NEXT_HOP = -12345;
40 
47 using AdjMatrix = boost::multi_array<double, 2>;
48 
49 struct PrintAdjMatrix
50 {
51  const AdjMatrix& matrix;
52  const NameMap& map;
53 };
54 
58 std::ostream&
59 operator<<(std::ostream& os, const PrintAdjMatrix& p)
60 {
61  size_t nRouters = p.map.size();
62 
63  os << "-----------Legend (routerName -> index)------\n";
64  for (size_t i = 0; i < nRouters; ++i) {
65  os << "Router:" << *p.map.getRouterNameByMappingNo(i)
66  << " Index:" << i << "\n";
67  }
68  os << " |";
69  for (size_t i = 0; i < nRouters; ++i) {
70  os << i << " ";
71  }
72  os << "\n";
73  os << "--";
74  for (size_t i = 0; i < nRouters; ++i) {
75  os << "--";
76  }
77  os << "\n";
78 
79  for (size_t i = 0; i < nRouters; i++) {
80  os << i << "|";
81  for (size_t j = 0; j < nRouters; j++) {
82  double cost = p.matrix[i][j];
83  if (cost == NO_NEXT_HOP) {
84  os << "0 ";
85  }
86  else {
87  os << cost << " ";
88  }
89  }
90  os << "\n";
91  }
92 
93  return os;
94 }
95 
104 AdjMatrix
105 makeAdjMatrix(const Lsdb& lsdb, NameMap& map)
106 {
107  // Create the matrix to have N rows and N columns, where N is number of routers.
108  size_t nRouters = map.size();
109  AdjMatrix matrix(boost::extents[nRouters][nRouters]);
110 
111  // Initialize all elements to NON_ADJACENT_COST.
112  std::fill_n(matrix.origin(), matrix.num_elements(), Adjacent::NON_ADJACENT_COST);
113 
114  // For each LSA represented in the map
115  auto lsaRange = lsdb.getLsdbIterator<AdjLsa>();
116  for (auto lsaIt = lsaRange.first; lsaIt != lsaRange.second; ++lsaIt) {
117  auto adjLsa = std::static_pointer_cast<AdjLsa>(*lsaIt);
118  auto row = map.getMappingNoByRouterName(adjLsa->getOriginRouter());
119 
120  std::list<Adjacent> adl = adjLsa->getAdl().getAdjList();
121  // For each adjacency represented in the LSA
122  for (const auto& adjacent : adl) {
123  auto col = map.getMappingNoByRouterName(adjacent.getName());
124  double cost = adjacent.getLinkCost();
125 
126  if (row && col && *row < static_cast<int32_t>(nRouters)
127  && *col < static_cast<int32_t>(nRouters)) {
128  matrix[*row][*col] = cost;
129  }
130  }
131  }
132 
133  // Links that do not have the same cost for both directions should
134  // have their costs corrected:
135  //
136  // If the cost of one side of the link is NON_ADJACENT_COST (i.e. broken) or negative,
137  // both direction of the link should have their cost corrected to NON_ADJACENT_COST.
138  //
139  // Otherwise, both sides of the link should use the larger of the two costs.
140  //
141  // Additionally, this means that we can halve the amount of space
142  // that the matrix uses by only maintaining a triangle.
143  // - But that is not yet implemented.
144  for (size_t row = 0; row < nRouters; ++row) {
145  for (size_t col = 0; col < nRouters; ++col) {
146  double& toCost = matrix[row][col];
147  double& fromCost = matrix[col][row];
148 
149  if (fromCost == toCost) {
150  continue;
151  }
152 
153  // If both sides of the link are up, use the larger cost else break the link
154  double correctedCost = Adjacent::NON_ADJACENT_COST;
155  if (toCost >= 0 && fromCost >= 0) {
156  correctedCost = std::max(toCost, fromCost);
157  }
158 
159  NLSR_LOG_WARN("Cost between [" << row << "][" << col << "] and [" << col << "][" << row <<
160  "] are not the same (" << toCost << " != " << fromCost << "). " <<
161  "Correcting to cost: " << correctedCost);
162 
163  toCost = correctedCost;
164  fromCost = correctedCost;
165  }
166  }
167 
168  return matrix;
169 }
170 
171 void
172 sortQueueByDistance(std::vector<int>& q, const std::vector<double>& dist, size_t start)
173 {
174  for (size_t i = start; i < q.size(); ++i) {
175  for (size_t j = i + 1; j < q.size(); ++j) {
176  if (dist[q[j]] < dist[q[i]]) {
177  std::swap(q[i], q[j]);
178  }
179  }
180  }
181 }
182 
183 bool
184 isNotExplored(std::vector<int>& q, int u, size_t start)
185 {
186  for (size_t i = start; i < q.size(); i++) {
187  if (q[i] == u) {
188  return true;
189  }
190  }
191  return false;
192 }
193 
194 struct Link
195 {
196  size_t index;
197  double cost;
198 };
199 
203 std::vector<Link>
204 gatherLinks(const AdjMatrix& matrix, int sourceRouter)
205 {
206  size_t nRouters = matrix.size();
207  std::vector<Link> result;
208  result.reserve(nRouters);
209  for (size_t i = 0; i < nRouters; ++i) {
210  if (i == static_cast<size_t>(sourceRouter)) {
211  continue;
212  }
213  double cost = matrix[sourceRouter][i];
214  if (cost >= 0.0) {
215  result.emplace_back(Link{i, cost});
216  }
217  }
218  return result;
219 }
220 
224 void
225 simulateOneNeighbor(AdjMatrix& matrix, int sourceRouter, const Link& accessibleNeighbor)
226 {
227  size_t nRouters = matrix.size();
228  for (size_t i = 0; i < nRouters; ++i) {
229  if (i == accessibleNeighbor.index) {
230  matrix[sourceRouter][i] = accessibleNeighbor.cost;
231  }
232  else {
233  // if "i" is not a link to the source, set its cost to a non adjacent value.
234  matrix[sourceRouter][i] = Adjacent::NON_ADJACENT_COST;
235  }
236  }
237 }
238 
239 class DijkstraResult
240 {
241 public:
242  int
243  getNextHop(int dest, int source) const
244  {
245  int nextHop = NO_NEXT_HOP;
246  while (parent[dest] != EMPTY_PARENT) {
247  nextHop = dest;
248  dest = parent[dest];
249  }
250  if (dest != source) {
251  nextHop = NO_NEXT_HOP;
252  }
253  return nextHop;
254  }
255 
256 public:
257  std::vector<int> parent;
258  std::vector<double> distance;
259 };
260 
264 DijkstraResult
265 calculateDijkstraPath(const AdjMatrix& matrix, int sourceRouter)
266 {
267  size_t nRouters = matrix.size();
268  std::vector<int> parent(nRouters, EMPTY_PARENT);
269  // Array where the ith element is the distance to the router with mapping no i.
270  std::vector<double> distance(nRouters, INF_DISTANCE);
271  // Each cell represents the router with that mapping no.
272  std::vector<int> q(nRouters);
273  for (size_t i = 0 ; i < nRouters; ++i) {
274  q[i] = static_cast<int>(i);
275  }
276 
277  size_t head = 0;
278  // Distance to source from source is always 0.
279  distance[sourceRouter] = 0;
280  sortQueueByDistance(q, distance, head);
281  // While we haven't visited every node.
282  while (head < nRouters) {
283  int u = q[head]; // Set u to be the current node pointed to by head.
284  if (distance[u] == INF_DISTANCE) {
285  break; // This can only happen when there are no accessible nodes.
286  }
287  // Iterate over the adjacent nodes to u.
288  for (size_t v = 0; v < nRouters; ++v) {
289  // If the current node is accessible and we haven't visited it yet.
290  if (matrix[u][v] >= 0 && isNotExplored(q, v, head + 1)) {
291  // And if the distance to this node + from this node to v
292  // is less than the distance from our source node to v
293  // that we got when we built the adj LSAs
294  double newDistance = distance[u] + matrix[u][v];
295  if (newDistance < distance[v]) {
296  // Set the new distance
297  distance[v] = newDistance;
298  // Set how we get there.
299  parent[v] = u;
300  }
301  }
302  }
303  // Increment the head position, resort the list by distance from where we are.
304  ++head;
305  sortQueueByDistance(q, distance, head);
306  }
307 
308  return DijkstraResult{std::move(parent), std::move(distance)};
309 }
310 
314 void
315 addNextHopsToRoutingTable(RoutingTable& rt, const NameMap& map, int sourceRouter,
316  const AdjacencyList& adjacencies, const DijkstraResult& dr)
317 {
318  NLSR_LOG_DEBUG("addNextHopsToRoutingTable Called");
319  int nRouters = static_cast<int>(map.size());
320 
321  // For each router we have
322  for (int i = 0; i < nRouters; ++i) {
323  if (i == sourceRouter) {
324  continue;
325  }
326 
327  // Obtain the next hop that was determined by the algorithm
328  int nextHopRouter = dr.getNextHop(i, sourceRouter);
329  if (nextHopRouter == NO_NEXT_HOP) {
330  continue;
331  }
332  // If this router is accessible at all
333 
334  // Fetch its distance
335  double routeCost = dr.distance[i];
336  // Fetch its actual name
337  auto nextHopRouterName = map.getRouterNameByMappingNo(nextHopRouter);
338  BOOST_ASSERT(nextHopRouterName.has_value());
339  auto nextHopFace = adjacencies.getAdjacent(*nextHopRouterName).getFaceUri();
340  // Add next hop to routing table
341  NextHop nh(nextHopFace, routeCost);
342  rt.addNextHop(*map.getRouterNameByMappingNo(i), nh);
343  }
344 }
345 
346 } // anonymous namespace
347 
348 void
350  const Lsdb& lsdb)
351 {
352  NLSR_LOG_DEBUG("calculateLinkStateRoutingPath called");
353 
354  auto sourceRouter = map.getMappingNoByRouterName(confParam.getRouterPrefix());
355  if (!sourceRouter) {
356  NLSR_LOG_DEBUG("Source router is absent, nothing to do");
357  return;
358  }
359 
360  AdjMatrix matrix = makeAdjMatrix(lsdb, map);
361  NLSR_LOG_DEBUG((PrintAdjMatrix{matrix, map}));
362 
363  if (confParam.getMaxFacesPerPrefix() == 1) {
364  // In the single path case we can simply run Dijkstra's algorithm.
365  auto dr = calculateDijkstraPath(matrix, *sourceRouter);
366  // Inform the routing table of the new next hops.
367  addNextHopsToRoutingTable(rt, map, *sourceRouter, confParam.getAdjacencyList(), dr);
368  }
369  else {
370  // Multi Path
371  // Gets a sparse listing of adjacencies for path calculation
372  auto links = gatherLinks(matrix, *sourceRouter);
373  for (const auto& link : links) {
374  // Simulate that only the current neighbor is accessible
375  simulateOneNeighbor(matrix, *sourceRouter, link);
376  NLSR_LOG_DEBUG((PrintAdjMatrix{matrix, map}));
377  // Do Dijkstra's algorithm using the current neighbor as your start.
378  auto dr = calculateDijkstraPath(matrix, *sourceRouter);
379  // Update the routing table with the calculations.
380  addNextHopsToRoutingTable(rt, map, *sourceRouter, confParam.getAdjacencyList(), dr);
381  }
382  }
383 }
384 
385 } // namespace nlsr
static constexpr double NON_ADJACENT_COST
Definition: adjacent.hpp:187
A class to house all the configuration parameters for NLSR.
const ndn::Name & getRouterPrefix() const
AdjacencyList & getAdjacencyList()
uint32_t getMaxFacesPerPrefix() const
Assigning numbers to router names.
Definition: name-map.hpp:46
std::optional< ndn::Name > getRouterNameByMappingNo(int32_t mn) const
Find router name by its mapping number.
Definition: name-map.cpp:37
std::optional< int32_t > getMappingNoByRouterName(const ndn::Name &rtrName) const
Find mapping number of a router name.
Definition: name-map.cpp:47
size_t size() const
Return number of entries in this container.
Definition: name-map.hpp:120
Copyright (c) 2014-2018, The University of Memphis, Regents of the University of California.
#define NLSR_LOG_DEBUG(x)
Definition: logger.hpp:38
#define INIT_LOGGER(name)
Definition: logger.hpp:35
#define NLSR_LOG_WARN(x)
Definition: logger.hpp:40
@ RoutingTable
Definition: tlv-nlsr.hpp:47
Copyright (c) 2014-2020, The University of Memphis, Regents of the University of California.
std::ostream & operator<<(std::ostream &os, const Adjacent &adjacent)
Definition: adjacent.cpp:176
void calculateLinkStateRoutingPath(NameMap &map, RoutingTable &rt, ConfParameter &confParam, const Lsdb &lsdb)