blob: 2eaa15f9b302cbee50643aa7065f7a9eebdaa2f8 [file] [log] [blame]
akmhoque3d06e792014-05-27 16:23:20 -05001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Ashlesh Gawande57a87172020-05-09 19:47:06 -07002/*
Junxiao Shib5734842024-01-09 21:14:53 +00003 * Copyright (c) 2014-2024, The University of Memphis,
Vince Lehman41b173e2015-05-07 14:13:26 -05004 * Regents of the University of California,
5 * Arizona Board of Regents.
akmhoque3d06e792014-05-27 16:23:20 -05006 *
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/>.
Ashlesh Gawande57a87172020-05-09 19:47:06 -070020 */
Nick Gordone40377d2017-08-11 15:10:02 -050021
Junxiao Shiaead5882024-02-21 12:32:02 +000022#include "routing-calculator.hpp"
Junxiao Shi4eb4eae2024-02-14 12:36:57 +000023#include "name-map.hpp"
akmhoque53353462014-04-22 08:43:45 -050024#include "nexthop.hpp"
akmhoque53353462014-04-22 08:43:45 -050025
Junxiao Shiaead5882024-02-21 12:32:02 +000026#include "adjacent.hpp"
27#include "logger.hpp"
28#include "nlsr.hpp"
29
Junxiao Shi29b91ff2024-02-22 04:11:47 +000030#include <boost/multi_array.hpp>
Vince Lehman9a709032014-09-13 16:28:07 -050031
akmhoque53353462014-04-22 08:43:45 -050032namespace nlsr {
Junxiao Shi29b91ff2024-02-22 04:11:47 +000033namespace {
akmhoque53353462014-04-22 08:43:45 -050034
Junxiao Shiaead5882024-02-21 12:32:02 +000035INIT_LOGGER(route.RoutingCalculatorLinkState);
36
Davide Pesavento658fd852023-05-10 22:15:03 -040037constexpr int EMPTY_PARENT = -12345;
38constexpr double INF_DISTANCE = 2147483647;
Davide Pesavento658fd852023-05-10 22:15:03 -040039constexpr int NO_NEXT_HOP = -12345;
dulalsaurabd0816a32019-07-26 13:11:24 +000040
Junxiao Shi29b91ff2024-02-22 04:11:47 +000041/**
42 * @brief Adjacency matrix.
43 *
44 * The matrix shall be a 2D array with N rows and N columns, where N is the number of routers.
45 * Element i,j is the cost from router i to router j.
46 */
47using AdjMatrix = boost::multi_array<double, 2>;
Vince Lehman9a709032014-09-13 16:28:07 -050048
Junxiao Shi29b91ff2024-02-22 04:11:47 +000049struct PrintAdjMatrix
50{
51 const AdjMatrix& matrix;
52 const NameMap& map;
53};
54
55/**
56 * @brief Print adjacency matrix.
57 */
58std::ostream&
59operator<<(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";
akmhoque53353462014-04-22 08:43:45 -050067 }
Junxiao Shi29b91ff2024-02-22 04:11:47 +000068 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";
akmhoque53353462014-04-22 08:43:45 -050078
Junxiao Shi29b91ff2024-02-22 04:11:47 +000079 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 }
akmhoque157b0a42014-05-13 00:26:37 -050089 }
Junxiao Shi29b91ff2024-02-22 04:11:47 +000090 os << "\n";
akmhoque53353462014-04-22 08:43:45 -050091 }
Junxiao Shi29b91ff2024-02-22 04:11:47 +000092
93 return os;
akmhoque53353462014-04-22 08:43:45 -050094}
95
Junxiao Shi29b91ff2024-02-22 04:11:47 +000096/**
97 * @brief Allocate and populate adjacency matrix.
98 *
99 * The adjacency matrix is resized to match the number of routers in @p map .
100 * Costs from Adjacency LSAs are filled into the matrix; in case of a mismatch in bidirectional
101 * costs, the higher cost is assigned for both directions.
102 * All other elements are set to @c NON_ADJACENT_COST .
103 */
104AdjMatrix
105makeAdjMatrix(const Lsdb& lsdb, NameMap& map)
akmhoque53353462014-04-22 08:43:45 -0500106{
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000107 // 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
Nick G97e34942016-07-11 14:46:27 -0500114 // For each LSA represented in the map
Ashlesh Gawande57a87172020-05-09 19:47:06 -0700115 auto lsaRange = lsdb.getLsdbIterator<AdjLsa>();
116 for (auto lsaIt = lsaRange.first; lsaIt != lsaRange.second; ++lsaIt) {
117 auto adjLsa = std::static_pointer_cast<AdjLsa>(*lsaIt);
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000118 auto row = map.getMappingNoByRouterName(adjLsa->getOriginRouter());
Vince Lehman9a709032014-09-13 16:28:07 -0500119
Ashlesh Gawande57a87172020-05-09 19:47:06 -0700120 std::list<Adjacent> adl = adjLsa->getAdl().getAdjList();
Nick G97e34942016-07-11 14:46:27 -0500121 // For each adjacency represented in the LSA
Ashlesh Gawande85998a12017-12-07 22:22:13 -0600122 for (const auto& adjacent : adl) {
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000123 auto col = map.getMappingNoByRouterName(adjacent.getName());
Ashlesh Gawande85998a12017-12-07 22:22:13 -0600124 double cost = adjacent.getLinkCost();
Vince Lehman9a709032014-09-13 16:28:07 -0500125
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000126 if (row && col && *row < static_cast<int32_t>(nRouters)
127 && *col < static_cast<int32_t>(nRouters)) {
128 matrix[*row][*col] = cost;
akmhoque53353462014-04-22 08:43:45 -0500129 }
130 }
131 }
Vince Lehman41b173e2015-05-07 14:13:26 -0500132
Nick G97e34942016-07-11 14:46:27 -0500133 // Links that do not have the same cost for both directions should
134 // have their costs corrected:
Vince Lehman41b173e2015-05-07 14:13:26 -0500135 //
dulalsaurabd0816a32019-07-26 13:11:24 +0000136 // 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.
Vince Lehman41b173e2015-05-07 14:13:26 -0500138 //
139 // Otherwise, both sides of the link should use the larger of the two costs.
140 //
Nick G97e34942016-07-11 14:46:27 -0500141 // 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.
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000144 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];
Vince Lehman41b173e2015-05-07 14:13:26 -0500148
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000149 if (fromCost == toCost) {
150 continue;
151 }
Vince Lehman41b173e2015-05-07 14:13:26 -0500152
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000153 // 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 }
Vince Lehman41b173e2015-05-07 14:13:26 -0500158
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000159 NLSR_LOG_WARN("Cost between [" << row << "][" << col << "] and [" << col << "][" << row <<
160 "] are not the same (" << toCost << " != " << fromCost << "). " <<
161 "Correcting to cost: " << correctedCost);
Vince Lehman41b173e2015-05-07 14:13:26 -0500162
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000163 toCost = correctedCost;
164 fromCost = correctedCost;
165 }
166 }
167
168 return matrix;
169}
170
171void
172sortQueueByDistance(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]);
Vince Lehman41b173e2015-05-07 14:13:26 -0500178 }
179 }
180 }
akmhoque53353462014-04-22 08:43:45 -0500181}
182
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000183bool
184isNotExplored(std::vector<int>& q, int u, size_t start)
akmhoque53353462014-04-22 08:43:45 -0500185{
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000186 for (size_t i = start; i < q.size(); i++) {
187 if (q[i] == u) {
188 return true;
akmhoque157b0a42014-05-13 00:26:37 -0500189 }
akmhoque53353462014-04-22 08:43:45 -0500190 }
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000191 return false;
akmhoque53353462014-04-22 08:43:45 -0500192}
193
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000194struct Link
akmhoque53353462014-04-22 08:43:45 -0500195{
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000196 size_t index;
197 double cost;
198};
199
200/**
201 * @brief List adjacencies and link costs from a source router.
202 */
203std::vector<Link>
204gatherLinks(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
221/**
222 * @brief Adjust link costs to simulate having only one accessible neighbor.
223 */
224void
225simulateOneNeighbor(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;
akmhoque53353462014-04-22 08:43:45 -0500231 }
akmhoque157b0a42014-05-13 00:26:37 -0500232 else {
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000233 // 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;
akmhoque53353462014-04-22 08:43:45 -0500235 }
236 }
237}
238
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000239class DijkstraResult
akmhoque53353462014-04-22 08:43:45 -0500240{
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000241public:
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];
akmhoque53353462014-04-22 08:43:45 -0500249 }
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000250 if (dest != source) {
251 nextHop = NO_NEXT_HOP;
akmhoque53353462014-04-22 08:43:45 -0500252 }
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000253 return nextHop;
akmhoque53353462014-04-22 08:43:45 -0500254 }
akmhoque53353462014-04-22 08:43:45 -0500255
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000256public:
257 std::vector<int> parent;
258 std::vector<double> distance;
259};
260
261/**
262 * @brief Compute the shortest path from a source router to every other router.
263 */
264DijkstraResult
265calculateDijkstraPath(const AdjMatrix& matrix, int sourceRouter)
akmhoque53353462014-04-22 08:43:45 -0500266{
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000267 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);
akmhoque53353462014-04-22 08:43:45 -0500275 }
akmhoque53353462014-04-22 08:43:45 -0500276
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000277 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.
akmhoque53353462014-04-22 08:43:45 -0500286 }
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000287 // 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;
akmhoque53353462014-04-22 08:43:45 -0500300 }
301 }
akmhoque53353462014-04-22 08:43:45 -0500302 }
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000303 // Increment the head position, resort the list by distance from where we are.
304 ++head;
305 sortQueueByDistance(q, distance, head);
akmhoque53353462014-04-22 08:43:45 -0500306 }
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000307
308 return DijkstraResult{std::move(parent), std::move(distance)};
akmhoque53353462014-04-22 08:43:45 -0500309}
310
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000311/**
312 * @brief Insert shortest paths into the routing table.
313 */
akmhoque53353462014-04-22 08:43:45 -0500314void
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000315addNextHopsToRoutingTable(RoutingTable& rt, const NameMap& map, int sourceRouter,
316 const AdjacencyList& adjacencies, const DijkstraResult& dr)
akmhoque53353462014-04-22 08:43:45 -0500317{
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000318 NLSR_LOG_DEBUG("addNextHopsToRoutingTable Called");
319 int nRouters = static_cast<int>(map.size());
Vince Lehman9a709032014-09-13 16:28:07 -0500320
Nick G97e34942016-07-11 14:46:27 -0500321 // For each router we have
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000322 for (int i = 0; i < nRouters; ++i) {
323 if (i == sourceRouter) {
324 continue;
akmhoque53353462014-04-22 08:43:45 -0500325 }
akmhoque53353462014-04-22 08:43:45 -0500326
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000327 // 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;
akmhoque53353462014-04-22 08:43:45 -0500331 }
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000332 // 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);
akmhoque53353462014-04-22 08:43:45 -0500343 }
344}
345
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000346} // anonymous namespace
akmhoque53353462014-04-22 08:43:45 -0500347
akmhoque53353462014-04-22 08:43:45 -0500348void
Junxiao Shiaead5882024-02-21 12:32:02 +0000349calculateLinkStateRoutingPath(NameMap& map, RoutingTable& rt, ConfParameter& confParam,
350 const Lsdb& lsdb)
akmhoque53353462014-04-22 08:43:45 -0500351{
Junxiao Shi29b91ff2024-02-22 04:11:47 +0000352 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 }
akmhoque53353462014-04-22 08:43:45 -0500383}
384
Nick Gordonfad8e252016-08-11 14:21:38 -0500385} // namespace nlsr