blob: d34a395c15ff1f92527645986fb20cf5820f743f [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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.sdnip;
Jonathan Hart335ef462014-10-16 08:20:46 -070017
Pingping3855f312014-10-22 12:50:37 -070018import java.util.Collection;
Pingping3855f312014-10-22 12:50:37 -070019import java.util.HashSet;
20import java.util.Iterator;
21import java.util.LinkedList;
22import java.util.List;
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080023import java.util.Map;
Pingping3855f312014-10-22 12:50:37 -070024import java.util.Set;
25import java.util.concurrent.BlockingQueue;
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080026import java.util.concurrent.ConcurrentHashMap;
Pingping3855f312014-10-22 12:50:37 -070027import java.util.concurrent.ExecutorService;
28import java.util.concurrent.Executors;
29import java.util.concurrent.LinkedBlockingQueue;
Pingping3855f312014-10-22 12:50:37 -070030
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080031import org.apache.commons.lang3.tuple.Pair;
Jonathan Hart7dd4a332014-12-04 17:37:05 -080032import org.onlab.packet.Ethernet;
Jonathan Hart7dd4a332014-12-04 17:37:05 -080033import org.onlab.packet.IpAddress;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080034import org.onlab.packet.IpPrefix;
35import org.onlab.packet.Ip4Address;
Jonathan Hart7dd4a332014-12-04 17:37:05 -080036import org.onlab.packet.MacAddress;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.core.ApplicationId;
38import org.onosproject.net.ConnectPoint;
39import org.onosproject.net.Host;
40import org.onosproject.net.flow.DefaultTrafficSelector;
41import org.onosproject.net.flow.DefaultTrafficTreatment;
42import org.onosproject.net.flow.TrafficSelector;
43import org.onosproject.net.flow.TrafficTreatment;
44import org.onosproject.net.host.HostEvent;
45import org.onosproject.net.host.HostListener;
46import org.onosproject.net.host.HostService;
47import org.onosproject.net.intent.MultiPointToSinglePointIntent;
48import org.onosproject.sdnip.config.BgpPeer;
49import org.onosproject.sdnip.config.Interface;
50import org.onosproject.sdnip.config.SdnIpConfigurationService;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070051import org.slf4j.Logger;
52import org.slf4j.LoggerFactory;
53
Pingping3855f312014-10-22 12:50:37 -070054import com.google.common.collect.HashMultimap;
55import com.google.common.collect.Multimaps;
56import com.google.common.collect.SetMultimap;
57import com.google.common.util.concurrent.ThreadFactoryBuilder;
58import com.googlecode.concurrenttrees.common.KeyValuePair;
59import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
60import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
61import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
Jonathan Hart335ef462014-10-16 08:20:46 -070062
Jonathan Hart335ef462014-10-16 08:20:46 -070063/**
64 * This class processes BGP route update, translates each update into a intent
65 * and submits the intent.
Jonathan Hart335ef462014-10-16 08:20:46 -070066 */
67public class Router implements RouteListener {
68
69 private static final Logger log = LoggerFactory.getLogger(Router.class);
70
Jonathan Hart0b04bed2014-10-16 16:39:19 -070071 // Store all route updates in a radix tree.
72 // The key in this tree is the binary string of prefix of the route.
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080073 private InvertedRadixTree<RouteEntry> ribTable4;
74 private InvertedRadixTree<RouteEntry> ribTable6;
Jonathan Hart335ef462014-10-16 08:20:46 -070075
76 // Stores all incoming route updates in a queue.
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080077 private final BlockingQueue<Collection<RouteUpdate>> routeUpdatesQueue;
Jonathan Hart335ef462014-10-16 08:20:46 -070078
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080079 // The IpAddress is the next hop address of each route update.
80 private final SetMultimap<IpAddress, RouteEntry> routesWaitingOnArp;
Jonathan Hart335ef462014-10-16 08:20:46 -070081
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080082 // The IPv4 address to MAC address mapping
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080083 private final Map<IpAddress, MacAddress> ip2Mac;
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080084
Thomas Vachuskab97cf282014-10-20 23:31:12 -070085 private final ApplicationId appId;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080086 private final IntentSynchronizer intentSynchronizer;
87 private final HostService hostService;
Jonathan Hart9965d772014-12-02 10:28:34 -080088 private final SdnIpConfigurationService configService;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080089 private final InterfaceService interfaceService;
90 private final ExecutorService bgpUpdatesExecutor;
91 private final HostListener hostListener;
Jonathan Hart335ef462014-10-16 08:20:46 -070092
93 /**
94 * Class constructor.
95 *
Jonathan Hart31582d12014-10-22 13:52:41 -070096 * @param appId the application ID
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080097 * @param intentSynchronizer the intent synchronizer
Jonathan Hart31582d12014-10-22 13:52:41 -070098 * @param configService the configuration service
Thomas Vachuskab97cf282014-10-20 23:31:12 -070099 * @param interfaceService the interface service
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800100 * @param hostService the host service
Jonathan Hart335ef462014-10-16 08:20:46 -0700101 */
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800102 public Router(ApplicationId appId, IntentSynchronizer intentSynchronizer,
Jonathan Hart9965d772014-12-02 10:28:34 -0800103 SdnIpConfigurationService configService,
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800104 InterfaceService interfaceService,
105 HostService hostService) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700106 this.appId = appId;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800107 this.intentSynchronizer = intentSynchronizer;
Jonathan Hart31582d12014-10-22 13:52:41 -0700108 this.configService = configService;
Jonathan Hart335ef462014-10-16 08:20:46 -0700109 this.interfaceService = interfaceService;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800110 this.hostService = hostService;
Jonathan Hart335ef462014-10-16 08:20:46 -0700111
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800112 this.hostListener = new InternalHostListener();
113
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800114 ribTable4 = new ConcurrentInvertedRadixTree<>(
115 new DefaultByteArrayNodeFactory());
116 ribTable6 = new ConcurrentInvertedRadixTree<>(
Jonathan Hart335ef462014-10-16 08:20:46 -0700117 new DefaultByteArrayNodeFactory());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800118 routeUpdatesQueue = new LinkedBlockingQueue<>();
Jonathan Hart335ef462014-10-16 08:20:46 -0700119 routesWaitingOnArp = Multimaps.synchronizedSetMultimap(
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800120 HashMultimap.<IpAddress, RouteEntry>create());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800121 ip2Mac = new ConcurrentHashMap<>();
Jonathan Hart335ef462014-10-16 08:20:46 -0700122
123 bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
Pavlin Radoslavov8b752442014-11-18 14:34:37 -0800124 new ThreadFactoryBuilder()
125 .setNameFormat("sdnip-bgp-updates-%d").build());
Jonathan Hart335ef462014-10-16 08:20:46 -0700126 }
127
128 /**
Jonathan Hart739c8352014-10-29 17:49:26 -0700129 * Starts the router.
Jonathan Hart335ef462014-10-16 08:20:46 -0700130 */
131 public void start() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800132 this.hostService.addListener(hostListener);
133
Jonathan Hart335ef462014-10-16 08:20:46 -0700134 bgpUpdatesExecutor.execute(new Runnable() {
135 @Override
136 public void run() {
137 doUpdatesThread();
138 }
139 });
Jonathan Hart335ef462014-10-16 08:20:46 -0700140 }
141
Jonathan Hart739c8352014-10-29 17:49:26 -0700142 /**
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800143 * Stops the router.
Jonathan Hart739c8352014-10-29 17:49:26 -0700144 */
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800145 public void stop() {
146 this.hostService.removeListener(hostListener);
147
148 // Stop the thread(s)
Jonathan Hart739c8352014-10-29 17:49:26 -0700149 bgpUpdatesExecutor.shutdownNow();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800150
151 synchronized (this) {
152 // Cleanup all local state
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800153 ribTable4 = new ConcurrentInvertedRadixTree<>(
154 new DefaultByteArrayNodeFactory());
155 ribTable6 = new ConcurrentInvertedRadixTree<>(
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800156 new DefaultByteArrayNodeFactory());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800157 routeUpdatesQueue.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800158 routesWaitingOnArp.clear();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800159 ip2Mac.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800160 }
Jonathan Hart739c8352014-10-29 17:49:26 -0700161 }
162
Jonathan Hart335ef462014-10-16 08:20:46 -0700163 @Override
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800164 public void update(Collection<RouteUpdate> routeUpdates) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700165 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800166 routeUpdatesQueue.put(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700167 } catch (InterruptedException e) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800168 log.debug("Interrupted while putting on routeUpdatesQueue", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700169 Thread.currentThread().interrupt();
170 }
171 }
172
173 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700174 * Thread for handling route updates.
175 */
176 private void doUpdatesThread() {
177 boolean interrupted = false;
178 try {
179 while (!interrupted) {
180 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800181 Collection<RouteUpdate> routeUpdates =
182 routeUpdatesQueue.take();
183 processRouteUpdates(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700184 } catch (InterruptedException e) {
185 log.debug("Interrupted while taking from updates queue", e);
186 interrupted = true;
187 } catch (Exception e) {
188 log.debug("exception", e);
189 }
190 }
191 } finally {
192 if (interrupted) {
193 Thread.currentThread().interrupt();
194 }
195 }
196 }
197
198 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800199 * Gets all IPv4 routes from the RIB.
200 *
201 * @return all IPv4 routes from the RIB
202 */
203 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 */
222 public Collection<RouteEntry> getRoutes6() {
223 Iterator<KeyValuePair<RouteEntry>> it =
224 ribTable6.getKeyValuePairsForKeysStartingWith("").iterator();
225
226 List<RouteEntry> routes = new LinkedList<>();
227
228 while (it.hasNext()) {
229 KeyValuePair<RouteEntry> entry = it.next();
230 routes.add(entry.getValue());
231 }
232
233 return routes;
234 }
235
236 /**
237 * Finds a route in the RIB for a prefix. The prefix can be either IPv4 or
238 * IPv6.
239 *
240 * @param prefix the prefix to use
241 * @return the route if found, otherwise null
242 */
243 RouteEntry findRibRoute(IpPrefix prefix) {
244 String binaryString = RouteEntry.createBinaryString(prefix);
245 if (prefix.version() == Ip4Address.VERSION) {
246 // IPv4
247 return ribTable4.getValueForExactKey(binaryString);
248 }
249 // IPv6
250 return ribTable6.getValueForExactKey(binaryString);
251 }
252
253 /**
254 * Adds a route to the RIB. The route can be either IPv4 or IPv6.
255 *
256 * @param routeEntry the route entry to use
257 */
258 void addRibRoute(RouteEntry routeEntry) {
259 if (routeEntry.prefix().version() == Ip4Address.VERSION) {
260 // IPv4
261 ribTable4.put(RouteEntry.createBinaryString(routeEntry.prefix()),
262 routeEntry);
263 } else {
264 // IPv6
265 ribTable6.put(RouteEntry.createBinaryString(routeEntry.prefix()),
266 routeEntry);
267 }
268 }
269
270 /**
271 * Removes a route for a prefix from the RIB. The prefix can be either IPv4
272 * or IPv6.
273 *
274 * @param prefix the prefix to use
275 * @return true if the rotue was found and removed, otherwise false
276 */
277 boolean removeRibRoute(IpPrefix prefix) {
278 if (prefix.version() == Ip4Address.VERSION) {
279 // IPv4
280 return ribTable4.remove(RouteEntry.createBinaryString(prefix));
281 }
282 // IPv6
283 return ribTable6.remove(RouteEntry.createBinaryString(prefix));
284 }
285
286 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800287 * Processes route updates.
288 *
289 * @param routeUpdates the route updates to process
290 */
291 void processRouteUpdates(Collection<RouteUpdate> routeUpdates) {
292 synchronized (this) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800293 Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800294 submitIntents = new LinkedList<>();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800295 Collection<IpPrefix> withdrawPrefixes = new LinkedList<>();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800296 MultiPointToSinglePointIntent intent;
297
298 for (RouteUpdate update : routeUpdates) {
299 switch (update.type()) {
300 case UPDATE:
301 intent = processRouteAdd(update.routeEntry(),
302 withdrawPrefixes);
303 if (intent != null) {
304 submitIntents.add(Pair.of(update.routeEntry().prefix(),
305 intent));
306 }
307 break;
308 case DELETE:
309 processRouteDelete(update.routeEntry(), withdrawPrefixes);
310 break;
311 default:
312 log.error("Unknown update Type: {}", update.type());
313 break;
314 }
315 }
316
317 intentSynchronizer.updateRouteIntents(submitIntents,
318 withdrawPrefixes);
319 }
320 }
321
322 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700323 * Processes adding a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700324 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800325 * The route entry is added to the radix tree. If there was an existing
326 * next hop for this prefix, but the next hop was different, then the
327 * old route entry is deleted.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700328 * </p>
Thomas Vachuska4b420772014-10-30 16:46:17 -0700329 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800330 * NOTE: Currently, we don't handle routes if the next hop is within the
331 * SDN domain.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700332 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700333 *
334 * @param routeEntry the route entry to add
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800335 * @param withdrawPrefixes the collection of accumulated prefixes whose
336 * intents will be withdrawn
337 * @return the corresponding intent that should be submitted, or null
Jonathan Hart335ef462014-10-16 08:20:46 -0700338 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800339 private MultiPointToSinglePointIntent processRouteAdd(
340 RouteEntry routeEntry,
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800341 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800342 log.debug("Processing route add: {}", routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700343
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800344 // Find the old next-hop if we are updating an old route entry
345 IpAddress oldNextHop = null;
346 RouteEntry oldRouteEntry = findRibRoute(routeEntry.prefix());
347 if (oldRouteEntry != null) {
348 oldNextHop = oldRouteEntry.nextHop();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800349 }
350
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800351 // Add the new route to the RIB
352 addRibRoute(routeEntry);
353
354 if (oldNextHop != null) {
355 if (oldNextHop.equals(routeEntry.nextHop())) {
356 return null; // No change
357 }
358 //
359 // Update an existing nexthop for the prefix.
360 // We need to remove the old flows for this prefix from the
361 // switches before the new flows are added.
362 //
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800363 withdrawPrefixes.add(routeEntry.prefix());
364 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800365
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800366 if (routeEntry.nextHop().isZero()) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800367 // Route originated by SDN domain
368 // We don't handle these at the moment
369 log.debug("Own route {} to {}",
370 routeEntry.prefix(), routeEntry.nextHop());
371 return null;
372 }
373
374 //
375 // Find the MAC address of next hop router for this route entry.
376 // If the MAC address can not be found in ARP cache, then this prefix
377 // will be put in routesWaitingOnArp queue. Otherwise, generate
378 // a new route intent.
379 //
380
381 // Monitor the IP address for updates of the MAC address
Jonathan Hart31582d12014-10-22 13:52:41 -0700382 hostService.startMonitoringIp(routeEntry.nextHop());
383
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800384 // Check if we know the MAC address of the next hop
385 MacAddress nextHopMacAddress = ip2Mac.get(routeEntry.nextHop());
386 if (nextHopMacAddress == null) {
387 Set<Host> hosts = hostService.getHostsByIp(routeEntry.nextHop());
388 if (!hosts.isEmpty()) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800389 nextHopMacAddress = hosts.iterator().next().mac();
390 }
391 if (nextHopMacAddress != null) {
392 ip2Mac.put(routeEntry.nextHop(), nextHopMacAddress);
393 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700394 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700395 if (nextHopMacAddress == null) {
396 routesWaitingOnArp.put(routeEntry.nextHop(), routeEntry);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800397 return null;
Jonathan Hart335ef462014-10-16 08:20:46 -0700398 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800399 return generateRouteIntent(routeEntry.prefix(), routeEntry.nextHop(),
400 nextHopMacAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700401 }
402
403 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800404 * Generates a route intent for a prefix, the next hop IP address, and
405 * the next hop MAC address.
406 * <p/>
407 * This method will find the egress interface for the intent.
408 * Intent will match dst IP prefix and rewrite dst MAC address at all other
409 * border switches, then forward packets according to dst MAC address.
Jonathan Hart335ef462014-10-16 08:20:46 -0700410 *
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700411 * @param prefix IP prefix of the route to add
412 * @param nextHopIpAddress IP address of the next hop
Jonathan Hart335ef462014-10-16 08:20:46 -0700413 * @param nextHopMacAddress MAC address of the next hop
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800414 * @return the generated intent, or null if no intent should be submitted
Jonathan Hart335ef462014-10-16 08:20:46 -0700415 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800416 private MultiPointToSinglePointIntent generateRouteIntent(
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800417 IpPrefix prefix,
418 IpAddress nextHopIpAddress,
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800419 MacAddress nextHopMacAddress) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700420
421 // Find the attachment point (egress interface) of the next hop
422 Interface egressInterface;
Jonathan Hart31582d12014-10-22 13:52:41 -0700423 if (configService.getBgpPeers().containsKey(nextHopIpAddress)) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700424 // Route to a peer
425 log.debug("Route to peer {}", nextHopIpAddress);
426 BgpPeer peer =
Jonathan Hart31582d12014-10-22 13:52:41 -0700427 configService.getBgpPeers().get(nextHopIpAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700428 egressInterface =
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700429 interfaceService.getInterface(peer.connectPoint());
Jonathan Hart335ef462014-10-16 08:20:46 -0700430 } else {
431 // Route to non-peer
432 log.debug("Route to non-peer {}", nextHopIpAddress);
433 egressInterface =
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700434 interfaceService.getMatchingInterface(nextHopIpAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700435 if (egressInterface == null) {
436 log.warn("No outgoing interface found for {}",
437 nextHopIpAddress);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800438 return null;
Jonathan Hart335ef462014-10-16 08:20:46 -0700439 }
440 }
441
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800442 //
443 // Generate the intent itself
444 //
Jonathan Hart335ef462014-10-16 08:20:46 -0700445 Set<ConnectPoint> ingressPorts = new HashSet<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800446 ConnectPoint egressPort = egressInterface.connectPoint();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800447 log.debug("Generating intent for prefix {}, next hop mac {}",
448 prefix, nextHopMacAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700449
450 for (Interface intf : interfaceService.getInterfaces()) {
Jonathan Hart2e3eef32014-11-12 11:05:40 -0800451 if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700452 ConnectPoint srcPort = intf.connectPoint();
453 ingressPorts.add(srcPort);
454 }
455 }
456
457 // Match the destination IP prefix at the first hop
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800458 TrafficSelector selector;
459 if (prefix.version() == Ip4Address.VERSION) {
460 selector = DefaultTrafficSelector.builder()
Jonathan Hart335ef462014-10-16 08:20:46 -0700461 .matchEthType(Ethernet.TYPE_IPV4)
462 .matchIPDst(prefix)
463 .build();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800464 } else {
465 selector = DefaultTrafficSelector.builder()
466 .matchEthType(Ethernet.TYPE_IPV6)
467 .matchIPDst(prefix)
468 .build();
469 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700470
471 // Rewrite the destination MAC address
Jonathan Hart335ef462014-10-16 08:20:46 -0700472 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
473 .setEthDst(nextHopMacAddress)
474 .build();
475
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800476 return new MultiPointToSinglePointIntent(appId, selector, treatment,
477 ingressPorts, egressPort);
Jonathan Hart335ef462014-10-16 08:20:46 -0700478 }
479
480 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800481 * Processes the deletion of a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700482 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800483 * The prefix for the routing entry is removed from radix tree.
484 * If the operation is successful, the prefix is added to the collection
485 * of prefixes whose intents that will be withdrawn.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700486 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700487 *
488 * @param routeEntry the route entry to delete
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800489 * @param withdrawPrefixes the collection of accumulated prefixes whose
490 * intents will be withdrawn
Jonathan Hart335ef462014-10-16 08:20:46 -0700491 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800492 private void processRouteDelete(RouteEntry routeEntry,
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800493 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800494 log.debug("Processing route delete: {}", routeEntry);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800495 boolean isRemoved = removeRibRoute(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700496
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800497 if (isRemoved) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800498 //
499 // Only withdraw intents if an entry was actually removed from the
500 // tree. If no entry was removed, the <prefix, nexthop> wasn't
501 // there so it's probably already been removed and we don't
502 // need to do anything.
503 //
504 withdrawPrefixes.add(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700505 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700506
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800507 routesWaitingOnArp.remove(routeEntry.nextHop(), routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700508 }
509
510 /**
Jonathan Hart31582d12014-10-22 13:52:41 -0700511 * Signals the Router that the MAC to IP mapping has potentially been
512 * updated. This has the effect of updating the MAC address for any
513 * installed prefixes if it has changed, as well as installing any pending
514 * prefixes that were waiting for MAC resolution.
Jonathan Hart335ef462014-10-16 08:20:46 -0700515 *
Jonathan Hart31582d12014-10-22 13:52:41 -0700516 * @param ipAddress the IP address that an event was received for
517 * @param macAddress the most recently known MAC address for the IP address
Jonathan Hart335ef462014-10-16 08:20:46 -0700518 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800519 private void updateMac(IpAddress ipAddress, MacAddress macAddress) {
520 log.debug("Received updated MAC info: {} => {}", ipAddress,
521 macAddress);
Jonathan Hart31582d12014-10-22 13:52:41 -0700522
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800523 //
524 // We synchronize on "this" to prevent changes to the Radix tree
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700525 // while we're pushing intents. If the tree changes, the
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800526 // tree and the intents could get out of sync.
527 //
Jonathan Hart335ef462014-10-16 08:20:46 -0700528 synchronized (this) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800529 Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800530 submitIntents = new LinkedList<>();
531 MultiPointToSinglePointIntent intent;
Jonathan Hart335ef462014-10-16 08:20:46 -0700532
533 Set<RouteEntry> routesToPush =
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800534 routesWaitingOnArp.removeAll(ipAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700535
536 for (RouteEntry routeEntry : routesToPush) {
537 // These will always be adds
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800538 RouteEntry foundRouteEntry = findRibRoute(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700539 if (foundRouteEntry != null &&
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800540 foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700541 // We only push prefix flows if the prefix is still in the
Jonathan Hart0b04bed2014-10-16 16:39:19 -0700542 // radix tree and the next hop is the same as our
Jonathan Hart335ef462014-10-16 08:20:46 -0700543 // update.
544 // The prefix could have been removed while we were waiting
545 // for the ARP, or the next hop could have changed.
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800546 intent = generateRouteIntent(routeEntry.prefix(),
547 ipAddress, macAddress);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800548 if (intent != null) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800549 submitIntents.add(Pair.of(routeEntry.prefix(),
550 intent));
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800551 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700552 } else {
Jonathan Hart31582d12014-10-22 13:52:41 -0700553 log.debug("{} has been revoked before the MAC was resolved",
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800554 routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700555 }
556 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800557
558 if (!submitIntents.isEmpty()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800559 Collection<IpPrefix> withdrawPrefixes = new LinkedList<>();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800560 intentSynchronizer.updateRouteIntents(submitIntents,
561 withdrawPrefixes);
562 }
563
564 ip2Mac.put(ipAddress, macAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700565 }
566 }
567
568 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700569 * Listener for host events.
570 */
571 class InternalHostListener implements HostListener {
572 @Override
573 public void event(HostEvent event) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800574 log.debug("Received HostEvent {}", event);
575
576 Host host = event.subject();
577 switch (event.type()) {
578 case HOST_ADDED:
579 // FALLTHROUGH
580 case HOST_UPDATED:
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800581 for (IpAddress ipAddress : host.ipAddresses()) {
582 updateMac(ipAddress, host.mac());
Jonathan Hart335ef462014-10-16 08:20:46 -0700583 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800584 break;
585 case HOST_REMOVED:
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800586 for (IpAddress ipAddress : host.ipAddresses()) {
587 ip2Mac.remove(ipAddress);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800588 }
589 break;
590 default:
591 break;
Jonathan Hart335ef462014-10-16 08:20:46 -0700592 }
593 }
594 }
595}