blob: 75d789abc5261df992b034dbd0a92697ee554a48 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
Jonathan Hart2da1e602015-02-18 19:09:24 -080016package org.onosproject.routing.impl;
Jonathan Hart335ef462014-10-16 08:20:46 -070017
Jonathan Hart66018992015-07-31 11:19:27 -070018import com.google.common.collect.HashMultimap;
19import com.google.common.collect.Multimaps;
20import com.google.common.collect.SetMultimap;
21import com.google.common.util.concurrent.ThreadFactoryBuilder;
22import com.googlecode.concurrenttrees.common.KeyValuePair;
23import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
24import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
25import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
Jonathan Hart41349e92015-02-09 14:14:02 -080026import org.apache.felix.scr.annotations.Activate;
27import org.apache.felix.scr.annotations.Component;
28import org.apache.felix.scr.annotations.Deactivate;
29import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
31import org.apache.felix.scr.annotations.Service;
Pingping Line28ae4c2015-03-13 11:37:03 -070032import org.onlab.packet.Ip4Address;
33import org.onlab.packet.Ip6Address;
Jonathan Hart552e31f2015-02-06 11:11:59 -080034import org.onlab.packet.IpAddress;
35import org.onlab.packet.IpPrefix;
36import org.onlab.packet.MacAddress;
Jonathan Hart66018992015-07-31 11:19:27 -070037import org.onosproject.core.CoreService;
Jonathan Hart4cb39882015-08-12 23:50:55 -040038import org.onosproject.incubator.net.intf.InterfaceService;
Jonathan Hart552e31f2015-02-06 11:11:59 -080039import org.onosproject.net.Host;
40import org.onosproject.net.host.HostEvent;
41import org.onosproject.net.host.HostListener;
42import org.onosproject.net.host.HostService;
Jonathan Hart2da1e602015-02-18 19:09:24 -080043import org.onosproject.routing.BgpService;
44import org.onosproject.routing.FibEntry;
45import org.onosproject.routing.FibListener;
46import org.onosproject.routing.FibUpdate;
47import org.onosproject.routing.RouteEntry;
48import org.onosproject.routing.RouteListener;
49import org.onosproject.routing.RouteUpdate;
50import org.onosproject.routing.RoutingService;
Pingping Line28ae4c2015-03-13 11:37:03 -070051import org.onosproject.routing.config.RoutingConfigurationService;
Jonathan Hart552e31f2015-02-06 11:11:59 -080052import org.slf4j.Logger;
53import org.slf4j.LoggerFactory;
54
Jonathan Hart66018992015-07-31 11:19:27 -070055import java.util.Collection;
56import java.util.Collections;
57import java.util.Iterator;
58import java.util.LinkedList;
59import java.util.List;
60import java.util.Map;
61import java.util.Set;
62import java.util.concurrent.BlockingQueue;
63import java.util.concurrent.ConcurrentHashMap;
64import java.util.concurrent.ExecutorService;
65import java.util.concurrent.Executors;
66import java.util.concurrent.LinkedBlockingQueue;
Jonathan Hart41349e92015-02-09 14:14:02 -080067
Jonathan Hart66018992015-07-31 11:19:27 -070068import static com.google.common.base.Preconditions.checkNotNull;
Pingping Line28ae4c2015-03-13 11:37:03 -070069import static org.onosproject.routing.RouteEntry.createBinaryString;
70
Jonathan Hart335ef462014-10-16 08:20:46 -070071/**
Jonathan Hart552e31f2015-02-06 11:11:59 -080072 * This class processes route updates and maintains a Routing Information Base
73 * (RIB). After route updates have been processed and next hops have been
74 * resolved, FIB updates are sent to any listening FIB components.
Jonathan Hart335ef462014-10-16 08:20:46 -070075 */
Jonathan Hart41349e92015-02-09 14:14:02 -080076@Component(immediate = true)
77@Service
78public class Router implements RoutingService {
Jonathan Hart335ef462014-10-16 08:20:46 -070079
80 private static final Logger log = LoggerFactory.getLogger(Router.class);
81
Jonathan Hart552e31f2015-02-06 11:11:59 -080082 // Route entries are stored in a radix tree.
Jonathan Hart0b04bed2014-10-16 16:39:19 -070083 // The key in this tree is the binary string of prefix of the route.
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080084 private InvertedRadixTree<RouteEntry> ribTable4;
85 private InvertedRadixTree<RouteEntry> ribTable6;
Jonathan Hart335ef462014-10-16 08:20:46 -070086
87 // Stores all incoming route updates in a queue.
Pingping Line28ae4c2015-03-13 11:37:03 -070088 private final BlockingQueue<Collection<RouteUpdate>> routeUpdatesQueue =
89 new LinkedBlockingQueue<>();
Jonathan Hart335ef462014-10-16 08:20:46 -070090
Pingping Line28ae4c2015-03-13 11:37:03 -070091 // Next-hop IP address to route entry mapping for next hops pending MAC
92 // resolution
Jonathan Hart41349e92015-02-09 14:14:02 -080093 private SetMultimap<IpAddress, RouteEntry> routesWaitingOnArp;
Jonathan Hart335ef462014-10-16 08:20:46 -070094
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080095 // The IPv4 address to MAC address mapping
Jonathan Hart41349e92015-02-09 14:14:02 -080096 private final Map<IpAddress, MacAddress> ip2Mac = new ConcurrentHashMap<>();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080097
Jonathan Hart41349e92015-02-09 14:14:02 -080098 private FibListener fibComponent;
Jonathan Hart335ef462014-10-16 08:20:46 -070099
Jonathan Hart41349e92015-02-09 14:14:02 -0800100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart66018992015-07-31 11:19:27 -0700101 protected CoreService coreService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart41349e92015-02-09 14:14:02 -0800104 protected HostService hostService;
Jonathan Hart335ef462014-10-16 08:20:46 -0700105
Jonathan Hart41349e92015-02-09 14:14:02 -0800106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected BgpService bgpService;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800108
Pingping Line28ae4c2015-03-13 11:37:03 -0700109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart4cb39882015-08-12 23:50:55 -0400110 protected InterfaceService interfaceService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Pingping Line28ae4c2015-03-13 11:37:03 -0700113 protected RoutingConfigurationService routingConfigurationService;
114
Jonathan Hart41349e92015-02-09 14:14:02 -0800115 private ExecutorService bgpUpdatesExecutor;
116 private final HostListener hostListener = new InternalHostListener();
117
118 @Activate
119 public void activate() {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800120 ribTable4 = new ConcurrentInvertedRadixTree<>(
121 new DefaultByteArrayNodeFactory());
122 ribTable6 = new ConcurrentInvertedRadixTree<>(
Jonathan Hart335ef462014-10-16 08:20:46 -0700123 new DefaultByteArrayNodeFactory());
Pingping Line28ae4c2015-03-13 11:37:03 -0700124
Jonathan Hart335ef462014-10-16 08:20:46 -0700125 routesWaitingOnArp = Multimaps.synchronizedSetMultimap(
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800126 HashMultimap.<IpAddress, RouteEntry>create());
Jonathan Hart335ef462014-10-16 08:20:46 -0700127
Jonathan Hart66018992015-07-31 11:19:27 -0700128 coreService.registerApplication(ROUTER_APP_ID);
129
Jonathan Hart335ef462014-10-16 08:20:46 -0700130 bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
Pavlin Radoslavov8b752442014-11-18 14:34:37 -0800131 new ThreadFactoryBuilder()
132 .setNameFormat("sdnip-bgp-updates-%d").build());
Jonathan Hart335ef462014-10-16 08:20:46 -0700133 }
134
Jonathan Hart41349e92015-02-09 14:14:02 -0800135 @Deactivate
136 public void deactivate() {
137 log.debug("Stopped");
138 }
139
140 @Override
Pingping Line28ae4c2015-03-13 11:37:03 -0700141 public void addFibListener(FibListener fibListener) {
142 this.fibComponent = checkNotNull(fibListener);
Pingping Line28ae4c2015-03-13 11:37:03 -0700143 }
144
145 @Override
146 public void start() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800147 this.hostService.addListener(hostListener);
148
Jonathan Hartd24fafb2015-02-09 17:55:32 -0800149 bgpService.start(new InternalRouteListener());
Jonathan Hart41349e92015-02-09 14:14:02 -0800150
Sho SHIMIZU74626412015-09-11 11:46:27 -0700151 bgpUpdatesExecutor.execute(this::doUpdatesThread);
Jonathan Hart335ef462014-10-16 08:20:46 -0700152 }
153
Jonathan Hart41349e92015-02-09 14:14:02 -0800154 @Override
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800155 public void stop() {
Jonathan Hart41349e92015-02-09 14:14:02 -0800156 bgpService.stop();
157
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800158 this.hostService.removeListener(hostListener);
159
160 // Stop the thread(s)
Jonathan Hart739c8352014-10-29 17:49:26 -0700161 bgpUpdatesExecutor.shutdownNow();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800162
163 synchronized (this) {
164 // Cleanup all local state
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800165 ribTable4 = new ConcurrentInvertedRadixTree<>(
Pingping Line28ae4c2015-03-13 11:37:03 -0700166 new DefaultByteArrayNodeFactory());
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800167 ribTable6 = new ConcurrentInvertedRadixTree<>(
Pingping Line28ae4c2015-03-13 11:37:03 -0700168 new DefaultByteArrayNodeFactory());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800169 routeUpdatesQueue.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800170 routesWaitingOnArp.clear();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800171 ip2Mac.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800172 }
Jonathan Hart739c8352014-10-29 17:49:26 -0700173 }
174
Jonathan Hart41349e92015-02-09 14:14:02 -0800175 /**
176 * Entry point for route updates.
177 *
178 * @param routeUpdates collection of route updates to process
179 */
180 private void update(Collection<RouteUpdate> routeUpdates) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700181 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800182 routeUpdatesQueue.put(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700183 } catch (InterruptedException e) {
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700184 log.error("Interrupted while putting on routeUpdatesQueue", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700185 Thread.currentThread().interrupt();
186 }
187 }
188
189 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700190 * Thread for handling route updates.
191 */
192 private void doUpdatesThread() {
193 boolean interrupted = false;
194 try {
195 while (!interrupted) {
196 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800197 Collection<RouteUpdate> routeUpdates =
Pingping Line28ae4c2015-03-13 11:37:03 -0700198 routeUpdatesQueue.take();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800199 processRouteUpdates(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700200 } catch (InterruptedException e) {
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700201 log.error("Interrupted while taking from updates queue", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700202 interrupted = true;
203 } catch (Exception e) {
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700204 log.error("exception", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700205 }
206 }
207 } finally {
208 if (interrupted) {
209 Thread.currentThread().interrupt();
210 }
211 }
212 }
213
214 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800215 * Gets all IPv4 routes from the RIB.
216 *
217 * @return all IPv4 routes from the RIB
218 */
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700219 @Override
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800220 public Collection<RouteEntry> getRoutes4() {
221 Iterator<KeyValuePair<RouteEntry>> it =
Pingping Line28ae4c2015-03-13 11:37:03 -0700222 ribTable4.getKeyValuePairsForKeysStartingWith("").iterator();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800223
224 List<RouteEntry> routes = new LinkedList<>();
225
226 while (it.hasNext()) {
227 KeyValuePair<RouteEntry> entry = it.next();
228 routes.add(entry.getValue());
229 }
230
231 return routes;
232 }
233
234 /**
235 * Gets all IPv6 routes from the RIB.
236 *
237 * @return all IPv6 routes from the RIB
238 */
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700239 @Override
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800240 public Collection<RouteEntry> getRoutes6() {
241 Iterator<KeyValuePair<RouteEntry>> it =
Pingping Line28ae4c2015-03-13 11:37:03 -0700242 ribTable6.getKeyValuePairsForKeysStartingWith("").iterator();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800243
244 List<RouteEntry> routes = new LinkedList<>();
245
246 while (it.hasNext()) {
247 KeyValuePair<RouteEntry> entry = it.next();
248 routes.add(entry.getValue());
249 }
250
251 return routes;
252 }
253
254 /**
255 * Finds a route in the RIB for a prefix. The prefix can be either IPv4 or
256 * IPv6.
257 *
258 * @param prefix the prefix to use
259 * @return the route if found, otherwise null
260 */
261 RouteEntry findRibRoute(IpPrefix prefix) {
Pingping Line28ae4c2015-03-13 11:37:03 -0700262 String binaryString = createBinaryString(prefix);
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700263 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800264 // IPv4
265 return ribTable4.getValueForExactKey(binaryString);
266 }
267 // IPv6
268 return ribTable6.getValueForExactKey(binaryString);
269 }
270
271 /**
272 * Adds a route to the RIB. The route can be either IPv4 or IPv6.
273 *
274 * @param routeEntry the route entry to use
275 */
276 void addRibRoute(RouteEntry routeEntry) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700277 if (routeEntry.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800278 // IPv4
Jonathan Hart9a426f82015-09-03 15:43:13 +0200279 ribTable4.put(createBinaryString(routeEntry.prefix()), routeEntry);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800280 } else {
281 // IPv6
Jonathan Hart9a426f82015-09-03 15:43:13 +0200282 ribTable6.put(createBinaryString(routeEntry.prefix()), routeEntry);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800283 }
284 }
285
286 /**
287 * Removes a route for a prefix from the RIB. The prefix can be either IPv4
288 * or IPv6.
289 *
290 * @param prefix the prefix to use
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800291 * @return true if the route was found and removed, otherwise false
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800292 */
293 boolean removeRibRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700294 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800295 // IPv4
Pingping Line28ae4c2015-03-13 11:37:03 -0700296 return ribTable4.remove(createBinaryString(prefix));
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800297 }
298 // IPv6
Pingping Line28ae4c2015-03-13 11:37:03 -0700299 return ribTable6.remove(createBinaryString(prefix));
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800300 }
301
302 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800303 * Processes route updates.
304 *
305 * @param routeUpdates the route updates to process
306 */
307 void processRouteUpdates(Collection<RouteUpdate> routeUpdates) {
308 synchronized (this) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800309 Collection<IpPrefix> withdrawPrefixes = new LinkedList<>();
Jonathan Hart552e31f2015-02-06 11:11:59 -0800310 Collection<FibUpdate> fibUpdates = new LinkedList<>();
311 Collection<FibUpdate> fibWithdraws = new LinkedList<>();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800312
313 for (RouteUpdate update : routeUpdates) {
314 switch (update.type()) {
315 case UPDATE:
Pingping Line28ae4c2015-03-13 11:37:03 -0700316
Jonathan Hart552e31f2015-02-06 11:11:59 -0800317 FibEntry fib = processRouteAdd(update.routeEntry(),
Pingping Line28ae4c2015-03-13 11:37:03 -0700318 withdrawPrefixes);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800319 if (fib != null) {
320 fibUpdates.add(new FibUpdate(FibUpdate.Type.UPDATE, fib));
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800321 }
Jonathan Hart552e31f2015-02-06 11:11:59 -0800322
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800323 break;
324 case DELETE:
325 processRouteDelete(update.routeEntry(), withdrawPrefixes);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800326
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800327 break;
328 default:
329 log.error("Unknown update Type: {}", update.type());
330 break;
331 }
332 }
333
Jonathan Hart552e31f2015-02-06 11:11:59 -0800334 withdrawPrefixes.forEach(p -> fibWithdraws.add(new FibUpdate(
335 FibUpdate.Type.DELETE, new FibEntry(p, null, null))));
336
Jonathan Hart41349e92015-02-09 14:14:02 -0800337 if (!fibUpdates.isEmpty() || !fibWithdraws.isEmpty()) {
338 fibComponent.update(fibUpdates, fibWithdraws);
339 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800340 }
341 }
342
343 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700344 * Processes adding a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700345 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800346 * The route entry is added to the radix tree. If there was an existing
347 * next hop for this prefix, but the next hop was different, then the
348 * old route entry is deleted.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700349 * </p>
Thomas Vachuska4b420772014-10-30 16:46:17 -0700350 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800351 * NOTE: Currently, we don't handle routes if the next hop is within the
352 * SDN domain.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700353 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700354 *
355 * @param routeEntry the route entry to add
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800356 * @param withdrawPrefixes the collection of accumulated prefixes whose
357 * intents will be withdrawn
Jonathan Hart552e31f2015-02-06 11:11:59 -0800358 * @return the corresponding FIB entry change, or null
Jonathan Hart335ef462014-10-16 08:20:46 -0700359 */
Pingping Line28ae4c2015-03-13 11:37:03 -0700360 private FibEntry processRouteAdd(RouteEntry routeEntry,
361 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800362 log.debug("Processing route add: {}", routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700363
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800364 // Find the old next-hop if we are updating an old route entry
365 IpAddress oldNextHop = null;
366 RouteEntry oldRouteEntry = findRibRoute(routeEntry.prefix());
367 if (oldRouteEntry != null) {
368 oldNextHop = oldRouteEntry.nextHop();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800369 }
370
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800371 // Add the new route to the RIB
372 addRibRoute(routeEntry);
373
374 if (oldNextHop != null) {
375 if (oldNextHop.equals(routeEntry.nextHop())) {
376 return null; // No change
377 }
378 //
379 // Update an existing nexthop for the prefix.
380 // We need to remove the old flows for this prefix from the
381 // switches before the new flows are added.
382 //
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800383 withdrawPrefixes.add(routeEntry.prefix());
384 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800385
Pingping Line28ae4c2015-03-13 11:37:03 -0700386 if (routingConfigurationService.isIpPrefixLocal(routeEntry.prefix())) {
387 // Route originated by local SDN domain
388 // We don't handle these here, reactive routing APP will handle
389 // these
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800390 log.debug("Own route {} to {}",
Pingping Line28ae4c2015-03-13 11:37:03 -0700391 routeEntry.prefix(), routeEntry.nextHop());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800392 return null;
393 }
394
395 //
396 // Find the MAC address of next hop router for this route entry.
397 // If the MAC address can not be found in ARP cache, then this prefix
398 // will be put in routesWaitingOnArp queue. Otherwise, generate
399 // a new route intent.
400 //
401
402 // Monitor the IP address for updates of the MAC address
Jonathan Hart31582d12014-10-22 13:52:41 -0700403 hostService.startMonitoringIp(routeEntry.nextHop());
404
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800405 // Check if we know the MAC address of the next hop
406 MacAddress nextHopMacAddress = ip2Mac.get(routeEntry.nextHop());
407 if (nextHopMacAddress == null) {
408 Set<Host> hosts = hostService.getHostsByIp(routeEntry.nextHop());
409 if (!hosts.isEmpty()) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800410 nextHopMacAddress = hosts.iterator().next().mac();
411 }
412 if (nextHopMacAddress != null) {
413 ip2Mac.put(routeEntry.nextHop(), nextHopMacAddress);
414 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700415 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700416 if (nextHopMacAddress == null) {
417 routesWaitingOnArp.put(routeEntry.nextHop(), routeEntry);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800418 return null;
Jonathan Hart335ef462014-10-16 08:20:46 -0700419 }
Jonathan Hart552e31f2015-02-06 11:11:59 -0800420 return new FibEntry(routeEntry.prefix(), routeEntry.nextHop(),
Pingping Line28ae4c2015-03-13 11:37:03 -0700421 nextHopMacAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700422 }
423
424 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800425 * Processes the deletion of a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700426 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800427 * The prefix for the routing entry is removed from radix tree.
428 * If the operation is successful, the prefix is added to the collection
429 * of prefixes whose intents that will be withdrawn.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700430 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700431 *
432 * @param routeEntry the route entry to delete
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800433 * @param withdrawPrefixes the collection of accumulated prefixes whose
434 * intents will be withdrawn
Jonathan Hart335ef462014-10-16 08:20:46 -0700435 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800436 private void processRouteDelete(RouteEntry routeEntry,
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800437 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800438 log.debug("Processing route delete: {}", routeEntry);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800439 boolean isRemoved = removeRibRoute(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700440
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800441 if (isRemoved) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800442 //
443 // Only withdraw intents if an entry was actually removed from the
444 // tree. If no entry was removed, the <prefix, nexthop> wasn't
445 // there so it's probably already been removed and we don't
446 // need to do anything.
447 //
448 withdrawPrefixes.add(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700449 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700450
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800451 routesWaitingOnArp.remove(routeEntry.nextHop(), routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700452 }
453
454 /**
Jonathan Hart31582d12014-10-22 13:52:41 -0700455 * Signals the Router that the MAC to IP mapping has potentially been
456 * updated. This has the effect of updating the MAC address for any
457 * installed prefixes if it has changed, as well as installing any pending
458 * prefixes that were waiting for MAC resolution.
Jonathan Hart335ef462014-10-16 08:20:46 -0700459 *
Jonathan Hart31582d12014-10-22 13:52:41 -0700460 * @param ipAddress the IP address that an event was received for
461 * @param macAddress the most recently known MAC address for the IP address
Jonathan Hart335ef462014-10-16 08:20:46 -0700462 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800463 private void updateMac(IpAddress ipAddress, MacAddress macAddress) {
464 log.debug("Received updated MAC info: {} => {}", ipAddress,
Pingping Line28ae4c2015-03-13 11:37:03 -0700465 macAddress);
Jonathan Hart31582d12014-10-22 13:52:41 -0700466
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800467 //
468 // We synchronize on "this" to prevent changes to the Radix tree
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700469 // while we're pushing intents. If the tree changes, the
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800470 // tree and the intents could get out of sync.
471 //
Jonathan Hart335ef462014-10-16 08:20:46 -0700472 synchronized (this) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800473 Collection<FibUpdate> submitFibEntries = new LinkedList<>();
Jonathan Hart335ef462014-10-16 08:20:46 -0700474
475 Set<RouteEntry> routesToPush =
Pingping Line28ae4c2015-03-13 11:37:03 -0700476 routesWaitingOnArp.removeAll(ipAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700477
478 for (RouteEntry routeEntry : routesToPush) {
479 // These will always be adds
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800480 RouteEntry foundRouteEntry = findRibRoute(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700481 if (foundRouteEntry != null &&
Pingping Line28ae4c2015-03-13 11:37:03 -0700482 foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800483 // We only push FIB updates if the prefix is still in the
484 // radix tree and the next hop is the same as our entry.
Jonathan Hart335ef462014-10-16 08:20:46 -0700485 // The prefix could have been removed while we were waiting
486 // for the ARP, or the next hop could have changed.
Jonathan Hart552e31f2015-02-06 11:11:59 -0800487 submitFibEntries.add(new FibUpdate(FibUpdate.Type.UPDATE,
Pingping Line28ae4c2015-03-13 11:37:03 -0700488 new FibEntry(routeEntry.prefix(),
489 ipAddress, macAddress)));
Jonathan Hart335ef462014-10-16 08:20:46 -0700490 } else {
Jonathan Hart31582d12014-10-22 13:52:41 -0700491 log.debug("{} has been revoked before the MAC was resolved",
Pingping Line28ae4c2015-03-13 11:37:03 -0700492 routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700493 }
494 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800495
Jonathan Hart552e31f2015-02-06 11:11:59 -0800496 if (!submitFibEntries.isEmpty()) {
497 fibComponent.update(submitFibEntries, Collections.emptyList());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800498 }
499
500 ip2Mac.put(ipAddress, macAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700501 }
502 }
503
504 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700505 * Listener for host events.
506 */
507 class InternalHostListener implements HostListener {
508 @Override
509 public void event(HostEvent event) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800510 log.debug("Received HostEvent {}", event);
511
512 Host host = event.subject();
513 switch (event.type()) {
514 case HOST_ADDED:
515 // FALLTHROUGH
516 case HOST_UPDATED:
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800517 for (IpAddress ipAddress : host.ipAddresses()) {
518 updateMac(ipAddress, host.mac());
Jonathan Hart335ef462014-10-16 08:20:46 -0700519 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800520 break;
521 case HOST_REMOVED:
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800522 for (IpAddress ipAddress : host.ipAddresses()) {
523 ip2Mac.remove(ipAddress);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800524 }
525 break;
526 default:
527 break;
Jonathan Hart335ef462014-10-16 08:20:46 -0700528 }
529 }
530 }
Jonathan Hart41349e92015-02-09 14:14:02 -0800531
532 /**
533 * Listener for route events.
534 */
535 private class InternalRouteListener implements RouteListener {
536 @Override
537 public void update(Collection<RouteUpdate> routeUpdates) {
538 Router.this.update(routeUpdates);
539 }
540 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700541
542 @Override
Pingping Line28ae4c2015-03-13 11:37:03 -0700543 public RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress) {
544 RouteEntry routeEntry = null;
545 Iterable<RouteEntry> routeEntries;
546
547 if (ipAddress.isIp4()) {
548 routeEntries = ribTable4.getValuesForKeysPrefixing(
549 createBinaryString(
550 IpPrefix.valueOf(ipAddress, Ip4Address.BIT_LENGTH)));
551 } else {
552 routeEntries = ribTable6.getValuesForKeysPrefixing(
553 createBinaryString(
554 IpPrefix.valueOf(ipAddress, Ip6Address.BIT_LENGTH)));
555 }
556 if (routeEntries == null) {
557 return null;
558 }
559 Iterator<RouteEntry> it = routeEntries.iterator();
560 while (it.hasNext()) {
561 routeEntry = it.next();
562 }
563 return routeEntry;
564 }
565
Jonathan Hart335ef462014-10-16 08:20:46 -0700566}