blob: 71990a1cb00cc34a6a0da0f3624ad7bcbfddf87e [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
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -070018import static com.google.common.base.Preconditions.checkNotNull;
19
20import java.util.Collection;
21import java.util.Collections;
22import java.util.Iterator;
23import java.util.LinkedList;
24import java.util.List;
25import java.util.Map;
26import java.util.Set;
27import java.util.concurrent.BlockingQueue;
28import java.util.concurrent.ConcurrentHashMap;
29import java.util.concurrent.ExecutorService;
30import java.util.concurrent.Executors;
31import java.util.concurrent.LinkedBlockingQueue;
32
Jonathan Hart41349e92015-02-09 14:14:02 -080033import org.apache.felix.scr.annotations.Activate;
34import org.apache.felix.scr.annotations.Component;
35import org.apache.felix.scr.annotations.Deactivate;
36import org.apache.felix.scr.annotations.Reference;
37import org.apache.felix.scr.annotations.ReferenceCardinality;
38import org.apache.felix.scr.annotations.Service;
Jonathan Hart552e31f2015-02-06 11:11:59 -080039import org.onlab.packet.IpAddress;
40import org.onlab.packet.IpPrefix;
41import org.onlab.packet.MacAddress;
42import org.onosproject.net.Host;
43import org.onosproject.net.host.HostEvent;
44import org.onosproject.net.host.HostListener;
45import org.onosproject.net.host.HostService;
Jonathan Hart2da1e602015-02-18 19:09:24 -080046import org.onosproject.routing.BgpService;
47import org.onosproject.routing.FibEntry;
48import org.onosproject.routing.FibListener;
49import org.onosproject.routing.FibUpdate;
50import org.onosproject.routing.RouteEntry;
51import org.onosproject.routing.RouteListener;
52import org.onosproject.routing.RouteUpdate;
53import org.onosproject.routing.RoutingService;
Jonathan Hart552e31f2015-02-06 11:11:59 -080054import org.slf4j.Logger;
55import org.slf4j.LoggerFactory;
56
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -070057import com.google.common.collect.HashMultimap;
58import com.google.common.collect.Multimaps;
59import com.google.common.collect.SetMultimap;
60import com.google.common.util.concurrent.ThreadFactoryBuilder;
61import com.googlecode.concurrenttrees.common.KeyValuePair;
62import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
63import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
64import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
Jonathan Hart41349e92015-02-09 14:14:02 -080065
Jonathan Hart335ef462014-10-16 08:20:46 -070066/**
Jonathan Hart552e31f2015-02-06 11:11:59 -080067 * This class processes route updates and maintains a Routing Information Base
68 * (RIB). After route updates have been processed and next hops have been
69 * resolved, FIB updates are sent to any listening FIB components.
Jonathan Hart335ef462014-10-16 08:20:46 -070070 */
Jonathan Hart41349e92015-02-09 14:14:02 -080071@Component(immediate = true)
72@Service
73public class Router implements RoutingService {
Jonathan Hart335ef462014-10-16 08:20:46 -070074
75 private static final Logger log = LoggerFactory.getLogger(Router.class);
76
Jonathan Hart552e31f2015-02-06 11:11:59 -080077 // Route entries are stored in a radix tree.
Jonathan Hart0b04bed2014-10-16 16:39:19 -070078 // The key in this tree is the binary string of prefix of the route.
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080079 private InvertedRadixTree<RouteEntry> ribTable4;
80 private InvertedRadixTree<RouteEntry> ribTable6;
Jonathan Hart335ef462014-10-16 08:20:46 -070081
82 // Stores all incoming route updates in a queue.
Jonathan Hart41349e92015-02-09 14:14:02 -080083 private final BlockingQueue<Collection<RouteUpdate>> routeUpdatesQueue
84 = new LinkedBlockingQueue<>();
Jonathan Hart335ef462014-10-16 08:20:46 -070085
Jonathan Hart552e31f2015-02-06 11:11:59 -080086 // Next-hop IP address to route entry mapping for next hops pending MAC resolution
Jonathan Hart41349e92015-02-09 14:14:02 -080087 private SetMultimap<IpAddress, RouteEntry> routesWaitingOnArp;
Jonathan Hart335ef462014-10-16 08:20:46 -070088
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080089 // The IPv4 address to MAC address mapping
Jonathan Hart41349e92015-02-09 14:14:02 -080090 private final Map<IpAddress, MacAddress> ip2Mac = new ConcurrentHashMap<>();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080091
Jonathan Hart41349e92015-02-09 14:14:02 -080092 private FibListener fibComponent;
Jonathan Hart335ef462014-10-16 08:20:46 -070093
Jonathan Hart41349e92015-02-09 14:14:02 -080094 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected HostService hostService;
Jonathan Hart335ef462014-10-16 08:20:46 -070096
Jonathan Hart41349e92015-02-09 14:14:02 -080097 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected BgpService bgpService;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080099
Jonathan Hart41349e92015-02-09 14:14:02 -0800100 private ExecutorService bgpUpdatesExecutor;
101 private final HostListener hostListener = new InternalHostListener();
102
103 @Activate
104 public void activate() {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800105 ribTable4 = new ConcurrentInvertedRadixTree<>(
106 new DefaultByteArrayNodeFactory());
107 ribTable6 = new ConcurrentInvertedRadixTree<>(
Jonathan Hart335ef462014-10-16 08:20:46 -0700108 new DefaultByteArrayNodeFactory());
Jonathan Hart335ef462014-10-16 08:20:46 -0700109 routesWaitingOnArp = Multimaps.synchronizedSetMultimap(
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800110 HashMultimap.<IpAddress, RouteEntry>create());
Jonathan Hart335ef462014-10-16 08:20:46 -0700111
112 bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
Pavlin Radoslavov8b752442014-11-18 14:34:37 -0800113 new ThreadFactoryBuilder()
114 .setNameFormat("sdnip-bgp-updates-%d").build());
Jonathan Hart335ef462014-10-16 08:20:46 -0700115 }
116
Jonathan Hart41349e92015-02-09 14:14:02 -0800117 @Deactivate
118 public void deactivate() {
119 log.debug("Stopped");
120 }
121
122 @Override
123 public void start(FibListener listener) {
124 this.fibComponent = checkNotNull(listener);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800125 this.hostService.addListener(hostListener);
126
Jonathan Hartd24fafb2015-02-09 17:55:32 -0800127 bgpService.start(new InternalRouteListener());
Jonathan Hart41349e92015-02-09 14:14:02 -0800128
Jonathan Hart335ef462014-10-16 08:20:46 -0700129 bgpUpdatesExecutor.execute(new Runnable() {
130 @Override
131 public void run() {
132 doUpdatesThread();
133 }
134 });
Jonathan Hart335ef462014-10-16 08:20:46 -0700135 }
136
Jonathan Hart41349e92015-02-09 14:14:02 -0800137 @Override
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800138 public void stop() {
Jonathan Hart41349e92015-02-09 14:14:02 -0800139 bgpService.stop();
140
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800141 this.hostService.removeListener(hostListener);
142
143 // Stop the thread(s)
Jonathan Hart739c8352014-10-29 17:49:26 -0700144 bgpUpdatesExecutor.shutdownNow();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800145
146 synchronized (this) {
147 // Cleanup all local state
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800148 ribTable4 = new ConcurrentInvertedRadixTree<>(
149 new DefaultByteArrayNodeFactory());
150 ribTable6 = new ConcurrentInvertedRadixTree<>(
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800151 new DefaultByteArrayNodeFactory());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800152 routeUpdatesQueue.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800153 routesWaitingOnArp.clear();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800154 ip2Mac.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800155 }
Jonathan Hart739c8352014-10-29 17:49:26 -0700156 }
157
Jonathan Hart41349e92015-02-09 14:14:02 -0800158 /**
159 * Entry point for route updates.
160 *
161 * @param routeUpdates collection of route updates to process
162 */
163 private void update(Collection<RouteUpdate> routeUpdates) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700164 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800165 routeUpdatesQueue.put(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700166 } catch (InterruptedException e) {
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700167 log.error("Interrupted while putting on routeUpdatesQueue", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700168 Thread.currentThread().interrupt();
169 }
170 }
171
172 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700173 * Thread for handling route updates.
174 */
175 private void doUpdatesThread() {
176 boolean interrupted = false;
177 try {
178 while (!interrupted) {
179 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800180 Collection<RouteUpdate> routeUpdates =
181 routeUpdatesQueue.take();
182 processRouteUpdates(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700183 } catch (InterruptedException e) {
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700184 log.error("Interrupted while taking from updates queue", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700185 interrupted = true;
186 } catch (Exception e) {
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700187 log.error("exception", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700188 }
189 }
190 } finally {
191 if (interrupted) {
192 Thread.currentThread().interrupt();
193 }
194 }
195 }
196
197 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800198 * Gets all IPv4 routes from the RIB.
199 *
200 * @return all IPv4 routes from the RIB
201 */
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700202 @Override
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800203 public Collection<RouteEntry> getRoutes4() {
204 Iterator<KeyValuePair<RouteEntry>> it =
205 ribTable4.getKeyValuePairsForKeysStartingWith("").iterator();
206
207 List<RouteEntry> routes = new LinkedList<>();
208
209 while (it.hasNext()) {
210 KeyValuePair<RouteEntry> entry = it.next();
211 routes.add(entry.getValue());
212 }
213
214 return routes;
215 }
216
217 /**
218 * Gets all IPv6 routes from the RIB.
219 *
220 * @return all IPv6 routes from the RIB
221 */
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700222 @Override
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800223 public Collection<RouteEntry> getRoutes6() {
224 Iterator<KeyValuePair<RouteEntry>> it =
225 ribTable6.getKeyValuePairsForKeysStartingWith("").iterator();
226
227 List<RouteEntry> routes = new LinkedList<>();
228
229 while (it.hasNext()) {
230 KeyValuePair<RouteEntry> entry = it.next();
231 routes.add(entry.getValue());
232 }
233
234 return routes;
235 }
236
237 /**
238 * Finds a route in the RIB for a prefix. The prefix can be either IPv4 or
239 * IPv6.
240 *
241 * @param prefix the prefix to use
242 * @return the route if found, otherwise null
243 */
244 RouteEntry findRibRoute(IpPrefix prefix) {
245 String binaryString = RouteEntry.createBinaryString(prefix);
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700246 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800247 // IPv4
248 return ribTable4.getValueForExactKey(binaryString);
249 }
250 // IPv6
251 return ribTable6.getValueForExactKey(binaryString);
252 }
253
254 /**
255 * Adds a route to the RIB. The route can be either IPv4 or IPv6.
256 *
257 * @param routeEntry the route entry to use
258 */
259 void addRibRoute(RouteEntry routeEntry) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700260 if (routeEntry.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800261 // IPv4
262 ribTable4.put(RouteEntry.createBinaryString(routeEntry.prefix()),
263 routeEntry);
264 } else {
265 // IPv6
266 ribTable6.put(RouteEntry.createBinaryString(routeEntry.prefix()),
267 routeEntry);
268 }
269 }
270
271 /**
272 * Removes a route for a prefix from the RIB. The prefix can be either IPv4
273 * or IPv6.
274 *
275 * @param prefix the prefix to use
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800276 * @return true if the route was found and removed, otherwise false
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800277 */
278 boolean removeRibRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700279 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800280 // IPv4
281 return ribTable4.remove(RouteEntry.createBinaryString(prefix));
282 }
283 // IPv6
284 return ribTable6.remove(RouteEntry.createBinaryString(prefix));
285 }
286
287 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800288 * Processes route updates.
289 *
290 * @param routeUpdates the route updates to process
291 */
292 void processRouteUpdates(Collection<RouteUpdate> routeUpdates) {
293 synchronized (this) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800294 Collection<IpPrefix> withdrawPrefixes = new LinkedList<>();
Jonathan Hart552e31f2015-02-06 11:11:59 -0800295 Collection<FibUpdate> fibUpdates = new LinkedList<>();
296 Collection<FibUpdate> fibWithdraws = new LinkedList<>();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800297
298 for (RouteUpdate update : routeUpdates) {
299 switch (update.type()) {
300 case UPDATE:
Jonathan Hart552e31f2015-02-06 11:11:59 -0800301 FibEntry fib = processRouteAdd(update.routeEntry(),
302 withdrawPrefixes);
303 if (fib != null) {
304 fibUpdates.add(new FibUpdate(FibUpdate.Type.UPDATE, fib));
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800305 }
Jonathan Hart552e31f2015-02-06 11:11:59 -0800306
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800307 break;
308 case DELETE:
309 processRouteDelete(update.routeEntry(), withdrawPrefixes);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800310
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800311 break;
312 default:
313 log.error("Unknown update Type: {}", update.type());
314 break;
315 }
316 }
317
Jonathan Hart552e31f2015-02-06 11:11:59 -0800318 withdrawPrefixes.forEach(p -> fibWithdraws.add(new FibUpdate(
319 FibUpdate.Type.DELETE, new FibEntry(p, null, null))));
320
Jonathan Hart41349e92015-02-09 14:14:02 -0800321 if (!fibUpdates.isEmpty() || !fibWithdraws.isEmpty()) {
322 fibComponent.update(fibUpdates, fibWithdraws);
323 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800324 }
325 }
326
327 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700328 * Processes adding a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700329 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800330 * The route entry is added to the radix tree. If there was an existing
331 * next hop for this prefix, but the next hop was different, then the
332 * old route entry is deleted.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700333 * </p>
Thomas Vachuska4b420772014-10-30 16:46:17 -0700334 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800335 * NOTE: Currently, we don't handle routes if the next hop is within the
336 * SDN domain.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700337 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700338 *
339 * @param routeEntry the route entry to add
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800340 * @param withdrawPrefixes the collection of accumulated prefixes whose
341 * intents will be withdrawn
Jonathan Hart552e31f2015-02-06 11:11:59 -0800342 * @return the corresponding FIB entry change, or null
Jonathan Hart335ef462014-10-16 08:20:46 -0700343 */
Jonathan Hart552e31f2015-02-06 11:11:59 -0800344 private FibEntry processRouteAdd(
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800345 RouteEntry routeEntry,
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800346 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800347 log.debug("Processing route add: {}", routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700348
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800349 // Find the old next-hop if we are updating an old route entry
350 IpAddress oldNextHop = null;
351 RouteEntry oldRouteEntry = findRibRoute(routeEntry.prefix());
352 if (oldRouteEntry != null) {
353 oldNextHop = oldRouteEntry.nextHop();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800354 }
355
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800356 // Add the new route to the RIB
357 addRibRoute(routeEntry);
358
359 if (oldNextHop != null) {
360 if (oldNextHop.equals(routeEntry.nextHop())) {
361 return null; // No change
362 }
363 //
364 // Update an existing nexthop for the prefix.
365 // We need to remove the old flows for this prefix from the
366 // switches before the new flows are added.
367 //
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800368 withdrawPrefixes.add(routeEntry.prefix());
369 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800370
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800371 if (routeEntry.nextHop().isZero()) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800372 // Route originated by SDN domain
373 // We don't handle these at the moment
374 log.debug("Own route {} to {}",
375 routeEntry.prefix(), routeEntry.nextHop());
376 return null;
377 }
378
379 //
380 // Find the MAC address of next hop router for this route entry.
381 // If the MAC address can not be found in ARP cache, then this prefix
382 // will be put in routesWaitingOnArp queue. Otherwise, generate
383 // a new route intent.
384 //
385
386 // Monitor the IP address for updates of the MAC address
Jonathan Hart31582d12014-10-22 13:52:41 -0700387 hostService.startMonitoringIp(routeEntry.nextHop());
388
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800389 // Check if we know the MAC address of the next hop
390 MacAddress nextHopMacAddress = ip2Mac.get(routeEntry.nextHop());
391 if (nextHopMacAddress == null) {
392 Set<Host> hosts = hostService.getHostsByIp(routeEntry.nextHop());
393 if (!hosts.isEmpty()) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800394 nextHopMacAddress = hosts.iterator().next().mac();
395 }
396 if (nextHopMacAddress != null) {
397 ip2Mac.put(routeEntry.nextHop(), nextHopMacAddress);
398 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700399 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700400 if (nextHopMacAddress == null) {
401 routesWaitingOnArp.put(routeEntry.nextHop(), routeEntry);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800402 return null;
Jonathan Hart335ef462014-10-16 08:20:46 -0700403 }
Jonathan Hart552e31f2015-02-06 11:11:59 -0800404 return new FibEntry(routeEntry.prefix(), routeEntry.nextHop(),
405 nextHopMacAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700406 }
407
408 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800409 * Processes the deletion of a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700410 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800411 * The prefix for the routing entry is removed from radix tree.
412 * If the operation is successful, the prefix is added to the collection
413 * of prefixes whose intents that will be withdrawn.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700414 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700415 *
416 * @param routeEntry the route entry to delete
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800417 * @param withdrawPrefixes the collection of accumulated prefixes whose
418 * intents will be withdrawn
Jonathan Hart335ef462014-10-16 08:20:46 -0700419 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800420 private void processRouteDelete(RouteEntry routeEntry,
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800421 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800422 log.debug("Processing route delete: {}", routeEntry);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800423 boolean isRemoved = removeRibRoute(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700424
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800425 if (isRemoved) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800426 //
427 // Only withdraw intents if an entry was actually removed from the
428 // tree. If no entry was removed, the <prefix, nexthop> wasn't
429 // there so it's probably already been removed and we don't
430 // need to do anything.
431 //
432 withdrawPrefixes.add(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700433 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700434
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800435 routesWaitingOnArp.remove(routeEntry.nextHop(), routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700436 }
437
438 /**
Jonathan Hart31582d12014-10-22 13:52:41 -0700439 * Signals the Router that the MAC to IP mapping has potentially been
440 * updated. This has the effect of updating the MAC address for any
441 * installed prefixes if it has changed, as well as installing any pending
442 * prefixes that were waiting for MAC resolution.
Jonathan Hart335ef462014-10-16 08:20:46 -0700443 *
Jonathan Hart31582d12014-10-22 13:52:41 -0700444 * @param ipAddress the IP address that an event was received for
445 * @param macAddress the most recently known MAC address for the IP address
Jonathan Hart335ef462014-10-16 08:20:46 -0700446 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800447 private void updateMac(IpAddress ipAddress, MacAddress macAddress) {
448 log.debug("Received updated MAC info: {} => {}", ipAddress,
449 macAddress);
Jonathan Hart31582d12014-10-22 13:52:41 -0700450
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800451 //
452 // We synchronize on "this" to prevent changes to the Radix tree
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700453 // while we're pushing intents. If the tree changes, the
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800454 // tree and the intents could get out of sync.
455 //
Jonathan Hart335ef462014-10-16 08:20:46 -0700456 synchronized (this) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800457 Collection<FibUpdate> submitFibEntries = new LinkedList<>();
Jonathan Hart335ef462014-10-16 08:20:46 -0700458
459 Set<RouteEntry> routesToPush =
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800460 routesWaitingOnArp.removeAll(ipAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700461
462 for (RouteEntry routeEntry : routesToPush) {
463 // These will always be adds
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800464 RouteEntry foundRouteEntry = findRibRoute(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700465 if (foundRouteEntry != null &&
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800466 foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800467 // We only push FIB updates if the prefix is still in the
468 // radix tree and the next hop is the same as our entry.
Jonathan Hart335ef462014-10-16 08:20:46 -0700469 // The prefix could have been removed while we were waiting
470 // for the ARP, or the next hop could have changed.
Jonathan Hart552e31f2015-02-06 11:11:59 -0800471 submitFibEntries.add(new FibUpdate(FibUpdate.Type.UPDATE,
472 new FibEntry(routeEntry.prefix(),
473 ipAddress, macAddress)));
Jonathan Hart335ef462014-10-16 08:20:46 -0700474 } else {
Jonathan Hart31582d12014-10-22 13:52:41 -0700475 log.debug("{} has been revoked before the MAC was resolved",
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800476 routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700477 }
478 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800479
Jonathan Hart552e31f2015-02-06 11:11:59 -0800480 if (!submitFibEntries.isEmpty()) {
481 fibComponent.update(submitFibEntries, Collections.emptyList());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800482 }
483
484 ip2Mac.put(ipAddress, macAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700485 }
486 }
487
488 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700489 * Listener for host events.
490 */
491 class InternalHostListener implements HostListener {
492 @Override
493 public void event(HostEvent event) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800494 log.debug("Received HostEvent {}", event);
495
496 Host host = event.subject();
497 switch (event.type()) {
498 case HOST_ADDED:
499 // FALLTHROUGH
500 case HOST_UPDATED:
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800501 for (IpAddress ipAddress : host.ipAddresses()) {
502 updateMac(ipAddress, host.mac());
Jonathan Hart335ef462014-10-16 08:20:46 -0700503 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800504 break;
505 case HOST_REMOVED:
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800506 for (IpAddress ipAddress : host.ipAddresses()) {
507 ip2Mac.remove(ipAddress);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800508 }
509 break;
510 default:
511 break;
Jonathan Hart335ef462014-10-16 08:20:46 -0700512 }
513 }
514 }
Jonathan Hart41349e92015-02-09 14:14:02 -0800515
516 /**
517 * Listener for route events.
518 */
519 private class InternalRouteListener implements RouteListener {
520 @Override
521 public void update(Collection<RouteUpdate> routeUpdates) {
522 Router.this.update(routeUpdates);
523 }
524 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700525}