blob: e5e807e7609efa5c0dea82e64d8de79583249469 [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;
20import com.google.common.collect.Multimap;
21import com.google.common.collect.Multimaps;
22import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
23import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
24import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
25import org.apache.felix.scr.annotations.Activate;
26import org.apache.felix.scr.annotations.Component;
27import org.apache.felix.scr.annotations.Service;
28import org.onlab.packet.IpAddress;
29import org.onlab.packet.IpPrefix;
30import org.onlab.packet.MacAddress;
31import org.onosproject.incubator.net.routing.ResolvedRoute;
32import org.onosproject.incubator.net.routing.Route;
33import org.onosproject.incubator.net.routing.RouteEvent;
34import org.onosproject.incubator.net.routing.RouteStore;
35import org.onosproject.incubator.net.routing.RouteStoreDelegate;
36import org.onosproject.incubator.net.routing.RouteTableId;
37import org.onosproject.store.AbstractStore;
38
39import java.util.Collection;
40import java.util.Collections;
41import java.util.Iterator;
42import java.util.Map;
43import java.util.Set;
44import java.util.concurrent.ConcurrentHashMap;
45
46/**
47 * Route store based on in-memory storage.
48 */
49@Service
50@Component
51public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegate>
52 implements RouteStore {
53
54 private Map<RouteTableId, RouteTable> routeTables;
55 private static final RouteTableId IPV4 = new RouteTableId("ipv4");
56 private static final RouteTableId IPV6 = new RouteTableId("ipv6");
57
58 private Map<IpAddress, MacAddress> nextHops = new ConcurrentHashMap<>();
59
60 @Activate
61 protected void activate() {
62 routeTables = new ConcurrentHashMap<>();
63
64 routeTables.put(IPV4, new RouteTable());
65 routeTables.put(IPV6, new RouteTable());
66 }
67
68 @Override
69 public void updateRoute(Route route) {
70 getDefaultRouteTable(route).update(route);
71 }
72
73 @Override
74 public void removeRoute(Route route) {
75 RouteTable table = getDefaultRouteTable(route);
76 table.remove(route);
77 if (table.getRoutesForNextHop(route.nextHop()).isEmpty()) {
78 nextHops.remove(route.nextHop());
79 }
80 }
81
82 @Override
83 public Set<RouteTableId> getRouteTables() {
84 return routeTables.keySet();
85 }
86
87 @Override
88 public Collection<Route> getRoutes(RouteTableId table) {
89 RouteTable routeTable = routeTables.get(table);
90 if (routeTable == null) {
91 return Collections.emptySet();
92 }
93 return routeTable.getRoutes();
94 }
95
96 @Override
97 public Route longestPrefixMatch(IpAddress ip) {
98 return getDefaultRouteTable(ip).longestPrefixMatch(ip);
99 }
100
101 @Override
102 public void updateNextHop(IpAddress ip, MacAddress mac) {
103 Collection<Route> routes = getDefaultRouteTable(ip).getRoutesForNextHop(ip);
104 if (!routes.isEmpty() && !mac.equals(nextHops.get(ip))) {
105 nextHops.put(ip, mac);
106
107 for (Route route : routes) {
108 notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, new ResolvedRoute(route, mac)));
109 }
110 }
111 }
112
113 @Override
114 public void removeNextHop(IpAddress ip, MacAddress mac) {
115 if (nextHops.remove(ip, mac)) {
116 Collection<Route> routes = getDefaultRouteTable(ip).getRoutesForNextHop(ip);
117 for (Route route : routes) {
118 notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, new ResolvedRoute(route, null)));
119 }
120 }
121 }
122
123 @Override
124 public MacAddress getNextHop(IpAddress ip) {
125 return nextHops.get(ip);
126 }
127
128 private RouteTable getDefaultRouteTable(Route route) {
129 return getDefaultRouteTable(route.prefix().address());
130 }
131
132 private RouteTable getDefaultRouteTable(IpAddress ip) {
133 RouteTableId routeTableId = (ip.isIp4()) ? IPV4 : IPV6;
134 return routeTables.get(routeTableId);
135 }
136
137 private static String createBinaryString(IpPrefix ipPrefix) {
138 byte[] octets = ipPrefix.address().toOctets();
139 StringBuilder result = new StringBuilder(ipPrefix.prefixLength());
140 result.append("0");
141 for (int i = 0; i < ipPrefix.prefixLength(); i++) {
142 int byteOffset = i / Byte.SIZE;
143 int bitOffset = i % Byte.SIZE;
144 int mask = 1 << (Byte.SIZE - 1 - bitOffset);
145 byte value = octets[byteOffset];
146 boolean isSet = ((value & mask) != 0);
147 result.append(isSet ? "1" : "0");
148 }
149
150 return result.toString();
151 }
152
153 /**
154 * Route table into which routes can be placed.
155 */
156 private class RouteTable {
157 private final InvertedRadixTree<Route> routeTable;
158
159 private final Map<IpPrefix, Route> routes = new ConcurrentHashMap<>();
160 private final Multimap<IpAddress, Route> reverseIndex =
161 Multimaps.synchronizedMultimap(HashMultimap.create());
162
163 /**
164 * Creates a new route table.
165 */
166 public RouteTable() {
167 routeTable = new ConcurrentInvertedRadixTree<>(
168 new DefaultByteArrayNodeFactory());
169 }
170
171 /**
172 * Adds or updates the route in the route table.
173 *
174 * @param route route to update
175 */
176 public void update(Route route) {
177 synchronized (this) {
178 Route oldRoute = routes.put(route.prefix(), route);
179 routeTable.put(createBinaryString(route.prefix()), route);
180
181 // TODO manage routes from multiple providers
182 reverseIndex.remove(route.nextHop(), oldRoute);
183 reverseIndex.put(route.nextHop(), route);
184
185 if (oldRoute != null && !oldRoute.nextHop().equals(route.nextHop())) {
186 // Remove old route because new one is different
187 // TODO ROUTE_UPDATED?
188 notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, new ResolvedRoute(oldRoute, null)));
189 }
190 }
191 }
192
193 /**
194 * Removes the route from the route table.
195 *
196 * @param route route to remove
197 */
198 public void remove(Route route) {
199 synchronized (this) {
200 Route removed = routes.remove(route.prefix());
201 routeTable.remove(createBinaryString(route.prefix()));
202 reverseIndex.remove(route.nextHop(), route);
203
204 if (removed != null) {
205 notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, new ResolvedRoute(route, null)));
206 }
207 }
208 }
209
210 /**
211 * Returns the routes pointing to a particular next hop.
212 *
213 * @param ip next hop IP address
214 * @return routes for the next hop
215 */
216 public Collection<Route> getRoutesForNextHop(IpAddress ip) {
217 return reverseIndex.get(ip);
218 }
219
220 /**
221 * Returns all routes in the route table.
222 *
223 * @return all routes
224 */
225 public Collection<Route> getRoutes() {
226 return routes.values();
227 }
228
229 /**
230 * Performs a longest prefix match with the given IP in the route table.
231 *
232 * @param ip IP address to look up
233 * @return most specific prefix containing the given
234 */
235 public Route longestPrefixMatch(IpAddress ip) {
236 Iterable<Route> prefixes =
237 routeTable.getValuesForKeysPrefixing(createBinaryString(ip.toIpPrefix()));
238
239 Iterator<Route> it = prefixes.iterator();
240
241 Route route = null;
242 while (it.hasNext()) {
243 route = it.next();
244 }
245
246 return route;
247 }
248 }
249
250}