Jonathan Hart
Committed by Gerrit Code Review

Implementation of the route service

Change-Id: I4e905cf868ad69c426e4f4155dfd83e1f8b00335
1 +/*
2 + * Copyright 2016 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 +
17 +package org.onosproject.cli.net;
18 +
19 +import org.apache.karaf.shell.commands.Argument;
20 +import org.apache.karaf.shell.commands.Command;
21 +import org.onlab.packet.IpAddress;
22 +import org.onlab.packet.IpPrefix;
23 +import org.onosproject.cli.AbstractShellCommand;
24 +import org.onosproject.incubator.net.routing.Route;
25 +import org.onosproject.incubator.net.routing.RouteAdminService;
26 +
27 +import java.util.Collections;
28 +
29 +/**
30 + * Command to add a route to the routing table.
31 + */
32 +@Command(scope = "onos", name = "route-add",
33 + description = "Adds a route to the route table")
34 +public class RouteAddCommand extends AbstractShellCommand {
35 +
36 + @Argument(index = 0, name = "prefix", description = "IP prefix of the route",
37 + required = true)
38 + String prefixString = null;
39 +
40 + @Argument(index = 1, name = "nextHop", description = "IP address of the next hop",
41 + required = true)
42 + String nextHopString = null;
43 +
44 + @Override
45 + protected void execute() {
46 + RouteAdminService service = AbstractShellCommand.get(RouteAdminService.class);
47 +
48 + IpPrefix prefix = IpPrefix.valueOf(prefixString);
49 + IpAddress nextHop = IpAddress.valueOf(nextHopString);
50 +
51 + service.update(Collections.singleton(new Route(Route.Source.STATIC, prefix, nextHop)));
52 + }
53 +
54 +}
1 +/*
2 + * Copyright 2016 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 +
17 +package org.onosproject.cli.net;
18 +
19 +import org.apache.karaf.shell.commands.Argument;
20 +import org.apache.karaf.shell.commands.Command;
21 +import org.onlab.packet.IpPrefix;
22 +import org.onosproject.cli.AbstractShellCommand;
23 +import org.onosproject.incubator.net.routing.Route;
24 +import org.onosproject.incubator.net.routing.RouteAdminService;
25 +
26 +import java.util.Collections;
27 +
28 +/**
29 + * Command to remove a route from the routing table.
30 + */
31 +@Command(scope = "onos", name = "route-remove",
32 + description = "Removes a route from the route table")
33 +public class RouteRemoveCommand extends AbstractShellCommand {
34 +
35 + @Argument(index = 0, name = "prefix", description = "IP prefix of the route",
36 + required = true)
37 + String prefixString = null;
38 +
39 + @Override
40 + protected void execute() {
41 + RouteAdminService service = AbstractShellCommand.get(RouteAdminService.class);
42 +
43 + IpPrefix prefix = IpPrefix.valueOf(prefixString);
44 +
45 + service.withdraw(Collections.singleton(new Route(Route.Source.STATIC, prefix, null)));
46 + }
47 +
48 +}
1 +/*
2 + * Copyright 2016 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 +package org.onosproject.cli.net;
17 +
18 +import org.apache.karaf.shell.commands.Command;
19 +import org.onosproject.cli.AbstractShellCommand;
20 +import org.onosproject.incubator.net.routing.Route;
21 +import org.onosproject.incubator.net.routing.RouteService;
22 +import org.onosproject.incubator.net.routing.RouteTableId;
23 +
24 +import java.util.Collection;
25 +import java.util.Map;
26 +
27 +/**
28 + * Command to show the routes in the routing tables.
29 + */
30 +// TODO update command name when we switch over to new rib
31 +@Command(scope = "onos", name = "routes2",
32 + description = "Lists all routes in the route store")
33 +public class RoutesListCommand extends AbstractShellCommand {
34 +
35 + private static final String FORMAT_HEADER =
36 + " Network Next Hop";
37 + private static final String FORMAT_ROUTE =
38 + " %-18s %-15s";
39 +
40 + private static final String FORMAT_TABLE = "Table: %s";
41 + private static final String FORMAT_TOTAL = " Total: %d";
42 +
43 + @Override
44 + protected void execute() {
45 + RouteService service = AbstractShellCommand.get(RouteService.class);
46 +
47 + Map<RouteTableId, Collection<Route>> allRoutes = service.getAllRoutes();
48 +
49 + allRoutes.forEach((id, routes) -> {
50 + print(FORMAT_TABLE, id);
51 + print(FORMAT_HEADER);
52 + routes.forEach(r -> print(FORMAT_ROUTE, r.prefix(), r.nextHop()));
53 + print(FORMAT_TOTAL, routes.size());
54 + print("");
55 + });
56 +
57 + }
58 +
59 +}
...@@ -497,6 +497,16 @@ ...@@ -497,6 +497,16 @@
497 </command> 497 </command>
498 498
499 <command> 499 <command>
500 + <action class="org.onosproject.cli.net.RoutesListCommand"/>
501 + </command>
502 + <command>
503 + <action class="org.onosproject.cli.net.RouteAddCommand"/>
504 + </command>
505 + <command>
506 + <action class="org.onosproject.cli.net.RouteRemoveCommand"/>
507 + </command>
508 +
509 + <command>
500 <action class="org.onosproject.cli.net.GlobalLabelCommand"/> 510 <action class="org.onosproject.cli.net.GlobalLabelCommand"/>
501 </command> 511 </command>
502 <command> 512 <command>
......
...@@ -18,7 +18,7 @@ package org.onosproject.net.host; ...@@ -18,7 +18,7 @@ package org.onosproject.net.host;
18 import org.onosproject.store.StoreDelegate; 18 import org.onosproject.store.StoreDelegate;
19 19
20 /** 20 /**
21 - * Infrastructure link store delegate abstraction. 21 + * Host store delegate abstraction.
22 */ 22 */
23 public interface HostStoreDelegate extends StoreDelegate<HostEvent> { 23 public interface HostStoreDelegate extends StoreDelegate<HostEvent> {
24 } 24 }
......
...@@ -70,8 +70,7 @@ public class Route { ...@@ -70,8 +70,7 @@ public class Route {
70 */ 70 */
71 public Route(Source source, IpPrefix prefix, IpAddress nextHop) { 71 public Route(Source source, IpPrefix prefix, IpAddress nextHop) {
72 checkNotNull(prefix); 72 checkNotNull(prefix);
73 - checkNotNull(nextHop); 73 + checkArgument(nextHop == null || prefix.version().equals(nextHop.version()), VERSION_MISMATCH);
74 - checkArgument(prefix.version().equals(nextHop.version()), VERSION_MISMATCH);
75 74
76 this.source = checkNotNull(source); 75 this.source = checkNotNull(source);
77 this.prefix = prefix; 76 this.prefix = prefix;
......
1 +/*
2 + * Copyright 2016 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 +
17 +package org.onosproject.incubator.net.routing;
18 +
19 +import java.util.Collection;
20 +
21 +/**
22 + * Service allowing mutation of unicast routing state.
23 + */
24 +public interface RouteAdminService extends RouteService {
25 +
26 + /**
27 + * Updates the given routes in the route service.
28 + *
29 + * @param routes collection of routes to update
30 + */
31 + void update(Collection<Route> routes);
32 +
33 + /**
34 + * Withdraws the given routes from the route service.
35 + *
36 + * @param routes collection of routes to withdraw
37 + */
38 + void withdraw(Collection<Route> routes);
39 +}
...@@ -20,47 +20,28 @@ import org.onlab.packet.IpAddress; ...@@ -20,47 +20,28 @@ import org.onlab.packet.IpAddress;
20 import org.onosproject.event.ListenerService; 20 import org.onosproject.event.ListenerService;
21 21
22 import java.util.Collection; 22 import java.util.Collection;
23 +import java.util.Map;
23 24
24 /** 25 /**
25 - * IP unicast routing service. 26 + * Unicast IP route service.
26 */ 27 */
27 public interface RouteService extends ListenerService<RouteEvent, RouteListener> { 28 public interface RouteService extends ListenerService<RouteEvent, RouteListener> {
28 29
29 /** 30 /**
30 - * Gets all routes. 31 + * Returns all routes for all route tables in the system.
31 * 32 *
32 - * @return collection of all routes 33 + * @return map of route table name to routes in that table
33 */ 34 */
34 - Collection<Route> getRoutes(); 35 + Map<RouteTableId, Collection<Route>> getAllRoutes();
35 36
36 /** 37 /**
37 - * Gets routes learnt from a particular source. 38 + * Performs a longest prefix match on the given IP address. The call will
39 + * return the route with the most specific prefix that contains the given
40 + * IP address.
38 * 41 *
39 - * @param source route source 42 + * @param ip IP address
40 - * @return collection of routes
41 - */
42 - Collection<Route> getRoutesFromSource(Route.Source source);
43 -
44 - /**
45 - * Gets the longest prefix route that matches the given IP address.
46 - *
47 - * @param ip IP addres
48 * @return longest prefix matched route 43 * @return longest prefix matched route
49 */ 44 */
50 Route longestPrefixMatch(IpAddress ip); 45 Route longestPrefixMatch(IpAddress ip);
51 46
52 - //TODO should mutation methods be pushed to a different interface?
53 - /**
54 - * Updates the given routes in the route service.
55 - *
56 - * @param routes collection of routes to update
57 - */
58 - void update(Collection<Route> routes);
59 -
60 - /**
61 - * Withdraws the given routes from the route service.
62 - *
63 - * @param routes collection of routes to withdraw
64 - */
65 - void withdraw(Collection<Route> routes);
66 } 47 }
......
1 +/*
2 + * Copyright 2016 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 +
17 +package org.onosproject.incubator.net.routing;
18 +
19 +import org.onlab.packet.IpAddress;
20 +import org.onlab.packet.MacAddress;
21 +import org.onosproject.store.Store;
22 +
23 +import java.util.Collection;
24 +import java.util.Set;
25 +
26 +/**
27 + * Unicast route store.
28 + */
29 +public interface RouteStore extends Store<RouteEvent, RouteStoreDelegate> {
30 +
31 + /**
32 + * Adds or updates the given route in the store.
33 + *
34 + * @param route route to add or update
35 + */
36 + void updateRoute(Route route);
37 +
38 + /**
39 + * Removes the given route from the store.
40 + *
41 + * @param route route to remove
42 + */
43 + void removeRoute(Route route);
44 +
45 + /**
46 + * Returns the IDs for all route tables in the store.
47 + *
48 + * @return route table IDs
49 + */
50 + Set<RouteTableId> getRouteTables();
51 +
52 + /**
53 + * Returns the routes for a particular route table.
54 + *
55 + * @param table route table
56 + * @return collection of route in the table
57 + */
58 + Collection<Route> getRoutes(RouteTableId table);
59 +
60 + /**
61 + * Performs a longest prefix match with the given IP address.
62 + *
63 + * @param ip IP to look up
64 + * @return longest prefix match route
65 + */
66 + Route longestPrefixMatch(IpAddress ip);
67 +
68 + /**
69 + * Updates a next hop IP and MAC in the store.
70 + *
71 + * @param ip IP address
72 + * @param mac MAC address
73 + */
74 + void updateNextHop(IpAddress ip, MacAddress mac);
75 +
76 + /**
77 + * Removes a next hop IP and MAC from the store.
78 + *
79 + * @param ip IP address
80 + * @param mac MAC address
81 + */
82 + void removeNextHop(IpAddress ip, MacAddress mac);
83 +
84 + /**
85 + * Returns the MAC address of the given next hop.
86 + *
87 + * @param ip next hop IP
88 + * @return MAC address
89 + */
90 + MacAddress getNextHop(IpAddress ip);
91 +}
1 +/*
2 + * Copyright 2016 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 +
17 +package org.onosproject.incubator.net.routing;
18 +
19 +import org.onosproject.store.StoreDelegate;
20 +
21 +/**
22 + * Route store delegate abstraction.
23 + */
24 +public interface RouteStoreDelegate extends StoreDelegate<RouteEvent> {
25 +}
1 +/*
2 + * Copyright 2016 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 +
17 +package org.onosproject.incubator.net.routing;
18 +
19 +import java.util.Objects;
20 +
21 +/**
22 + * Identifier for a routing table.
23 + */
24 +public class RouteTableId {
25 + private final String name;
26 +
27 + /**
28 + * Creates a new route table ID.
29 + *
30 + * @param name unique name for the route table
31 + */
32 + public RouteTableId(String name) {
33 + this.name = name;
34 + }
35 +
36 + /**
37 + * Returns the name of the route table.
38 + *
39 + * @return table name
40 + */
41 + public String name() {
42 + return name;
43 + }
44 +
45 + @Override
46 + public boolean equals(Object obj) {
47 + if (this == obj) {
48 + return true;
49 + }
50 +
51 + if (obj instanceof RouteTableId) {
52 + RouteTableId that = (RouteTableId) obj;
53 +
54 + return Objects.equals(this.name, that.name);
55 + }
56 + return false;
57 + }
58 +
59 + @Override
60 + public int hashCode() {
61 + return Objects.hash(name);
62 + }
63 +
64 + @Override
65 + public String toString() {
66 + return name;
67 + }
68 +}
1 +/*
2 + * Copyright 2016 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 +
17 +package org.onosproject.incubator.net.routing.impl;
18 +
19 +import org.apache.felix.scr.annotations.Activate;
20 +import org.apache.felix.scr.annotations.Component;
21 +import org.apache.felix.scr.annotations.Deactivate;
22 +import org.apache.felix.scr.annotations.Reference;
23 +import org.apache.felix.scr.annotations.ReferenceCardinality;
24 +import org.apache.felix.scr.annotations.Service;
25 +import org.onlab.packet.IpAddress;
26 +import org.onlab.packet.MacAddress;
27 +import org.onosproject.event.ListenerService;
28 +import org.onosproject.incubator.net.routing.ResolvedRoute;
29 +import org.onosproject.incubator.net.routing.Route;
30 +import org.onosproject.incubator.net.routing.RouteAdminService;
31 +import org.onosproject.incubator.net.routing.RouteEvent;
32 +import org.onosproject.incubator.net.routing.RouteListener;
33 +import org.onosproject.incubator.net.routing.RouteService;
34 +import org.onosproject.incubator.net.routing.RouteStore;
35 +import org.onosproject.incubator.net.routing.RouteStoreDelegate;
36 +import org.onosproject.incubator.net.routing.RouteTableId;
37 +import org.onosproject.net.Host;
38 +import org.onosproject.net.host.HostEvent;
39 +import org.onosproject.net.host.HostListener;
40 +import org.onosproject.net.host.HostService;
41 +import org.slf4j.Logger;
42 +import org.slf4j.LoggerFactory;
43 +
44 +import javax.annotation.concurrent.GuardedBy;
45 +import java.util.Collection;
46 +import java.util.Collections;
47 +import java.util.HashMap;
48 +import java.util.Map;
49 +import java.util.Optional;
50 +import java.util.Set;
51 +import java.util.concurrent.BlockingQueue;
52 +import java.util.concurrent.ExecutorService;
53 +import java.util.concurrent.LinkedBlockingQueue;
54 +import java.util.concurrent.ThreadFactory;
55 +import java.util.function.Function;
56 +import java.util.stream.Collectors;
57 +
58 +import static java.util.concurrent.Executors.newSingleThreadExecutor;
59 +import static org.onlab.util.Tools.groupedThreads;
60 +
61 +/**
62 + * Implementation of the unicast route service.
63 + */
64 +@Service
65 +@Component
66 +public class RouteManager implements ListenerService<RouteEvent, RouteListener>,
67 + RouteService, RouteAdminService {
68 +
69 + private final Logger log = LoggerFactory.getLogger(getClass());
70 +
71 + private RouteStoreDelegate delegate = new InternalRouteStoreDelegate();
72 + private InternalHostListener hostListener = new InternalHostListener();
73 +
74 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 + protected RouteStore routeStore;
76 +
77 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 + protected HostService hostService;
79 +
80 + @GuardedBy(value = "this")
81 + private Map<RouteListener, ListenerQueue> listeners = new HashMap<>();
82 +
83 + private ThreadFactory threadFactory;
84 +
85 + @Activate
86 + protected void activate() {
87 + threadFactory = groupedThreads("onos/route", "listener-%d");
88 +
89 + routeStore.setDelegate(delegate);
90 + hostService.addListener(hostListener);
91 +
92 + }
93 +
94 + @Deactivate
95 + protected void deactivate() {
96 + listeners.values().forEach(l -> l.stop());
97 +
98 + routeStore.unsetDelegate(delegate);
99 + hostService.removeListener(hostListener);
100 + }
101 +
102 + /**
103 + * {@inheritDoc}
104 + *
105 + * In a departure from other services in ONOS, calling addListener will
106 + * cause all current routes to be pushed to the listener before any new
107 + * events are sent. This allows a listener to easily get the exact set of
108 + * routes without worrying about missing any.
109 + *
110 + * @param listener listener to be added
111 + */
112 + @Override
113 + public void addListener(RouteListener listener) {
114 + synchronized (this) {
115 + log.debug("Synchronizing current routes to new listener");
116 + ListenerQueue l = new ListenerQueue(listener);
117 + routeStore.getRouteTables().forEach(table -> {
118 + Collection<Route> routes = routeStore.getRoutes(table);
119 + if (routes != null) {
120 + routes.forEach(route ->
121 + l.post(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED,
122 + new ResolvedRoute(route, routeStore.getNextHop(route.nextHop())))));
123 + }
124 + });
125 +
126 + listeners.put(listener, l);
127 +
128 + l.start();
129 + log.debug("Route synchronization complete");
130 + }
131 + }
132 +
133 + @Override
134 + public void removeListener(RouteListener listener) {
135 + synchronized (this) {
136 + ListenerQueue l = listeners.remove(listener);
137 + if (l != null) {
138 + l.stop();
139 + }
140 + }
141 + }
142 +
143 + /**
144 + * Posts an event to all listeners.
145 + *
146 + * @param event event
147 + */
148 + private void post(RouteEvent event) {
149 + synchronized (this) {
150 + listeners.values().forEach(l -> l.post(event));
151 + }
152 + }
153 +
154 + @Override
155 + public Map<RouteTableId, Collection<Route>> getAllRoutes() {
156 + return routeStore.getRouteTables().stream()
157 + .collect(Collectors.toMap(Function.identity(),
158 + table -> (table == null) ?
159 + Collections.emptySet() : routeStore.getRoutes(table)));
160 + }
161 +
162 + @Override
163 + public Route longestPrefixMatch(IpAddress ip) {
164 + return routeStore.longestPrefixMatch(ip);
165 + }
166 +
167 + @Override
168 + public void update(Collection<Route> routes) {
169 + synchronized (this) {
170 + routes.forEach(route -> {
171 + routeStore.updateRoute(route);
172 + resolve(route);
173 + });
174 + }
175 + }
176 +
177 + @Override
178 + public void withdraw(Collection<Route> routes) {
179 + synchronized (this) {
180 + routes.forEach(route -> routeStore.removeRoute(route));
181 + }
182 + }
183 +
184 + private void resolve(Route route) {
185 + // Monitor the IP address for updates of the MAC address
186 + hostService.startMonitoringIp(route.nextHop());
187 +
188 + MacAddress nextHopMac = routeStore.getNextHop(route.nextHop());
189 + if (nextHopMac == null) {
190 + Set<Host> hosts = hostService.getHostsByIp(route.nextHop());
191 + Optional<Host> host = hosts.stream().findFirst();
192 + if (host.isPresent()) {
193 + nextHopMac = host.get().mac();
194 + }
195 + }
196 +
197 + if (nextHopMac != null) {
198 + routeStore.updateNextHop(route.nextHop(), nextHopMac);
199 + }
200 + }
201 +
202 + private void hostUpdated(Host host) {
203 + synchronized (this) {
204 + for (IpAddress ip : host.ipAddresses()) {
205 + routeStore.updateNextHop(ip, host.mac());
206 + }
207 + }
208 + }
209 +
210 + private void hostRemoved(Host host) {
211 + synchronized (this) {
212 + for (IpAddress ip : host.ipAddresses()) {
213 + routeStore.removeNextHop(ip, host.mac());
214 + }
215 + }
216 + }
217 +
218 + /**
219 + * Queues updates for a route listener to ensure they are received in the
220 + * correct order.
221 + */
222 + private class ListenerQueue {
223 +
224 + private final ExecutorService executorService;
225 + private final BlockingQueue<RouteEvent> queue;
226 + private final RouteListener listener;
227 +
228 + /**
229 + * Creates a new listener queue.
230 + *
231 + * @param listener route listener to queue updates for
232 + */
233 + public ListenerQueue(RouteListener listener) {
234 + this.listener = listener;
235 + queue = new LinkedBlockingQueue<>();
236 + executorService = newSingleThreadExecutor(threadFactory);
237 + }
238 +
239 + /**
240 + * Posts and event to the listener.
241 + *
242 + * @param event event
243 + */
244 + public void post(RouteEvent event) {
245 + queue.add(event);
246 + }
247 +
248 + /**
249 + * Initiates event delivery to the listener.
250 + */
251 + public void start() {
252 + executorService.execute(this::poll);
253 + }
254 +
255 + /**
256 + * Halts event delivery to the listener.
257 + */
258 + public void stop() {
259 + executorService.shutdown();
260 + }
261 +
262 + private void poll() {
263 + try {
264 + while (true) {
265 + listener.event(queue.take());
266 + }
267 + } catch (InterruptedException e) {
268 + log.info("Route listener event thread shutting down: {}", e.getMessage());
269 + }
270 + }
271 +
272 + }
273 +
274 + /**
275 + * Delegate to receive events from the route store.
276 + */
277 + private class InternalRouteStoreDelegate implements RouteStoreDelegate {
278 + @Override
279 + public void notify(RouteEvent event) {
280 + post(event);
281 + }
282 + }
283 +
284 + /**
285 + * Internal listener for host events.
286 + */
287 + private class InternalHostListener implements HostListener {
288 + @Override
289 + public void event(HostEvent event) {
290 + switch (event.type()) {
291 + case HOST_ADDED:
292 + case HOST_UPDATED:
293 + hostUpdated(event.subject());
294 + break;
295 + case HOST_REMOVED:
296 + hostRemoved(event.subject());
297 + break;
298 + case HOST_MOVED:
299 + break;
300 + default:
301 + break;
302 + }
303 + }
304 + }
305 +
306 +}
1 +/*
2 + * Copyright 2016 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 +
17 +/**
18 + * Implementation of route service.
19 + */
20 +package org.onosproject.incubator.net.routing.impl;
...@@ -53,6 +53,13 @@ ...@@ -53,6 +53,13 @@
53 <version>${project.version}</version> 53 <version>${project.version}</version>
54 </dependency> 54 </dependency>
55 55
56 + <!-- FIXME: not OSGi-ready -->
57 + <dependency>
58 + <groupId>com.googlecode.concurrent-trees</groupId>
59 + <artifactId>concurrent-trees</artifactId>
60 + <scope>compile</scope>
61 + </dependency>
62 +
56 <dependency> 63 <dependency>
57 <groupId>com.google.guava</groupId> 64 <groupId>com.google.guava</groupId>
58 <artifactId>guava-testlib</artifactId> 65 <artifactId>guava-testlib</artifactId>
......
1 +/*
2 + * Copyright 2016 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 +
17 +package org.onosproject.incubator.store.routing.impl;
18 +
19 +import com.google.common.collect.HashMultimap;
20 +import com.google.common.collect.Multimap;
21 +import com.google.common.collect.Multimaps;
22 +import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
23 +import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
24 +import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
25 +import org.apache.felix.scr.annotations.Activate;
26 +import org.apache.felix.scr.annotations.Component;
27 +import org.apache.felix.scr.annotations.Service;
28 +import org.onlab.packet.IpAddress;
29 +import org.onlab.packet.IpPrefix;
30 +import org.onlab.packet.MacAddress;
31 +import org.onosproject.incubator.net.routing.ResolvedRoute;
32 +import org.onosproject.incubator.net.routing.Route;
33 +import org.onosproject.incubator.net.routing.RouteEvent;
34 +import org.onosproject.incubator.net.routing.RouteStore;
35 +import org.onosproject.incubator.net.routing.RouteStoreDelegate;
36 +import org.onosproject.incubator.net.routing.RouteTableId;
37 +import org.onosproject.store.AbstractStore;
38 +
39 +import java.util.Collection;
40 +import java.util.Collections;
41 +import java.util.Iterator;
42 +import java.util.Map;
43 +import java.util.Set;
44 +import java.util.concurrent.ConcurrentHashMap;
45 +
46 +/**
47 + * Route store based on in-memory storage.
48 + */
49 +@Service
50 +@Component
51 +public 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 +}
1 +/*
2 + * Copyright 2016 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 +
17 +/**
18 + * Implementation of the unicast routing service.
19 + */
20 +package org.onosproject.incubator.store.routing.impl;