blob: c286ab136c736bc8e766e41010a49566e4476745 [file] [log] [blame]
Jonathan Hartbfc5c482016-04-05 18:57:00 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Jonathan Hartbfc5c482016-04-05 18:57:00 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Ray Milkey69ec8712017-08-08 13:00:43 -070017package org.onosproject.routeservice.store;
Jonathan Hartbfc5c482016-04-05 18:57:00 -070018
piere91c87f2019-10-16 16:58:20 +020019import com.google.common.collect.Sets;
Jonathan Hartfd176612016-04-11 10:42:10 -070020import com.googlecode.concurrenttrees.common.KeyValuePair;
Jonathan Hartbfc5c482016-04-05 18:57:00 -070021import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
22import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
23import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
Jonathan Hartbfc5c482016-04-05 18:57:00 -070024import org.onlab.packet.IpAddress;
25import org.onlab.packet.IpPrefix;
Ray Milkey69ec8712017-08-08 13:00:43 -070026import org.onosproject.routeservice.InternalRouteEvent;
27import org.onosproject.routeservice.Route;
28import org.onosproject.routeservice.RouteSet;
29import org.onosproject.routeservice.RouteStore;
30import org.onosproject.routeservice.RouteStoreDelegate;
31import org.onosproject.routeservice.RouteTableId;
32import org.onosproject.routeservice.RouteTools;
Jonathan Hartbfc5c482016-04-05 18:57:00 -070033import org.onosproject.store.AbstractStore;
Jonathan Hartfd176612016-04-11 10:42:10 -070034import org.slf4j.Logger;
35import org.slf4j.LoggerFactory;
Jonathan Hartbfc5c482016-04-05 18:57:00 -070036
37import java.util.Collection;
38import java.util.Collections;
piere91c87f2019-10-16 16:58:20 +020039import java.util.HashMap;
Jonathan Hartbfc5c482016-04-05 18:57:00 -070040import java.util.Iterator;
Jonathan Hartfd176612016-04-11 10:42:10 -070041import java.util.LinkedList;
42import java.util.List;
Jonathan Hartbfc5c482016-04-05 18:57:00 -070043import java.util.Map;
44import java.util.Set;
45import java.util.concurrent.ConcurrentHashMap;
Jonathan Hart96c146b2017-02-24 16:32:00 -080046import java.util.stream.Collectors;
Jonathan Hartbfc5c482016-04-05 18:57:00 -070047
Charles Chanb21d69a2016-11-11 17:46:14 -080048import static com.google.common.base.Preconditions.checkNotNull;
49
Jonathan Hartbfc5c482016-04-05 18:57:00 -070050/**
51 * Route store based on in-memory storage.
52 */
Jonathan Hart96c146b2017-02-24 16:32:00 -080053public class LocalRouteStore extends AbstractStore<InternalRouteEvent, RouteStoreDelegate>
Jonathan Hartbfc5c482016-04-05 18:57:00 -070054 implements RouteStore {
55
Jonathan Hartfd176612016-04-11 10:42:10 -070056 private Logger log = LoggerFactory.getLogger(getClass());
57
Jonathan Hartbfc5c482016-04-05 18:57:00 -070058 private Map<RouteTableId, RouteTable> routeTables;
59 private static final RouteTableId IPV4 = new RouteTableId("ipv4");
60 private static final RouteTableId IPV6 = new RouteTableId("ipv6");
61
Charles Chan0214ded2016-11-18 17:48:37 -080062 /**
63 * Sets up local route store.
64 */
Jonathan Hart6c2e7962016-04-11 13:54:09 -070065 public void activate() {
Jonathan Hartbfc5c482016-04-05 18:57:00 -070066 routeTables = new ConcurrentHashMap<>();
67
Jonathan Hart96c146b2017-02-24 16:32:00 -080068 routeTables.put(IPV4, new RouteTable(IPV4));
69 routeTables.put(IPV6, new RouteTable(IPV6));
Charles Chan0214ded2016-11-18 17:48:37 -080070
71 log.info("Started");
72 }
73
74 /**
Jonathan Hart96c146b2017-02-24 16:32:00 -080075 * Cleans up local route store.
Charles Chan0214ded2016-11-18 17:48:37 -080076 */
77 public void deactivate() {
78 log.info("Stopped");
Jonathan Hartbfc5c482016-04-05 18:57:00 -070079 }
80
81 @Override
82 public void updateRoute(Route route) {
83 getDefaultRouteTable(route).update(route);
84 }
85
86 @Override
piere91c87f2019-10-16 16:58:20 +020087 public void updateRoutes(Collection<Route> routes) {
88 Map<RouteTableId, Set<Route>> computedTables = computeRouteTablesFromRoutes(routes);
89 computedTables.forEach(
90 ((routeTableId, routesToAdd) -> getDefaultRouteTable(routeTableId).update(routesToAdd))
91 );
92 }
93
94 @Override
Jonathan Hartbfc5c482016-04-05 18:57:00 -070095 public void removeRoute(Route route) {
Jonathan Hart96c146b2017-02-24 16:32:00 -080096 getDefaultRouteTable(route).remove(route);
Jonathan Hartbfc5c482016-04-05 18:57:00 -070097 }
98
99 @Override
piere91c87f2019-10-16 16:58:20 +0200100 public void removeRoutes(Collection<Route> routes) {
101 Map<RouteTableId, Set<Route>> computedTables = computeRouteTablesFromRoutes(routes);
102 computedTables.forEach(
103 ((routeTableId, routesToRemove) -> getDefaultRouteTable(routeTableId).remove(routesToRemove))
104 );
105 }
106
107 @Override
Daniel Ginsburg83b76452018-06-09 01:43:59 +0300108 public void replaceRoute(Route route) {
109 getDefaultRouteTable(route).replace(route);
110 }
111
112 @Override
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700113 public Set<RouteTableId> getRouteTables() {
114 return routeTables.keySet();
115 }
116
117 @Override
Jonathan Hart96c146b2017-02-24 16:32:00 -0800118 public Collection<RouteSet> getRoutes(RouteTableId table) {
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700119 RouteTable routeTable = routeTables.get(table);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800120 if (routeTable != null) {
121 return routeTable.getRouteSets();
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700122 }
Jonathan Hart96c146b2017-02-24 16:32:00 -0800123 return null;
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700124 }
125
126 @Override
Jonathan Hartfd176612016-04-11 10:42:10 -0700127 public Collection<Route> getRoutesForNextHop(IpAddress ip) {
128 return getDefaultRouteTable(ip).getRoutesForNextHop(ip);
129 }
130
131 @Override
piere91c87f2019-10-16 16:58:20 +0200132 public Collection<RouteSet> getRoutesForNextHops(Collection<IpAddress> nextHops) {
133 Map<RouteTableId, Set<IpAddress>> computedTables = computeRouteTablesFromIps(nextHops);
134 return computedTables.entrySet().stream()
135 .map(entry -> getDefaultRouteTable(entry.getKey()).getRoutesForNextHops(entry.getValue()))
136 .flatMap(Collection::stream)
137 .collect(Collectors.toList());
138 }
139
140 @Override
Jonathan Hart96c146b2017-02-24 16:32:00 -0800141 public RouteSet getRoutes(IpPrefix prefix) {
142 return getDefaultRouteTable(prefix.address()).getRoutes(prefix);
143 }
144
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700145 private RouteTable getDefaultRouteTable(Route route) {
146 return getDefaultRouteTable(route.prefix().address());
147 }
148
149 private RouteTable getDefaultRouteTable(IpAddress ip) {
150 RouteTableId routeTableId = (ip.isIp4()) ? IPV4 : IPV6;
151 return routeTables.get(routeTableId);
152 }
153
piere91c87f2019-10-16 16:58:20 +0200154 private RouteTable getDefaultRouteTable(RouteTableId routeTableId) {
155 return routeTables.get(routeTableId);
156 }
157
158 private Map<RouteTableId, Set<Route>> computeRouteTablesFromRoutes(Collection<Route> routes) {
159 Map<RouteTableId, Set<Route>> computedTables = new HashMap<>();
160 routes.forEach(route -> {
161 RouteTableId routeTableId = (route.prefix().address().isIp4()) ? IPV4 : IPV6;
162 Set<Route> tempRoutes = computedTables.computeIfAbsent(routeTableId, k -> Sets.newHashSet());
163 tempRoutes.add(route);
164 });
165 return computedTables;
166 }
167
168 private Map<RouteTableId, Set<IpAddress>> computeRouteTablesFromIps(Collection<IpAddress> ipAddresses) {
169 Map<RouteTableId, Set<IpAddress>> computedTables = new HashMap<>();
170 ipAddresses.forEach(ipAddress -> {
171 RouteTableId routeTableId = (ipAddress.isIp4()) ? IPV4 : IPV6;
172 Set<IpAddress> tempIpAddresses = computedTables.computeIfAbsent(routeTableId, k -> Sets.newHashSet());
173 tempIpAddresses.add(ipAddress);
174 });
175 return computedTables;
176 }
177
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700178 /**
179 * Route table into which routes can be placed.
180 */
181 private class RouteTable {
182 private final InvertedRadixTree<Route> routeTable;
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700183 private final Map<IpPrefix, Route> routes = new ConcurrentHashMap<>();
Jonathan Hart96c146b2017-02-24 16:32:00 -0800184 private final RouteTableId id;
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700185
186 /**
187 * Creates a new route table.
188 */
Jonathan Hart96c146b2017-02-24 16:32:00 -0800189 public RouteTable(RouteTableId id) {
190 this.id = checkNotNull(id);
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700191 routeTable = new ConcurrentInvertedRadixTree<>(
192 new DefaultByteArrayNodeFactory());
193 }
194
195 /**
196 * Adds or updates the route in the route table.
197 *
198 * @param route route to update
199 */
200 public void update(Route route) {
201 synchronized (this) {
202 Route oldRoute = routes.put(route.prefix(), route);
Charles Chanb21d69a2016-11-11 17:46:14 -0800203
204 // No need to proceed if the new route is the same
205 if (route.equals(oldRoute)) {
206 return;
207 }
208
Ray Milkey69ec8712017-08-08 13:00:43 -0700209 routeTable.put(RouteTools.createBinaryString(route.prefix()), route);
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700210
Jonathan Hart96c146b2017-02-24 16:32:00 -0800211 notifyDelegate(new InternalRouteEvent(
212 InternalRouteEvent.Type.ROUTE_ADDED, singletonRouteSet(route)));
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700213 }
214 }
215
216 /**
piere91c87f2019-10-16 16:58:20 +0200217 * Adds or updates the routes in the route table.
218 *
219 * @param routes routes to update
220 */
221 public void update(Collection<Route> routes) {
222 synchronized (this) {
223 routes.forEach(this::update);
224 }
225 }
226
227 /**
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700228 * Removes the route from the route table.
229 *
230 * @param route route to remove
231 */
232 public void remove(Route route) {
233 synchronized (this) {
234 Route removed = routes.remove(route.prefix());
Ray Milkey69ec8712017-08-08 13:00:43 -0700235 routeTable.remove(RouteTools.createBinaryString(route.prefix()));
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700236
237 if (removed != null) {
Jonathan Hart96c146b2017-02-24 16:32:00 -0800238 notifyDelegate(new InternalRouteEvent(
239 InternalRouteEvent.Type.ROUTE_REMOVED, emptyRouteSet(route.prefix())));
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700240 }
241 }
242 }
243
244 /**
piere91c87f2019-10-16 16:58:20 +0200245 * Adds or updates the routes in the route table.
246 *
247 * @param routes routes to update
248 */
249 public void remove(Collection<Route> routes) {
250 synchronized (this) {
251 routes.forEach(this::remove);
252 }
253 }
254
255 /**
Daniel Ginsburg83b76452018-06-09 01:43:59 +0300256 * Replace the route in the route table.
257 */
258 public void replace(Route route) {
259 update(route);
260 }
261
262 /**
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700263 * Returns the routes pointing to a particular next hop.
264 *
265 * @param ip next hop IP address
266 * @return routes for the next hop
267 */
268 public Collection<Route> getRoutesForNextHop(IpAddress ip) {
Jonathan Hart96c146b2017-02-24 16:32:00 -0800269 return routes.values()
270 .stream()
271 .filter(route -> route.nextHop().equals(ip))
272 .collect(Collectors.toSet());
273 }
274
piere91c87f2019-10-16 16:58:20 +0200275 /**
276 * Returns the routes pointing to the next hops.
277 *
278 * @param ips next hops IP addresses
279 * @return routes for the next hop
280 */
281 public Collection<RouteSet> getRoutesForNextHops(Collection<IpAddress> ips) {
282 // First create a reduced snapshot of the store iterating one time the map
283 Map<IpPrefix, Set<Route>> filteredRouteStore = new HashMap<>();
284 routes.values().stream()
285 .filter(r -> ips.contains(r.nextHop()))
286 .forEach(r -> {
287 Collection<Route> tempRoutes = filteredRouteStore.computeIfAbsent(
288 r.prefix(), k -> Sets.newHashSet());
289 tempRoutes.add(r);
290 });
291 // Return the collection of the routeSet we have to resolve
292 return filteredRouteStore.entrySet().stream()
293 .map(entry -> new RouteSet(id, entry.getKey(), entry.getValue()))
294 .collect(Collectors.toSet());
295 }
296
Jonathan Hart96c146b2017-02-24 16:32:00 -0800297 public RouteSet getRoutes(IpPrefix prefix) {
298 Route route = routes.get(prefix);
299 if (route != null) {
300 return singletonRouteSet(route);
301 }
302 return null;
303 }
304
305 public Collection<RouteSet> getRouteSets() {
306 return routes.values().stream()
307 .map(this::singletonRouteSet)
308 .collect(Collectors.toSet());
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700309 }
310
311 /**
312 * Returns all routes in the route table.
313 *
314 * @return all routes
315 */
316 public Collection<Route> getRoutes() {
Jonathan Hartfd176612016-04-11 10:42:10 -0700317 Iterator<KeyValuePair<Route>> it =
318 routeTable.getKeyValuePairsForKeysStartingWith("").iterator();
319
320 List<Route> routes = new LinkedList<>();
321
322 while (it.hasNext()) {
323 KeyValuePair<Route> entry = it.next();
324 routes.add(entry.getValue());
325 }
326
327 return routes;
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700328 }
329
330 /**
331 * Performs a longest prefix match with the given IP in the route table.
332 *
333 * @param ip IP address to look up
334 * @return most specific prefix containing the given
335 */
336 public Route longestPrefixMatch(IpAddress ip) {
337 Iterable<Route> prefixes =
Ray Milkey69ec8712017-08-08 13:00:43 -0700338 routeTable.getValuesForKeysPrefixing(RouteTools.createBinaryString(ip.toIpPrefix()));
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700339
340 Iterator<Route> it = prefixes.iterator();
341
342 Route route = null;
343 while (it.hasNext()) {
344 route = it.next();
345 }
346
347 return route;
348 }
Jonathan Hart96c146b2017-02-24 16:32:00 -0800349
350 private RouteSet singletonRouteSet(Route route) {
351 return new RouteSet(id, route.prefix(), Collections.singleton(route));
352 }
353
354 private RouteSet emptyRouteSet(IpPrefix prefix) {
355 return new RouteSet(id, prefix, Collections.emptySet());
356 }
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700357 }
358
359}