blob: d3f97b9d6405f85a909e889d3176a513eaf0dfee [file] [log] [blame]
Jonathan Hart96c146b2017-02-24 16:32:00 -08001/*
2 * Copyright 2017-present Open Networking Laboratory
3 *
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.net.routing.impl;
18
19import com.googlecode.concurrenttrees.common.KeyValuePair;
20import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
21import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
22import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
23import org.onlab.packet.IpAddress;
24import org.onlab.packet.IpPrefix;
25import org.onlab.util.GuavaCollectors;
26import org.onlab.util.Tools;
27import org.onosproject.incubator.net.routing.ResolvedRoute;
28import org.onosproject.incubator.net.routing.RouteEvent;
29import org.onosproject.incubator.net.routing.RouteTableId;
30
31import java.util.Collection;
32import java.util.Collections;
33import java.util.Map;
34import java.util.Optional;
35import java.util.Set;
36import java.util.concurrent.ConcurrentHashMap;
37
38import static org.onosproject.incubator.net.routing.RouteTools.createBinaryString;
39
40/**
41 * Stores routes that have been resolved.
42 */
43public class DefaultResolvedRouteStore implements ResolvedRouteStore {
44
45 private Map<RouteTableId, RouteTable> routeTables;
46 private static final RouteTableId IPV4 = new RouteTableId("ipv4");
47 private static final RouteTableId IPV6 = new RouteTableId("ipv6");
48
49 /**
50 * Creates a new resolved route store.
51 */
52 public DefaultResolvedRouteStore() {
53 routeTables = new ConcurrentHashMap<>();
54
55 routeTables.put(IPV4, new RouteTable());
56 routeTables.put(IPV6, new RouteTable());
57 }
58
59 @Override
60 public RouteEvent updateRoute(ResolvedRoute route) {
61 return getDefaultRouteTable(route).update(route);
62 }
63
64 @Override
65 public RouteEvent removeRoute(IpPrefix prefix) {
66 RouteTable table = getDefaultRouteTable(prefix.address());
67 return table.remove(prefix);
68 }
69
70 @Override
71 public Set<RouteTableId> getRouteTables() {
72 return routeTables.keySet();
73 }
74
75 @Override
76 public Collection<ResolvedRoute> getRoutes(RouteTableId table) {
77 RouteTable routeTable = routeTables.get(table);
78 if (routeTable == null) {
79 return Collections.emptySet();
80 }
81 return routeTable.getRoutes();
82 }
83
84 @Override
85 public Optional<ResolvedRoute> getRoute(IpPrefix prefix) {
86 return getDefaultRouteTable(prefix.address()).getRoute(prefix);
87 }
88
89 @Override
90 public Optional<ResolvedRoute> longestPrefixMatch(IpAddress ip) {
91 return getDefaultRouteTable(ip).longestPrefixMatch(ip);
92 }
93
94 private RouteTable getDefaultRouteTable(ResolvedRoute route) {
95 return getDefaultRouteTable(route.prefix().address());
96 }
97
98 private RouteTable getDefaultRouteTable(IpAddress ip) {
99 RouteTableId routeTableId = (ip.isIp4()) ? IPV4 : IPV6;
100 return routeTables.get(routeTableId);
101 }
102
103 /**
104 * Route table into which routes can be placed.
105 */
106 private class RouteTable {
107 private final InvertedRadixTree<ResolvedRoute> routeTable;
108
109 /**
110 * Creates a new route table.
111 */
112 public RouteTable() {
113 routeTable = new ConcurrentInvertedRadixTree<>(
114 new DefaultByteArrayNodeFactory());
115 }
116
117 /**
118 * Adds or updates the route in the route table.
119 *
120 * @param route route to update
121 */
122 public RouteEvent update(ResolvedRoute route) {
123 synchronized (this) {
124 ResolvedRoute oldRoute = routeTable.put(createBinaryString(route.prefix()), route);
125
126 // No need to proceed if the new route is the same
127 if (route.equals(oldRoute)) {
128 return null;
129 }
130
131 if (oldRoute == null) {
132 return new RouteEvent(RouteEvent.Type.ROUTE_ADDED, route);
133 } else {
134 return new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, route, oldRoute);
135 }
136 }
137 }
138
139 /**
140 * Removes the route from the route table.
141 *
142 * @param prefix prefix to remove
143 */
144 public RouteEvent remove(IpPrefix prefix) {
145 synchronized (this) {
146 String key = createBinaryString(prefix);
147
148 ResolvedRoute route = routeTable.getValueForExactKey(key);
149
150 if (route != null) {
151 routeTable.remove(key);
152 return new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, route);
153 }
154 return null;
155 }
156 }
157
158 /**
159 * Returns all routes in the route table.
160 *
161 * @return all routes
162 */
163 public Collection<ResolvedRoute> getRoutes() {
164 return Tools.stream(routeTable.getKeyValuePairsForKeysStartingWith(""))
165 .map(KeyValuePair::getValue)
166 .collect(GuavaCollectors.toImmutableList());
167 }
168
169 /**
170 * Returns the best route for the given prefix, if one exists.
171 *
172 * @param prefix IP prefix
173 * @return best route
174 */
175 public Optional<ResolvedRoute> getRoute(IpPrefix prefix) {
176 return Optional.ofNullable(routeTable.getValueForExactKey(createBinaryString(prefix)));
177 }
178
179 /**
180 * Performs a longest prefix match with the given IP in the route table.
181 *
182 * @param ip IP address to look up
183 * @return most specific prefix containing the given
184 */
185 public Optional<ResolvedRoute> longestPrefixMatch(IpAddress ip) {
186 return Tools.stream(routeTable.getValuesForKeysPrefixing(createBinaryString(ip.toIpPrefix())))
187 .reduce((a, b) -> b); // reduces to the last element in the stream
188 }
189 }
190}