blob: fcab78e658d559860de778c3a216400c66848677 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 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 Hart552e31f2015-02-06 11:11:59 -080018import 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;
Jonathan Hart552e31f2015-02-06 11:11:59 -080032import org.onlab.packet.IpAddress;
33import org.onlab.packet.IpPrefix;
34import org.onlab.packet.MacAddress;
35import org.onosproject.net.Host;
36import org.onosproject.net.host.HostEvent;
37import org.onosproject.net.host.HostListener;
38import org.onosproject.net.host.HostService;
Jonathan Hart2da1e602015-02-18 19:09:24 -080039import org.onosproject.routing.BgpService;
40import org.onosproject.routing.FibEntry;
41import org.onosproject.routing.FibListener;
42import org.onosproject.routing.FibUpdate;
43import org.onosproject.routing.RouteEntry;
44import org.onosproject.routing.RouteListener;
45import org.onosproject.routing.RouteUpdate;
46import org.onosproject.routing.RoutingService;
Jonathan Hart552e31f2015-02-06 11:11:59 -080047import org.slf4j.Logger;
48import org.slf4j.LoggerFactory;
49
Pingping3855f312014-10-22 12:50:37 -070050import java.util.Collection;
Jonathan Hart552e31f2015-02-06 11:11:59 -080051import java.util.Collections;
Pingping3855f312014-10-22 12:50:37 -070052import java.util.Iterator;
53import java.util.LinkedList;
54import java.util.List;
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080055import java.util.Map;
Pingping3855f312014-10-22 12:50:37 -070056import java.util.Set;
57import java.util.concurrent.BlockingQueue;
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080058import java.util.concurrent.ConcurrentHashMap;
Pingping3855f312014-10-22 12:50:37 -070059import java.util.concurrent.ExecutorService;
60import java.util.concurrent.Executors;
61import java.util.concurrent.LinkedBlockingQueue;
Pingping3855f312014-10-22 12:50:37 -070062
Jonathan Hart41349e92015-02-09 14:14:02 -080063import static com.google.common.base.Preconditions.checkNotNull;
64
Jonathan Hart335ef462014-10-16 08:20:46 -070065/**
Jonathan Hart552e31f2015-02-06 11:11:59 -080066 * This class processes route updates and maintains a Routing Information Base
67 * (RIB). After route updates have been processed and next hops have been
68 * resolved, FIB updates are sent to any listening FIB components.
Jonathan Hart335ef462014-10-16 08:20:46 -070069 */
Jonathan Hart41349e92015-02-09 14:14:02 -080070@Component(immediate = true)
71@Service
72public class Router implements RoutingService {
Jonathan Hart335ef462014-10-16 08:20:46 -070073
74 private static final Logger log = LoggerFactory.getLogger(Router.class);
75
Jonathan Hart552e31f2015-02-06 11:11:59 -080076 // Route entries are stored in a radix tree.
Jonathan Hart0b04bed2014-10-16 16:39:19 -070077 // The key in this tree is the binary string of prefix of the route.
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080078 private InvertedRadixTree<RouteEntry> ribTable4;
79 private InvertedRadixTree<RouteEntry> ribTable6;
Jonathan Hart335ef462014-10-16 08:20:46 -070080
81 // Stores all incoming route updates in a queue.
Jonathan Hart41349e92015-02-09 14:14:02 -080082 private final BlockingQueue<Collection<RouteUpdate>> routeUpdatesQueue
83 = new LinkedBlockingQueue<>();
Jonathan Hart335ef462014-10-16 08:20:46 -070084
Jonathan Hart552e31f2015-02-06 11:11:59 -080085 // Next-hop IP address to route entry mapping for next hops pending MAC resolution
Jonathan Hart41349e92015-02-09 14:14:02 -080086 private SetMultimap<IpAddress, RouteEntry> routesWaitingOnArp;
Jonathan Hart335ef462014-10-16 08:20:46 -070087
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080088 // The IPv4 address to MAC address mapping
Jonathan Hart41349e92015-02-09 14:14:02 -080089 private final Map<IpAddress, MacAddress> ip2Mac = new ConcurrentHashMap<>();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080090
Jonathan Hart41349e92015-02-09 14:14:02 -080091 private FibListener fibComponent;
Jonathan Hart335ef462014-10-16 08:20:46 -070092
Jonathan Hart41349e92015-02-09 14:14:02 -080093 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected HostService hostService;
Jonathan Hart335ef462014-10-16 08:20:46 -070095
Jonathan Hart41349e92015-02-09 14:14:02 -080096 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected BgpService bgpService;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080098
Jonathan Hart41349e92015-02-09 14:14:02 -080099 private ExecutorService bgpUpdatesExecutor;
100 private final HostListener hostListener = new InternalHostListener();
101
102 @Activate
103 public void activate() {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800104 ribTable4 = new ConcurrentInvertedRadixTree<>(
105 new DefaultByteArrayNodeFactory());
106 ribTable6 = new ConcurrentInvertedRadixTree<>(
Jonathan Hart335ef462014-10-16 08:20:46 -0700107 new DefaultByteArrayNodeFactory());
Jonathan Hart335ef462014-10-16 08:20:46 -0700108 routesWaitingOnArp = Multimaps.synchronizedSetMultimap(
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800109 HashMultimap.<IpAddress, RouteEntry>create());
Jonathan Hart335ef462014-10-16 08:20:46 -0700110
111 bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
Pavlin Radoslavov8b752442014-11-18 14:34:37 -0800112 new ThreadFactoryBuilder()
113 .setNameFormat("sdnip-bgp-updates-%d").build());
Jonathan Hart335ef462014-10-16 08:20:46 -0700114 }
115
Jonathan Hart41349e92015-02-09 14:14:02 -0800116 @Deactivate
117 public void deactivate() {
118 log.debug("Stopped");
119 }
120
121 @Override
122 public void start(FibListener listener) {
123 this.fibComponent = checkNotNull(listener);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800124 this.hostService.addListener(hostListener);
125
Jonathan Hartd24fafb2015-02-09 17:55:32 -0800126 bgpService.start(new InternalRouteListener());
Jonathan Hart41349e92015-02-09 14:14:02 -0800127
Jonathan Hart335ef462014-10-16 08:20:46 -0700128 bgpUpdatesExecutor.execute(new Runnable() {
129 @Override
130 public void run() {
131 doUpdatesThread();
132 }
133 });
Jonathan Hart335ef462014-10-16 08:20:46 -0700134 }
135
Jonathan Hart41349e92015-02-09 14:14:02 -0800136 @Override
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800137 public void stop() {
Jonathan Hart41349e92015-02-09 14:14:02 -0800138 bgpService.stop();
139
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800140 this.hostService.removeListener(hostListener);
141
142 // Stop the thread(s)
Jonathan Hart739c8352014-10-29 17:49:26 -0700143 bgpUpdatesExecutor.shutdownNow();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800144
145 synchronized (this) {
146 // Cleanup all local state
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800147 ribTable4 = new ConcurrentInvertedRadixTree<>(
148 new DefaultByteArrayNodeFactory());
149 ribTable6 = new ConcurrentInvertedRadixTree<>(
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800150 new DefaultByteArrayNodeFactory());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800151 routeUpdatesQueue.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800152 routesWaitingOnArp.clear();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800153 ip2Mac.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800154 }
Jonathan Hart739c8352014-10-29 17:49:26 -0700155 }
156
Jonathan Hart41349e92015-02-09 14:14:02 -0800157 /**
158 * Entry point for route updates.
159 *
160 * @param routeUpdates collection of route updates to process
161 */
162 private void update(Collection<RouteUpdate> routeUpdates) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700163 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800164 routeUpdatesQueue.put(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700165 } catch (InterruptedException e) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800166 log.debug("Interrupted while putting on routeUpdatesQueue", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700167 Thread.currentThread().interrupt();
168 }
169 }
170
171 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700172 * Thread for handling route updates.
173 */
174 private void doUpdatesThread() {
175 boolean interrupted = false;
176 try {
177 while (!interrupted) {
178 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800179 Collection<RouteUpdate> routeUpdates =
180 routeUpdatesQueue.take();
181 processRouteUpdates(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700182 } catch (InterruptedException e) {
183 log.debug("Interrupted while taking from updates queue", e);
184 interrupted = true;
185 } catch (Exception e) {
186 log.debug("exception", e);
187 }
188 }
189 } finally {
190 if (interrupted) {
191 Thread.currentThread().interrupt();
192 }
193 }
194 }
195
196 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800197 * Gets all IPv4 routes from the RIB.
198 *
199 * @return all IPv4 routes from the RIB
200 */
201 public Collection<RouteEntry> getRoutes4() {
202 Iterator<KeyValuePair<RouteEntry>> it =
203 ribTable4.getKeyValuePairsForKeysStartingWith("").iterator();
204
205 List<RouteEntry> routes = new LinkedList<>();
206
207 while (it.hasNext()) {
208 KeyValuePair<RouteEntry> entry = it.next();
209 routes.add(entry.getValue());
210 }
211
212 return routes;
213 }
214
215 /**
216 * Gets all IPv6 routes from the RIB.
217 *
218 * @return all IPv6 routes from the RIB
219 */
220 public Collection<RouteEntry> getRoutes6() {
221 Iterator<KeyValuePair<RouteEntry>> it =
222 ribTable6.getKeyValuePairsForKeysStartingWith("").iterator();
223
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 * Finds a route in the RIB for a prefix. The prefix can be either IPv4 or
236 * IPv6.
237 *
238 * @param prefix the prefix to use
239 * @return the route if found, otherwise null
240 */
241 RouteEntry findRibRoute(IpPrefix prefix) {
242 String binaryString = RouteEntry.createBinaryString(prefix);
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700243 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800244 // IPv4
245 return ribTable4.getValueForExactKey(binaryString);
246 }
247 // IPv6
248 return ribTable6.getValueForExactKey(binaryString);
249 }
250
251 /**
252 * Adds a route to the RIB. The route can be either IPv4 or IPv6.
253 *
254 * @param routeEntry the route entry to use
255 */
256 void addRibRoute(RouteEntry routeEntry) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700257 if (routeEntry.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800258 // IPv4
259 ribTable4.put(RouteEntry.createBinaryString(routeEntry.prefix()),
260 routeEntry);
261 } else {
262 // IPv6
263 ribTable6.put(RouteEntry.createBinaryString(routeEntry.prefix()),
264 routeEntry);
265 }
266 }
267
268 /**
269 * Removes a route for a prefix from the RIB. The prefix can be either IPv4
270 * or IPv6.
271 *
272 * @param prefix the prefix to use
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800273 * @return true if the route was found and removed, otherwise false
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800274 */
275 boolean removeRibRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700276 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800277 // IPv4
278 return ribTable4.remove(RouteEntry.createBinaryString(prefix));
279 }
280 // IPv6
281 return ribTable6.remove(RouteEntry.createBinaryString(prefix));
282 }
283
284 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800285 * Processes route updates.
286 *
287 * @param routeUpdates the route updates to process
288 */
289 void processRouteUpdates(Collection<RouteUpdate> routeUpdates) {
290 synchronized (this) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800291 Collection<IpPrefix> withdrawPrefixes = new LinkedList<>();
Jonathan Hart552e31f2015-02-06 11:11:59 -0800292 Collection<FibUpdate> fibUpdates = new LinkedList<>();
293 Collection<FibUpdate> fibWithdraws = new LinkedList<>();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800294
295 for (RouteUpdate update : routeUpdates) {
296 switch (update.type()) {
297 case UPDATE:
Jonathan Hart552e31f2015-02-06 11:11:59 -0800298 FibEntry fib = processRouteAdd(update.routeEntry(),
299 withdrawPrefixes);
300 if (fib != null) {
301 fibUpdates.add(new FibUpdate(FibUpdate.Type.UPDATE, fib));
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800302 }
Jonathan Hart552e31f2015-02-06 11:11:59 -0800303
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800304 break;
305 case DELETE:
306 processRouteDelete(update.routeEntry(), withdrawPrefixes);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800307
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800308 break;
309 default:
310 log.error("Unknown update Type: {}", update.type());
311 break;
312 }
313 }
314
Jonathan Hart552e31f2015-02-06 11:11:59 -0800315 withdrawPrefixes.forEach(p -> fibWithdraws.add(new FibUpdate(
316 FibUpdate.Type.DELETE, new FibEntry(p, null, null))));
317
Jonathan Hart41349e92015-02-09 14:14:02 -0800318 if (!fibUpdates.isEmpty() || !fibWithdraws.isEmpty()) {
319 fibComponent.update(fibUpdates, fibWithdraws);
320 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800321 }
322 }
323
324 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700325 * Processes adding a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700326 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800327 * The route entry is added to the radix tree. If there was an existing
328 * next hop for this prefix, but the next hop was different, then the
329 * old route entry is deleted.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700330 * </p>
Thomas Vachuska4b420772014-10-30 16:46:17 -0700331 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800332 * NOTE: Currently, we don't handle routes if the next hop is within the
333 * SDN domain.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700334 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700335 *
336 * @param routeEntry the route entry to add
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800337 * @param withdrawPrefixes the collection of accumulated prefixes whose
338 * intents will be withdrawn
Jonathan Hart552e31f2015-02-06 11:11:59 -0800339 * @return the corresponding FIB entry change, or null
Jonathan Hart335ef462014-10-16 08:20:46 -0700340 */
Jonathan Hart552e31f2015-02-06 11:11:59 -0800341 private FibEntry processRouteAdd(
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800342 RouteEntry routeEntry,
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800343 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800344 log.debug("Processing route add: {}", routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700345
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800346 // Find the old next-hop if we are updating an old route entry
347 IpAddress oldNextHop = null;
348 RouteEntry oldRouteEntry = findRibRoute(routeEntry.prefix());
349 if (oldRouteEntry != null) {
350 oldNextHop = oldRouteEntry.nextHop();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800351 }
352
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800353 // Add the new route to the RIB
354 addRibRoute(routeEntry);
355
356 if (oldNextHop != null) {
357 if (oldNextHop.equals(routeEntry.nextHop())) {
358 return null; // No change
359 }
360 //
361 // Update an existing nexthop for the prefix.
362 // We need to remove the old flows for this prefix from the
363 // switches before the new flows are added.
364 //
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800365 withdrawPrefixes.add(routeEntry.prefix());
366 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800367
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800368 if (routeEntry.nextHop().isZero()) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800369 // Route originated by SDN domain
370 // We don't handle these at the moment
371 log.debug("Own route {} to {}",
372 routeEntry.prefix(), routeEntry.nextHop());
373 return null;
374 }
375
376 //
377 // Find the MAC address of next hop router for this route entry.
378 // If the MAC address can not be found in ARP cache, then this prefix
379 // will be put in routesWaitingOnArp queue. Otherwise, generate
380 // a new route intent.
381 //
382
383 // Monitor the IP address for updates of the MAC address
Jonathan Hart31582d12014-10-22 13:52:41 -0700384 hostService.startMonitoringIp(routeEntry.nextHop());
385
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800386 // Check if we know the MAC address of the next hop
387 MacAddress nextHopMacAddress = ip2Mac.get(routeEntry.nextHop());
388 if (nextHopMacAddress == null) {
389 Set<Host> hosts = hostService.getHostsByIp(routeEntry.nextHop());
390 if (!hosts.isEmpty()) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800391 nextHopMacAddress = hosts.iterator().next().mac();
392 }
393 if (nextHopMacAddress != null) {
394 ip2Mac.put(routeEntry.nextHop(), nextHopMacAddress);
395 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700396 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700397 if (nextHopMacAddress == null) {
398 routesWaitingOnArp.put(routeEntry.nextHop(), routeEntry);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800399 return null;
Jonathan Hart335ef462014-10-16 08:20:46 -0700400 }
Jonathan Hart552e31f2015-02-06 11:11:59 -0800401 return new FibEntry(routeEntry.prefix(), routeEntry.nextHop(),
402 nextHopMacAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700403 }
404
405 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800406 * Processes the deletion of a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700407 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800408 * The prefix for the routing entry is removed from radix tree.
409 * If the operation is successful, the prefix is added to the collection
410 * of prefixes whose intents that will be withdrawn.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700411 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700412 *
413 * @param routeEntry the route entry to delete
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800414 * @param withdrawPrefixes the collection of accumulated prefixes whose
415 * intents will be withdrawn
Jonathan Hart335ef462014-10-16 08:20:46 -0700416 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800417 private void processRouteDelete(RouteEntry routeEntry,
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800418 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800419 log.debug("Processing route delete: {}", routeEntry);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800420 boolean isRemoved = removeRibRoute(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700421
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800422 if (isRemoved) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800423 //
424 // Only withdraw intents if an entry was actually removed from the
425 // tree. If no entry was removed, the <prefix, nexthop> wasn't
426 // there so it's probably already been removed and we don't
427 // need to do anything.
428 //
429 withdrawPrefixes.add(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700430 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700431
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800432 routesWaitingOnArp.remove(routeEntry.nextHop(), routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700433 }
434
435 /**
Jonathan Hart31582d12014-10-22 13:52:41 -0700436 * Signals the Router that the MAC to IP mapping has potentially been
437 * updated. This has the effect of updating the MAC address for any
438 * installed prefixes if it has changed, as well as installing any pending
439 * prefixes that were waiting for MAC resolution.
Jonathan Hart335ef462014-10-16 08:20:46 -0700440 *
Jonathan Hart31582d12014-10-22 13:52:41 -0700441 * @param ipAddress the IP address that an event was received for
442 * @param macAddress the most recently known MAC address for the IP address
Jonathan Hart335ef462014-10-16 08:20:46 -0700443 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800444 private void updateMac(IpAddress ipAddress, MacAddress macAddress) {
445 log.debug("Received updated MAC info: {} => {}", ipAddress,
446 macAddress);
Jonathan Hart31582d12014-10-22 13:52:41 -0700447
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800448 //
449 // We synchronize on "this" to prevent changes to the Radix tree
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700450 // while we're pushing intents. If the tree changes, the
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800451 // tree and the intents could get out of sync.
452 //
Jonathan Hart335ef462014-10-16 08:20:46 -0700453 synchronized (this) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800454 Collection<FibUpdate> submitFibEntries = new LinkedList<>();
Jonathan Hart335ef462014-10-16 08:20:46 -0700455
456 Set<RouteEntry> routesToPush =
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800457 routesWaitingOnArp.removeAll(ipAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700458
459 for (RouteEntry routeEntry : routesToPush) {
460 // These will always be adds
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800461 RouteEntry foundRouteEntry = findRibRoute(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700462 if (foundRouteEntry != null &&
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800463 foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800464 // We only push FIB updates if the prefix is still in the
465 // radix tree and the next hop is the same as our entry.
Jonathan Hart335ef462014-10-16 08:20:46 -0700466 // The prefix could have been removed while we were waiting
467 // for the ARP, or the next hop could have changed.
Jonathan Hart552e31f2015-02-06 11:11:59 -0800468 submitFibEntries.add(new FibUpdate(FibUpdate.Type.UPDATE,
469 new FibEntry(routeEntry.prefix(),
470 ipAddress, macAddress)));
Jonathan Hart335ef462014-10-16 08:20:46 -0700471 } else {
Jonathan Hart31582d12014-10-22 13:52:41 -0700472 log.debug("{} has been revoked before the MAC was resolved",
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800473 routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700474 }
475 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800476
Jonathan Hart552e31f2015-02-06 11:11:59 -0800477 if (!submitFibEntries.isEmpty()) {
478 fibComponent.update(submitFibEntries, Collections.emptyList());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800479 }
480
481 ip2Mac.put(ipAddress, macAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700482 }
483 }
484
485 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700486 * Listener for host events.
487 */
488 class InternalHostListener implements HostListener {
489 @Override
490 public void event(HostEvent event) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800491 log.debug("Received HostEvent {}", event);
492
493 Host host = event.subject();
494 switch (event.type()) {
495 case HOST_ADDED:
496 // FALLTHROUGH
497 case HOST_UPDATED:
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800498 for (IpAddress ipAddress : host.ipAddresses()) {
499 updateMac(ipAddress, host.mac());
Jonathan Hart335ef462014-10-16 08:20:46 -0700500 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800501 break;
502 case HOST_REMOVED:
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800503 for (IpAddress ipAddress : host.ipAddresses()) {
504 ip2Mac.remove(ipAddress);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800505 }
506 break;
507 default:
508 break;
Jonathan Hart335ef462014-10-16 08:20:46 -0700509 }
510 }
511 }
Jonathan Hart41349e92015-02-09 14:14:02 -0800512
513 /**
514 * Listener for route events.
515 */
516 private class InternalRouteListener implements RouteListener {
517 @Override
518 public void update(Collection<RouteUpdate> routeUpdates) {
519 Router.this.update(routeUpdates);
520 }
521 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700522}