blob: 2b2213a0aa9ae62a15377b1b99a8f1c747f27fc8 [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 Hart6cd2f352015-01-13 17:44:45 -080033import org.onlab.packet.Ip4Address;
Jonathan Hart7dd4a332014-12-04 17:37:05 -080034import org.onlab.packet.IpAddress;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080035import org.onlab.packet.IpPrefix;
Jonathan Hart7dd4a332014-12-04 17:37:05 -080036import org.onlab.packet.MacAddress;
Jonathan Hart6cd2f352015-01-13 17:44:45 -080037import org.onlab.packet.VlanId;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.core.ApplicationId;
39import org.onosproject.net.ConnectPoint;
40import org.onosproject.net.Host;
41import org.onosproject.net.flow.DefaultTrafficSelector;
42import org.onosproject.net.flow.DefaultTrafficTreatment;
43import org.onosproject.net.flow.TrafficSelector;
44import org.onosproject.net.flow.TrafficTreatment;
45import org.onosproject.net.host.HostEvent;
46import org.onosproject.net.host.HostListener;
47import org.onosproject.net.host.HostService;
48import org.onosproject.net.intent.MultiPointToSinglePointIntent;
49import org.onosproject.sdnip.config.BgpPeer;
50import org.onosproject.sdnip.config.Interface;
51import org.onosproject.sdnip.config.SdnIpConfigurationService;
Thomas Vachuskab97cf282014-10-20 23:31:12 -070052import org.slf4j.Logger;
53import org.slf4j.LoggerFactory;
54
Pingping3855f312014-10-22 12:50:37 -070055import com.google.common.collect.HashMultimap;
56import com.google.common.collect.Multimaps;
57import com.google.common.collect.SetMultimap;
58import com.google.common.util.concurrent.ThreadFactoryBuilder;
59import com.googlecode.concurrenttrees.common.KeyValuePair;
60import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
61import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
62import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
Jonathan Hart335ef462014-10-16 08:20:46 -070063
Jonathan Hart335ef462014-10-16 08:20:46 -070064/**
65 * This class processes BGP route update, translates each update into a intent
66 * and submits the intent.
Jonathan Hart335ef462014-10-16 08:20:46 -070067 */
68public class Router implements RouteListener {
69
70 private static final Logger log = LoggerFactory.getLogger(Router.class);
71
Jonathan Hart0b04bed2014-10-16 16:39:19 -070072 // Store all route updates in a radix tree.
73 // The key in this tree is the binary string of prefix of the route.
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080074 private InvertedRadixTree<RouteEntry> ribTable4;
75 private InvertedRadixTree<RouteEntry> ribTable6;
Jonathan Hart335ef462014-10-16 08:20:46 -070076
77 // Stores all incoming route updates in a queue.
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080078 private final BlockingQueue<Collection<RouteUpdate>> routeUpdatesQueue;
Jonathan Hart335ef462014-10-16 08:20:46 -070079
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080080 // The IpAddress is the next hop address of each route update.
81 private final SetMultimap<IpAddress, RouteEntry> routesWaitingOnArp;
Jonathan Hart335ef462014-10-16 08:20:46 -070082
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080083 // The IPv4 address to MAC address mapping
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080084 private final Map<IpAddress, MacAddress> ip2Mac;
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080085
Thomas Vachuskab97cf282014-10-20 23:31:12 -070086 private final ApplicationId appId;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080087 private final IntentSynchronizer intentSynchronizer;
88 private final HostService hostService;
Jonathan Hart9965d772014-12-02 10:28:34 -080089 private final SdnIpConfigurationService configService;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080090 private final InterfaceService interfaceService;
91 private final ExecutorService bgpUpdatesExecutor;
92 private final HostListener hostListener;
Jonathan Hart335ef462014-10-16 08:20:46 -070093
94 /**
95 * Class constructor.
96 *
Jonathan Hart31582d12014-10-22 13:52:41 -070097 * @param appId the application ID
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080098 * @param intentSynchronizer the intent synchronizer
Jonathan Hart31582d12014-10-22 13:52:41 -070099 * @param configService the configuration service
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700100 * @param interfaceService the interface service
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800101 * @param hostService the host service
Jonathan Hart335ef462014-10-16 08:20:46 -0700102 */
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800103 public Router(ApplicationId appId, IntentSynchronizer intentSynchronizer,
Jonathan Hart9965d772014-12-02 10:28:34 -0800104 SdnIpConfigurationService configService,
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800105 InterfaceService interfaceService,
106 HostService hostService) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700107 this.appId = appId;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800108 this.intentSynchronizer = intentSynchronizer;
Jonathan Hart31582d12014-10-22 13:52:41 -0700109 this.configService = configService;
Jonathan Hart335ef462014-10-16 08:20:46 -0700110 this.interfaceService = interfaceService;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800111 this.hostService = hostService;
Jonathan Hart335ef462014-10-16 08:20:46 -0700112
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800113 this.hostListener = new InternalHostListener();
114
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800115 ribTable4 = new ConcurrentInvertedRadixTree<>(
116 new DefaultByteArrayNodeFactory());
117 ribTable6 = new ConcurrentInvertedRadixTree<>(
Jonathan Hart335ef462014-10-16 08:20:46 -0700118 new DefaultByteArrayNodeFactory());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800119 routeUpdatesQueue = new LinkedBlockingQueue<>();
Jonathan Hart335ef462014-10-16 08:20:46 -0700120 routesWaitingOnArp = Multimaps.synchronizedSetMultimap(
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800121 HashMultimap.<IpAddress, RouteEntry>create());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800122 ip2Mac = new ConcurrentHashMap<>();
Jonathan Hart335ef462014-10-16 08:20:46 -0700123
124 bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
Pavlin Radoslavov8b752442014-11-18 14:34:37 -0800125 new ThreadFactoryBuilder()
126 .setNameFormat("sdnip-bgp-updates-%d").build());
Jonathan Hart335ef462014-10-16 08:20:46 -0700127 }
128
129 /**
Jonathan Hart739c8352014-10-29 17:49:26 -0700130 * Starts the router.
Jonathan Hart335ef462014-10-16 08:20:46 -0700131 */
132 public void start() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800133 this.hostService.addListener(hostListener);
134
Jonathan Hart335ef462014-10-16 08:20:46 -0700135 bgpUpdatesExecutor.execute(new Runnable() {
136 @Override
137 public void run() {
138 doUpdatesThread();
139 }
140 });
Jonathan Hart335ef462014-10-16 08:20:46 -0700141 }
142
Jonathan Hart739c8352014-10-29 17:49:26 -0700143 /**
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800144 * Stops the router.
Jonathan Hart739c8352014-10-29 17:49:26 -0700145 */
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800146 public void stop() {
147 this.hostService.removeListener(hostListener);
148
149 // Stop the thread(s)
Jonathan Hart739c8352014-10-29 17:49:26 -0700150 bgpUpdatesExecutor.shutdownNow();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800151
152 synchronized (this) {
153 // Cleanup all local state
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800154 ribTable4 = new ConcurrentInvertedRadixTree<>(
155 new DefaultByteArrayNodeFactory());
156 ribTable6 = new ConcurrentInvertedRadixTree<>(
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800157 new DefaultByteArrayNodeFactory());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800158 routeUpdatesQueue.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800159 routesWaitingOnArp.clear();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800160 ip2Mac.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800161 }
Jonathan Hart739c8352014-10-29 17:49:26 -0700162 }
163
Jonathan Hart335ef462014-10-16 08:20:46 -0700164 @Override
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800165 public void update(Collection<RouteUpdate> routeUpdates) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700166 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800167 routeUpdatesQueue.put(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700168 } catch (InterruptedException e) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800169 log.debug("Interrupted while putting on routeUpdatesQueue", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700170 Thread.currentThread().interrupt();
171 }
172 }
173
174 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700175 * Thread for handling route updates.
176 */
177 private void doUpdatesThread() {
178 boolean interrupted = false;
179 try {
180 while (!interrupted) {
181 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800182 Collection<RouteUpdate> routeUpdates =
183 routeUpdatesQueue.take();
184 processRouteUpdates(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700185 } catch (InterruptedException e) {
186 log.debug("Interrupted while taking from updates queue", e);
187 interrupted = true;
188 } catch (Exception e) {
189 log.debug("exception", e);
190 }
191 }
192 } finally {
193 if (interrupted) {
194 Thread.currentThread().interrupt();
195 }
196 }
197 }
198
199 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800200 * Gets all IPv4 routes from the RIB.
201 *
202 * @return all IPv4 routes from the RIB
203 */
204 public Collection<RouteEntry> getRoutes4() {
205 Iterator<KeyValuePair<RouteEntry>> it =
206 ribTable4.getKeyValuePairsForKeysStartingWith("").iterator();
207
208 List<RouteEntry> routes = new LinkedList<>();
209
210 while (it.hasNext()) {
211 KeyValuePair<RouteEntry> entry = it.next();
212 routes.add(entry.getValue());
213 }
214
215 return routes;
216 }
217
218 /**
219 * Gets all IPv6 routes from the RIB.
220 *
221 * @return all IPv6 routes from the RIB
222 */
223 public Collection<RouteEntry> getRoutes6() {
224 Iterator<KeyValuePair<RouteEntry>> it =
225 ribTable6.getKeyValuePairsForKeysStartingWith("").iterator();
226
227 List<RouteEntry> routes = new LinkedList<>();
228
229 while (it.hasNext()) {
230 KeyValuePair<RouteEntry> entry = it.next();
231 routes.add(entry.getValue());
232 }
233
234 return routes;
235 }
236
237 /**
238 * Finds a route in the RIB for a prefix. The prefix can be either IPv4 or
239 * IPv6.
240 *
241 * @param prefix the prefix to use
242 * @return the route if found, otherwise null
243 */
244 RouteEntry findRibRoute(IpPrefix prefix) {
245 String binaryString = RouteEntry.createBinaryString(prefix);
246 if (prefix.version() == Ip4Address.VERSION) {
247 // IPv4
248 return ribTable4.getValueForExactKey(binaryString);
249 }
250 // IPv6
251 return ribTable6.getValueForExactKey(binaryString);
252 }
253
254 /**
255 * Adds a route to the RIB. The route can be either IPv4 or IPv6.
256 *
257 * @param routeEntry the route entry to use
258 */
259 void addRibRoute(RouteEntry routeEntry) {
260 if (routeEntry.prefix().version() == Ip4Address.VERSION) {
261 // IPv4
262 ribTable4.put(RouteEntry.createBinaryString(routeEntry.prefix()),
263 routeEntry);
264 } else {
265 // IPv6
266 ribTable6.put(RouteEntry.createBinaryString(routeEntry.prefix()),
267 routeEntry);
268 }
269 }
270
271 /**
272 * Removes a route for a prefix from the RIB. The prefix can be either IPv4
273 * or IPv6.
274 *
275 * @param prefix the prefix to use
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800276 * @return true if the route was found and removed, otherwise false
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800277 */
278 boolean removeRibRoute(IpPrefix prefix) {
279 if (prefix.version() == Ip4Address.VERSION) {
280 // IPv4
281 return ribTable4.remove(RouteEntry.createBinaryString(prefix));
282 }
283 // IPv6
284 return ribTable6.remove(RouteEntry.createBinaryString(prefix));
285 }
286
287 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800288 * Processes route updates.
289 *
290 * @param routeUpdates the route updates to process
291 */
292 void processRouteUpdates(Collection<RouteUpdate> routeUpdates) {
293 synchronized (this) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800294 Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800295 submitIntents = new LinkedList<>();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800296 Collection<IpPrefix> withdrawPrefixes = new LinkedList<>();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800297 MultiPointToSinglePointIntent intent;
298
299 for (RouteUpdate update : routeUpdates) {
300 switch (update.type()) {
301 case UPDATE:
302 intent = processRouteAdd(update.routeEntry(),
303 withdrawPrefixes);
304 if (intent != null) {
305 submitIntents.add(Pair.of(update.routeEntry().prefix(),
306 intent));
307 }
308 break;
309 case DELETE:
310 processRouteDelete(update.routeEntry(), withdrawPrefixes);
311 break;
312 default:
313 log.error("Unknown update Type: {}", update.type());
314 break;
315 }
316 }
317
318 intentSynchronizer.updateRouteIntents(submitIntents,
319 withdrawPrefixes);
320 }
321 }
322
323 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700324 * Processes adding a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700325 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800326 * The route entry is added to the radix tree. If there was an existing
327 * next hop for this prefix, but the next hop was different, then the
328 * old route entry is deleted.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700329 * </p>
Thomas Vachuska4b420772014-10-30 16:46:17 -0700330 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800331 * NOTE: Currently, we don't handle routes if the next hop is within the
332 * SDN domain.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700333 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700334 *
335 * @param routeEntry the route entry to add
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800336 * @param withdrawPrefixes the collection of accumulated prefixes whose
337 * intents will be withdrawn
338 * @return the corresponding intent that should be submitted, or null
Jonathan Hart335ef462014-10-16 08:20:46 -0700339 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800340 private MultiPointToSinglePointIntent processRouteAdd(
341 RouteEntry routeEntry,
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800342 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800343 log.debug("Processing route add: {}", routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700344
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800345 // Find the old next-hop if we are updating an old route entry
346 IpAddress oldNextHop = null;
347 RouteEntry oldRouteEntry = findRibRoute(routeEntry.prefix());
348 if (oldRouteEntry != null) {
349 oldNextHop = oldRouteEntry.nextHop();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800350 }
351
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800352 // Add the new route to the RIB
353 addRibRoute(routeEntry);
354
355 if (oldNextHop != null) {
356 if (oldNextHop.equals(routeEntry.nextHop())) {
357 return null; // No change
358 }
359 //
360 // Update an existing nexthop for the prefix.
361 // We need to remove the old flows for this prefix from the
362 // switches before the new flows are added.
363 //
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800364 withdrawPrefixes.add(routeEntry.prefix());
365 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800366
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800367 if (routeEntry.nextHop().isZero()) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800368 // Route originated by SDN domain
369 // We don't handle these at the moment
370 log.debug("Own route {} to {}",
371 routeEntry.prefix(), routeEntry.nextHop());
372 return null;
373 }
374
375 //
376 // Find the MAC address of next hop router for this route entry.
377 // If the MAC address can not be found in ARP cache, then this prefix
378 // will be put in routesWaitingOnArp queue. Otherwise, generate
379 // a new route intent.
380 //
381
382 // Monitor the IP address for updates of the MAC address
Jonathan Hart31582d12014-10-22 13:52:41 -0700383 hostService.startMonitoringIp(routeEntry.nextHop());
384
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800385 // Check if we know the MAC address of the next hop
386 MacAddress nextHopMacAddress = ip2Mac.get(routeEntry.nextHop());
387 if (nextHopMacAddress == null) {
388 Set<Host> hosts = hostService.getHostsByIp(routeEntry.nextHop());
389 if (!hosts.isEmpty()) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800390 nextHopMacAddress = hosts.iterator().next().mac();
391 }
392 if (nextHopMacAddress != null) {
393 ip2Mac.put(routeEntry.nextHop(), nextHopMacAddress);
394 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700395 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700396 if (nextHopMacAddress == null) {
397 routesWaitingOnArp.put(routeEntry.nextHop(), routeEntry);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800398 return null;
Jonathan Hart335ef462014-10-16 08:20:46 -0700399 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800400 return generateRouteIntent(routeEntry.prefix(), routeEntry.nextHop(),
401 nextHopMacAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700402 }
403
404 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800405 * Generates a route intent for a prefix, the next hop IP address, and
406 * the next hop MAC address.
407 * <p/>
408 * This method will find the egress interface for the intent.
409 * Intent will match dst IP prefix and rewrite dst MAC address at all other
410 * border switches, then forward packets according to dst MAC address.
Jonathan Hart335ef462014-10-16 08:20:46 -0700411 *
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700412 * @param prefix IP prefix of the route to add
413 * @param nextHopIpAddress IP address of the next hop
Jonathan Hart335ef462014-10-16 08:20:46 -0700414 * @param nextHopMacAddress MAC address of the next hop
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800415 * @return the generated intent, or null if no intent should be submitted
Jonathan Hart335ef462014-10-16 08:20:46 -0700416 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800417 private MultiPointToSinglePointIntent generateRouteIntent(
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800418 IpPrefix prefix,
419 IpAddress nextHopIpAddress,
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800420 MacAddress nextHopMacAddress) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700421
422 // Find the attachment point (egress interface) of the next hop
423 Interface egressInterface;
Jonathan Hart31582d12014-10-22 13:52:41 -0700424 if (configService.getBgpPeers().containsKey(nextHopIpAddress)) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700425 // Route to a peer
426 log.debug("Route to peer {}", nextHopIpAddress);
427 BgpPeer peer =
Jonathan Hart31582d12014-10-22 13:52:41 -0700428 configService.getBgpPeers().get(nextHopIpAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700429 egressInterface =
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700430 interfaceService.getInterface(peer.connectPoint());
Jonathan Hart335ef462014-10-16 08:20:46 -0700431 } else {
432 // Route to non-peer
433 log.debug("Route to non-peer {}", nextHopIpAddress);
434 egressInterface =
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700435 interfaceService.getMatchingInterface(nextHopIpAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700436 if (egressInterface == null) {
437 log.warn("No outgoing interface found for {}",
438 nextHopIpAddress);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800439 return null;
Jonathan Hart335ef462014-10-16 08:20:46 -0700440 }
441 }
442
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800443 //
444 // Generate the intent itself
445 //
Jonathan Hart335ef462014-10-16 08:20:46 -0700446 Set<ConnectPoint> ingressPorts = new HashSet<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800447 ConnectPoint egressPort = egressInterface.connectPoint();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800448 log.debug("Generating intent for prefix {}, next hop mac {}",
449 prefix, nextHopMacAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700450
451 for (Interface intf : interfaceService.getInterfaces()) {
Jonathan Hart2e3eef32014-11-12 11:05:40 -0800452 if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700453 ConnectPoint srcPort = intf.connectPoint();
454 ingressPorts.add(srcPort);
455 }
456 }
457
458 // Match the destination IP prefix at the first hop
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800459 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800460 if (prefix.version() == Ip4Address.VERSION) {
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800461 selector.matchEthType(Ethernet.TYPE_IPV4);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800462 } else {
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800463 selector.matchEthType(Ethernet.TYPE_IPV6);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800464 }
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800465 selector.matchIPDst(prefix);
Jonathan Hart335ef462014-10-16 08:20:46 -0700466
467 // Rewrite the destination MAC address
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800468 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
469 .setEthDst(nextHopMacAddress);
470 if (!egressInterface.vlan().equals(VlanId.NONE)) {
471 treatment.setVlanId(egressInterface.vlan());
472 // If we set VLAN ID, we have to make sure a VLAN tag exists.
473 // TODO support no VLAN -> VLAN routing
474 selector.matchVlanId(VlanId.ANY);
475 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700476
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800477 return new MultiPointToSinglePointIntent(appId, selector.build(),
478 treatment.build(),
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800479 ingressPorts, egressPort);
Jonathan Hart335ef462014-10-16 08:20:46 -0700480 }
481
482 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800483 * Processes the deletion of a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700484 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800485 * The prefix for the routing entry is removed from radix tree.
486 * If the operation is successful, the prefix is added to the collection
487 * of prefixes whose intents that will be withdrawn.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700488 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700489 *
490 * @param routeEntry the route entry to delete
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800491 * @param withdrawPrefixes the collection of accumulated prefixes whose
492 * intents will be withdrawn
Jonathan Hart335ef462014-10-16 08:20:46 -0700493 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800494 private void processRouteDelete(RouteEntry routeEntry,
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800495 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800496 log.debug("Processing route delete: {}", routeEntry);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800497 boolean isRemoved = removeRibRoute(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700498
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800499 if (isRemoved) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800500 //
501 // Only withdraw intents if an entry was actually removed from the
502 // tree. If no entry was removed, the <prefix, nexthop> wasn't
503 // there so it's probably already been removed and we don't
504 // need to do anything.
505 //
506 withdrawPrefixes.add(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700507 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700508
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800509 routesWaitingOnArp.remove(routeEntry.nextHop(), routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700510 }
511
512 /**
Jonathan Hart31582d12014-10-22 13:52:41 -0700513 * Signals the Router that the MAC to IP mapping has potentially been
514 * updated. This has the effect of updating the MAC address for any
515 * installed prefixes if it has changed, as well as installing any pending
516 * prefixes that were waiting for MAC resolution.
Jonathan Hart335ef462014-10-16 08:20:46 -0700517 *
Jonathan Hart31582d12014-10-22 13:52:41 -0700518 * @param ipAddress the IP address that an event was received for
519 * @param macAddress the most recently known MAC address for the IP address
Jonathan Hart335ef462014-10-16 08:20:46 -0700520 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800521 private void updateMac(IpAddress ipAddress, MacAddress macAddress) {
522 log.debug("Received updated MAC info: {} => {}", ipAddress,
523 macAddress);
Jonathan Hart31582d12014-10-22 13:52:41 -0700524
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800525 //
526 // We synchronize on "this" to prevent changes to the Radix tree
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700527 // while we're pushing intents. If the tree changes, the
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800528 // tree and the intents could get out of sync.
529 //
Jonathan Hart335ef462014-10-16 08:20:46 -0700530 synchronized (this) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800531 Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800532 submitIntents = new LinkedList<>();
533 MultiPointToSinglePointIntent intent;
Jonathan Hart335ef462014-10-16 08:20:46 -0700534
535 Set<RouteEntry> routesToPush =
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800536 routesWaitingOnArp.removeAll(ipAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700537
538 for (RouteEntry routeEntry : routesToPush) {
539 // These will always be adds
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800540 RouteEntry foundRouteEntry = findRibRoute(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700541 if (foundRouteEntry != null &&
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800542 foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700543 // We only push prefix flows if the prefix is still in the
Jonathan Hart0b04bed2014-10-16 16:39:19 -0700544 // radix tree and the next hop is the same as our
Jonathan Hart335ef462014-10-16 08:20:46 -0700545 // update.
546 // The prefix could have been removed while we were waiting
547 // for the ARP, or the next hop could have changed.
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800548 intent = generateRouteIntent(routeEntry.prefix(),
549 ipAddress, macAddress);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800550 if (intent != null) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800551 submitIntents.add(Pair.of(routeEntry.prefix(),
552 intent));
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800553 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700554 } else {
Jonathan Hart31582d12014-10-22 13:52:41 -0700555 log.debug("{} has been revoked before the MAC was resolved",
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800556 routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700557 }
558 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800559
560 if (!submitIntents.isEmpty()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800561 Collection<IpPrefix> withdrawPrefixes = new LinkedList<>();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800562 intentSynchronizer.updateRouteIntents(submitIntents,
563 withdrawPrefixes);
564 }
565
566 ip2Mac.put(ipAddress, macAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700567 }
568 }
569
570 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700571 * Listener for host events.
572 */
573 class InternalHostListener implements HostListener {
574 @Override
575 public void event(HostEvent event) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800576 log.debug("Received HostEvent {}", event);
577
578 Host host = event.subject();
579 switch (event.type()) {
580 case HOST_ADDED:
581 // FALLTHROUGH
582 case HOST_UPDATED:
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800583 for (IpAddress ipAddress : host.ipAddresses()) {
584 updateMac(ipAddress, host.mac());
Jonathan Hart335ef462014-10-16 08:20:46 -0700585 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800586 break;
587 case HOST_REMOVED:
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800588 for (IpAddress ipAddress : host.ipAddresses()) {
589 ip2Mac.remove(ipAddress);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800590 }
591 break;
592 default:
593 break;
Jonathan Hart335ef462014-10-16 08:20:46 -0700594 }
595 }
596 }
597}