blob: 0e5a4b7df93c5b6e6e5e18153922d757264ce08a [file] [log] [blame]
Philipp Moll61a2be82019-07-12 08:33:09 +02001# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2#
phmoll9efd7392020-07-21 12:28:22 +02003# Copyright (C) 2015-2020, The University of Memphis,
Philipp Moll61a2be82019-07-12 08:33:09 +02004# Arizona Board of Regents,
5# Regents of the University of California.
6#
7# This file is part of Mini-NDN.
8# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
9#
10# Mini-NDN is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 3 of the License, or
13# (at your option) any later version.
14#
15# Mini-NDN is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with Mini-NDN, e.g., in COPYING.md file.
22# If not, see <http://www.gnu.org/licenses/>.
23
Ashlesh Gawande6c86e302019-09-17 22:27:05 -050024from igraph import Graph
phmoll9efd7392020-07-21 12:28:22 +020025from mininet.log import info, debug
26
Philipp Moll61a2be82019-07-12 08:33:09 +020027
Ashlesh Gawande6c86e302019-09-17 22:27:05 -050028class LinkInfo(object):
Philipp Moll61a2be82019-07-12 08:33:09 +020029 """
30 This class is used to encapsule link information (IP and interface names).
31 """
32
33 def __init__(self, start_intf_name, start_ip, end_intf_name, end_ip):
34 self.start_intf_name = start_intf_name
35 self.start_intf_ip = start_ip
36 self.end_intf_name = end_intf_name
37 self.end_ip = end_ip
38
phmoll9efd7392020-07-21 12:28:22 +020039
Ashlesh Gawande6c86e302019-09-17 22:27:05 -050040class IPRoutingHelper(object):
Philipp Moll61a2be82019-07-12 08:33:09 +020041 """The routing helper allows to run IP-based evaluations with Mini-NDN. It configures static IP
42 routes to all nodes, which means that all nodes can reach all other nodes in the network
43 reachable, even when relaying is required.
44
45 Usage from Experiment folder: `IPRoutingHelper.calcAllRoutes(self.net)`
46 """
47
48 @staticmethod
49 def findLinkInformation(links, first_node, second_node):
50 """ This method returns link information of a link connecting two nodes.
51
52 :param links: All links in the emulation topology
53 :param first_node: Current node which is looked at
54 :param second_node: Target node (neighbour of first_node)
55 :return: Link information as LinkInfo object, or returns null None if the
56 nodes are not directly connected
57 """
58 for link in links:
59 if link.intf1.node.name == first_node and link.intf2.node.name == second_node:
60 return LinkInfo(link.intf1.name, link.intf1.ip, link.intf2.name, link.intf2.ip)
61 elif link.intf2.node.name == first_node and link.intf1.node.name == second_node:
62 return LinkInfo(link.intf2.name, link.intf2.ip, link.intf1.name, link.intf1.ip)
63
64 return None
65
66 @staticmethod
phmoll9efd7392020-07-21 12:28:22 +020067 def calculateAllSubPaths(path):
68 """ This method returns all subpaths in forward and reverse order of a given path
69
70 :param path: Given path for which subpaths are calculated
71 :return: List of all subpaths
72 """
73 paths = []
74
75 # Append original path and reversed path
76 paths.append(path)
77 reversed = path[:]
78 reversed.reverse()
79 paths.append(reversed)
80
81 # Iterate over all possible lengths for subpaths
82 for subpath_length in range(2, len(path)):
83 # Get all subpaths of length subpath_lenght of the given path
84 for start_index in range(0, len(path) - subpath_length + 1):
85 subpath = path[start_index: start_index + subpath_length]
86 paths.append(subpath)
87 subpath = subpath[:]
88 subpath.reverse()
89 paths.append(subpath)
90 return paths
91
92 @staticmethod
93 def replaceExistingSubpaths(path, existing_paths):
94
95 subpaths = []
96 for subpath_length in range(3, len(path)):
97 for start_index in range(0, len(path) - subpath_length + 1):
98 subpaths.append(path[start_index: start_index + subpath_length])
99 subpaths.reverse()
100
101 for subpath in subpaths:
102 if len(subpath) == len(path):
103 continue
104 if (subpath[0], subpath[-1]) in existing_paths:
105 existing = existing_paths[(subpath[0], subpath[-1])]
106 path = path[:path.index(existing[0])] + existing[:] + path[
107 path.index(existing[-1]) + 1:]
108 break
109 return path
110
111 @staticmethod
Philipp Moll61a2be82019-07-12 08:33:09 +0200112 def calcAllRoutes(net):
113 """ Configures IP routes between all nodes in the emulation topology. This is done in three
114 steps:
115
116 1) IP forwarding is enabled on all nodes
117 2) The igraph lib is used to calculate all shortest paths between the nodes
118 3) Route add commands are used to actually configure the ip routes
119
120 :param net:
121 """
122
123 mini_nodes = net.hosts
124 mini_links = net.links
125
126 # Enabling IP forwaring on all nodes
Ashlesh Gawande6c86e302019-09-17 22:27:05 -0500127 info('Configure IP forwarding on all nodes\n')
Philipp Moll61a2be82019-07-12 08:33:09 +0200128 for node in mini_nodes:
Ashlesh Gawande6c86e302019-09-17 22:27:05 -0500129 node.cmd('sysctl -w net.ipv4.ip_forward=1')
Philipp Moll61a2be82019-07-12 08:33:09 +0200130
phmoll9efd7392020-07-21 12:28:22 +0200131 # Create the network graph to calculate all shortest paths between nodes
Philipp Moll61a2be82019-07-12 08:33:09 +0200132 node_names = [node.name for node in mini_nodes]
133 links = []
134 for link in mini_links:
135 links.append((link.intf1.node.name, link.intf2.node.name))
136 links.append((link.intf2.node.name, link.intf1.node.name))
Philipp Moll61a2be82019-07-12 08:33:09 +0200137 networkGraph = Graph()
138 networkGraph = networkGraph.as_directed()
139 for node in node_names:
140 networkGraph.add_vertex(node)
141 for (a, b) in links:
142 networkGraph.add_edges([(a, b), (b, a)])
143
phmoll9efd7392020-07-21 12:28:22 +0200144 existing_paths = {} # Variable existing_paths stores all paths that are installed
145 shortest_paths = [] # List of calculated shorted paths betweeen all nodes
146
147 # Calculate shortest paths between all nodes using libigraph
Philipp Moll61a2be82019-07-12 08:33:09 +0200148 for from_node in node_names:
149 for to_node in node_names:
150 if from_node != to_node:
phmoll9efd7392020-07-21 12:28:22 +0200151 if (from_node, to_node) in existing_paths \
152 or (to_node, from_node) in existing_paths:
153 continue
Philipp Moll61a2be82019-07-12 08:33:09 +0200154 paths = networkGraph.get_all_shortest_paths(from_node, to_node)
155 if len(paths) == 0:
156 continue
phmoll9efd7392020-07-21 12:28:22 +0200157 paths.sort(key=lambda x: str(x))
158 paths.sort(key=lambda x: len(x))
159 shortest_path = paths[0] # Shortest path with node indizes as nodes
160 # Translate node indizes to node names
161 shortest_path_nodenames = [networkGraph.vs['name'][node]
162 for node in shortest_path]
163 shortest_paths.append(shortest_path_nodenames)
164
165 # Iterate over shortest paths and store subpaths that need to be installed on the nodes.
166 # Also, it is made sure that all paths and reverse paths are the same
167 shortest_paths.sort(key=lambda x: len(x), reverse=True)
168 for path in shortest_paths:
169 # Replace already existing subpaths of the path to make sure that no two paths between
170 # two nodes exist
171 path = IPRoutingHelper.replaceExistingSubpaths(path, existing_paths)
172
173 # Mark all subpaths of path to install on nodes, unless they already exist
174 subpaths = IPRoutingHelper.calculateAllSubPaths(path)
175 for subpath in subpaths:
176 if (subpath[0], subpath[-1]) not in existing_paths:
177 existing_paths[(subpath[0], subpath[-1])] = subpath
Philipp Moll61a2be82019-07-12 08:33:09 +0200178
Ashlesh Gawande6c86e302019-09-17 22:27:05 -0500179 # Iterate over all paths and configure the routes using the 'route add'
180 info('Configure routes on all nodes\n')
phmoll9efd7392020-07-21 12:28:22 +0200181 for path in existing_paths.values():
Philipp Moll61a2be82019-07-12 08:33:09 +0200182 start_node = path[0]
183 end_node = path[-1]
184 mini_start = net.get(start_node)
185 mini_end = net.get(end_node)
186
187 link_info = IPRoutingHelper.findLinkInformation(mini_links, path[0], path[1])
188 start_intf = link_info.start_intf_name
189
phmoll9efd7392020-07-21 12:28:22 +0200190 # Configure the route for every IP address of the destination
Philipp Moll61a2be82019-07-12 08:33:09 +0200191 for intf in mini_end.intfs:
192 addr = mini_end.intfs[intf].ip
193 if len(path) == 2:
194 # For direct connection, configure exit interface
phmoll9efd7392020-07-21 12:28:22 +0200195 debug('[{}] route add -host {} dev {}\n'.format(start_node, addr, start_intf))
Ashlesh Gawande6c86e302019-09-17 22:27:05 -0500196 mini_start.cmd('route add -host {} dev {}'.format(addr, start_intf))
Philipp Moll61a2be82019-07-12 08:33:09 +0200197 elif len(path) > 2:
198 # For longer paths, configure next hop as gateway
199 gateway_ip = link_info.end_ip
phmoll9efd7392020-07-21 12:28:22 +0200200 debug('[{}] route add -host {} dev {} gw {}\n'
201 .format(start_node, addr, start_intf, gateway_ip))
Ashlesh Gawande6c86e302019-09-17 22:27:05 -0500202 mini_start.cmd('route add -host {} dev {} gw {}'
Philipp Moll61a2be82019-07-12 08:33:09 +0200203 .format(addr, start_intf, gateway_ip))