blob: 98d008a51741b6193dce474e914a7b3a54b37e70 [file] [log] [blame]
Jonathan Hartbfc5c482016-04-05 18:57:00 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
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
17package org.onosproject.incubator.store.routing.impl;
18
19import com.google.common.collect.HashMultimap;
Jonathan Hartfd176612016-04-11 10:42:10 -070020import com.google.common.collect.ImmutableMap;
Jonathan Hartbfc5c482016-04-05 18:57:00 -070021import com.google.common.collect.Multimap;
22import com.google.common.collect.Multimaps;
Jonathan Hartfd176612016-04-11 10:42:10 -070023import com.googlecode.concurrenttrees.common.KeyValuePair;
Jonathan Hartbfc5c482016-04-05 18:57:00 -070024import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
25import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
26import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
27import org.apache.felix.scr.annotations.Activate;
28import org.apache.felix.scr.annotations.Component;
29import org.apache.felix.scr.annotations.Service;
30import org.onlab.packet.IpAddress;
31import org.onlab.packet.IpPrefix;
32import org.onlab.packet.MacAddress;
33import org.onosproject.incubator.net.routing.ResolvedRoute;
34import org.onosproject.incubator.net.routing.Route;
35import org.onosproject.incubator.net.routing.RouteEvent;
36import org.onosproject.incubator.net.routing.RouteStore;
37import org.onosproject.incubator.net.routing.RouteStoreDelegate;
38import org.onosproject.incubator.net.routing.RouteTableId;
39import org.onosproject.store.AbstractStore;
Jonathan Hartfd176612016-04-11 10:42:10 -070040import org.slf4j.Logger;
41import org.slf4j.LoggerFactory;
Jonathan Hartbfc5c482016-04-05 18:57:00 -070042
43import java.util.Collection;
44import java.util.Collections;
45import java.util.Iterator;
Jonathan Hartfd176612016-04-11 10:42:10 -070046import java.util.LinkedList;
47import java.util.List;
Jonathan Hartbfc5c482016-04-05 18:57:00 -070048import java.util.Map;
49import java.util.Set;
50import java.util.concurrent.ConcurrentHashMap;
51
52/**
53 * Route store based on in-memory storage.
54 */
55@Service
56@Component
57public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegate>
58 implements RouteStore {
59
Jonathan Hartfd176612016-04-11 10:42:10 -070060 private Logger log = LoggerFactory.getLogger(getClass());
61
Jonathan Hartbfc5c482016-04-05 18:57:00 -070062 private Map<RouteTableId, RouteTable> routeTables;
63 private static final RouteTableId IPV4 = new RouteTableId("ipv4");
64 private static final RouteTableId IPV6 = new RouteTableId("ipv6");
65
66 private Map<IpAddress, MacAddress> nextHops = new ConcurrentHashMap<>();
67
68 @Activate
69 protected void activate() {
70 routeTables = new ConcurrentHashMap<>();
71
72 routeTables.put(IPV4, new RouteTable());
73 routeTables.put(IPV6, new RouteTable());
74 }
75
76 @Override
77 public void updateRoute(Route route) {
78 getDefaultRouteTable(route).update(route);
79 }
80
81 @Override
82 public void removeRoute(Route route) {
83 RouteTable table = getDefaultRouteTable(route);
84 table.remove(route);
Jonathan Hartfd176612016-04-11 10:42:10 -070085 Collection<Route> routes = table.getRoutesForNextHop(route.nextHop());
86
87 if (routes.isEmpty()) {
Jonathan Hartbfc5c482016-04-05 18:57:00 -070088 nextHops.remove(route.nextHop());
89 }
90 }
91
92 @Override
93 public Set<RouteTableId> getRouteTables() {
94 return routeTables.keySet();
95 }
96
97 @Override
98 public Collection<Route> getRoutes(RouteTableId table) {
99 RouteTable routeTable = routeTables.get(table);
100 if (routeTable == null) {
101 return Collections.emptySet();
102 }
103 return routeTable.getRoutes();
104 }
105
106 @Override
107 public Route longestPrefixMatch(IpAddress ip) {
108 return getDefaultRouteTable(ip).longestPrefixMatch(ip);
109 }
110
111 @Override
Jonathan Hartfd176612016-04-11 10:42:10 -0700112 public Collection<Route> getRoutesForNextHop(IpAddress ip) {
113 return getDefaultRouteTable(ip).getRoutesForNextHop(ip);
114 }
115
116 @Override
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700117 public void updateNextHop(IpAddress ip, MacAddress mac) {
118 Collection<Route> routes = getDefaultRouteTable(ip).getRoutesForNextHop(ip);
Jonathan Hartfd176612016-04-11 10:42:10 -0700119
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700120 if (!routes.isEmpty() && !mac.equals(nextHops.get(ip))) {
121 nextHops.put(ip, mac);
122
123 for (Route route : routes) {
124 notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, new ResolvedRoute(route, mac)));
125 }
126 }
127 }
128
129 @Override
130 public void removeNextHop(IpAddress ip, MacAddress mac) {
131 if (nextHops.remove(ip, mac)) {
132 Collection<Route> routes = getDefaultRouteTable(ip).getRoutesForNextHop(ip);
133 for (Route route : routes) {
134 notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, new ResolvedRoute(route, null)));
135 }
136 }
137 }
138
139 @Override
140 public MacAddress getNextHop(IpAddress ip) {
141 return nextHops.get(ip);
142 }
143
Jonathan Hartfd176612016-04-11 10:42:10 -0700144 @Override
145 public Map<IpAddress, MacAddress> getNextHops() {
146 return ImmutableMap.copyOf(nextHops);
147 }
148
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700149 private RouteTable getDefaultRouteTable(Route route) {
150 return getDefaultRouteTable(route.prefix().address());
151 }
152
153 private RouteTable getDefaultRouteTable(IpAddress ip) {
154 RouteTableId routeTableId = (ip.isIp4()) ? IPV4 : IPV6;
155 return routeTables.get(routeTableId);
156 }
157
158 private static String createBinaryString(IpPrefix ipPrefix) {
159 byte[] octets = ipPrefix.address().toOctets();
160 StringBuilder result = new StringBuilder(ipPrefix.prefixLength());
161 result.append("0");
162 for (int i = 0; i < ipPrefix.prefixLength(); i++) {
163 int byteOffset = i / Byte.SIZE;
164 int bitOffset = i % Byte.SIZE;
165 int mask = 1 << (Byte.SIZE - 1 - bitOffset);
166 byte value = octets[byteOffset];
167 boolean isSet = ((value & mask) != 0);
168 result.append(isSet ? "1" : "0");
169 }
170
171 return result.toString();
172 }
173
174 /**
175 * Route table into which routes can be placed.
176 */
177 private class RouteTable {
178 private final InvertedRadixTree<Route> routeTable;
179
180 private final Map<IpPrefix, Route> routes = new ConcurrentHashMap<>();
181 private final Multimap<IpAddress, Route> reverseIndex =
182 Multimaps.synchronizedMultimap(HashMultimap.create());
183
184 /**
185 * Creates a new route table.
186 */
187 public RouteTable() {
188 routeTable = new ConcurrentInvertedRadixTree<>(
189 new DefaultByteArrayNodeFactory());
190 }
191
192 /**
193 * Adds or updates the route in the route table.
194 *
195 * @param route route to update
196 */
197 public void update(Route route) {
198 synchronized (this) {
199 Route oldRoute = routes.put(route.prefix(), route);
200 routeTable.put(createBinaryString(route.prefix()), route);
201
202 // TODO manage routes from multiple providers
Jonathan Hartfd176612016-04-11 10:42:10 -0700203
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700204 reverseIndex.put(route.nextHop(), route);
205
Jonathan Hartfd176612016-04-11 10:42:10 -0700206 if (oldRoute != null) {
207 reverseIndex.remove(oldRoute.nextHop(), oldRoute);
208
209 if (reverseIndex.get(oldRoute.nextHop()).isEmpty()) {
210 nextHops.remove(oldRoute.nextHop());
211 }
212 }
213
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700214 if (oldRoute != null && !oldRoute.nextHop().equals(route.nextHop())) {
215 // Remove old route because new one is different
216 // TODO ROUTE_UPDATED?
217 notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, new ResolvedRoute(oldRoute, null)));
218 }
Jonathan Hartfd176612016-04-11 10:42:10 -0700219
220 MacAddress nextHopMac = nextHops.get(route.nextHop());
221 if (nextHopMac != null) {
222 notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, new ResolvedRoute(route, nextHopMac)));
223 }
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700224 }
225 }
226
227 /**
228 * 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());
235 routeTable.remove(createBinaryString(route.prefix()));
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700236
237 if (removed != null) {
Jonathan Hartfd176612016-04-11 10:42:10 -0700238 reverseIndex.remove(removed.nextHop(), removed);
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700239 notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, new ResolvedRoute(route, null)));
240 }
241 }
242 }
243
244 /**
245 * Returns the routes pointing to a particular next hop.
246 *
247 * @param ip next hop IP address
248 * @return routes for the next hop
249 */
250 public Collection<Route> getRoutesForNextHop(IpAddress ip) {
251 return reverseIndex.get(ip);
252 }
253
254 /**
255 * Returns all routes in the route table.
256 *
257 * @return all routes
258 */
259 public Collection<Route> getRoutes() {
Jonathan Hartfd176612016-04-11 10:42:10 -0700260 Iterator<KeyValuePair<Route>> it =
261 routeTable.getKeyValuePairsForKeysStartingWith("").iterator();
262
263 List<Route> routes = new LinkedList<>();
264
265 while (it.hasNext()) {
266 KeyValuePair<Route> entry = it.next();
267 routes.add(entry.getValue());
268 }
269
270 return routes;
Jonathan Hartbfc5c482016-04-05 18:57:00 -0700271 }
272
273 /**
274 * Performs a longest prefix match with the given IP in the route table.
275 *
276 * @param ip IP address to look up
277 * @return most specific prefix containing the given
278 */
279 public Route longestPrefixMatch(IpAddress ip) {
280 Iterable<Route> prefixes =
281 routeTable.getValuesForKeysPrefixing(createBinaryString(ip.toIpPrefix()));
282
283 Iterator<Route> it = prefixes.iterator();
284
285 Route route = null;
286 while (it.hasNext()) {
287 route = it.next();
288 }
289
290 return route;
291 }
292 }
293
294}