blob: 0017d5276a8f4275cb5bffd7a4de3dd35fc4c531 [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 Hart552e31f2015-02-06 11:11:59 -080038import org.onosproject.net.Host;
39import org.onosproject.net.host.HostEvent;
40import org.onosproject.net.host.HostListener;
41import org.onosproject.net.host.HostService;
Jonathan Hart3930f632015-10-19 12:12:51 -070042import org.onosproject.routing.RouteSourceService;
Jonathan Hart2da1e602015-02-18 19:09:24 -080043import org.onosproject.routing.FibEntry;
44import org.onosproject.routing.FibListener;
45import org.onosproject.routing.FibUpdate;
46import org.onosproject.routing.RouteEntry;
47import org.onosproject.routing.RouteListener;
48import org.onosproject.routing.RouteUpdate;
49import org.onosproject.routing.RoutingService;
Pingping Line28ae4c2015-03-13 11:37:03 -070050import org.onosproject.routing.config.RoutingConfigurationService;
Jonathan Hart552e31f2015-02-06 11:11:59 -080051import org.slf4j.Logger;
52import org.slf4j.LoggerFactory;
53
Jonathan Hart66018992015-07-31 11:19:27 -070054import java.util.Collection;
55import java.util.Collections;
56import java.util.Iterator;
57import java.util.LinkedList;
58import java.util.List;
59import java.util.Map;
60import java.util.Set;
61import java.util.concurrent.BlockingQueue;
62import java.util.concurrent.ConcurrentHashMap;
63import java.util.concurrent.ExecutorService;
64import java.util.concurrent.Executors;
65import java.util.concurrent.LinkedBlockingQueue;
Jonathan Hart41349e92015-02-09 14:14:02 -080066
Jonathan Hart66018992015-07-31 11:19:27 -070067import static com.google.common.base.Preconditions.checkNotNull;
Pingping Line28ae4c2015-03-13 11:37:03 -070068import static org.onosproject.routing.RouteEntry.createBinaryString;
69
Jonathan Hart335ef462014-10-16 08:20:46 -070070/**
Jonathan Hart552e31f2015-02-06 11:11:59 -080071 * This class processes route updates and maintains a Routing Information Base
72 * (RIB). After route updates have been processed and next hops have been
73 * resolved, FIB updates are sent to any listening FIB components.
Jonathan Hart335ef462014-10-16 08:20:46 -070074 */
Jonathan Hartc22e8472015-11-17 18:25:45 -080075@Component(immediate = true, enabled = false)
Jonathan Hart41349e92015-02-09 14:14:02 -080076@Service
77public class Router implements RoutingService {
Jonathan Hart335ef462014-10-16 08:20:46 -070078
79 private static final Logger log = LoggerFactory.getLogger(Router.class);
80
Jonathan Hart552e31f2015-02-06 11:11:59 -080081 // Route entries are stored in a radix tree.
Jonathan Hart0b04bed2014-10-16 16:39:19 -070082 // The key in this tree is the binary string of prefix of the route.
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080083 private InvertedRadixTree<RouteEntry> ribTable4;
84 private InvertedRadixTree<RouteEntry> ribTable6;
Jonathan Hart335ef462014-10-16 08:20:46 -070085
86 // Stores all incoming route updates in a queue.
Pingping Line28ae4c2015-03-13 11:37:03 -070087 private final BlockingQueue<Collection<RouteUpdate>> routeUpdatesQueue =
88 new LinkedBlockingQueue<>();
Jonathan Hart335ef462014-10-16 08:20:46 -070089
Pingping Line28ae4c2015-03-13 11:37:03 -070090 // Next-hop IP address to route entry mapping for next hops pending MAC
91 // resolution
Jonathan Hart41349e92015-02-09 14:14:02 -080092 private SetMultimap<IpAddress, RouteEntry> routesWaitingOnArp;
Jonathan Hart335ef462014-10-16 08:20:46 -070093
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080094 // The IPv4 address to MAC address mapping
Jonathan Hart41349e92015-02-09 14:14:02 -080095 private final Map<IpAddress, MacAddress> ip2Mac = new ConcurrentHashMap<>();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080096
Jonathan Hart41349e92015-02-09 14:14:02 -080097 private FibListener fibComponent;
Jonathan Hart335ef462014-10-16 08:20:46 -070098
Jonathan Hart41349e92015-02-09 14:14:02 -080099 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart66018992015-07-31 11:19:27 -0700100 protected CoreService coreService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart41349e92015-02-09 14:14:02 -0800103 protected HostService hostService;
Jonathan Hart335ef462014-10-16 08:20:46 -0700104
Jonathan Hart41349e92015-02-09 14:14:02 -0800105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart3930f632015-10-19 12:12:51 -0700106 protected RouteSourceService routeSourceService;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800107
Pingping Line28ae4c2015-03-13 11:37:03 -0700108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected RoutingConfigurationService routingConfigurationService;
110
Jonathan Hart41349e92015-02-09 14:14:02 -0800111 private ExecutorService bgpUpdatesExecutor;
112 private final HostListener hostListener = new InternalHostListener();
113
114 @Activate
115 public void activate() {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800116 ribTable4 = new ConcurrentInvertedRadixTree<>(
117 new DefaultByteArrayNodeFactory());
118 ribTable6 = new ConcurrentInvertedRadixTree<>(
Jonathan Hart335ef462014-10-16 08:20:46 -0700119 new DefaultByteArrayNodeFactory());
Pingping Line28ae4c2015-03-13 11:37:03 -0700120
Jonathan Hart335ef462014-10-16 08:20:46 -0700121 routesWaitingOnArp = Multimaps.synchronizedSetMultimap(
Jonathan Hart365335e2015-12-10 11:09:53 -0800122 HashMultimap.create());
Jonathan Hart335ef462014-10-16 08:20:46 -0700123
Jonathan Hart66018992015-07-31 11:19:27 -0700124 coreService.registerApplication(ROUTER_APP_ID);
125
Jonathan Hart335ef462014-10-16 08:20:46 -0700126 bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
Pavlin Radoslavov8b752442014-11-18 14:34:37 -0800127 new ThreadFactoryBuilder()
Jonathan Hartde15e1c2016-02-03 10:51:50 -0800128 .setNameFormat("rib-updates-%d").build());
Jonathan Hart335ef462014-10-16 08:20:46 -0700129 }
130
Jonathan Hart41349e92015-02-09 14:14:02 -0800131 @Deactivate
132 public void deactivate() {
133 log.debug("Stopped");
134 }
135
136 @Override
Pingping Line28ae4c2015-03-13 11:37:03 -0700137 public void addFibListener(FibListener fibListener) {
138 this.fibComponent = checkNotNull(fibListener);
Pingping Line28ae4c2015-03-13 11:37:03 -0700139 }
140
141 @Override
142 public void start() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800143 this.hostService.addListener(hostListener);
144
Jonathan Hart3930f632015-10-19 12:12:51 -0700145 routeSourceService.start(new InternalRouteListener());
Jonathan Hart41349e92015-02-09 14:14:02 -0800146
Sho SHIMIZU74626412015-09-11 11:46:27 -0700147 bgpUpdatesExecutor.execute(this::doUpdatesThread);
Jonathan Hart335ef462014-10-16 08:20:46 -0700148 }
149
Jonathan Hart41349e92015-02-09 14:14:02 -0800150 @Override
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800151 public void stop() {
Jonathan Hart3930f632015-10-19 12:12:51 -0700152 routeSourceService.stop();
Jonathan Hart41349e92015-02-09 14:14:02 -0800153
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800154 this.hostService.removeListener(hostListener);
155
156 // Stop the thread(s)
Jonathan Hart739c8352014-10-29 17:49:26 -0700157 bgpUpdatesExecutor.shutdownNow();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800158
159 synchronized (this) {
160 // Cleanup all local state
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800161 ribTable4 = new ConcurrentInvertedRadixTree<>(
Pingping Line28ae4c2015-03-13 11:37:03 -0700162 new DefaultByteArrayNodeFactory());
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800163 ribTable6 = new ConcurrentInvertedRadixTree<>(
Pingping Line28ae4c2015-03-13 11:37:03 -0700164 new DefaultByteArrayNodeFactory());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800165 routeUpdatesQueue.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800166 routesWaitingOnArp.clear();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800167 ip2Mac.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800168 }
Jonathan Hart739c8352014-10-29 17:49:26 -0700169 }
170
Jonathan Hart41349e92015-02-09 14:14:02 -0800171 /**
172 * Entry point for route updates.
173 *
174 * @param routeUpdates collection of route updates to process
175 */
176 private void update(Collection<RouteUpdate> routeUpdates) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700177 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800178 routeUpdatesQueue.put(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700179 } catch (InterruptedException e) {
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700180 log.error("Interrupted while putting on routeUpdatesQueue", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700181 Thread.currentThread().interrupt();
182 }
183 }
184
185 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700186 * Thread for handling route updates.
187 */
188 private void doUpdatesThread() {
189 boolean interrupted = false;
190 try {
191 while (!interrupted) {
192 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800193 Collection<RouteUpdate> routeUpdates =
Pingping Line28ae4c2015-03-13 11:37:03 -0700194 routeUpdatesQueue.take();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800195 processRouteUpdates(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700196 } catch (InterruptedException e) {
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700197 log.error("Interrupted while taking from updates queue", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700198 interrupted = true;
199 } catch (Exception e) {
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700200 log.error("exception", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700201 }
202 }
203 } finally {
204 if (interrupted) {
205 Thread.currentThread().interrupt();
206 }
207 }
208 }
209
210 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800211 * Gets all IPv4 routes from the RIB.
212 *
213 * @return all IPv4 routes from the RIB
214 */
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700215 @Override
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800216 public Collection<RouteEntry> getRoutes4() {
217 Iterator<KeyValuePair<RouteEntry>> it =
Pingping Line28ae4c2015-03-13 11:37:03 -0700218 ribTable4.getKeyValuePairsForKeysStartingWith("").iterator();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800219
220 List<RouteEntry> routes = new LinkedList<>();
221
222 while (it.hasNext()) {
223 KeyValuePair<RouteEntry> entry = it.next();
224 routes.add(entry.getValue());
225 }
226
227 return routes;
228 }
229
230 /**
231 * Gets all IPv6 routes from the RIB.
232 *
233 * @return all IPv6 routes from the RIB
234 */
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700235 @Override
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800236 public Collection<RouteEntry> getRoutes6() {
237 Iterator<KeyValuePair<RouteEntry>> it =
Pingping Line28ae4c2015-03-13 11:37:03 -0700238 ribTable6.getKeyValuePairsForKeysStartingWith("").iterator();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800239
240 List<RouteEntry> routes = new LinkedList<>();
241
242 while (it.hasNext()) {
243 KeyValuePair<RouteEntry> entry = it.next();
244 routes.add(entry.getValue());
245 }
246
247 return routes;
248 }
249
250 /**
251 * Finds a route in the RIB for a prefix. The prefix can be either IPv4 or
252 * IPv6.
253 *
254 * @param prefix the prefix to use
255 * @return the route if found, otherwise null
256 */
257 RouteEntry findRibRoute(IpPrefix prefix) {
Pingping Line28ae4c2015-03-13 11:37:03 -0700258 String binaryString = createBinaryString(prefix);
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700259 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800260 // IPv4
261 return ribTable4.getValueForExactKey(binaryString);
262 }
263 // IPv6
264 return ribTable6.getValueForExactKey(binaryString);
265 }
266
267 /**
268 * Adds a route to the RIB. The route can be either IPv4 or IPv6.
269 *
270 * @param routeEntry the route entry to use
271 */
272 void addRibRoute(RouteEntry routeEntry) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700273 if (routeEntry.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800274 // IPv4
Jonathan Hart9a426f82015-09-03 15:43:13 +0200275 ribTable4.put(createBinaryString(routeEntry.prefix()), routeEntry);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800276 } else {
277 // IPv6
Jonathan Hart9a426f82015-09-03 15:43:13 +0200278 ribTable6.put(createBinaryString(routeEntry.prefix()), routeEntry);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800279 }
280 }
281
282 /**
283 * Removes a route for a prefix from the RIB. The prefix can be either IPv4
284 * or IPv6.
285 *
286 * @param prefix the prefix to use
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800287 * @return true if the route was found and removed, otherwise false
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800288 */
289 boolean removeRibRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700290 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800291 // IPv4
Pingping Line28ae4c2015-03-13 11:37:03 -0700292 return ribTable4.remove(createBinaryString(prefix));
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800293 }
294 // IPv6
Pingping Line28ae4c2015-03-13 11:37:03 -0700295 return ribTable6.remove(createBinaryString(prefix));
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800296 }
297
298 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800299 * Processes route updates.
300 *
301 * @param routeUpdates the route updates to process
302 */
303 void processRouteUpdates(Collection<RouteUpdate> routeUpdates) {
304 synchronized (this) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800305 Collection<IpPrefix> withdrawPrefixes = new LinkedList<>();
Jonathan Hart552e31f2015-02-06 11:11:59 -0800306 Collection<FibUpdate> fibUpdates = new LinkedList<>();
307 Collection<FibUpdate> fibWithdraws = new LinkedList<>();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800308
309 for (RouteUpdate update : routeUpdates) {
310 switch (update.type()) {
311 case UPDATE:
Pingping Line28ae4c2015-03-13 11:37:03 -0700312
Jonathan Hart552e31f2015-02-06 11:11:59 -0800313 FibEntry fib = processRouteAdd(update.routeEntry(),
Pingping Line28ae4c2015-03-13 11:37:03 -0700314 withdrawPrefixes);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800315 if (fib != null) {
316 fibUpdates.add(new FibUpdate(FibUpdate.Type.UPDATE, fib));
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800317 }
Jonathan Hart552e31f2015-02-06 11:11:59 -0800318
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800319 break;
320 case DELETE:
321 processRouteDelete(update.routeEntry(), withdrawPrefixes);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800322
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800323 break;
324 default:
325 log.error("Unknown update Type: {}", update.type());
326 break;
327 }
328 }
329
Jonathan Hart552e31f2015-02-06 11:11:59 -0800330 withdrawPrefixes.forEach(p -> fibWithdraws.add(new FibUpdate(
331 FibUpdate.Type.DELETE, new FibEntry(p, null, null))));
332
Jonathan Hart41349e92015-02-09 14:14:02 -0800333 if (!fibUpdates.isEmpty() || !fibWithdraws.isEmpty()) {
334 fibComponent.update(fibUpdates, fibWithdraws);
335 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800336 }
337 }
338
339 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700340 * Processes adding a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700341 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800342 * The route entry is added to the radix tree. If there was an existing
343 * next hop for this prefix, but the next hop was different, then the
344 * old route entry is deleted.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700345 * </p>
Thomas Vachuska4b420772014-10-30 16:46:17 -0700346 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800347 * NOTE: Currently, we don't handle routes if the next hop is within the
348 * SDN domain.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700349 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700350 *
351 * @param routeEntry the route entry to add
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800352 * @param withdrawPrefixes the collection of accumulated prefixes whose
353 * intents will be withdrawn
Jonathan Hart552e31f2015-02-06 11:11:59 -0800354 * @return the corresponding FIB entry change, or null
Jonathan Hart335ef462014-10-16 08:20:46 -0700355 */
Pingping Line28ae4c2015-03-13 11:37:03 -0700356 private FibEntry processRouteAdd(RouteEntry routeEntry,
357 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800358 log.debug("Processing route add: {}", routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700359
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800360 // Find the old next-hop if we are updating an old route entry
361 IpAddress oldNextHop = null;
362 RouteEntry oldRouteEntry = findRibRoute(routeEntry.prefix());
363 if (oldRouteEntry != null) {
364 oldNextHop = oldRouteEntry.nextHop();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800365 }
366
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800367 // Add the new route to the RIB
368 addRibRoute(routeEntry);
369
370 if (oldNextHop != null) {
371 if (oldNextHop.equals(routeEntry.nextHop())) {
372 return null; // No change
373 }
374 //
375 // Update an existing nexthop for the prefix.
376 // We need to remove the old flows for this prefix from the
377 // switches before the new flows are added.
378 //
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800379 withdrawPrefixes.add(routeEntry.prefix());
380 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800381
Pingping Line28ae4c2015-03-13 11:37:03 -0700382 if (routingConfigurationService.isIpPrefixLocal(routeEntry.prefix())) {
383 // Route originated by local SDN domain
384 // We don't handle these here, reactive routing APP will handle
385 // these
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800386 log.debug("Own route {} to {}",
Pingping Line28ae4c2015-03-13 11:37:03 -0700387 routeEntry.prefix(), routeEntry.nextHop());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800388 return null;
389 }
390
391 //
392 // Find the MAC address of next hop router for this route entry.
393 // If the MAC address can not be found in ARP cache, then this prefix
394 // will be put in routesWaitingOnArp queue. Otherwise, generate
395 // a new route intent.
396 //
397
398 // Monitor the IP address for updates of the MAC address
Jonathan Hart31582d12014-10-22 13:52:41 -0700399 hostService.startMonitoringIp(routeEntry.nextHop());
400
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800401 // Check if we know the MAC address of the next hop
402 MacAddress nextHopMacAddress = ip2Mac.get(routeEntry.nextHop());
403 if (nextHopMacAddress == null) {
404 Set<Host> hosts = hostService.getHostsByIp(routeEntry.nextHop());
405 if (!hosts.isEmpty()) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800406 nextHopMacAddress = hosts.iterator().next().mac();
407 }
408 if (nextHopMacAddress != null) {
409 ip2Mac.put(routeEntry.nextHop(), nextHopMacAddress);
410 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700411 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700412 if (nextHopMacAddress == null) {
413 routesWaitingOnArp.put(routeEntry.nextHop(), routeEntry);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800414 return null;
Jonathan Hart335ef462014-10-16 08:20:46 -0700415 }
Jonathan Hart552e31f2015-02-06 11:11:59 -0800416 return new FibEntry(routeEntry.prefix(), routeEntry.nextHop(),
Pingping Line28ae4c2015-03-13 11:37:03 -0700417 nextHopMacAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700418 }
419
420 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800421 * Processes the deletion of a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700422 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800423 * The prefix for the routing entry is removed from radix tree.
424 * If the operation is successful, the prefix is added to the collection
425 * of prefixes whose intents that will be withdrawn.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700426 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700427 *
428 * @param routeEntry the route entry to delete
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800429 * @param withdrawPrefixes the collection of accumulated prefixes whose
430 * intents will be withdrawn
Jonathan Hart335ef462014-10-16 08:20:46 -0700431 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800432 private void processRouteDelete(RouteEntry routeEntry,
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800433 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800434 log.debug("Processing route delete: {}", routeEntry);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800435 boolean isRemoved = removeRibRoute(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700436
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800437 if (isRemoved) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800438 //
439 // Only withdraw intents if an entry was actually removed from the
440 // tree. If no entry was removed, the <prefix, nexthop> wasn't
441 // there so it's probably already been removed and we don't
442 // need to do anything.
443 //
444 withdrawPrefixes.add(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700445 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700446
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800447 routesWaitingOnArp.remove(routeEntry.nextHop(), routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700448 }
449
450 /**
Jonathan Hart31582d12014-10-22 13:52:41 -0700451 * Signals the Router that the MAC to IP mapping has potentially been
452 * updated. This has the effect of updating the MAC address for any
453 * installed prefixes if it has changed, as well as installing any pending
454 * prefixes that were waiting for MAC resolution.
Jonathan Hart335ef462014-10-16 08:20:46 -0700455 *
Jonathan Hart31582d12014-10-22 13:52:41 -0700456 * @param ipAddress the IP address that an event was received for
457 * @param macAddress the most recently known MAC address for the IP address
Jonathan Hart335ef462014-10-16 08:20:46 -0700458 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800459 private void updateMac(IpAddress ipAddress, MacAddress macAddress) {
460 log.debug("Received updated MAC info: {} => {}", ipAddress,
Pingping Line28ae4c2015-03-13 11:37:03 -0700461 macAddress);
Jonathan Hart31582d12014-10-22 13:52:41 -0700462
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800463 //
464 // We synchronize on "this" to prevent changes to the Radix tree
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700465 // while we're pushing intents. If the tree changes, the
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800466 // tree and the intents could get out of sync.
467 //
Jonathan Hart335ef462014-10-16 08:20:46 -0700468 synchronized (this) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800469 Collection<FibUpdate> submitFibEntries = new LinkedList<>();
Jonathan Hart335ef462014-10-16 08:20:46 -0700470
471 Set<RouteEntry> routesToPush =
Pingping Line28ae4c2015-03-13 11:37:03 -0700472 routesWaitingOnArp.removeAll(ipAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700473
474 for (RouteEntry routeEntry : routesToPush) {
475 // These will always be adds
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800476 RouteEntry foundRouteEntry = findRibRoute(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700477 if (foundRouteEntry != null &&
Pingping Line28ae4c2015-03-13 11:37:03 -0700478 foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800479 // We only push FIB updates if the prefix is still in the
480 // radix tree and the next hop is the same as our entry.
Jonathan Hart335ef462014-10-16 08:20:46 -0700481 // The prefix could have been removed while we were waiting
482 // for the ARP, or the next hop could have changed.
Jonathan Hart552e31f2015-02-06 11:11:59 -0800483 submitFibEntries.add(new FibUpdate(FibUpdate.Type.UPDATE,
Pingping Line28ae4c2015-03-13 11:37:03 -0700484 new FibEntry(routeEntry.prefix(),
485 ipAddress, macAddress)));
Jonathan Hart335ef462014-10-16 08:20:46 -0700486 } else {
Jonathan Hart31582d12014-10-22 13:52:41 -0700487 log.debug("{} has been revoked before the MAC was resolved",
Pingping Line28ae4c2015-03-13 11:37:03 -0700488 routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700489 }
490 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800491
Jonathan Hart552e31f2015-02-06 11:11:59 -0800492 if (!submitFibEntries.isEmpty()) {
493 fibComponent.update(submitFibEntries, Collections.emptyList());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800494 }
495
496 ip2Mac.put(ipAddress, macAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700497 }
498 }
499
500 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700501 * Listener for host events.
502 */
503 class InternalHostListener implements HostListener {
504 @Override
505 public void event(HostEvent event) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800506 log.debug("Received HostEvent {}", event);
507
508 Host host = event.subject();
509 switch (event.type()) {
510 case HOST_ADDED:
511 // FALLTHROUGH
512 case HOST_UPDATED:
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800513 for (IpAddress ipAddress : host.ipAddresses()) {
514 updateMac(ipAddress, host.mac());
Jonathan Hart335ef462014-10-16 08:20:46 -0700515 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800516 break;
517 case HOST_REMOVED:
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800518 for (IpAddress ipAddress : host.ipAddresses()) {
519 ip2Mac.remove(ipAddress);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800520 }
521 break;
522 default:
523 break;
Jonathan Hart335ef462014-10-16 08:20:46 -0700524 }
525 }
526 }
Jonathan Hart41349e92015-02-09 14:14:02 -0800527
528 /**
529 * Listener for route events.
530 */
531 private class InternalRouteListener implements RouteListener {
532 @Override
533 public void update(Collection<RouteUpdate> routeUpdates) {
534 Router.this.update(routeUpdates);
535 }
536 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700537
538 @Override
Pingping Line28ae4c2015-03-13 11:37:03 -0700539 public RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress) {
540 RouteEntry routeEntry = null;
541 Iterable<RouteEntry> routeEntries;
542
543 if (ipAddress.isIp4()) {
544 routeEntries = ribTable4.getValuesForKeysPrefixing(
545 createBinaryString(
546 IpPrefix.valueOf(ipAddress, Ip4Address.BIT_LENGTH)));
547 } else {
548 routeEntries = ribTable6.getValuesForKeysPrefixing(
549 createBinaryString(
550 IpPrefix.valueOf(ipAddress, Ip6Address.BIT_LENGTH)));
551 }
552 if (routeEntries == null) {
553 return null;
554 }
555 Iterator<RouteEntry> it = routeEntries.iterator();
556 while (it.hasNext()) {
557 routeEntry = it.next();
558 }
559 return routeEntry;
560 }
561
Jonathan Hart335ef462014-10-16 08:20:46 -0700562}