blob: 34aeaa7b10a28c1d324b2e912148223bafbc7d04 [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;
20import com.google.common.collect.Maps;
Jonathan Hart96c146b2017-02-24 16:32:00 -080021import com.googlecode.concurrenttrees.common.KeyValuePair;
22import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
23import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
24import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
25import org.onlab.packet.IpAddress;
26import org.onlab.packet.IpPrefix;
27import org.onlab.util.GuavaCollectors;
28import org.onlab.util.Tools;
Ray Milkey69ec8712017-08-08 13:00:43 -070029import org.onosproject.routeservice.ResolvedRoute;
30import org.onosproject.routeservice.RouteEvent;
31import org.onosproject.routeservice.RouteTableId;
Jonathan Hart96c146b2017-02-24 16:32:00 -080032
33import java.util.Collection;
34import java.util.Collections;
35import java.util.Map;
36import java.util.Optional;
37import java.util.Set;
38import java.util.concurrent.ConcurrentHashMap;
39
Ray Milkey69ec8712017-08-08 13:00:43 -070040import static org.onosproject.routeservice.RouteTools.createBinaryString;
Jonathan Hart96c146b2017-02-24 16:32:00 -080041
42/**
43 * Stores routes that have been resolved.
44 */
45public class DefaultResolvedRouteStore implements ResolvedRouteStore {
46
47 private Map<RouteTableId, RouteTable> routeTables;
48 private static final RouteTableId IPV4 = new RouteTableId("ipv4");
49 private static final RouteTableId IPV6 = new RouteTableId("ipv6");
50
51 /**
52 * Creates a new resolved route store.
53 */
54 public DefaultResolvedRouteStore() {
55 routeTables = new ConcurrentHashMap<>();
56
57 routeTables.put(IPV4, new RouteTable());
58 routeTables.put(IPV6, new RouteTable());
59 }
60
61 @Override
Jonathan Hartf7021682017-03-22 18:17:21 -070062 public RouteEvent updateRoute(ResolvedRoute route, Set<ResolvedRoute> alternatives) {
63 return getDefaultRouteTable(route).update(route, alternatives);
Jonathan Hart96c146b2017-02-24 16:32:00 -080064 }
65
66 @Override
67 public RouteEvent removeRoute(IpPrefix prefix) {
68 RouteTable table = getDefaultRouteTable(prefix.address());
69 return table.remove(prefix);
70 }
71
72 @Override
73 public Set<RouteTableId> getRouteTables() {
74 return routeTables.keySet();
75 }
76
77 @Override
78 public Collection<ResolvedRoute> getRoutes(RouteTableId table) {
79 RouteTable routeTable = routeTables.get(table);
80 if (routeTable == null) {
81 return Collections.emptySet();
82 }
83 return routeTable.getRoutes();
84 }
85
86 @Override
87 public Optional<ResolvedRoute> getRoute(IpPrefix prefix) {
88 return getDefaultRouteTable(prefix.address()).getRoute(prefix);
89 }
90
91 @Override
Jonathan Hartf7021682017-03-22 18:17:21 -070092 public Collection<ResolvedRoute> getAllRoutes(IpPrefix prefix) {
93 return getDefaultRouteTable(prefix.address()).getAllRoutes(prefix);
94 }
95
96 @Override
Jonathan Hart96c146b2017-02-24 16:32:00 -080097 public Optional<ResolvedRoute> longestPrefixMatch(IpAddress ip) {
98 return getDefaultRouteTable(ip).longestPrefixMatch(ip);
99 }
100
101 private RouteTable getDefaultRouteTable(ResolvedRoute route) {
102 return getDefaultRouteTable(route.prefix().address());
103 }
104
105 private RouteTable getDefaultRouteTable(IpAddress ip) {
106 RouteTableId routeTableId = (ip.isIp4()) ? IPV4 : IPV6;
107 return routeTables.get(routeTableId);
108 }
109
110 /**
111 * Route table into which routes can be placed.
112 */
113 private class RouteTable {
114 private final InvertedRadixTree<ResolvedRoute> routeTable;
Jonathan Hartf7021682017-03-22 18:17:21 -0700115 private final Map<IpPrefix, Set<ResolvedRoute>> alternativeRoutes;
Jonathan Hart96c146b2017-02-24 16:32:00 -0800116
117 /**
118 * Creates a new route table.
119 */
120 public RouteTable() {
121 routeTable = new ConcurrentInvertedRadixTree<>(
122 new DefaultByteArrayNodeFactory());
Jonathan Hartf7021682017-03-22 18:17:21 -0700123
124 alternativeRoutes = Maps.newHashMap();
Jonathan Hart96c146b2017-02-24 16:32:00 -0800125 }
126
127 /**
128 * Adds or updates the route in the route table.
129 *
130 * @param route route to update
Jonathan Hartf7021682017-03-22 18:17:21 -0700131 * @param alternatives alternative routes
Jonathan Hart96c146b2017-02-24 16:32:00 -0800132 */
Jonathan Hartf7021682017-03-22 18:17:21 -0700133 public RouteEvent update(ResolvedRoute route, Set<ResolvedRoute> alternatives) {
134 Set<ResolvedRoute> immutableAlternatives = checkAlternatives(route, alternatives);
135
Jonathan Hart96c146b2017-02-24 16:32:00 -0800136 synchronized (this) {
137 ResolvedRoute oldRoute = routeTable.put(createBinaryString(route.prefix()), route);
Jonathan Hartf7021682017-03-22 18:17:21 -0700138 Set<ResolvedRoute> oldRoutes = alternativeRoutes.put(route.prefix(), immutableAlternatives);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800139
Jonathan Hartf7021682017-03-22 18:17:21 -0700140 if (!route.equals(oldRoute)) {
141 if (oldRoute == null) {
142 return new RouteEvent(RouteEvent.Type.ROUTE_ADDED, route,
143 immutableAlternatives);
144 } else {
145 return new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, route,
146 oldRoute, immutableAlternatives);
147 }
Jonathan Hart96c146b2017-02-24 16:32:00 -0800148 }
149
Jonathan Hartf7021682017-03-22 18:17:21 -0700150 if (!immutableAlternatives.equals(oldRoutes)) {
151 return new RouteEvent(RouteEvent.Type.ALTERNATIVE_ROUTES_CHANGED,
152 route, immutableAlternatives);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800153 }
Jonathan Hartf7021682017-03-22 18:17:21 -0700154
155 return null;
156 }
157 }
158
159 /**
160 * Checks that the best route is present in the alternatives list and
161 * returns an immutable set of alternatives.
162 *
163 * @param route best route
164 * @param alternatives alternatives
165 * @return immutable set of alternative routes
166 */
167 private Set<ResolvedRoute> checkAlternatives(ResolvedRoute route, Set<ResolvedRoute> alternatives) {
168 if (!alternatives.contains(route)) {
169 return ImmutableSet.<ResolvedRoute>builder()
170 .addAll(alternatives)
171 .add(route)
172 .build();
173 } else {
174 return ImmutableSet.copyOf(alternatives);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800175 }
176 }
177
178 /**
179 * Removes the route from the route table.
180 *
181 * @param prefix prefix to remove
182 */
183 public RouteEvent remove(IpPrefix prefix) {
184 synchronized (this) {
185 String key = createBinaryString(prefix);
186
187 ResolvedRoute route = routeTable.getValueForExactKey(key);
Jonathan Hartf7021682017-03-22 18:17:21 -0700188 alternativeRoutes.remove(prefix);
Jonathan Hart96c146b2017-02-24 16:32:00 -0800189
190 if (route != null) {
191 routeTable.remove(key);
192 return new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, route);
193 }
194 return null;
195 }
196 }
197
198 /**
199 * Returns all routes in the route table.
200 *
201 * @return all routes
202 */
203 public Collection<ResolvedRoute> getRoutes() {
204 return Tools.stream(routeTable.getKeyValuePairsForKeysStartingWith(""))
205 .map(KeyValuePair::getValue)
206 .collect(GuavaCollectors.toImmutableList());
207 }
208
209 /**
210 * Returns the best route for the given prefix, if one exists.
211 *
212 * @param prefix IP prefix
213 * @return best route
214 */
215 public Optional<ResolvedRoute> getRoute(IpPrefix prefix) {
216 return Optional.ofNullable(routeTable.getValueForExactKey(createBinaryString(prefix)));
217 }
218
Jonathan Hartf7021682017-03-22 18:17:21 -0700219 public Collection<ResolvedRoute> getAllRoutes(IpPrefix prefix) {
220 return alternativeRoutes.getOrDefault(prefix, Collections.emptySet());
221 }
222
Jonathan Hart96c146b2017-02-24 16:32:00 -0800223 /**
224 * Performs a longest prefix match with the given IP in the route table.
225 *
226 * @param ip IP address to look up
227 * @return most specific prefix containing the given
228 */
229 public Optional<ResolvedRoute> longestPrefixMatch(IpAddress ip) {
230 return Tools.stream(routeTable.getValuesForKeysPrefixing(createBinaryString(ip.toIpPrefix())))
231 .reduce((a, b) -> b); // reduces to the last element in the stream
232 }
233 }
234}