blob: c5c77410f26be50467cb86ad9b4a12026accb3b5 [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;
Pingping Line28ae4c2015-03-13 11:37:03 -070038import org.onosproject.net.ConnectPoint;
Jonathan Hart552e31f2015-02-06 11:11:59 -080039import org.onosproject.net.Host;
40import org.onosproject.net.host.HostEvent;
41import org.onosproject.net.host.HostListener;
42import org.onosproject.net.host.HostService;
Jonathan Hart2da1e602015-02-18 19:09:24 -080043import org.onosproject.routing.BgpService;
44import org.onosproject.routing.FibEntry;
45import org.onosproject.routing.FibListener;
46import org.onosproject.routing.FibUpdate;
Pingping Line28ae4c2015-03-13 11:37:03 -070047import org.onosproject.routing.IntentRequestListener;
Jonathan Hart2da1e602015-02-18 19:09:24 -080048import org.onosproject.routing.RouteEntry;
49import org.onosproject.routing.RouteListener;
50import org.onosproject.routing.RouteUpdate;
51import org.onosproject.routing.RoutingService;
Pingping Line28ae4c2015-03-13 11:37:03 -070052import org.onosproject.routing.config.Interface;
53import org.onosproject.routing.config.RoutingConfigurationService;
Jonathan Hart552e31f2015-02-06 11:11:59 -080054import org.slf4j.Logger;
55import org.slf4j.LoggerFactory;
56
Jonathan Hart66018992015-07-31 11:19:27 -070057import java.util.Collection;
58import java.util.Collections;
59import java.util.Iterator;
60import java.util.LinkedList;
61import java.util.List;
62import java.util.Map;
63import java.util.Set;
64import java.util.concurrent.BlockingQueue;
65import java.util.concurrent.ConcurrentHashMap;
66import java.util.concurrent.ExecutorService;
67import java.util.concurrent.Executors;
68import java.util.concurrent.LinkedBlockingQueue;
Jonathan Hart41349e92015-02-09 14:14:02 -080069
Jonathan Hart66018992015-07-31 11:19:27 -070070import static com.google.common.base.Preconditions.checkNotNull;
Pingping Line28ae4c2015-03-13 11:37:03 -070071import static org.onosproject.routing.RouteEntry.createBinaryString;
72
Jonathan Hart335ef462014-10-16 08:20:46 -070073/**
Jonathan Hart552e31f2015-02-06 11:11:59 -080074 * This class processes route updates and maintains a Routing Information Base
75 * (RIB). After route updates have been processed and next hops have been
76 * resolved, FIB updates are sent to any listening FIB components.
Jonathan Hart335ef462014-10-16 08:20:46 -070077 */
Jonathan Hart41349e92015-02-09 14:14:02 -080078@Component(immediate = true)
79@Service
80public class Router implements RoutingService {
Jonathan Hart335ef462014-10-16 08:20:46 -070081
82 private static final Logger log = LoggerFactory.getLogger(Router.class);
83
Jonathan Hart552e31f2015-02-06 11:11:59 -080084 // Route entries are stored in a radix tree.
Jonathan Hart0b04bed2014-10-16 16:39:19 -070085 // The key in this tree is the binary string of prefix of the route.
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080086 private InvertedRadixTree<RouteEntry> ribTable4;
87 private InvertedRadixTree<RouteEntry> ribTable6;
Jonathan Hart335ef462014-10-16 08:20:46 -070088
89 // Stores all incoming route updates in a queue.
Pingping Line28ae4c2015-03-13 11:37:03 -070090 private final BlockingQueue<Collection<RouteUpdate>> routeUpdatesQueue =
91 new LinkedBlockingQueue<>();
Jonathan Hart335ef462014-10-16 08:20:46 -070092
Pingping Line28ae4c2015-03-13 11:37:03 -070093 // Next-hop IP address to route entry mapping for next hops pending MAC
94 // resolution
Jonathan Hart41349e92015-02-09 14:14:02 -080095 private SetMultimap<IpAddress, RouteEntry> routesWaitingOnArp;
Jonathan Hart335ef462014-10-16 08:20:46 -070096
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080097 // The IPv4 address to MAC address mapping
Jonathan Hart41349e92015-02-09 14:14:02 -080098 private final Map<IpAddress, MacAddress> ip2Mac = new ConcurrentHashMap<>();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080099
Jonathan Hart41349e92015-02-09 14:14:02 -0800100 private FibListener fibComponent;
Pingping Line28ae4c2015-03-13 11:37:03 -0700101 private IntentRequestListener intentRequestListener;
Jonathan Hart335ef462014-10-16 08:20:46 -0700102
Jonathan Hart41349e92015-02-09 14:14:02 -0800103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart66018992015-07-31 11:19:27 -0700104 protected CoreService coreService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart41349e92015-02-09 14:14:02 -0800107 protected HostService hostService;
Jonathan Hart335ef462014-10-16 08:20:46 -0700108
Jonathan Hart41349e92015-02-09 14:14:02 -0800109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected BgpService bgpService;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800111
Pingping Line28ae4c2015-03-13 11:37:03 -0700112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected RoutingConfigurationService routingConfigurationService;
114
Jonathan Hart41349e92015-02-09 14:14:02 -0800115 private ExecutorService bgpUpdatesExecutor;
116 private final HostListener hostListener = new InternalHostListener();
117
118 @Activate
119 public void activate() {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800120 ribTable4 = new ConcurrentInvertedRadixTree<>(
121 new DefaultByteArrayNodeFactory());
122 ribTable6 = new ConcurrentInvertedRadixTree<>(
Jonathan Hart335ef462014-10-16 08:20:46 -0700123 new DefaultByteArrayNodeFactory());
Pingping Line28ae4c2015-03-13 11:37:03 -0700124
Jonathan Hart335ef462014-10-16 08:20:46 -0700125 routesWaitingOnArp = Multimaps.synchronizedSetMultimap(
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800126 HashMultimap.<IpAddress, RouteEntry>create());
Jonathan Hart335ef462014-10-16 08:20:46 -0700127
Jonathan Hart66018992015-07-31 11:19:27 -0700128 coreService.registerApplication(ROUTER_APP_ID);
129
Jonathan Hart335ef462014-10-16 08:20:46 -0700130 bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
Pavlin Radoslavov8b752442014-11-18 14:34:37 -0800131 new ThreadFactoryBuilder()
132 .setNameFormat("sdnip-bgp-updates-%d").build());
Jonathan Hart335ef462014-10-16 08:20:46 -0700133 }
134
Jonathan Hart41349e92015-02-09 14:14:02 -0800135 @Deactivate
136 public void deactivate() {
137 log.debug("Stopped");
138 }
139
140 @Override
Pingping Line28ae4c2015-03-13 11:37:03 -0700141 public void addFibListener(FibListener fibListener) {
142 this.fibComponent = checkNotNull(fibListener);
143
144 }
145
146 @Override
147 public void addIntentRequestListener(IntentRequestListener intentRequestListener) {
148 this.intentRequestListener = checkNotNull(intentRequestListener);
149 }
150
151 @Override
152 public void start() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800153 this.hostService.addListener(hostListener);
154
Jonathan Hartd24fafb2015-02-09 17:55:32 -0800155 bgpService.start(new InternalRouteListener());
Jonathan Hart41349e92015-02-09 14:14:02 -0800156
Jonathan Hart335ef462014-10-16 08:20:46 -0700157 bgpUpdatesExecutor.execute(new Runnable() {
158 @Override
159 public void run() {
160 doUpdatesThread();
161 }
162 });
Jonathan Hart335ef462014-10-16 08:20:46 -0700163 }
164
Jonathan Hart41349e92015-02-09 14:14:02 -0800165 @Override
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800166 public void stop() {
Jonathan Hart41349e92015-02-09 14:14:02 -0800167 bgpService.stop();
168
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800169 this.hostService.removeListener(hostListener);
170
171 // Stop the thread(s)
Jonathan Hart739c8352014-10-29 17:49:26 -0700172 bgpUpdatesExecutor.shutdownNow();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800173
174 synchronized (this) {
175 // Cleanup all local state
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800176 ribTable4 = new ConcurrentInvertedRadixTree<>(
Pingping Line28ae4c2015-03-13 11:37:03 -0700177 new DefaultByteArrayNodeFactory());
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800178 ribTable6 = new ConcurrentInvertedRadixTree<>(
Pingping Line28ae4c2015-03-13 11:37:03 -0700179 new DefaultByteArrayNodeFactory());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800180 routeUpdatesQueue.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800181 routesWaitingOnArp.clear();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800182 ip2Mac.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800183 }
Jonathan Hart739c8352014-10-29 17:49:26 -0700184 }
185
Jonathan Hart41349e92015-02-09 14:14:02 -0800186 /**
187 * Entry point for route updates.
188 *
189 * @param routeUpdates collection of route updates to process
190 */
191 private void update(Collection<RouteUpdate> routeUpdates) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700192 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800193 routeUpdatesQueue.put(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700194 } catch (InterruptedException e) {
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700195 log.error("Interrupted while putting on routeUpdatesQueue", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700196 Thread.currentThread().interrupt();
197 }
198 }
199
200 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700201 * Thread for handling route updates.
202 */
203 private void doUpdatesThread() {
204 boolean interrupted = false;
205 try {
206 while (!interrupted) {
207 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800208 Collection<RouteUpdate> routeUpdates =
Pingping Line28ae4c2015-03-13 11:37:03 -0700209 routeUpdatesQueue.take();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800210 processRouteUpdates(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700211 } catch (InterruptedException e) {
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700212 log.error("Interrupted while taking from updates queue", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700213 interrupted = true;
214 } catch (Exception e) {
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700215 log.error("exception", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700216 }
217 }
218 } finally {
219 if (interrupted) {
220 Thread.currentThread().interrupt();
221 }
222 }
223 }
224
225 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800226 * Gets all IPv4 routes from the RIB.
227 *
228 * @return all IPv4 routes from the RIB
229 */
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700230 @Override
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800231 public Collection<RouteEntry> getRoutes4() {
232 Iterator<KeyValuePair<RouteEntry>> it =
Pingping Line28ae4c2015-03-13 11:37:03 -0700233 ribTable4.getKeyValuePairsForKeysStartingWith("").iterator();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800234
235 List<RouteEntry> routes = new LinkedList<>();
236
237 while (it.hasNext()) {
238 KeyValuePair<RouteEntry> entry = it.next();
239 routes.add(entry.getValue());
240 }
241
242 return routes;
243 }
244
245 /**
246 * Gets all IPv6 routes from the RIB.
247 *
248 * @return all IPv6 routes from the RIB
249 */
Srikanth Vavilapalli89ad3772015-03-25 18:00:38 -0700250 @Override
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800251 public Collection<RouteEntry> getRoutes6() {
252 Iterator<KeyValuePair<RouteEntry>> it =
Pingping Line28ae4c2015-03-13 11:37:03 -0700253 ribTable6.getKeyValuePairsForKeysStartingWith("").iterator();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800254
255 List<RouteEntry> routes = new LinkedList<>();
256
257 while (it.hasNext()) {
258 KeyValuePair<RouteEntry> entry = it.next();
259 routes.add(entry.getValue());
260 }
261
262 return routes;
263 }
264
265 /**
266 * Finds a route in the RIB for a prefix. The prefix can be either IPv4 or
267 * IPv6.
268 *
269 * @param prefix the prefix to use
270 * @return the route if found, otherwise null
271 */
272 RouteEntry findRibRoute(IpPrefix prefix) {
Pingping Line28ae4c2015-03-13 11:37:03 -0700273 String binaryString = createBinaryString(prefix);
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700274 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800275 // IPv4
276 return ribTable4.getValueForExactKey(binaryString);
277 }
278 // IPv6
279 return ribTable6.getValueForExactKey(binaryString);
280 }
281
282 /**
283 * Adds a route to the RIB. The route can be either IPv4 or IPv6.
284 *
285 * @param routeEntry the route entry to use
286 */
287 void addRibRoute(RouteEntry routeEntry) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700288 if (routeEntry.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800289 // IPv4
Pingping Line28ae4c2015-03-13 11:37:03 -0700290 ribTable4.put(createBinaryString(routeEntry.prefix()),
291 routeEntry);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800292 } else {
293 // IPv6
Pingping Line28ae4c2015-03-13 11:37:03 -0700294 ribTable6.put(createBinaryString(routeEntry.prefix()),
295 routeEntry);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800296 }
297 }
298
299 /**
300 * Removes a route for a prefix from the RIB. The prefix can be either IPv4
301 * or IPv6.
302 *
303 * @param prefix the prefix to use
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800304 * @return true if the route was found and removed, otherwise false
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800305 */
306 boolean removeRibRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700307 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800308 // IPv4
Pingping Line28ae4c2015-03-13 11:37:03 -0700309 return ribTable4.remove(createBinaryString(prefix));
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800310 }
311 // IPv6
Pingping Line28ae4c2015-03-13 11:37:03 -0700312 return ribTable6.remove(createBinaryString(prefix));
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800313 }
314
315 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800316 * Processes route updates.
317 *
318 * @param routeUpdates the route updates to process
319 */
320 void processRouteUpdates(Collection<RouteUpdate> routeUpdates) {
321 synchronized (this) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800322 Collection<IpPrefix> withdrawPrefixes = new LinkedList<>();
Jonathan Hart552e31f2015-02-06 11:11:59 -0800323 Collection<FibUpdate> fibUpdates = new LinkedList<>();
324 Collection<FibUpdate> fibWithdraws = new LinkedList<>();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800325
326 for (RouteUpdate update : routeUpdates) {
327 switch (update.type()) {
328 case UPDATE:
Pingping Line28ae4c2015-03-13 11:37:03 -0700329
Jonathan Hart552e31f2015-02-06 11:11:59 -0800330 FibEntry fib = processRouteAdd(update.routeEntry(),
Pingping Line28ae4c2015-03-13 11:37:03 -0700331 withdrawPrefixes);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800332 if (fib != null) {
333 fibUpdates.add(new FibUpdate(FibUpdate.Type.UPDATE, fib));
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800334 }
Jonathan Hart552e31f2015-02-06 11:11:59 -0800335
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800336 break;
337 case DELETE:
338 processRouteDelete(update.routeEntry(), withdrawPrefixes);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800339
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800340 break;
341 default:
342 log.error("Unknown update Type: {}", update.type());
343 break;
344 }
345 }
346
Jonathan Hart552e31f2015-02-06 11:11:59 -0800347 withdrawPrefixes.forEach(p -> fibWithdraws.add(new FibUpdate(
348 FibUpdate.Type.DELETE, new FibEntry(p, null, null))));
349
Jonathan Hart41349e92015-02-09 14:14:02 -0800350 if (!fibUpdates.isEmpty() || !fibWithdraws.isEmpty()) {
351 fibComponent.update(fibUpdates, fibWithdraws);
352 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800353 }
354 }
355
356 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700357 * Processes adding a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700358 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800359 * The route entry is added to the radix tree. If there was an existing
360 * next hop for this prefix, but the next hop was different, then the
361 * old route entry is deleted.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700362 * </p>
Thomas Vachuska4b420772014-10-30 16:46:17 -0700363 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800364 * NOTE: Currently, we don't handle routes if the next hop is within the
365 * SDN domain.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700366 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700367 *
368 * @param routeEntry the route entry to add
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800369 * @param withdrawPrefixes the collection of accumulated prefixes whose
370 * intents will be withdrawn
Jonathan Hart552e31f2015-02-06 11:11:59 -0800371 * @return the corresponding FIB entry change, or null
Jonathan Hart335ef462014-10-16 08:20:46 -0700372 */
Pingping Line28ae4c2015-03-13 11:37:03 -0700373 private FibEntry processRouteAdd(RouteEntry routeEntry,
374 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800375 log.debug("Processing route add: {}", routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700376
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800377 // Find the old next-hop if we are updating an old route entry
378 IpAddress oldNextHop = null;
379 RouteEntry oldRouteEntry = findRibRoute(routeEntry.prefix());
380 if (oldRouteEntry != null) {
381 oldNextHop = oldRouteEntry.nextHop();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800382 }
383
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800384 // Add the new route to the RIB
385 addRibRoute(routeEntry);
386
387 if (oldNextHop != null) {
388 if (oldNextHop.equals(routeEntry.nextHop())) {
389 return null; // No change
390 }
391 //
392 // Update an existing nexthop for the prefix.
393 // We need to remove the old flows for this prefix from the
394 // switches before the new flows are added.
395 //
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800396 withdrawPrefixes.add(routeEntry.prefix());
397 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800398
Pingping Line28ae4c2015-03-13 11:37:03 -0700399 if (routingConfigurationService.isIpPrefixLocal(routeEntry.prefix())) {
400 // Route originated by local SDN domain
401 // We don't handle these here, reactive routing APP will handle
402 // these
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800403 log.debug("Own route {} to {}",
Pingping Line28ae4c2015-03-13 11:37:03 -0700404 routeEntry.prefix(), routeEntry.nextHop());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800405 return null;
406 }
407
408 //
409 // Find the MAC address of next hop router for this route entry.
410 // If the MAC address can not be found in ARP cache, then this prefix
411 // will be put in routesWaitingOnArp queue. Otherwise, generate
412 // a new route intent.
413 //
414
415 // Monitor the IP address for updates of the MAC address
Jonathan Hart31582d12014-10-22 13:52:41 -0700416 hostService.startMonitoringIp(routeEntry.nextHop());
417
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800418 // Check if we know the MAC address of the next hop
419 MacAddress nextHopMacAddress = ip2Mac.get(routeEntry.nextHop());
420 if (nextHopMacAddress == null) {
421 Set<Host> hosts = hostService.getHostsByIp(routeEntry.nextHop());
422 if (!hosts.isEmpty()) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800423 nextHopMacAddress = hosts.iterator().next().mac();
424 }
425 if (nextHopMacAddress != null) {
426 ip2Mac.put(routeEntry.nextHop(), nextHopMacAddress);
427 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700428 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700429 if (nextHopMacAddress == null) {
430 routesWaitingOnArp.put(routeEntry.nextHop(), routeEntry);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800431 return null;
Jonathan Hart335ef462014-10-16 08:20:46 -0700432 }
Jonathan Hart552e31f2015-02-06 11:11:59 -0800433 return new FibEntry(routeEntry.prefix(), routeEntry.nextHop(),
Pingping Line28ae4c2015-03-13 11:37:03 -0700434 nextHopMacAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700435 }
436
437 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800438 * Processes the deletion of a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700439 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800440 * The prefix for the routing entry is removed from radix tree.
441 * If the operation is successful, the prefix is added to the collection
442 * of prefixes whose intents that will be withdrawn.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700443 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700444 *
445 * @param routeEntry the route entry to delete
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800446 * @param withdrawPrefixes the collection of accumulated prefixes whose
447 * intents will be withdrawn
Jonathan Hart335ef462014-10-16 08:20:46 -0700448 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800449 private void processRouteDelete(RouteEntry routeEntry,
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800450 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800451 log.debug("Processing route delete: {}", routeEntry);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800452 boolean isRemoved = removeRibRoute(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700453
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800454 if (isRemoved) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800455 //
456 // Only withdraw intents if an entry was actually removed from the
457 // tree. If no entry was removed, the <prefix, nexthop> wasn't
458 // there so it's probably already been removed and we don't
459 // need to do anything.
460 //
461 withdrawPrefixes.add(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700462 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700463
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800464 routesWaitingOnArp.remove(routeEntry.nextHop(), routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700465 }
466
467 /**
Jonathan Hart31582d12014-10-22 13:52:41 -0700468 * Signals the Router that the MAC to IP mapping has potentially been
469 * updated. This has the effect of updating the MAC address for any
470 * installed prefixes if it has changed, as well as installing any pending
471 * prefixes that were waiting for MAC resolution.
Jonathan Hart335ef462014-10-16 08:20:46 -0700472 *
Jonathan Hart31582d12014-10-22 13:52:41 -0700473 * @param ipAddress the IP address that an event was received for
474 * @param macAddress the most recently known MAC address for the IP address
Jonathan Hart335ef462014-10-16 08:20:46 -0700475 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800476 private void updateMac(IpAddress ipAddress, MacAddress macAddress) {
477 log.debug("Received updated MAC info: {} => {}", ipAddress,
Pingping Line28ae4c2015-03-13 11:37:03 -0700478 macAddress);
Jonathan Hart31582d12014-10-22 13:52:41 -0700479
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800480 //
481 // We synchronize on "this" to prevent changes to the Radix tree
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700482 // while we're pushing intents. If the tree changes, the
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800483 // tree and the intents could get out of sync.
484 //
Jonathan Hart335ef462014-10-16 08:20:46 -0700485 synchronized (this) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800486 Collection<FibUpdate> submitFibEntries = new LinkedList<>();
Jonathan Hart335ef462014-10-16 08:20:46 -0700487
488 Set<RouteEntry> routesToPush =
Pingping Line28ae4c2015-03-13 11:37:03 -0700489 routesWaitingOnArp.removeAll(ipAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700490
491 for (RouteEntry routeEntry : routesToPush) {
492 // These will always be adds
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800493 RouteEntry foundRouteEntry = findRibRoute(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700494 if (foundRouteEntry != null &&
Pingping Line28ae4c2015-03-13 11:37:03 -0700495 foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800496 // We only push FIB updates if the prefix is still in the
497 // radix tree and the next hop is the same as our entry.
Jonathan Hart335ef462014-10-16 08:20:46 -0700498 // The prefix could have been removed while we were waiting
499 // for the ARP, or the next hop could have changed.
Jonathan Hart552e31f2015-02-06 11:11:59 -0800500 submitFibEntries.add(new FibUpdate(FibUpdate.Type.UPDATE,
Pingping Line28ae4c2015-03-13 11:37:03 -0700501 new FibEntry(routeEntry.prefix(),
502 ipAddress, macAddress)));
Jonathan Hart335ef462014-10-16 08:20:46 -0700503 } else {
Jonathan Hart31582d12014-10-22 13:52:41 -0700504 log.debug("{} has been revoked before the MAC was resolved",
Pingping Line28ae4c2015-03-13 11:37:03 -0700505 routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700506 }
507 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800508
Jonathan Hart552e31f2015-02-06 11:11:59 -0800509 if (!submitFibEntries.isEmpty()) {
510 fibComponent.update(submitFibEntries, Collections.emptyList());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800511 }
512
513 ip2Mac.put(ipAddress, macAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700514 }
515 }
516
517 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700518 * Listener for host events.
519 */
520 class InternalHostListener implements HostListener {
521 @Override
522 public void event(HostEvent event) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800523 log.debug("Received HostEvent {}", event);
524
525 Host host = event.subject();
526 switch (event.type()) {
527 case HOST_ADDED:
528 // FALLTHROUGH
529 case HOST_UPDATED:
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800530 for (IpAddress ipAddress : host.ipAddresses()) {
531 updateMac(ipAddress, host.mac());
Jonathan Hart335ef462014-10-16 08:20:46 -0700532 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800533 break;
534 case HOST_REMOVED:
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800535 for (IpAddress ipAddress : host.ipAddresses()) {
536 ip2Mac.remove(ipAddress);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800537 }
538 break;
539 default:
540 break;
Jonathan Hart335ef462014-10-16 08:20:46 -0700541 }
542 }
543 }
Jonathan Hart41349e92015-02-09 14:14:02 -0800544
545 /**
546 * Listener for route events.
547 */
548 private class InternalRouteListener implements RouteListener {
549 @Override
550 public void update(Collection<RouteUpdate> routeUpdates) {
551 Router.this.update(routeUpdates);
552 }
553 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700554
555 @Override
556 public LocationType getLocationType(IpAddress ipAddress) {
557 if (routingConfigurationService.isIpAddressLocal(ipAddress)) {
558 return LocationType.LOCAL;
559 } else if (getLongestMatchableRouteEntry(ipAddress) != null) {
560 return LocationType.INTERNET;
561 } else {
562 return LocationType.NO_ROUTE;
563 }
564 }
565
566 @Override
567 public RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress) {
568 RouteEntry routeEntry = null;
569 Iterable<RouteEntry> routeEntries;
570
571 if (ipAddress.isIp4()) {
572 routeEntries = ribTable4.getValuesForKeysPrefixing(
573 createBinaryString(
574 IpPrefix.valueOf(ipAddress, Ip4Address.BIT_LENGTH)));
575 } else {
576 routeEntries = ribTable6.getValuesForKeysPrefixing(
577 createBinaryString(
578 IpPrefix.valueOf(ipAddress, Ip6Address.BIT_LENGTH)));
579 }
580 if (routeEntries == null) {
581 return null;
582 }
583 Iterator<RouteEntry> it = routeEntries.iterator();
584 while (it.hasNext()) {
585 routeEntry = it.next();
586 }
587 return routeEntry;
588 }
589
590 @Override
591 public ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress) {
592 LocationType type = getLocationType(dstIpAddress);
593 if (type == LocationType.LOCAL) {
594 Set<Host> hosts = hostService.getHostsByIp(dstIpAddress);
595 if (!hosts.isEmpty()) {
596 return hosts.iterator().next().location();
597 } else {
598 hostService.startMonitoringIp(dstIpAddress);
599 return null;
600 }
601 } else if (type == LocationType.INTERNET) {
602 IpAddress nextHopIpAddress = null;
603 RouteEntry routeEntry = getLongestMatchableRouteEntry(dstIpAddress);
604 if (routeEntry != null) {
605 nextHopIpAddress = routeEntry.nextHop();
606 Interface it = routingConfigurationService
607 .getMatchingInterface(nextHopIpAddress);
608 if (it != null) {
609 return it.connectPoint();
610 } else {
611 return null;
612 }
613 } else {
614 return null;
615 }
616 } else {
617 return null;
618 }
619 }
620
621 @Override
622 public void packetReactiveProcessor(IpAddress dstIpAddress,
623 IpAddress srcIpAddress,
624 ConnectPoint srcConnectPoint,
625 MacAddress srcMacAddress) {
626 checkNotNull(dstIpAddress);
627 checkNotNull(srcIpAddress);
628 checkNotNull(srcConnectPoint);
629 checkNotNull(srcMacAddress);
630
631 //
632 // Step1: Try to update the existing intent first if it exists.
633 //
634 IpPrefix ipPrefix = null;
635 if (routingConfigurationService.isIpAddressLocal(dstIpAddress)) {
636 if (dstIpAddress.isIp4()) {
637 ipPrefix = IpPrefix.valueOf(dstIpAddress,
638 Ip4Address.BIT_LENGTH);
639 } else {
640 ipPrefix = IpPrefix.valueOf(dstIpAddress,
641 Ip6Address.BIT_LENGTH);
642 }
643 } else {
644 // Get IP prefix from BGP route table
645 RouteEntry routeEntry = getLongestMatchableRouteEntry(dstIpAddress);
646 if (routeEntry != null) {
647 ipPrefix = routeEntry.prefix();
648 }
649 }
650 if (ipPrefix != null
651 && intentRequestListener.mp2pIntentExists(ipPrefix)) {
652 intentRequestListener.updateExistingMp2pIntent(ipPrefix,
653 srcConnectPoint);
654 return;
655 }
656
657 //
658 // Step2: There is no existing intent for the destination IP address.
659 // Check whether it is necessary to create a new one. If necessary then
660 // create a new one.
661 //
662 TrafficType trafficType =
663 trafficTypeClassifier(srcConnectPoint, dstIpAddress);
664
665 switch (trafficType) {
666 case HOST_TO_INTERNET:
667 // If the destination IP address is outside the local SDN network.
668 // The Step 1 has already handled it. We do not need to do anything here.
669 break;
670 case INTERNET_TO_HOST:
671 intentRequestListener.setUpConnectivityInternetToHost(dstIpAddress);
672 break;
673 case HOST_TO_HOST:
674 intentRequestListener.setUpConnectivityHostToHost(dstIpAddress,
675 srcIpAddress, srcMacAddress, srcConnectPoint);
676 break;
677 case INTERNET_TO_INTERNET:
678 log.trace("This is transit traffic, "
679 + "the intent should be preinstalled already");
680 break;
681 case DROP:
682 // TODO here should setUpDropPaccketIntent(...);
683 // We need a new type of intent here.
684 break;
685 case UNKNOWN:
686 log.trace("This is unknown traffic, so we do nothing");
687 break;
688 default:
689 break;
690 }
691 }
692
693 /**
694 * Classifies the traffic and return the traffic type.
695 *
696 * @param srcConnectPoint the connect point where the packet comes from
697 * @param dstIp the destination IP address in packet
698 * @return the traffic type which this packet belongs to
699 */
700 private TrafficType trafficTypeClassifier(ConnectPoint srcConnectPoint,
701 IpAddress dstIp) {
702 LocationType dstIpLocationType = getLocationType(dstIp);
703 Interface srcInterface =
704 routingConfigurationService.getInterface(srcConnectPoint);
705
706 switch (dstIpLocationType) {
707 case INTERNET:
708 if (srcInterface == null) {
709 return TrafficType.HOST_TO_INTERNET;
710 } else {
711 return TrafficType.INTERNET_TO_INTERNET;
712 }
713 case LOCAL:
714 if (srcInterface == null) {
715 return TrafficType.HOST_TO_HOST;
716 } else {
717 // TODO Currently we only consider local public prefixes.
718 // In the future, we will consider the local private prefixes.
719 // If dstIpLocationType is a local private, we should return
720 // TrafficType.DROP.
721 return TrafficType.INTERNET_TO_HOST;
722 }
723 case NO_ROUTE:
724 return TrafficType.DROP;
725 default:
726 return TrafficType.UNKNOWN;
727 }
728 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700729}