blob: 51ef8c7554196e70c444d00026811edfad47a5d3 [file] [log] [blame]
Jonathan Hart96c146b2017-02-24 16:32:00 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Jonathan Hart96c146b2017-02-24 16:32:00 -08003 *
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.impl;
Jonathan Hart96c146b2017-02-24 16:32:00 -080018
Jonathan Hartf7021682017-03-22 18:17:21 -070019import com.google.common.collect.ImmutableSet;
Jonathan Hart96c146b2017-02-24 16:32:00 -080020import com.googlecode.concurrenttrees.common.KeyValuePair;
21import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
22import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
23import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
24import org.onlab.packet.IpAddress;
25import org.onlab.packet.IpPrefix;
26import org.onlab.util.GuavaCollectors;
27import org.onlab.util.Tools;
Ray Milkey69ec8712017-08-08 13:00:43 -070028import org.onosproject.routeservice.ResolvedRoute;
29import org.onosproject.routeservice.RouteEvent;
30import org.onosproject.routeservice.RouteTableId;
Jonathan Hart96c146b2017-02-24 16:32:00 -080031
32import java.util.Collection;
33import java.util.Collections;
34import java.util.Map;
35import java.util.Optional;
36import java.util.Set;
37import java.util.concurrent.ConcurrentHashMap;
38
Ray Milkey69ec8712017-08-08 13:00:43 -070039import static org.onosproject.routeservice.RouteTools.createBinaryString;
Jonathan Hart96c146b2017-02-24 16:32:00 -080040
41/**
42 * Stores routes that have been resolved.
43 */
44public class DefaultResolvedRouteStore implements ResolvedRouteStore {
45
46 private Map<RouteTableId, RouteTable> routeTables;
47 private static final RouteTableId IPV4 = new RouteTableId("ipv4");
48 private static final RouteTableId IPV6 = new RouteTableId("ipv6");
49
50 /**
51 * Creates a new resolved route store.
52 */
53 public DefaultResolvedRouteStore() {
54 routeTables = new ConcurrentHashMap<>();
55
56 routeTables.put(IPV4, new RouteTable());
57 routeTables.put(IPV6, new RouteTable());
58 }
59
60 @Override
Jonathan Hartf7021682017-03-22 18:17:21 -070061 public RouteEvent updateRoute(ResolvedRoute route, Set<ResolvedRoute> alternatives) {
62 return getDefaultRouteTable(route).update(route, alternatives);
Jonathan Hart96c146b2017-02-24 16:32:00 -080063 }
64
65 @Override
66 public RouteEvent removeRoute(IpPrefix prefix) {
67 RouteTable table = getDefaultRouteTable(prefix.address());
68 return table.remove(prefix);
69 }
70
71 @Override
72 public Set<RouteTableId> getRouteTables() {
73 return routeTables.keySet();
74 }
75
76 @Override
77 public Collection<ResolvedRoute> getRoutes(RouteTableId table) {
78 RouteTable routeTable = routeTables.get(table);
79 if (routeTable == null) {
80 return Collections.emptySet();
81 }
82 return routeTable.getRoutes();
83 }
84
85 @Override
86 public Optional<ResolvedRoute> getRoute(IpPrefix prefix) {
87 return getDefaultRouteTable(prefix.address()).getRoute(prefix);
88 }
89
90 @Override
Jonathan Hartf7021682017-03-22 18:17:21 -070091 public Collection<ResolvedRoute> getAllRoutes(IpPrefix prefix) {
92 return getDefaultRouteTable(prefix.address()).getAllRoutes(prefix);
93 }
94
95 @Override
Jonathan Hart96c146b2017-02-24 16:32:00 -080096 public Optional<ResolvedRoute> longestPrefixMatch(IpAddress ip) {
97 return getDefaultRouteTable(ip).longestPrefixMatch(ip);
98 }
99
100 private RouteTable getDefaultRouteTable(ResolvedRoute route) {
101 return getDefaultRouteTable(route.prefix().address());
102 }
103
104 private RouteTable getDefaultRouteTable(IpAddress ip) {
105 RouteTableId routeTableId = (ip.isIp4()) ? IPV4 : IPV6;
106 return routeTables.get(routeTableId);
107 }
108
109 /**
110 * Route table into which routes can be placed.
111 */
112 private class RouteTable {
113 private final InvertedRadixTree<ResolvedRoute> routeTable;
Jonathan Hartf7021682017-03-22 18:17:21 -0700114 private final Map<IpPrefix, Set<ResolvedRoute>> alternativeRoutes;
Jonathan Hart96c146b2017-02-24 16:32:00 -0800115
116 /**
117 * Creates a new route table.
118 */
119 public RouteTable() {
120 routeTable = new ConcurrentInvertedRadixTree<>(
121 new DefaultByteArrayNodeFactory());
Jonathan Hartf7021682017-03-22 18:17:21 -0700122
pier8a86db22019-10-16 16:58:20 +0200123 alternativeRoutes = new ConcurrentHashMap<>();
Jonathan Hart96c146b2017-02-24 16:32:00 -0800124 }
125
126 /**
127 * Adds or updates the route in the route table.
128 *
129 * @param route route to update
Jonathan Hartf7021682017-03-22 18:17:21 -0700130 * @param alternatives alternative routes
Jonathan Hart96c146b2017-02-24 16:32:00 -0800131 */
Jonathan Hartf7021682017-03-22 18:17:21 -0700132 public RouteEvent update(ResolvedRoute route, Set<ResolvedRoute> alternatives) {
133 Set<ResolvedRoute> immutableAlternatives = checkAlternatives(route, alternatives);
134
Jonathan Hart96c146b2017-02-24 16:32:00 -0800135 ResolvedRoute oldRoute = routeTable.put(createBinaryString(route.prefix()), route);
Jonathan Hartf7021682017-03-22 18:17:21 -0700136 Set<ResolvedRoute> oldRoutes = alternativeRoutes.put(route.prefix(), immutableAlternatives);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800137
Jonathan Hartf7021682017-03-22 18:17:21 -0700138 if (!route.equals(oldRoute)) {
139 if (oldRoute == null) {
140 return new RouteEvent(RouteEvent.Type.ROUTE_ADDED, route,
141 immutableAlternatives);
142 } else {
143 return new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, route,
Charles Chan2fde6d42017-08-23 14:46:43 -0700144 oldRoute, immutableAlternatives, oldRoutes);
Jonathan Hartf7021682017-03-22 18:17:21 -0700145 }
Jonathan Hart96c146b2017-02-24 16:32:00 -0800146 }
147
Jonathan Hartf7021682017-03-22 18:17:21 -0700148 if (!immutableAlternatives.equals(oldRoutes)) {
149 return new RouteEvent(RouteEvent.Type.ALTERNATIVE_ROUTES_CHANGED,
Charles Chanbbe17f72017-08-23 16:33:06 -0700150 route, null, immutableAlternatives, oldRoutes);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800151 }
Jonathan Hartf7021682017-03-22 18:17:21 -0700152
153 return null;
Jonathan Hartf7021682017-03-22 18:17:21 -0700154 }
155
156 /**
157 * Checks that the best route is present in the alternatives list and
158 * returns an immutable set of alternatives.
159 *
160 * @param route best route
161 * @param alternatives alternatives
162 * @return immutable set of alternative routes
163 */
164 private Set<ResolvedRoute> checkAlternatives(ResolvedRoute route, Set<ResolvedRoute> alternatives) {
165 if (!alternatives.contains(route)) {
166 return ImmutableSet.<ResolvedRoute>builder()
167 .addAll(alternatives)
168 .add(route)
169 .build();
170 } else {
171 return ImmutableSet.copyOf(alternatives);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800172 }
173 }
174
175 /**
176 * Removes the route from the route table.
177 *
178 * @param prefix prefix to remove
179 */
180 public RouteEvent remove(IpPrefix prefix) {
pier8a86db22019-10-16 16:58:20 +0200181 String key = createBinaryString(prefix);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800182
pier8a86db22019-10-16 16:58:20 +0200183 ResolvedRoute route = routeTable.getValueForExactKey(key);
184 Set<ResolvedRoute> alternatives = alternativeRoutes.remove(prefix);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800185
pier8a86db22019-10-16 16:58:20 +0200186 if (route != null) {
187 routeTable.remove(key);
188 return new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, route, alternatives);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800189 }
pier8a86db22019-10-16 16:58:20 +0200190 return null;
Jonathan Hart96c146b2017-02-24 16:32:00 -0800191 }
192
193 /**
194 * Returns all routes in the route table.
195 *
196 * @return all routes
197 */
198 public Collection<ResolvedRoute> getRoutes() {
199 return Tools.stream(routeTable.getKeyValuePairsForKeysStartingWith(""))
200 .map(KeyValuePair::getValue)
201 .collect(GuavaCollectors.toImmutableList());
202 }
203
204 /**
205 * Returns the best route for the given prefix, if one exists.
206 *
207 * @param prefix IP prefix
208 * @return best route
209 */
210 public Optional<ResolvedRoute> getRoute(IpPrefix prefix) {
211 return Optional.ofNullable(routeTable.getValueForExactKey(createBinaryString(prefix)));
212 }
213
Jonathan Hartf7021682017-03-22 18:17:21 -0700214 public Collection<ResolvedRoute> getAllRoutes(IpPrefix prefix) {
215 return alternativeRoutes.getOrDefault(prefix, Collections.emptySet());
216 }
217
Jonathan Hart96c146b2017-02-24 16:32:00 -0800218 /**
219 * Performs a longest prefix match with the given IP in the route table.
220 *
221 * @param ip IP address to look up
222 * @return most specific prefix containing the given
223 */
224 public Optional<ResolvedRoute> longestPrefixMatch(IpAddress ip) {
225 return Tools.stream(routeTable.getValuesForKeysPrefixing(createBinaryString(ip.toIpPrefix())))
226 .reduce((a, b) -> b); // reduces to the last element in the stream
227 }
228 }
229}