blob: 44cc3bf024c0f538f7e78819218f4d3518e28abd [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;
33import org.onlab.packet.Ip4Address;
34import org.onlab.packet.Ip4Prefix;
35import org.onlab.packet.IpAddress;
36import 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);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080070 // For routes announced by local BGP daemon in SDN network,
71 // the next hop will be 0.0.0.0.
72 private static final Ip4Address LOCAL_NEXT_HOP =
73 Ip4Address.valueOf("0.0.0.0");
Jonathan Hart335ef462014-10-16 08:20:46 -070074
Jonathan Hart0b04bed2014-10-16 16:39:19 -070075 // Store all route updates in a radix tree.
76 // The key in this tree is the binary string of prefix of the route.
Pavlin Radoslavov64c1ed12014-12-10 18:10:55 -080077 private InvertedRadixTree<RouteEntry> ribTable;
Jonathan Hart335ef462014-10-16 08:20:46 -070078
79 // Stores all incoming route updates in a queue.
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080080 private final BlockingQueue<Collection<RouteUpdate>> routeUpdatesQueue;
Jonathan Hart335ef462014-10-16 08:20:46 -070081
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080082 // The Ip4Address is the next hop address of each route update.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080083 private final SetMultimap<Ip4Address, RouteEntry> routesWaitingOnArp;
Jonathan Hart335ef462014-10-16 08:20:46 -070084
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080085 // The IPv4 address to MAC address mapping
86 private final Map<Ip4Address, MacAddress> ip2Mac;
87
Thomas Vachuskab97cf282014-10-20 23:31:12 -070088 private final ApplicationId appId;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080089 private final IntentSynchronizer intentSynchronizer;
90 private final HostService hostService;
Jonathan Hart9965d772014-12-02 10:28:34 -080091 private final SdnIpConfigurationService configService;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080092 private final InterfaceService interfaceService;
93 private final ExecutorService bgpUpdatesExecutor;
94 private final HostListener hostListener;
Jonathan Hart335ef462014-10-16 08:20:46 -070095
96 /**
97 * Class constructor.
98 *
Jonathan Hart31582d12014-10-22 13:52:41 -070099 * @param appId the application ID
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800100 * @param intentSynchronizer the intent synchronizer
Jonathan Hart31582d12014-10-22 13:52:41 -0700101 * @param configService the configuration service
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700102 * @param interfaceService the interface service
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800103 * @param hostService the host service
Jonathan Hart335ef462014-10-16 08:20:46 -0700104 */
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800105 public Router(ApplicationId appId, IntentSynchronizer intentSynchronizer,
Jonathan Hart9965d772014-12-02 10:28:34 -0800106 SdnIpConfigurationService configService,
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800107 InterfaceService interfaceService,
108 HostService hostService) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700109 this.appId = appId;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800110 this.intentSynchronizer = intentSynchronizer;
Jonathan Hart31582d12014-10-22 13:52:41 -0700111 this.configService = configService;
Jonathan Hart335ef462014-10-16 08:20:46 -0700112 this.interfaceService = interfaceService;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800113 this.hostService = hostService;
Jonathan Hart335ef462014-10-16 08:20:46 -0700114
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800115 this.hostListener = new InternalHostListener();
116
Pavlin Radoslavov64c1ed12014-12-10 18:10:55 -0800117 ribTable = 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 Radoslavov6b570732014-11-06 13:16:45 -0800121 HashMultimap.<Ip4Address, 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 Radoslavov64c1ed12014-12-10 18:10:55 -0800154 ribTable = new ConcurrentInvertedRadixTree<>(
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800155 new DefaultByteArrayNodeFactory());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800156 routeUpdatesQueue.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800157 routesWaitingOnArp.clear();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800158 ip2Mac.clear();
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800159 }
Jonathan Hart739c8352014-10-29 17:49:26 -0700160 }
161
Jonathan Hart335ef462014-10-16 08:20:46 -0700162 @Override
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800163 public void update(Collection<RouteUpdate> routeUpdates) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700164 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800165 routeUpdatesQueue.put(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700166 } catch (InterruptedException e) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800167 log.debug("Interrupted while putting on routeUpdatesQueue", e);
Jonathan Hart335ef462014-10-16 08:20:46 -0700168 Thread.currentThread().interrupt();
169 }
170 }
171
172 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700173 * Thread for handling route updates.
174 */
175 private void doUpdatesThread() {
176 boolean interrupted = false;
177 try {
178 while (!interrupted) {
179 try {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800180 Collection<RouteUpdate> routeUpdates =
181 routeUpdatesQueue.take();
182 processRouteUpdates(routeUpdates);
Jonathan Hart335ef462014-10-16 08:20:46 -0700183 } catch (InterruptedException e) {
184 log.debug("Interrupted while taking from updates queue", e);
185 interrupted = true;
186 } catch (Exception e) {
187 log.debug("exception", e);
188 }
189 }
190 } finally {
191 if (interrupted) {
192 Thread.currentThread().interrupt();
193 }
194 }
195 }
196
197 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800198 * Processes route updates.
199 *
200 * @param routeUpdates the route updates to process
201 */
202 void processRouteUpdates(Collection<RouteUpdate> routeUpdates) {
203 synchronized (this) {
204 Collection<Pair<Ip4Prefix, MultiPointToSinglePointIntent>>
205 submitIntents = new LinkedList<>();
206 Collection<Ip4Prefix> withdrawPrefixes = new LinkedList<>();
207 MultiPointToSinglePointIntent intent;
208
209 for (RouteUpdate update : routeUpdates) {
210 switch (update.type()) {
211 case UPDATE:
212 intent = processRouteAdd(update.routeEntry(),
213 withdrawPrefixes);
214 if (intent != null) {
215 submitIntents.add(Pair.of(update.routeEntry().prefix(),
216 intent));
217 }
218 break;
219 case DELETE:
220 processRouteDelete(update.routeEntry(), withdrawPrefixes);
221 break;
222 default:
223 log.error("Unknown update Type: {}", update.type());
224 break;
225 }
226 }
227
228 intentSynchronizer.updateRouteIntents(submitIntents,
229 withdrawPrefixes);
230 }
231 }
232
233 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700234 * Processes adding a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700235 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800236 * The route entry is added to the radix tree. If there was an existing
237 * next hop for this prefix, but the next hop was different, then the
238 * old route entry is deleted.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700239 * </p>
Thomas Vachuska4b420772014-10-30 16:46:17 -0700240 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800241 * NOTE: Currently, we don't handle routes if the next hop is within the
242 * SDN domain.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700243 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700244 *
245 * @param routeEntry the route entry to add
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800246 * @param withdrawPrefixes the collection of accumulated prefixes whose
247 * intents will be withdrawn
248 * @return the corresponding intent that should be submitted, or null
Jonathan Hart335ef462014-10-16 08:20:46 -0700249 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800250 private MultiPointToSinglePointIntent processRouteAdd(
251 RouteEntry routeEntry,
252 Collection<Ip4Prefix> withdrawPrefixes) {
253 log.debug("Processing route add: {}", routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700254
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800255 Ip4Prefix prefix = routeEntry.prefix();
256 Ip4Address nextHop = null;
257 RouteEntry foundRouteEntry =
Pavlin Radoslavov64c1ed12014-12-10 18:10:55 -0800258 ribTable.put(RouteEntry.createBinaryString(prefix), routeEntry);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800259 if (foundRouteEntry != null) {
260 nextHop = foundRouteEntry.nextHop();
261 }
262
263 if (nextHop != null && !nextHop.equals(routeEntry.nextHop())) {
264 // There was an existing nexthop for this prefix. This update
265 // supersedes that, so we need to remove the old flows for this
266 // prefix from the switches
267 withdrawPrefixes.add(routeEntry.prefix());
268 }
269 if (nextHop != null && nextHop.equals(routeEntry.nextHop())) {
270 return null;
271 }
272
273 if (routeEntry.nextHop().equals(LOCAL_NEXT_HOP)) {
274 // Route originated by SDN domain
275 // We don't handle these at the moment
276 log.debug("Own route {} to {}",
277 routeEntry.prefix(), routeEntry.nextHop());
278 return null;
279 }
280
281 //
282 // Find the MAC address of next hop router for this route entry.
283 // If the MAC address can not be found in ARP cache, then this prefix
284 // will be put in routesWaitingOnArp queue. Otherwise, generate
285 // a new route intent.
286 //
287
288 // Monitor the IP address for updates of the MAC address
Jonathan Hart31582d12014-10-22 13:52:41 -0700289 hostService.startMonitoringIp(routeEntry.nextHop());
290
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800291 // Check if we know the MAC address of the next hop
292 MacAddress nextHopMacAddress = ip2Mac.get(routeEntry.nextHop());
293 if (nextHopMacAddress == null) {
294 Set<Host> hosts = hostService.getHostsByIp(routeEntry.nextHop());
295 if (!hosts.isEmpty()) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800296 nextHopMacAddress = hosts.iterator().next().mac();
297 }
298 if (nextHopMacAddress != null) {
299 ip2Mac.put(routeEntry.nextHop(), nextHopMacAddress);
300 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700301 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700302 if (nextHopMacAddress == null) {
303 routesWaitingOnArp.put(routeEntry.nextHop(), routeEntry);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800304 return null;
Jonathan Hart335ef462014-10-16 08:20:46 -0700305 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800306 return generateRouteIntent(routeEntry.prefix(), routeEntry.nextHop(),
307 nextHopMacAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700308 }
309
310 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800311 * Generates a route intent for a prefix, the next hop IP address, and
312 * the next hop MAC address.
313 * <p/>
314 * This method will find the egress interface for the intent.
315 * Intent will match dst IP prefix and rewrite dst MAC address at all other
316 * border switches, then forward packets according to dst MAC address.
Jonathan Hart335ef462014-10-16 08:20:46 -0700317 *
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700318 * @param prefix IP prefix of the route to add
319 * @param nextHopIpAddress IP address of the next hop
Jonathan Hart335ef462014-10-16 08:20:46 -0700320 * @param nextHopMacAddress MAC address of the next hop
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800321 * @return the generated intent, or null if no intent should be submitted
Jonathan Hart335ef462014-10-16 08:20:46 -0700322 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800323 private MultiPointToSinglePointIntent generateRouteIntent(
324 Ip4Prefix prefix,
325 Ip4Address nextHopIpAddress,
326 MacAddress nextHopMacAddress) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700327
328 // Find the attachment point (egress interface) of the next hop
329 Interface egressInterface;
Jonathan Hart31582d12014-10-22 13:52:41 -0700330 if (configService.getBgpPeers().containsKey(nextHopIpAddress)) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700331 // Route to a peer
332 log.debug("Route to peer {}", nextHopIpAddress);
333 BgpPeer peer =
Jonathan Hart31582d12014-10-22 13:52:41 -0700334 configService.getBgpPeers().get(nextHopIpAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700335 egressInterface =
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700336 interfaceService.getInterface(peer.connectPoint());
Jonathan Hart335ef462014-10-16 08:20:46 -0700337 } else {
338 // Route to non-peer
339 log.debug("Route to non-peer {}", nextHopIpAddress);
340 egressInterface =
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700341 interfaceService.getMatchingInterface(nextHopIpAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700342 if (egressInterface == null) {
343 log.warn("No outgoing interface found for {}",
344 nextHopIpAddress);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800345 return null;
Jonathan Hart335ef462014-10-16 08:20:46 -0700346 }
347 }
348
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800349 //
350 // Generate the intent itself
351 //
Jonathan Hart335ef462014-10-16 08:20:46 -0700352 Set<ConnectPoint> ingressPorts = new HashSet<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800353 ConnectPoint egressPort = egressInterface.connectPoint();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800354 log.debug("Generating intent for prefix {}, next hop mac {}",
355 prefix, nextHopMacAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700356
357 for (Interface intf : interfaceService.getInterfaces()) {
Jonathan Hart2e3eef32014-11-12 11:05:40 -0800358 if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700359 ConnectPoint srcPort = intf.connectPoint();
360 ingressPorts.add(srcPort);
361 }
362 }
363
364 // Match the destination IP prefix at the first hop
Jonathan Hart335ef462014-10-16 08:20:46 -0700365 TrafficSelector selector = DefaultTrafficSelector.builder()
366 .matchEthType(Ethernet.TYPE_IPV4)
367 .matchIPDst(prefix)
368 .build();
369
370 // Rewrite the destination MAC address
Jonathan Hart335ef462014-10-16 08:20:46 -0700371 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
372 .setEthDst(nextHopMacAddress)
373 .build();
374
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800375 return new MultiPointToSinglePointIntent(appId, selector, treatment,
376 ingressPorts, egressPort);
Jonathan Hart335ef462014-10-16 08:20:46 -0700377 }
378
379 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800380 * Processes the deletion of a route entry.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700381 * <p>
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800382 * The prefix for the routing entry is removed from radix tree.
383 * If the operation is successful, the prefix is added to the collection
384 * of prefixes whose intents that will be withdrawn.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700385 * </p>
Jonathan Hart335ef462014-10-16 08:20:46 -0700386 *
387 * @param routeEntry the route entry to delete
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800388 * @param withdrawPrefixes the collection of accumulated prefixes whose
389 * intents will be withdrawn
Jonathan Hart335ef462014-10-16 08:20:46 -0700390 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800391 private void processRouteDelete(RouteEntry routeEntry,
392 Collection<Ip4Prefix> withdrawPrefixes) {
393 log.debug("Processing route delete: {}", routeEntry);
394 Ip4Prefix prefix = routeEntry.prefix();
Jonathan Hart335ef462014-10-16 08:20:46 -0700395
Pavlin Radoslavov64c1ed12014-12-10 18:10:55 -0800396 if (ribTable.remove(RouteEntry.createBinaryString(prefix))) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800397 //
398 // Only withdraw intents if an entry was actually removed from the
399 // tree. If no entry was removed, the <prefix, nexthop> wasn't
400 // there so it's probably already been removed and we don't
401 // need to do anything.
402 //
403 withdrawPrefixes.add(routeEntry.prefix());
Jonathan Hart335ef462014-10-16 08:20:46 -0700404 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700405
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800406 routesWaitingOnArp.remove(routeEntry.nextHop(), routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700407 }
408
409 /**
Jonathan Hart31582d12014-10-22 13:52:41 -0700410 * Signals the Router that the MAC to IP mapping has potentially been
411 * updated. This has the effect of updating the MAC address for any
412 * installed prefixes if it has changed, as well as installing any pending
413 * prefixes that were waiting for MAC resolution.
Jonathan Hart335ef462014-10-16 08:20:46 -0700414 *
Jonathan Hart31582d12014-10-22 13:52:41 -0700415 * @param ipAddress the IP address that an event was received for
416 * @param macAddress the most recently known MAC address for the IP address
Jonathan Hart335ef462014-10-16 08:20:46 -0700417 */
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800418 private void updateMac(Ip4Address ipAddress, MacAddress macAddress) {
Jonathan Hart31582d12014-10-22 13:52:41 -0700419 log.debug("Received updated MAC info: {} => {}", ipAddress, macAddress);
420
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700421 // We synchronize on this to prevent changes to the radix tree
422 // while we're pushing intents. If the tree changes, the
423 // tree and intents could get out of sync.
Jonathan Hart335ef462014-10-16 08:20:46 -0700424 synchronized (this) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800425 Collection<Pair<Ip4Prefix, MultiPointToSinglePointIntent>>
426 submitIntents = new LinkedList<>();
427 MultiPointToSinglePointIntent intent;
Jonathan Hart335ef462014-10-16 08:20:46 -0700428
429 Set<RouteEntry> routesToPush =
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700430 routesWaitingOnArp.removeAll(ipAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700431
432 for (RouteEntry routeEntry : routesToPush) {
433 // These will always be adds
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800434 Ip4Prefix prefix = routeEntry.prefix();
Jonathan Hart335ef462014-10-16 08:20:46 -0700435 String binaryString = RouteEntry.createBinaryString(prefix);
436 RouteEntry foundRouteEntry =
Pavlin Radoslavov64c1ed12014-12-10 18:10:55 -0800437 ribTable.getValueForExactKey(binaryString);
Jonathan Hart335ef462014-10-16 08:20:46 -0700438 if (foundRouteEntry != null &&
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800439 foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
Jonathan Hart335ef462014-10-16 08:20:46 -0700440 // We only push prefix flows if the prefix is still in the
Jonathan Hart0b04bed2014-10-16 16:39:19 -0700441 // radix tree and the next hop is the same as our
Jonathan Hart335ef462014-10-16 08:20:46 -0700442 // update.
443 // The prefix could have been removed while we were waiting
444 // for the ARP, or the next hop could have changed.
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800445 intent = generateRouteIntent(prefix, ipAddress,
446 macAddress);
447 if (intent != null) {
448 submitIntents.add(Pair.of(prefix, intent));
449 }
Jonathan Hart335ef462014-10-16 08:20:46 -0700450 } else {
Jonathan Hart31582d12014-10-22 13:52:41 -0700451 log.debug("{} has been revoked before the MAC was resolved",
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800452 routeEntry);
Jonathan Hart335ef462014-10-16 08:20:46 -0700453 }
454 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800455
456 if (!submitIntents.isEmpty()) {
457 Collection<Ip4Prefix> withdrawPrefixes = new LinkedList<>();
458 intentSynchronizer.updateRouteIntents(submitIntents,
459 withdrawPrefixes);
460 }
461
462 ip2Mac.put(ipAddress, macAddress);
Jonathan Hart335ef462014-10-16 08:20:46 -0700463 }
464 }
465
466 /**
467 * Gets the SDN-IP routes.
468 *
469 * @return the SDN-IP routes
470 */
471 public Collection<RouteEntry> getRoutes() {
472 Iterator<KeyValuePair<RouteEntry>> it =
Pavlin Radoslavov64c1ed12014-12-10 18:10:55 -0800473 ribTable.getKeyValuePairsForKeysStartingWith("").iterator();
Jonathan Hart335ef462014-10-16 08:20:46 -0700474
475 List<RouteEntry> routes = new LinkedList<>();
476
477 while (it.hasNext()) {
478 KeyValuePair<RouteEntry> entry = it.next();
479 routes.add(entry.getValue());
480 }
481
482 return routes;
483 }
484
485 /**
Jonathan Hart335ef462014-10-16 08:20:46 -0700486 * Listener for host events.
487 */
488 class InternalHostListener implements HostListener {
489 @Override
490 public void event(HostEvent event) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800491 log.debug("Received HostEvent {}", event);
492
493 Host host = event.subject();
494 switch (event.type()) {
495 case HOST_ADDED:
496 // FALLTHROUGH
497 case HOST_UPDATED:
Pavlin Radoslavov33f228a2014-10-27 19:33:16 -0700498 for (IpAddress ip : host.ipAddresses()) {
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800499 Ip4Address ip4Address = ip.getIp4Address();
500 if (ip4Address == null) {
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800501 continue;
502 }
503 updateMac(ip4Address, host.mac());
Jonathan Hart335ef462014-10-16 08:20:46 -0700504 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800505 break;
506 case HOST_REMOVED:
507 for (IpAddress ip : host.ipAddresses()) {
508 Ip4Address ip4Address = ip.getIp4Address();
509 if (ip4Address == null) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800510 continue;
511 }
512 ip2Mac.remove(ip4Address);
513 }
514 break;
515 default:
516 break;
Jonathan Hart335ef462014-10-16 08:20:46 -0700517 }
518 }
519 }
520}