blob: f1109934f541c653af1202632a11929044c8ca72 [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
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;
Pingping Line28ae4c2015-03-13 11:37:03 -070039import org.onlab.packet.Ip4Address;
40import org.onlab.packet.Ip6Address;
Jonathan Hart552e31f2015-02-06 11:11:59 -080041import org.onlab.packet.IpAddress;
42import org.onlab.packet.IpPrefix;
43import org.onlab.packet.MacAddress;
Pingping Line28ae4c2015-03-13 11:37:03 -070044import org.onosproject.net.ConnectPoint;
Jonathan Hart552e31f2015-02-06 11:11:59 -080045import org.onosproject.net.Host;
46import org.onosproject.net.host.HostEvent;
47import org.onosproject.net.host.HostListener;
48import org.onosproject.net.host.HostService;
Jonathan Hart2da1e602015-02-18 19:09:24 -080049import org.onosproject.routing.BgpService;
50import org.onosproject.routing.FibEntry;
51import org.onosproject.routing.FibListener;
52import org.onosproject.routing.FibUpdate;
Pingping Line28ae4c2015-03-13 11:37:03 -070053import org.onosproject.routing.IntentRequestListener;
Jonathan Hart2da1e602015-02-18 19:09:24 -080054import org.onosproject.routing.RouteEntry;
55import org.onosproject.routing.RouteListener;
56import org.onosproject.routing.RouteUpdate;
57import org.onosproject.routing.RoutingService;
Pingping Line28ae4c2015-03-13 11:37:03 -070058import org.onosproject.routing.config.Interface;
59import org.onosproject.routing.config.RoutingConfigurationService;
Jonathan Hart552e31f2015-02-06 11:11:59 -080060import org.slf4j.Logger;
61import org.slf4j.LoggerFactory;
62
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -070063import com.google.common.collect.HashMultimap;
64import com.google.common.collect.Multimaps;
65import com.google.common.collect.SetMultimap;
66import com.google.common.util.concurrent.ThreadFactoryBuilder;
67import com.googlecode.concurrenttrees.common.KeyValuePair;
68import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
69import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
70import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
Jonathan Hart41349e92015-02-09 14:14:02 -080071
Pingping Line28ae4c2015-03-13 11:37:03 -070072import static org.onosproject.routing.RouteEntry.createBinaryString;
73
Jonathan Hart335ef462014-10-16 08:20:46 -070074/**
Jonathan Hart552e31f2015-02-06 11:11:59 -080075 * This class processes route updates and maintains a Routing Information Base
76 * (RIB). After route updates have been processed and next hops have been
77 * resolved, FIB updates are sent to any listening FIB components.
Jonathan Hart335ef462014-10-16 08:20:46 -070078 */
Jonathan Hart41349e92015-02-09 14:14:02 -080079@Component(immediate = true)
80@Service
81public class Router implements RoutingService {
Jonathan Hart335ef462014-10-16 08:20:46 -070082
83 private static final Logger log = LoggerFactory.getLogger(Router.class);
84
Jonathan Hart552e31f2015-02-06 11:11:59 -080085 // Route entries are stored in a radix tree.
Jonathan Hart0b04bed2014-10-16 16:39:19 -070086 // The key in this tree is the binary string of prefix of the route.
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080087 private InvertedRadixTree<RouteEntry> ribTable4;
88 private InvertedRadixTree<RouteEntry> ribTable6;
Jonathan Hart335ef462014-10-16 08:20:46 -070089
90 // Stores all incoming route updates in a queue.
Pingping Line28ae4c2015-03-13 11:37:03 -070091 private final BlockingQueue<Collection<RouteUpdate>> routeUpdatesQueue =
92 new LinkedBlockingQueue<>();
Jonathan Hart335ef462014-10-16 08:20:46 -070093
Pingping Line28ae4c2015-03-13 11:37:03 -070094 // Next-hop IP address to route entry mapping for next hops pending MAC
95 // resolution
Jonathan Hart41349e92015-02-09 14:14:02 -080096 private SetMultimap<IpAddress, RouteEntry> routesWaitingOnArp;
Jonathan Hart335ef462014-10-16 08:20:46 -070097
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080098 // The IPv4 address to MAC address mapping
Jonathan Hart41349e92015-02-09 14:14:02 -080099 private final Map<IpAddress, MacAddress> ip2Mac = new ConcurrentHashMap<>();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800100
Jonathan Hart41349e92015-02-09 14:14:02 -0800101 private FibListener fibComponent;
Pingping Line28ae4c2015-03-13 11:37:03 -0700102 private IntentRequestListener intentRequestListener;
Jonathan Hart335ef462014-10-16 08:20:46 -0700103
Jonathan Hart41349e92015-02-09 14:14:02 -0800104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected HostService hostService;
Jonathan Hart335ef462014-10-16 08:20:46 -0700106
Jonathan Hart41349e92015-02-09 14:14:02 -0800107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected BgpService bgpService;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800109
Pingping Line28ae4c2015-03-13 11:37:03 -0700110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected RoutingConfigurationService routingConfigurationService;
112
Jonathan Hart41349e92015-02-09 14:14:02 -0800113 private ExecutorService bgpUpdatesExecutor;
114 private final HostListener hostListener = new InternalHostListener();
115
116 @Activate
117 public void activate() {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800118 ribTable4 = new ConcurrentInvertedRadixTree<>(
119 new DefaultByteArrayNodeFactory());
120 ribTable6 = new ConcurrentInvertedRadixTree<>(
Jonathan Hart335ef462014-10-16 08:20:46 -0700121 new DefaultByteArrayNodeFactory());
Pingping Line28ae4c2015-03-13 11:37:03 -0700122
Jonathan Hart335ef462014-10-16 08:20:46 -0700123 routesWaitingOnArp = Multimaps.synchronizedSetMultimap(
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800124 HashMultimap.<IpAddress, RouteEntry>create());
Jonathan Hart335ef462014-10-16 08:20:46 -0700125
126 bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
Pavlin Radoslavov8b752442014-11-18 14:34:37 -0800127 new ThreadFactoryBuilder()
128 .setNameFormat("sdnip-bgp-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);
139
140 }
141
142 @Override
143 public void addIntentRequestListener(IntentRequestListener intentRequestListener) {
144 this.intentRequestListener = checkNotNull(intentRequestListener);
145 }
146
147 @Override
148 public void start() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800149 this.hostService.addListener(hostListener);
150
Jonathan Hartd24fafb2015-02-09 17:55:32 -0800151 bgpService.start(new InternalRouteListener());
Jonathan Hart41349e92015-02-09 14:14:02 -0800152
Jonathan Hart335ef462014-10-16 08:20:46 -0700153 bgpUpdatesExecutor.execute(new Runnable() {
154 @Override
155 public void run() {
156 doUpdatesThread();
157 }
158 });
Jonathan Hart335ef462014-10-16 08:20:46 -0700159 }
160
Jonathan Hart41349e92015-02-09 14:14:02 -0800161 @Override
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800162 public void stop() {
Jonathan Hart41349e92015-02-09 14:14:02 -0800163 bgpService.stop();
164
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800165 this.hostService.removeListener(hostListener);
166
167 // Stop the thread(s)
Jonathan Hart739c8352014-10-29 17:49:26 -0700168 bgpUpdatesExecutor.shutdownNow();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800169
170 synchronized (this) {
171 // Cleanup all local state
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800172 ribTable4 = new ConcurrentInvertedRadixTree<>(
Pingping Line28ae4c2015-03-13 11:37:03 -0700173 new DefaultByteArrayNodeFactory());
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800174 ribTable6 = new ConcurrentInvertedRadixTree<>(
Pingping Line28ae4c2015-03-13 11:37:03 -0700175 new DefaultByteArrayNodeFactory());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800176 routeUpdatesQueue.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800177 routesWaitingOnArp.clear();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800178 ip2Mac.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800179 }
Jonathan Hart739c8352014-10-29 17:49:26 -0700180 }
181
Jonathan Hart41349e92015-02-09 14:14:02 -0800182 /**
183 * Entry point for route updates.
184 *
185 * @param routeUpdates collection of route updates to process
186 */
187 private void update(Collection<RouteUpdate> routeUpdates) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700188 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800189 routeUpdatesQueue.put(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700190 } catch (InterruptedException e) {
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700191 log.error("Interrupted while putting on routeUpdatesQueue", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700192 Thread.currentThread().interrupt();
193 }
194 }
195
196 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700197 * Thread for handling route updates.
198 */
199 private void doUpdatesThread() {
200 boolean interrupted = false;
201 try {
202 while (!interrupted) {
203 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800204 Collection<RouteUpdate> routeUpdates =
Pingping Line28ae4c2015-03-13 11:37:03 -0700205 routeUpdatesQueue.take();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800206 processRouteUpdates(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700207 } catch (InterruptedException e) {
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700208 log.error("Interrupted while taking from updates queue", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700209 interrupted = true;
210 } catch (Exception e) {
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700211 log.error("exception", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700212 }
213 }
214 } finally {
215 if (interrupted) {
216 Thread.currentThread().interrupt();
217 }
218 }
219 }
220
221 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800222 * Gets all IPv4 routes from the RIB.
223 *
224 * @return all IPv4 routes from the RIB
225 */
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700226 @Override
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800227 public Collection<RouteEntry> getRoutes4() {
228 Iterator<KeyValuePair<RouteEntry>> it =
Pingping Line28ae4c2015-03-13 11:37:03 -0700229 ribTable4.getKeyValuePairsForKeysStartingWith("").iterator();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800230
231 List<RouteEntry> routes = new LinkedList<>();
232
233 while (it.hasNext()) {
234 KeyValuePair<RouteEntry> entry = it.next();
235 routes.add(entry.getValue());
236 }
237
238 return routes;
239 }
240
241 /**
242 * Gets all IPv6 routes from the RIB.
243 *
244 * @return all IPv6 routes from the RIB
245 */
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700246 @Override
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800247 public Collection<RouteEntry> getRoutes6() {
248 Iterator<KeyValuePair<RouteEntry>> it =
Pingping Line28ae4c2015-03-13 11:37:03 -0700249 ribTable6.getKeyValuePairsForKeysStartingWith("").iterator();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800250
251 List<RouteEntry> routes = new LinkedList<>();
252
253 while (it.hasNext()) {
254 KeyValuePair<RouteEntry> entry = it.next();
255 routes.add(entry.getValue());
256 }
257
258 return routes;
259 }
260
261 /**
262 * Finds a route in the RIB for a prefix. The prefix can be either IPv4 or
263 * IPv6.
264 *
265 * @param prefix the prefix to use
266 * @return the route if found, otherwise null
267 */
268 RouteEntry findRibRoute(IpPrefix prefix) {
Pingping Line28ae4c2015-03-13 11:37:03 -0700269 String binaryString = createBinaryString(prefix);
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700270 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800271 // IPv4
272 return ribTable4.getValueForExactKey(binaryString);
273 }
274 // IPv6
275 return ribTable6.getValueForExactKey(binaryString);
276 }
277
278 /**
279 * Adds a route to the RIB. The route can be either IPv4 or IPv6.
280 *
281 * @param routeEntry the route entry to use
282 */
283 void addRibRoute(RouteEntry routeEntry) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700284 if (routeEntry.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800285 // IPv4
Pingping Line28ae4c2015-03-13 11:37:03 -0700286 ribTable4.put(createBinaryString(routeEntry.prefix()),
287 routeEntry);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800288 } else {
289 // IPv6
Pingping Line28ae4c2015-03-13 11:37:03 -0700290 ribTable6.put(createBinaryString(routeEntry.prefix()),
291 routeEntry);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800292 }
293 }
294
295 /**
296 * Removes a route for a prefix from the RIB. The prefix can be either IPv4
297 * or IPv6.
298 *
299 * @param prefix the prefix to use
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800300 * @return true if the route was found and removed, otherwise false
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800301 */
302 boolean removeRibRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700303 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800304 // IPv4
Pingping Line28ae4c2015-03-13 11:37:03 -0700305 return ribTable4.remove(createBinaryString(prefix));
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800306 }
307 // IPv6
Pingping Line28ae4c2015-03-13 11:37:03 -0700308 return ribTable6.remove(createBinaryString(prefix));
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800309 }
310
311 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800312 * Processes route updates.
313 *
314 * @param routeUpdates the route updates to process
315 */
316 void processRouteUpdates(Collection<RouteUpdate> routeUpdates) {
317 synchronized (this) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800318 Collection<IpPrefix> withdrawPrefixes = new LinkedList<>();
Jonathan Hart552e31f2015-02-06 11:11:59 -0800319 Collection<FibUpdate> fibUpdates = new LinkedList<>();
320 Collection<FibUpdate> fibWithdraws = new LinkedList<>();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800321
322 for (RouteUpdate update : routeUpdates) {
323 switch (update.type()) {
324 case UPDATE:
Pingping Line28ae4c2015-03-13 11:37:03 -0700325
Jonathan Hart552e31f2015-02-06 11:11:59 -0800326 FibEntry fib = processRouteAdd(update.routeEntry(),
Pingping Line28ae4c2015-03-13 11:37:03 -0700327 withdrawPrefixes);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800328 if (fib != null) {
329 fibUpdates.add(new FibUpdate(FibUpdate.Type.UPDATE, fib));
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800330 }
Jonathan Hart552e31f2015-02-06 11:11:59 -0800331
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800332 break;
333 case DELETE:
334 processRouteDelete(update.routeEntry(), withdrawPrefixes);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800335
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800336 break;
337 default:
338 log.error("Unknown update Type: {}", update.type());
339 break;
340 }
341 }
342
Jonathan Hart552e31f2015-02-06 11:11:59 -0800343 withdrawPrefixes.forEach(p -> fibWithdraws.add(new FibUpdate(
344 FibUpdate.Type.DELETE, new FibEntry(p, null, null))));
345
Jonathan Hart41349e92015-02-09 14:14:02 -0800346 if (!fibUpdates.isEmpty() || !fibWithdraws.isEmpty()) {
347 fibComponent.update(fibUpdates, fibWithdraws);
348 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800349 }
350 }
351
352 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700353 * Processes adding a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700354 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800355 * The route entry is added to the radix tree. If there was an existing
356 * next hop for this prefix, but the next hop was different, then the
357 * old route entry is deleted.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700358 * </p>
Thomas Vachuska4b420772014-10-30 16:46:17 -0700359 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800360 * NOTE: Currently, we don't handle routes if the next hop is within the
361 * SDN domain.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700362 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700363 *
364 * @param routeEntry the route entry to add
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800365 * @param withdrawPrefixes the collection of accumulated prefixes whose
366 * intents will be withdrawn
Jonathan Hart552e31f2015-02-06 11:11:59 -0800367 * @return the corresponding FIB entry change, or null
Jonathan Hart335ef462014-10-16 08:20:46 -0700368 */
Pingping Line28ae4c2015-03-13 11:37:03 -0700369 private FibEntry processRouteAdd(RouteEntry routeEntry,
370 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800371 log.debug("Processing route add: {}", routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700372
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800373 // Find the old next-hop if we are updating an old route entry
374 IpAddress oldNextHop = null;
375 RouteEntry oldRouteEntry = findRibRoute(routeEntry.prefix());
376 if (oldRouteEntry != null) {
377 oldNextHop = oldRouteEntry.nextHop();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800378 }
379
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800380 // Add the new route to the RIB
381 addRibRoute(routeEntry);
382
383 if (oldNextHop != null) {
384 if (oldNextHop.equals(routeEntry.nextHop())) {
385 return null; // No change
386 }
387 //
388 // Update an existing nexthop for the prefix.
389 // We need to remove the old flows for this prefix from the
390 // switches before the new flows are added.
391 //
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800392 withdrawPrefixes.add(routeEntry.prefix());
393 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800394
Pingping Line28ae4c2015-03-13 11:37:03 -0700395 if (routingConfigurationService.isIpPrefixLocal(routeEntry.prefix())) {
396 // Route originated by local SDN domain
397 // We don't handle these here, reactive routing APP will handle
398 // these
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800399 log.debug("Own route {} to {}",
Pingping Line28ae4c2015-03-13 11:37:03 -0700400 routeEntry.prefix(), routeEntry.nextHop());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800401 return null;
402 }
403
404 //
405 // Find the MAC address of next hop router for this route entry.
406 // If the MAC address can not be found in ARP cache, then this prefix
407 // will be put in routesWaitingOnArp queue. Otherwise, generate
408 // a new route intent.
409 //
410
411 // Monitor the IP address for updates of the MAC address
Jonathan Hart31582d12014-10-22 13:52:41 -0700412 hostService.startMonitoringIp(routeEntry.nextHop());
413
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800414 // Check if we know the MAC address of the next hop
415 MacAddress nextHopMacAddress = ip2Mac.get(routeEntry.nextHop());
416 if (nextHopMacAddress == null) {
417 Set<Host> hosts = hostService.getHostsByIp(routeEntry.nextHop());
418 if (!hosts.isEmpty()) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800419 nextHopMacAddress = hosts.iterator().next().mac();
420 }
421 if (nextHopMacAddress != null) {
422 ip2Mac.put(routeEntry.nextHop(), nextHopMacAddress);
423 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700424 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700425 if (nextHopMacAddress == null) {
426 routesWaitingOnArp.put(routeEntry.nextHop(), routeEntry);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800427 return null;
Jonathan Hart335ef462014-10-16 08:20:46 -0700428 }
Jonathan Hart552e31f2015-02-06 11:11:59 -0800429 return new FibEntry(routeEntry.prefix(), routeEntry.nextHop(),
Pingping Line28ae4c2015-03-13 11:37:03 -0700430 nextHopMacAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700431 }
432
433 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800434 * Processes the deletion of a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700435 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800436 * The prefix for the routing entry is removed from radix tree.
437 * If the operation is successful, the prefix is added to the collection
438 * of prefixes whose intents that will be withdrawn.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700439 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700440 *
441 * @param routeEntry the route entry to delete
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800442 * @param withdrawPrefixes the collection of accumulated prefixes whose
443 * intents will be withdrawn
Jonathan Hart335ef462014-10-16 08:20:46 -0700444 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800445 private void processRouteDelete(RouteEntry routeEntry,
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800446 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800447 log.debug("Processing route delete: {}", routeEntry);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800448 boolean isRemoved = removeRibRoute(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700449
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800450 if (isRemoved) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800451 //
452 // Only withdraw intents if an entry was actually removed from the
453 // tree. If no entry was removed, the <prefix, nexthop> wasn't
454 // there so it's probably already been removed and we don't
455 // need to do anything.
456 //
457 withdrawPrefixes.add(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700458 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700459
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800460 routesWaitingOnArp.remove(routeEntry.nextHop(), routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700461 }
462
463 /**
Jonathan Hart31582d12014-10-22 13:52:41 -0700464 * Signals the Router that the MAC to IP mapping has potentially been
465 * updated. This has the effect of updating the MAC address for any
466 * installed prefixes if it has changed, as well as installing any pending
467 * prefixes that were waiting for MAC resolution.
Jonathan Hart335ef462014-10-16 08:20:46 -0700468 *
Jonathan Hart31582d12014-10-22 13:52:41 -0700469 * @param ipAddress the IP address that an event was received for
470 * @param macAddress the most recently known MAC address for the IP address
Jonathan Hart335ef462014-10-16 08:20:46 -0700471 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800472 private void updateMac(IpAddress ipAddress, MacAddress macAddress) {
473 log.debug("Received updated MAC info: {} => {}", ipAddress,
Pingping Line28ae4c2015-03-13 11:37:03 -0700474 macAddress);
Jonathan Hart31582d12014-10-22 13:52:41 -0700475
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800476 //
477 // We synchronize on "this" to prevent changes to the Radix tree
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700478 // while we're pushing intents. If the tree changes, the
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800479 // tree and the intents could get out of sync.
480 //
Jonathan Hart335ef462014-10-16 08:20:46 -0700481 synchronized (this) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800482 Collection<FibUpdate> submitFibEntries = new LinkedList<>();
Jonathan Hart335ef462014-10-16 08:20:46 -0700483
484 Set<RouteEntry> routesToPush =
Pingping Line28ae4c2015-03-13 11:37:03 -0700485 routesWaitingOnArp.removeAll(ipAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700486
487 for (RouteEntry routeEntry : routesToPush) {
488 // These will always be adds
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800489 RouteEntry foundRouteEntry = findRibRoute(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700490 if (foundRouteEntry != null &&
Pingping Line28ae4c2015-03-13 11:37:03 -0700491 foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800492 // We only push FIB updates if the prefix is still in the
493 // radix tree and the next hop is the same as our entry.
Jonathan Hart335ef462014-10-16 08:20:46 -0700494 // The prefix could have been removed while we were waiting
495 // for the ARP, or the next hop could have changed.
Jonathan Hart552e31f2015-02-06 11:11:59 -0800496 submitFibEntries.add(new FibUpdate(FibUpdate.Type.UPDATE,
Pingping Line28ae4c2015-03-13 11:37:03 -0700497 new FibEntry(routeEntry.prefix(),
498 ipAddress, macAddress)));
Jonathan Hart335ef462014-10-16 08:20:46 -0700499 } else {
Jonathan Hart31582d12014-10-22 13:52:41 -0700500 log.debug("{} has been revoked before the MAC was resolved",
Pingping Line28ae4c2015-03-13 11:37:03 -0700501 routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700502 }
503 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800504
Jonathan Hart552e31f2015-02-06 11:11:59 -0800505 if (!submitFibEntries.isEmpty()) {
506 fibComponent.update(submitFibEntries, Collections.emptyList());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800507 }
508
509 ip2Mac.put(ipAddress, macAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700510 }
511 }
512
513 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700514 * Listener for host events.
515 */
516 class InternalHostListener implements HostListener {
517 @Override
518 public void event(HostEvent event) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800519 log.debug("Received HostEvent {}", event);
520
521 Host host = event.subject();
522 switch (event.type()) {
523 case HOST_ADDED:
524 // FALLTHROUGH
525 case HOST_UPDATED:
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800526 for (IpAddress ipAddress : host.ipAddresses()) {
527 updateMac(ipAddress, host.mac());
Jonathan Hart335ef462014-10-16 08:20:46 -0700528 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800529 break;
530 case HOST_REMOVED:
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800531 for (IpAddress ipAddress : host.ipAddresses()) {
532 ip2Mac.remove(ipAddress);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800533 }
534 break;
535 default:
536 break;
Jonathan Hart335ef462014-10-16 08:20:46 -0700537 }
538 }
539 }
Jonathan Hart41349e92015-02-09 14:14:02 -0800540
541 /**
542 * Listener for route events.
543 */
544 private class InternalRouteListener implements RouteListener {
545 @Override
546 public void update(Collection<RouteUpdate> routeUpdates) {
547 Router.this.update(routeUpdates);
548 }
549 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700550
551 @Override
552 public LocationType getLocationType(IpAddress ipAddress) {
553 if (routingConfigurationService.isIpAddressLocal(ipAddress)) {
554 return LocationType.LOCAL;
555 } else if (getLongestMatchableRouteEntry(ipAddress) != null) {
556 return LocationType.INTERNET;
557 } else {
558 return LocationType.NO_ROUTE;
559 }
560 }
561
562 @Override
563 public RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress) {
564 RouteEntry routeEntry = null;
565 Iterable<RouteEntry> routeEntries;
566
567 if (ipAddress.isIp4()) {
568 routeEntries = ribTable4.getValuesForKeysPrefixing(
569 createBinaryString(
570 IpPrefix.valueOf(ipAddress, Ip4Address.BIT_LENGTH)));
571 } else {
572 routeEntries = ribTable6.getValuesForKeysPrefixing(
573 createBinaryString(
574 IpPrefix.valueOf(ipAddress, Ip6Address.BIT_LENGTH)));
575 }
576 if (routeEntries == null) {
577 return null;
578 }
579 Iterator<RouteEntry> it = routeEntries.iterator();
580 while (it.hasNext()) {
581 routeEntry = it.next();
582 }
583 return routeEntry;
584 }
585
586 @Override
587 public ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress) {
588 LocationType type = getLocationType(dstIpAddress);
589 if (type == LocationType.LOCAL) {
590 Set<Host> hosts = hostService.getHostsByIp(dstIpAddress);
591 if (!hosts.isEmpty()) {
592 return hosts.iterator().next().location();
593 } else {
594 hostService.startMonitoringIp(dstIpAddress);
595 return null;
596 }
597 } else if (type == LocationType.INTERNET) {
598 IpAddress nextHopIpAddress = null;
599 RouteEntry routeEntry = getLongestMatchableRouteEntry(dstIpAddress);
600 if (routeEntry != null) {
601 nextHopIpAddress = routeEntry.nextHop();
602 Interface it = routingConfigurationService
603 .getMatchingInterface(nextHopIpAddress);
604 if (it != null) {
605 return it.connectPoint();
606 } else {
607 return null;
608 }
609 } else {
610 return null;
611 }
612 } else {
613 return null;
614 }
615 }
616
617 @Override
618 public void packetReactiveProcessor(IpAddress dstIpAddress,
619 IpAddress srcIpAddress,
620 ConnectPoint srcConnectPoint,
621 MacAddress srcMacAddress) {
622 checkNotNull(dstIpAddress);
623 checkNotNull(srcIpAddress);
624 checkNotNull(srcConnectPoint);
625 checkNotNull(srcMacAddress);
626
627 //
628 // Step1: Try to update the existing intent first if it exists.
629 //
630 IpPrefix ipPrefix = null;
631 if (routingConfigurationService.isIpAddressLocal(dstIpAddress)) {
632 if (dstIpAddress.isIp4()) {
633 ipPrefix = IpPrefix.valueOf(dstIpAddress,
634 Ip4Address.BIT_LENGTH);
635 } else {
636 ipPrefix = IpPrefix.valueOf(dstIpAddress,
637 Ip6Address.BIT_LENGTH);
638 }
639 } else {
640 // Get IP prefix from BGP route table
641 RouteEntry routeEntry = getLongestMatchableRouteEntry(dstIpAddress);
642 if (routeEntry != null) {
643 ipPrefix = routeEntry.prefix();
644 }
645 }
646 if (ipPrefix != null
647 && intentRequestListener.mp2pIntentExists(ipPrefix)) {
648 intentRequestListener.updateExistingMp2pIntent(ipPrefix,
649 srcConnectPoint);
650 return;
651 }
652
653 //
654 // Step2: There is no existing intent for the destination IP address.
655 // Check whether it is necessary to create a new one. If necessary then
656 // create a new one.
657 //
658 TrafficType trafficType =
659 trafficTypeClassifier(srcConnectPoint, dstIpAddress);
660
661 switch (trafficType) {
662 case HOST_TO_INTERNET:
663 // If the destination IP address is outside the local SDN network.
664 // The Step 1 has already handled it. We do not need to do anything here.
665 break;
666 case INTERNET_TO_HOST:
667 intentRequestListener.setUpConnectivityInternetToHost(dstIpAddress);
668 break;
669 case HOST_TO_HOST:
670 intentRequestListener.setUpConnectivityHostToHost(dstIpAddress,
671 srcIpAddress, srcMacAddress, srcConnectPoint);
672 break;
673 case INTERNET_TO_INTERNET:
674 log.trace("This is transit traffic, "
675 + "the intent should be preinstalled already");
676 break;
677 case DROP:
678 // TODO here should setUpDropPaccketIntent(...);
679 // We need a new type of intent here.
680 break;
681 case UNKNOWN:
682 log.trace("This is unknown traffic, so we do nothing");
683 break;
684 default:
685 break;
686 }
687 }
688
689 /**
690 * Classifies the traffic and return the traffic type.
691 *
692 * @param srcConnectPoint the connect point where the packet comes from
693 * @param dstIp the destination IP address in packet
694 * @return the traffic type which this packet belongs to
695 */
696 private TrafficType trafficTypeClassifier(ConnectPoint srcConnectPoint,
697 IpAddress dstIp) {
698 LocationType dstIpLocationType = getLocationType(dstIp);
699 Interface srcInterface =
700 routingConfigurationService.getInterface(srcConnectPoint);
701
702 switch (dstIpLocationType) {
703 case INTERNET:
704 if (srcInterface == null) {
705 return TrafficType.HOST_TO_INTERNET;
706 } else {
707 return TrafficType.INTERNET_TO_INTERNET;
708 }
709 case LOCAL:
710 if (srcInterface == null) {
711 return TrafficType.HOST_TO_HOST;
712 } else {
713 // TODO Currently we only consider local public prefixes.
714 // In the future, we will consider the local private prefixes.
715 // If dstIpLocationType is a local private, we should return
716 // TrafficType.DROP.
717 return TrafficType.INTERNET_TO_HOST;
718 }
719 case NO_ROUTE:
720 return TrafficType.DROP;
721 default:
722 return TrafficType.UNKNOWN;
723 }
724 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700725}