Jonathan Hart
Committed by Gerrit Code Review

Route CLI improvements and bug fixes

Change-Id: I4b4547f578cc053dc150066dadb68b6b2cbb82ee
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.Command;
20 +import org.onosproject.cli.AbstractShellCommand;
21 +import org.onosproject.incubator.net.routing.NextHop;
22 +import org.onosproject.incubator.net.routing.Route;
23 +import org.onosproject.incubator.net.routing.RouteService;
24 +
25 +import java.util.Collection;
26 +import java.util.Set;
27 +
28 +/**
29 + * Command to show information about routing next hops.
30 + */
31 +@Command(scope = "onos", name = "next-hops",
32 + description = "Lists all next hops in the route store")
33 +public class NextHopsListCommand 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 + private static final String FORMAT = "ip=%s, mac=%s, numRoutes=%s";
44 +
45 + @Override
46 + protected void execute() {
47 + RouteService service = AbstractShellCommand.get(RouteService.class);
48 +
49 + Set<NextHop> nextHops = service.getNextHops();
50 +
51 + nextHops.forEach(nextHop -> {
52 + Collection<Route> routes = service.getRoutesForNextHop(nextHop.ip());
53 + print(FORMAT, nextHop.ip(), nextHop.mac(), routes.size());
54 + });
55 +
56 + }
57 +
58 +}
...@@ -18,6 +18,7 @@ package org.onosproject.cli.net; ...@@ -18,6 +18,7 @@ package org.onosproject.cli.net;
18 18
19 import org.apache.karaf.shell.commands.Argument; 19 import org.apache.karaf.shell.commands.Argument;
20 import org.apache.karaf.shell.commands.Command; 20 import org.apache.karaf.shell.commands.Command;
21 +import org.onlab.packet.IpAddress;
21 import org.onlab.packet.IpPrefix; 22 import org.onlab.packet.IpPrefix;
22 import org.onosproject.cli.AbstractShellCommand; 23 import org.onosproject.cli.AbstractShellCommand;
23 import org.onosproject.incubator.net.routing.Route; 24 import org.onosproject.incubator.net.routing.Route;
...@@ -36,13 +37,18 @@ public class RouteRemoveCommand extends AbstractShellCommand { ...@@ -36,13 +37,18 @@ public class RouteRemoveCommand extends AbstractShellCommand {
36 required = true) 37 required = true)
37 String prefixString = null; 38 String prefixString = null;
38 39
40 + @Argument(index = 1, name = "prefix", description = "Next hop IP address",
41 + required = true)
42 + String nextHopString = null;
43 +
39 @Override 44 @Override
40 protected void execute() { 45 protected void execute() {
41 RouteAdminService service = AbstractShellCommand.get(RouteAdminService.class); 46 RouteAdminService service = AbstractShellCommand.get(RouteAdminService.class);
42 47
43 IpPrefix prefix = IpPrefix.valueOf(prefixString); 48 IpPrefix prefix = IpPrefix.valueOf(prefixString);
49 + IpAddress nextHop = IpAddress.valueOf(nextHopString);
44 50
45 - service.withdraw(Collections.singleton(new Route(Route.Source.STATIC, prefix, null))); 51 + service.withdraw(Collections.singleton(new Route(Route.Source.STATIC, prefix, nextHop)));
46 } 52 }
47 53
48 } 54 }
......
...@@ -505,6 +505,9 @@ ...@@ -505,6 +505,9 @@
505 <command> 505 <command>
506 <action class="org.onosproject.cli.net.RouteRemoveCommand"/> 506 <action class="org.onosproject.cli.net.RouteRemoveCommand"/>
507 </command> 507 </command>
508 + <command>
509 + <action class="org.onosproject.cli.net.NextHopsListCommand"/>
510 + </command>
508 511
509 <command> 512 <command>
510 <action class="org.onosproject.cli.net.GlobalLabelCommand"/> 513 <action class="org.onosproject.cli.net.GlobalLabelCommand"/>
......
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 +
22 +import java.util.Objects;
23 +
24 +import static com.google.common.base.MoreObjects.toStringHelper;
25 +
26 +/**
27 + * Describes a routing next hop.
28 + */
29 +public class NextHop {
30 +
31 + private final IpAddress ip;
32 + private final MacAddress mac;
33 +
34 + /**
35 + * Creates a new next hop.
36 + *
37 + * @param ip IP address
38 + * @param mac MAC address
39 + */
40 + public NextHop(IpAddress ip, MacAddress mac) {
41 + this.ip = ip;
42 + this.mac = mac;
43 + }
44 +
45 + /**
46 + * Returns the IP address of the next hop.
47 + *
48 + * @return IP address
49 + */
50 + public IpAddress ip() {
51 + return ip;
52 + }
53 +
54 + /**
55 + * Returns the MAC address of the next hop.
56 + *
57 + * @return MAC address
58 + */
59 + public MacAddress mac() {
60 + return mac;
61 + }
62 +
63 + @Override
64 + public int hashCode() {
65 + return Objects.hash(ip, mac);
66 + }
67 +
68 + @Override
69 + public boolean equals(Object other) {
70 + if (this == other) {
71 + return true;
72 + }
73 +
74 + if (!(other instanceof NextHop)) {
75 + return false;
76 + }
77 +
78 + NextHop that = (NextHop) other;
79 +
80 + return Objects.equals(this.ip, that.mac) &&
81 + Objects.equals(this.ip, that.mac);
82 + }
83 +
84 + @Override
85 + public String toString() {
86 + return toStringHelper(this)
87 + .add("ip", ip)
88 + .add("mac", mac)
89 + .toString();
90 + }
91 +}
...@@ -20,6 +20,8 @@ import org.onlab.packet.IpAddress; ...@@ -20,6 +20,8 @@ import org.onlab.packet.IpAddress;
20 import org.onlab.packet.IpPrefix; 20 import org.onlab.packet.IpPrefix;
21 import org.onlab.packet.MacAddress; 21 import org.onlab.packet.MacAddress;
22 22
23 +import static com.google.common.base.MoreObjects.toStringHelper;
24 +
23 /** 25 /**
24 * Represents a route with the next hop MAC address resolved. 26 * Represents a route with the next hop MAC address resolved.
25 */ 27 */
...@@ -80,4 +82,13 @@ public class ResolvedRoute { ...@@ -80,4 +82,13 @@ public class ResolvedRoute {
80 public MacAddress nextHopMac() { 82 public MacAddress nextHopMac() {
81 return nextHopMac; 83 return nextHopMac;
82 } 84 }
85 +
86 + @Override
87 + public String toString() {
88 + return toStringHelper(this)
89 + .add("prefix", prefix)
90 + .add("nextHop", nextHop)
91 + .add("nextHopMac", nextHopMac)
92 + .toString();
93 + }
83 } 94 }
......
...@@ -21,6 +21,7 @@ import org.onlab.packet.IpPrefix; ...@@ -21,6 +21,7 @@ import org.onlab.packet.IpPrefix;
21 21
22 import java.util.Objects; 22 import java.util.Objects;
23 23
24 +import static com.google.common.base.MoreObjects.toStringHelper;
24 import static com.google.common.base.Preconditions.checkArgument; 25 import static com.google.common.base.Preconditions.checkArgument;
25 import static com.google.common.base.Preconditions.checkNotNull; 26 import static com.google.common.base.Preconditions.checkNotNull;
26 27
...@@ -70,7 +71,8 @@ public class Route { ...@@ -70,7 +71,8 @@ public class Route {
70 */ 71 */
71 public Route(Source source, IpPrefix prefix, IpAddress nextHop) { 72 public Route(Source source, IpPrefix prefix, IpAddress nextHop) {
72 checkNotNull(prefix); 73 checkNotNull(prefix);
73 - checkArgument(nextHop == null || prefix.version().equals(nextHop.version()), VERSION_MISMATCH); 74 + checkNotNull(nextHop);
75 + checkArgument(prefix.version().equals(nextHop.version()), VERSION_MISMATCH);
74 76
75 this.source = checkNotNull(source); 77 this.source = checkNotNull(source);
76 this.prefix = prefix; 78 this.prefix = prefix;
...@@ -124,4 +126,12 @@ public class Route { ...@@ -124,4 +126,12 @@ public class Route {
124 return Objects.equals(this.prefix, that.prefix) && 126 return Objects.equals(this.prefix, that.prefix) &&
125 Objects.equals(this.nextHop, that.nextHop); 127 Objects.equals(this.nextHop, that.nextHop);
126 } 128 }
129 +
130 + @Override
131 + public String toString() {
132 + return toStringHelper(this)
133 + .add("prefix", prefix)
134 + .add("nextHop", nextHop)
135 + .toString();
136 + }
127 } 137 }
......
...@@ -21,6 +21,7 @@ import org.onosproject.event.ListenerService; ...@@ -21,6 +21,7 @@ import org.onosproject.event.ListenerService;
21 21
22 import java.util.Collection; 22 import java.util.Collection;
23 import java.util.Map; 23 import java.util.Map;
24 +import java.util.Set;
24 25
25 /** 26 /**
26 * Unicast IP route service. 27 * Unicast IP route service.
...@@ -44,4 +45,19 @@ public interface RouteService extends ListenerService<RouteEvent, RouteListener> ...@@ -44,4 +45,19 @@ public interface RouteService extends ListenerService<RouteEvent, RouteListener>
44 */ 45 */
45 Route longestPrefixMatch(IpAddress ip); 46 Route longestPrefixMatch(IpAddress ip);
46 47
48 + /**
49 + * Returns the routes for the given next hop.
50 + *
51 + * @param nextHop next hop IP address
52 + * @return routes for this next hop
53 + */
54 + Collection<Route> getRoutesForNextHop(IpAddress nextHop);
55 +
56 + /**
57 + * Returns all next hops in the route store.
58 + *
59 + * @return set of next hops
60 + */
61 + Set<NextHop> getNextHops();
62 +
47 } 63 }
......
...@@ -21,6 +21,7 @@ import org.onlab.packet.MacAddress; ...@@ -21,6 +21,7 @@ import org.onlab.packet.MacAddress;
21 import org.onosproject.store.Store; 21 import org.onosproject.store.Store;
22 22
23 import java.util.Collection; 23 import java.util.Collection;
24 +import java.util.Map;
24 import java.util.Set; 25 import java.util.Set;
25 26
26 /** 27 /**
...@@ -66,6 +67,14 @@ public interface RouteStore extends Store<RouteEvent, RouteStoreDelegate> { ...@@ -66,6 +67,14 @@ public interface RouteStore extends Store<RouteEvent, RouteStoreDelegate> {
66 Route longestPrefixMatch(IpAddress ip); 67 Route longestPrefixMatch(IpAddress ip);
67 68
68 /** 69 /**
70 + * Returns the routes that point to the given next hop IP address.
71 + *
72 + * @param ip IP address of the next hop
73 + * @return routes for the given next hop
74 + */
75 + Collection<Route> getRoutesForNextHop(IpAddress ip);
76 +
77 + /**
69 * Updates a next hop IP and MAC in the store. 78 * Updates a next hop IP and MAC in the store.
70 * 79 *
71 * @param ip IP address 80 * @param ip IP address
...@@ -88,4 +97,11 @@ public interface RouteStore extends Store<RouteEvent, RouteStoreDelegate> { ...@@ -88,4 +97,11 @@ public interface RouteStore extends Store<RouteEvent, RouteStoreDelegate> {
88 * @return MAC address 97 * @return MAC address
89 */ 98 */
90 MacAddress getNextHop(IpAddress ip); 99 MacAddress getNextHop(IpAddress ip);
100 +
101 + /**
102 + * Returns all next hops in the route store.
103 + *
104 + * @return next hops
105 + */
106 + Map<IpAddress, MacAddress> getNextHops();
91 } 107 }
......
...@@ -25,6 +25,7 @@ import org.apache.felix.scr.annotations.Service; ...@@ -25,6 +25,7 @@ import org.apache.felix.scr.annotations.Service;
25 import org.onlab.packet.IpAddress; 25 import org.onlab.packet.IpAddress;
26 import org.onlab.packet.MacAddress; 26 import org.onlab.packet.MacAddress;
27 import org.onosproject.event.ListenerService; 27 import org.onosproject.event.ListenerService;
28 +import org.onosproject.incubator.net.routing.NextHop;
28 import org.onosproject.incubator.net.routing.ResolvedRoute; 29 import org.onosproject.incubator.net.routing.ResolvedRoute;
29 import org.onosproject.incubator.net.routing.Route; 30 import org.onosproject.incubator.net.routing.Route;
30 import org.onosproject.incubator.net.routing.RouteAdminService; 31 import org.onosproject.incubator.net.routing.RouteAdminService;
...@@ -146,6 +147,7 @@ public class RouteManager implements ListenerService<RouteEvent, RouteListener>, ...@@ -146,6 +147,7 @@ public class RouteManager implements ListenerService<RouteEvent, RouteListener>,
146 * @param event event 147 * @param event event
147 */ 148 */
148 private void post(RouteEvent event) { 149 private void post(RouteEvent event) {
150 + log.debug("Sending event {}", event);
149 synchronized (this) { 151 synchronized (this) {
150 listeners.values().forEach(l -> l.post(event)); 152 listeners.values().forEach(l -> l.post(event));
151 } 153 }
...@@ -165,9 +167,22 @@ public class RouteManager implements ListenerService<RouteEvent, RouteListener>, ...@@ -165,9 +167,22 @@ public class RouteManager implements ListenerService<RouteEvent, RouteListener>,
165 } 167 }
166 168
167 @Override 169 @Override
170 + public Collection<Route> getRoutesForNextHop(IpAddress nextHop) {
171 + return routeStore.getRoutesForNextHop(nextHop);
172 + }
173 +
174 + @Override
175 + public Set<NextHop> getNextHops() {
176 + return routeStore.getNextHops().entrySet().stream()
177 + .map(entry -> new NextHop(entry.getKey(), entry.getValue()))
178 + .collect(Collectors.toSet());
179 + }
180 +
181 + @Override
168 public void update(Collection<Route> routes) { 182 public void update(Collection<Route> routes) {
169 synchronized (this) { 183 synchronized (this) {
170 routes.forEach(route -> { 184 routes.forEach(route -> {
185 + log.debug("Received update {}", route);
171 routeStore.updateRoute(route); 186 routeStore.updateRoute(route);
172 resolve(route); 187 resolve(route);
173 }); 188 });
...@@ -177,7 +192,10 @@ public class RouteManager implements ListenerService<RouteEvent, RouteListener>, ...@@ -177,7 +192,10 @@ public class RouteManager implements ListenerService<RouteEvent, RouteListener>,
177 @Override 192 @Override
178 public void withdraw(Collection<Route> routes) { 193 public void withdraw(Collection<Route> routes) {
179 synchronized (this) { 194 synchronized (this) {
180 - routes.forEach(route -> routeStore.removeRoute(route)); 195 + routes.forEach(route -> {
196 + log.debug("Received withdraw {}", routes);
197 + routeStore.removeRoute(route);
198 + });
181 } 199 }
182 } 200 }
183 201
......
...@@ -17,8 +17,10 @@ ...@@ -17,8 +17,10 @@
17 package org.onosproject.incubator.store.routing.impl; 17 package org.onosproject.incubator.store.routing.impl;
18 18
19 import com.google.common.collect.HashMultimap; 19 import com.google.common.collect.HashMultimap;
20 +import com.google.common.collect.ImmutableMap;
20 import com.google.common.collect.Multimap; 21 import com.google.common.collect.Multimap;
21 import com.google.common.collect.Multimaps; 22 import com.google.common.collect.Multimaps;
23 +import com.googlecode.concurrenttrees.common.KeyValuePair;
22 import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory; 24 import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
23 import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree; 25 import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
24 import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree; 26 import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
...@@ -35,10 +37,14 @@ import org.onosproject.incubator.net.routing.RouteStore; ...@@ -35,10 +37,14 @@ import org.onosproject.incubator.net.routing.RouteStore;
35 import org.onosproject.incubator.net.routing.RouteStoreDelegate; 37 import org.onosproject.incubator.net.routing.RouteStoreDelegate;
36 import org.onosproject.incubator.net.routing.RouteTableId; 38 import org.onosproject.incubator.net.routing.RouteTableId;
37 import org.onosproject.store.AbstractStore; 39 import org.onosproject.store.AbstractStore;
40 +import org.slf4j.Logger;
41 +import org.slf4j.LoggerFactory;
38 42
39 import java.util.Collection; 43 import java.util.Collection;
40 import java.util.Collections; 44 import java.util.Collections;
41 import java.util.Iterator; 45 import java.util.Iterator;
46 +import java.util.LinkedList;
47 +import java.util.List;
42 import java.util.Map; 48 import java.util.Map;
43 import java.util.Set; 49 import java.util.Set;
44 import java.util.concurrent.ConcurrentHashMap; 50 import java.util.concurrent.ConcurrentHashMap;
...@@ -51,6 +57,8 @@ import java.util.concurrent.ConcurrentHashMap; ...@@ -51,6 +57,8 @@ import java.util.concurrent.ConcurrentHashMap;
51 public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegate> 57 public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegate>
52 implements RouteStore { 58 implements RouteStore {
53 59
60 + private Logger log = LoggerFactory.getLogger(getClass());
61 +
54 private Map<RouteTableId, RouteTable> routeTables; 62 private Map<RouteTableId, RouteTable> routeTables;
55 private static final RouteTableId IPV4 = new RouteTableId("ipv4"); 63 private static final RouteTableId IPV4 = new RouteTableId("ipv4");
56 private static final RouteTableId IPV6 = new RouteTableId("ipv6"); 64 private static final RouteTableId IPV6 = new RouteTableId("ipv6");
...@@ -74,7 +82,9 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat ...@@ -74,7 +82,9 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat
74 public void removeRoute(Route route) { 82 public void removeRoute(Route route) {
75 RouteTable table = getDefaultRouteTable(route); 83 RouteTable table = getDefaultRouteTable(route);
76 table.remove(route); 84 table.remove(route);
77 - if (table.getRoutesForNextHop(route.nextHop()).isEmpty()) { 85 + Collection<Route> routes = table.getRoutesForNextHop(route.nextHop());
86 +
87 + if (routes.isEmpty()) {
78 nextHops.remove(route.nextHop()); 88 nextHops.remove(route.nextHop());
79 } 89 }
80 } 90 }
...@@ -99,8 +109,14 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat ...@@ -99,8 +109,14 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat
99 } 109 }
100 110
101 @Override 111 @Override
112 + public Collection<Route> getRoutesForNextHop(IpAddress ip) {
113 + return getDefaultRouteTable(ip).getRoutesForNextHop(ip);
114 + }
115 +
116 + @Override
102 public void updateNextHop(IpAddress ip, MacAddress mac) { 117 public void updateNextHop(IpAddress ip, MacAddress mac) {
103 Collection<Route> routes = getDefaultRouteTable(ip).getRoutesForNextHop(ip); 118 Collection<Route> routes = getDefaultRouteTable(ip).getRoutesForNextHop(ip);
119 +
104 if (!routes.isEmpty() && !mac.equals(nextHops.get(ip))) { 120 if (!routes.isEmpty() && !mac.equals(nextHops.get(ip))) {
105 nextHops.put(ip, mac); 121 nextHops.put(ip, mac);
106 122
...@@ -125,6 +141,11 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat ...@@ -125,6 +141,11 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat
125 return nextHops.get(ip); 141 return nextHops.get(ip);
126 } 142 }
127 143
144 + @Override
145 + public Map<IpAddress, MacAddress> getNextHops() {
146 + return ImmutableMap.copyOf(nextHops);
147 + }
148 +
128 private RouteTable getDefaultRouteTable(Route route) { 149 private RouteTable getDefaultRouteTable(Route route) {
129 return getDefaultRouteTable(route.prefix().address()); 150 return getDefaultRouteTable(route.prefix().address());
130 } 151 }
...@@ -179,14 +200,27 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat ...@@ -179,14 +200,27 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat
179 routeTable.put(createBinaryString(route.prefix()), route); 200 routeTable.put(createBinaryString(route.prefix()), route);
180 201
181 // TODO manage routes from multiple providers 202 // TODO manage routes from multiple providers
182 - reverseIndex.remove(route.nextHop(), oldRoute); 203 +
183 reverseIndex.put(route.nextHop(), route); 204 reverseIndex.put(route.nextHop(), route);
184 205
206 + 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 +
185 if (oldRoute != null && !oldRoute.nextHop().equals(route.nextHop())) { 214 if (oldRoute != null && !oldRoute.nextHop().equals(route.nextHop())) {
186 // Remove old route because new one is different 215 // Remove old route because new one is different
187 // TODO ROUTE_UPDATED? 216 // TODO ROUTE_UPDATED?
188 notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, new ResolvedRoute(oldRoute, null))); 217 notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, new ResolvedRoute(oldRoute, null)));
189 } 218 }
219 +
220 + MacAddress nextHopMac = nextHops.get(route.nextHop());
221 + if (nextHopMac != null) {
222 + notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, new ResolvedRoute(route, nextHopMac)));
223 + }
190 } 224 }
191 } 225 }
192 226
...@@ -199,9 +233,9 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat ...@@ -199,9 +233,9 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat
199 synchronized (this) { 233 synchronized (this) {
200 Route removed = routes.remove(route.prefix()); 234 Route removed = routes.remove(route.prefix());
201 routeTable.remove(createBinaryString(route.prefix())); 235 routeTable.remove(createBinaryString(route.prefix()));
202 - reverseIndex.remove(route.nextHop(), route);
203 236
204 if (removed != null) { 237 if (removed != null) {
238 + reverseIndex.remove(removed.nextHop(), removed);
205 notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, new ResolvedRoute(route, null))); 239 notifyDelegate(new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, new ResolvedRoute(route, null)));
206 } 240 }
207 } 241 }
...@@ -223,7 +257,17 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat ...@@ -223,7 +257,17 @@ public class LocalRouteStore extends AbstractStore<RouteEvent, RouteStoreDelegat
223 * @return all routes 257 * @return all routes
224 */ 258 */
225 public Collection<Route> getRoutes() { 259 public Collection<Route> getRoutes() {
226 - return routes.values(); 260 + 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;
227 } 271 }
228 272
229 /** 273 /**
......