blob: b309a7b8f57e7cb9c59c65547d53ee413f2c99d8 [file] [log] [blame]
Jonathan Hart335ef462014-10-16 08:20:46 -07001package org.onlab.onos.sdnip;
2
3import java.util.Collection;
4import java.util.HashMap;
5import java.util.HashSet;
6import java.util.Iterator;
7import java.util.LinkedList;
8import java.util.List;
9import java.util.Map;
10import java.util.Set;
11import java.util.concurrent.BlockingQueue;
12import java.util.concurrent.ConcurrentHashMap;
13import java.util.concurrent.ExecutorService;
14import java.util.concurrent.Executors;
15import java.util.concurrent.LinkedBlockingQueue;
16import java.util.concurrent.Semaphore;
17
18import org.apache.commons.lang3.tuple.Pair;
19import org.onlab.onos.net.ConnectPoint;
20import org.onlab.onos.net.Host;
21import org.onlab.onos.net.flow.DefaultTrafficSelector;
22import org.onlab.onos.net.flow.DefaultTrafficTreatment;
23import org.onlab.onos.net.flow.TrafficSelector;
24import org.onlab.onos.net.flow.TrafficTreatment;
25import org.onlab.onos.net.flow.criteria.Criteria.IPCriterion;
26import org.onlab.onos.net.flow.criteria.Criterion;
27import org.onlab.onos.net.flow.criteria.Criterion.Type;
28import org.onlab.onos.net.host.HostEvent;
29import org.onlab.onos.net.host.HostListener;
30import org.onlab.onos.net.host.HostService;
31import org.onlab.onos.net.intent.Intent;
32import org.onlab.onos.net.intent.IntentId;
33import org.onlab.onos.net.intent.IntentService;
34import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
35import org.onlab.onos.sdnip.config.BgpPeer;
36import org.onlab.onos.sdnip.config.Interface;
37import org.onlab.onos.sdnip.config.SdnIpConfigService;
38import org.onlab.packet.Ethernet;
39import org.onlab.packet.IpAddress;
40import org.onlab.packet.IpPrefix;
41import org.onlab.packet.MacAddress;
42import org.slf4j.Logger;
43import org.slf4j.LoggerFactory;
44
45import com.google.common.base.Objects;
46import com.google.common.collect.HashMultimap;
47import com.google.common.collect.Multimaps;
48import com.google.common.collect.SetMultimap;
49import com.google.common.util.concurrent.ThreadFactoryBuilder;
50import com.googlecode.concurrenttrees.common.KeyValuePair;
51import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
52import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
53import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
54
55/**
56 * This class processes BGP route update, translates each update into a intent
57 * and submits the intent.
58 *
59 * TODO: Make it thread-safe.
60 */
61public class Router implements RouteListener {
62
63 private static final Logger log = LoggerFactory.getLogger(Router.class);
64
65 // Store all route updates in a InvertedRadixTree.
66 // The key in this Tree is the binary sting of prefix of route.
67 // The Ip4Address is the next hop address of route, and is also the value
68 // of each entry.
69 private InvertedRadixTree<RouteEntry> bgpRoutes;
70
71 // Stores all incoming route updates in a queue.
72 private BlockingQueue<RouteUpdate> routeUpdates;
73
74 // The Ip4Address is the next hop address of each route update.
75 private SetMultimap<IpAddress, RouteEntry> routesWaitingOnArp;
76 private ConcurrentHashMap<IpPrefix, MultiPointToSinglePointIntent> pushedRouteIntents;
77
78 private IntentService intentService;
79 //private IProxyArpService proxyArp;
80 private HostService hostService;
81 private SdnIpConfigService configInfoService;
82 private InterfaceService interfaceService;
83
84 private ExecutorService bgpUpdatesExecutor;
85 private ExecutorService bgpIntentsSynchronizerExecutor;
86
87 // TODO temporary
88 private int intentId = Integer.MAX_VALUE / 2;
89
90 //
91 // State to deal with SDN-IP Leader election and pushing Intents
92 //
93 private Semaphore intentsSynchronizerSemaphore = new Semaphore(0);
94 private volatile boolean isElectedLeader = false;
95 private volatile boolean isActivatedLeader = false;
96
97 // For routes announced by local BGP deamon in SDN network,
98 // the next hop will be 0.0.0.0.
99 public static final IpAddress LOCAL_NEXT_HOP = IpAddress.valueOf("0.0.0.0");
100
101 /**
102 * Class constructor.
103 *
104 * @param intentService the intent service
105 * @param proxyArp the proxy ARP service
106 * @param configInfoService the configuration service
107 * @param interfaceService the interface service
108 */
109 public Router(IntentService intentService, HostService hostService,
110 SdnIpConfigService configInfoService, InterfaceService interfaceService) {
111
112 this.intentService = intentService;
113 this.hostService = hostService;
114 this.configInfoService = configInfoService;
115 this.interfaceService = interfaceService;
116
117 bgpRoutes = new ConcurrentInvertedRadixTree<>(
118 new DefaultByteArrayNodeFactory());
119 routeUpdates = new LinkedBlockingQueue<>();
120 routesWaitingOnArp = Multimaps.synchronizedSetMultimap(
121 HashMultimap.<IpAddress, RouteEntry>create());
122 pushedRouteIntents = new ConcurrentHashMap<>();
123
124 bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
125 new ThreadFactoryBuilder().setNameFormat("bgp-updates-%d").build());
126 bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor(
127 new ThreadFactoryBuilder()
128 .setNameFormat("bgp-intents-synchronizer-%d").build());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700129
130 this.hostService.addListener(new InternalHostListener());
Jonathan Hart335ef462014-10-16 08:20:46 -0700131 }
132
133 /**
134 * Starts the Router.
135 */
136 public void start() {
137
138 bgpUpdatesExecutor.execute(new Runnable() {
139 @Override
140 public void run() {
141 doUpdatesThread();
142 }
143 });
144
145 bgpIntentsSynchronizerExecutor.execute(new Runnable() {
146 @Override
147 public void run() {
148 doIntentSynchronizationThread();
149 }
150 });
151 }
152
153 //@Override TODO hook this up to something
154 public void leaderChanged(boolean isLeader) {
155 log.debug("Leader changed: {}", isLeader);
156
157 if (!isLeader) {
158 this.isElectedLeader = false;
159 this.isActivatedLeader = false;
160 return; // Nothing to do
161 }
162 this.isActivatedLeader = false;
163 this.isElectedLeader = true;
164
165 //
166 // Tell the Intents Synchronizer thread to start the synchronization
167 //
168 intentsSynchronizerSemaphore.release();
169 }
170
171 @Override
172 public void update(RouteUpdate routeUpdate) {
173 log.debug("Received new route Update: {}", routeUpdate);
174
175 try {
176 routeUpdates.put(routeUpdate);
177 } catch (InterruptedException e) {
178 log.debug("Interrupted while putting on routeUpdates queue", e);
179 Thread.currentThread().interrupt();
180 }
181 }
182
183 /**
184 * Thread for Intent Synchronization.
185 */
186 private void doIntentSynchronizationThread() {
187 boolean interrupted = false;
188 try {
189 while (!interrupted) {
190 try {
191 intentsSynchronizerSemaphore.acquire();
192 //
193 // Drain all permits, because a single synchronization is
194 // sufficient.
195 //
196 intentsSynchronizerSemaphore.drainPermits();
197 } catch (InterruptedException e) {
198 log.debug("Interrupted while waiting to become " +
199 "Intent Synchronization leader");
200 interrupted = true;
201 break;
202 }
203 syncIntents();
204 }
205 } finally {
206 if (interrupted) {
207 Thread.currentThread().interrupt();
208 }
209 }
210 }
211
212 /**
213 * Thread for handling route updates.
214 */
215 private void doUpdatesThread() {
216 boolean interrupted = false;
217 try {
218 while (!interrupted) {
219 try {
220 RouteUpdate update = routeUpdates.take();
221 switch (update.type()) {
222 case UPDATE:
223 processRouteAdd(update.routeEntry());
224 break;
225 case DELETE:
226 processRouteDelete(update.routeEntry());
227 break;
228 default:
229 log.error("Unknown update Type: {}", update.type());
230 break;
231 }
232 } catch (InterruptedException e) {
233 log.debug("Interrupted while taking from updates queue", e);
234 interrupted = true;
235 } catch (Exception e) {
236 log.debug("exception", e);
237 }
238 }
239 } finally {
240 if (interrupted) {
241 Thread.currentThread().interrupt();
242 }
243 }
244 }
245
246 /**
247 * Performs Intents Synchronization between the internally stored Route
248 * Intents and the installed Route Intents.
249 */
250 private void syncIntents() {
251 synchronized (this) {
252 if (!isElectedLeader) {
253 return; // Nothing to do: not the leader anymore
254 }
255 log.debug("Syncing SDN-IP Route Intents...");
256
257 Map<IpPrefix, MultiPointToSinglePointIntent> fetchedIntents =
258 new HashMap<>();
259
260 //
261 // Fetch all intents, and classify the Multi-Point-to-Point Intents
262 // based on the matching prefix.
263 //
264 for (Intent intent : intentService.getIntents()) {
265 //
266 // TODO: Ignore all intents that are not installed by
267 // the SDN-IP application.
268 //
269 if (!(intent instanceof MultiPointToSinglePointIntent)) {
270 continue;
271 }
272 MultiPointToSinglePointIntent mp2pIntent =
273 (MultiPointToSinglePointIntent) intent;
274 /*Match match = mp2pIntent.getMatch();
275 if (!(match instanceof PacketMatch)) {
276 continue;
277 }
278 PacketMatch packetMatch = (PacketMatch) match;
279 Ip4Prefix prefix = packetMatch.getDstIpAddress();
280 if (prefix == null) {
281 continue;
282 }
283 fetchedIntents.put(prefix, mp2pIntent);*/
284 for (Criterion criterion : mp2pIntent.selector().criteria()) {
285 if (criterion.type() == Type.IPV4_DST) {
286 IPCriterion ipCriterion = (IPCriterion) criterion;
287 fetchedIntents.put(ipCriterion.ip(), mp2pIntent);
288 }
289 }
290
291 }
292
293 //
294 // Compare for each prefix the local IN-MEMORY Intents with the
295 // FETCHED Intents:
296 // - If the IN-MEMORY Intent is same as the FETCHED Intent, store
297 // the FETCHED Intent in the local memory (i.e., override the
298 // IN-MEMORY Intent) to preserve the original Intent ID
299 // - if the IN-MEMORY Intent is not same as the FETCHED Intent,
300 // delete the FETCHED Intent, and push/install the IN-MEMORY
301 // Intent.
302 // - If there is an IN-MEMORY Intent for a prefix, but no FETCHED
303 // Intent for same prefix, then push/install the IN-MEMORY
304 // Intent.
305 // - If there is a FETCHED Intent for a prefix, but no IN-MEMORY
306 // Intent for same prefix, then delete/withdraw the FETCHED
307 // Intent.
308 //
309 Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>>
310 storeInMemoryIntents = new LinkedList<>();
311 Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>>
312 addIntents = new LinkedList<>();
313 Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>>
314 deleteIntents = new LinkedList<>();
315 for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry :
316 pushedRouteIntents.entrySet()) {
317 IpPrefix prefix = entry.getKey();
318 MultiPointToSinglePointIntent inMemoryIntent =
319 entry.getValue();
320 MultiPointToSinglePointIntent fetchedIntent =
321 fetchedIntents.get(prefix);
322
323 if (fetchedIntent == null) {
324 //
325 // No FETCHED Intent for same prefix: push the IN-MEMORY
326 // Intent.
327 //
328 addIntents.add(Pair.of(prefix, inMemoryIntent));
329 continue;
330 }
331
332 //
333 // If IN-MEMORY Intent is same as the FETCHED Intent,
334 // store the FETCHED Intent in the local memory.
335 //
336 if (compareMultiPointToSinglePointIntents(inMemoryIntent,
337 fetchedIntent)) {
338 storeInMemoryIntents.add(Pair.of(prefix, fetchedIntent));
339 } else {
340 //
341 // The IN-MEMORY Intent is not same as the FETCHED Intent,
342 // hence delete the FETCHED Intent, and install the
343 // IN-MEMORY Intent.
344 //
345 deleteIntents.add(Pair.of(prefix, fetchedIntent));
346 addIntents.add(Pair.of(prefix, inMemoryIntent));
347 }
348 fetchedIntents.remove(prefix);
349 }
350
351 //
352 // Any remaining FETCHED Intents have to be deleted/withdrawn
353 //
354 for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry :
355 fetchedIntents.entrySet()) {
356 IpPrefix prefix = entry.getKey();
357 MultiPointToSinglePointIntent fetchedIntent = entry.getValue();
358 deleteIntents.add(Pair.of(prefix, fetchedIntent));
359 }
360
361 //
362 // Perform the actions:
363 // 1. Store in memory fetched intents that are same. Can be done
364 // even if we are not the leader anymore
365 // 2. Delete intents: check if the leader before each operation
366 // 3. Add intents: check if the leader before each operation
367 //
368 for (Pair<IpPrefix, MultiPointToSinglePointIntent> pair :
369 storeInMemoryIntents) {
370 IpPrefix prefix = pair.getLeft();
371 MultiPointToSinglePointIntent intent = pair.getRight();
372 log.debug("Intent synchronization: updating in-memory " +
373 "Intent for prefix: {}", prefix);
374 pushedRouteIntents.put(prefix, intent);
375 }
376 //
377 isActivatedLeader = true; // Allow push of Intents
378 for (Pair<IpPrefix, MultiPointToSinglePointIntent> pair :
379 deleteIntents) {
380 IpPrefix prefix = pair.getLeft();
381 MultiPointToSinglePointIntent intent = pair.getRight();
382 if (!isElectedLeader) {
383 isActivatedLeader = false;
384 return;
385 }
386 log.debug("Intent synchronization: deleting Intent for " +
387 "prefix: {}", prefix);
388 intentService.withdraw(intent);
389 }
390 //
391 for (Pair<IpPrefix, MultiPointToSinglePointIntent> pair :
392 addIntents) {
393 IpPrefix prefix = pair.getLeft();
394 MultiPointToSinglePointIntent intent = pair.getRight();
395 if (!isElectedLeader) {
396 isActivatedLeader = false;
397 return;
398 }
399 log.debug("Intent synchronization: adding Intent for " +
400 "prefix: {}", prefix);
401 intentService.submit(intent);
402 }
403 if (!isElectedLeader) {
404 isActivatedLeader = false;
405 }
406 log.debug("Syncing SDN-IP routes completed.");
407 }
408 }
409
410 /**
411 * Compares two Multi-point to Single Point Intents whether they represent
412 * same logical intention.
413 *
414 * @param intent1 the first Intent to compare
415 * @param intent2 the second Intent to compare
416 * @return true if both Intents represent same logical intention, otherwise
417 * false
418 */
419 private boolean compareMultiPointToSinglePointIntents(
420 MultiPointToSinglePointIntent intent1,
421 MultiPointToSinglePointIntent intent2) {
422 /*Match match1 = intent1.getMatch();
423 Match match2 = intent2.getMatch();
424 Action action1 = intent1.getAction();
425 Action action2 = intent2.getAction();
426 Set<SwitchPort> ingressPorts1 = intent1.getIngressPorts();
427 Set<SwitchPort> ingressPorts2 = intent2.getIngressPorts();
428 SwitchPort egressPort1 = intent1.getEgressPort();
429 SwitchPort egressPort2 = intent2.getEgressPort();
430
431 return Objects.equal(match1, match2) &&
432 Objects.equal(action1, action2) &&
433 Objects.equal(egressPort1, egressPort2) &&
434 Objects.equal(ingressPorts1, ingressPorts2);*/
435 return Objects.equal(intent1.selector(), intent2.selector()) &&
436 Objects.equal(intent1.treatment(), intent2.treatment()) &&
437 Objects.equal(intent1.ingressPoints(), intent2.ingressPoints()) &&
438 Objects.equal(intent1.egressPoint(), intent2.egressPoint());
439 }
440
441 /**
442 * Processes adding a route entry.
443 * <p/>
444 * Put new route entry into InvertedRadixTree. If there was an existing
445 * nexthop for this prefix, but the next hop was different, then execute
446 * deleting old route entry. If the next hop is the SDN domain, we do not
447 * handle it at the moment. Otherwise, execute adding a route.
448 *
449 * @param routeEntry the route entry to add
450 */
451 protected void processRouteAdd(RouteEntry routeEntry) {
452 synchronized (this) {
453 log.debug("Processing route add: {}", routeEntry);
454
455 IpPrefix prefix = routeEntry.prefix();
456 IpAddress nextHop = null;
457 RouteEntry foundRouteEntry =
458 bgpRoutes.put(RouteEntry.createBinaryString(prefix),
459 routeEntry);
460 if (foundRouteEntry != null) {
461 nextHop = foundRouteEntry.nextHop();
462 }
463
464 if (nextHop != null && !nextHop.equals(routeEntry.nextHop())) {
465 // There was an existing nexthop for this prefix. This update
466 // supersedes that, so we need to remove the old flows for this
467 // prefix from the switches
468 executeRouteDelete(routeEntry);
469 }
470 if (nextHop != null && nextHop.equals(routeEntry.nextHop())) {
471 return;
472 }
473
474 if (routeEntry.nextHop().equals(LOCAL_NEXT_HOP)) {
475 // Route originated by SDN domain
476 // We don't handle these at the moment
477 log.debug("Own route {} to {}",
478 routeEntry.prefix(), routeEntry.nextHop());
479 return;
480 }
481
482 executeRouteAdd(routeEntry);
483 }
484 }
485
486 /**
487 * Executes adding a route entry.
488 * <p/>
489 * Find out the egress Interface and MAC address of next hop router for
490 * this route entry. If the MAC address can not be found in ARP cache,
491 * then this prefix will be put in routesWaitingOnArp queue. Otherwise,
492 * new route intent will be created and installed.
493 *
494 * @param routeEntry the route entry to add
495 */
496 private void executeRouteAdd(RouteEntry routeEntry) {
497 log.debug("Executing route add: {}", routeEntry);
498
499 // See if we know the MAC address of the next hop
500 //MacAddress nextHopMacAddress =
501 //proxyArp.getMacAddress(routeEntry.getNextHop());
502 MacAddress nextHopMacAddress = null;
503 Set<Host> hosts = hostService.getHostsByIp(
504 routeEntry.nextHop().toPrefix());
505 if (!hosts.isEmpty()) {
506 // TODO how to handle if multiple hosts are returned?
507 nextHopMacAddress = hosts.iterator().next().mac();
508 }
509
510 if (nextHopMacAddress == null) {
511 routesWaitingOnArp.put(routeEntry.nextHop(), routeEntry);
512 //proxyArp.sendArpRequest(routeEntry.getNextHop(), this, true);
513 // TODO maybe just do this for every prefix anyway
514 hostService.startMonitoringIp(routeEntry.nextHop());
515 return;
516 }
517
518 addRouteIntentToNextHop(routeEntry.prefix(),
519 routeEntry.nextHop(),
520 nextHopMacAddress);
521 }
522
523 /**
524 * Adds a route intent given a prefix and a next hop IP address. This
525 * method will find the egress interface for the intent.
526 *
527 * @param prefix IP prefix of the route to add
528 * @param nextHopIpAddress IP address of the next hop
529 * @param nextHopMacAddress MAC address of the next hop
530 */
531 private void addRouteIntentToNextHop(IpPrefix prefix,
532 IpAddress nextHopIpAddress,
533 MacAddress nextHopMacAddress) {
534
535 // Find the attachment point (egress interface) of the next hop
536 Interface egressInterface;
537 if (configInfoService.getBgpPeers().containsKey(nextHopIpAddress)) {
538 // Route to a peer
539 log.debug("Route to peer {}", nextHopIpAddress);
540 BgpPeer peer =
541 configInfoService.getBgpPeers().get(nextHopIpAddress);
542 egressInterface =
543 interfaceService.getInterface(peer.connectPoint());
544 } else {
545 // Route to non-peer
546 log.debug("Route to non-peer {}", nextHopIpAddress);
547 egressInterface =
548 interfaceService.getMatchingInterface(nextHopIpAddress);
549 if (egressInterface == null) {
550 log.warn("No outgoing interface found for {}",
551 nextHopIpAddress);
552 return;
553 }
554 }
555
556 doAddRouteIntent(prefix, egressInterface, nextHopMacAddress);
557 }
558
559 /**
560 * Installs a route intent for a prefix.
561 * <p/>
562 * Intent will match dst IP prefix and rewrite dst MAC address at all other
563 * border switches, then forward packets according to dst MAC address.
564 *
565 * @param prefix IP prefix from route
566 * @param egressInterface egress Interface connected to next hop router
567 * @param nextHopMacAddress MAC address of next hop router
568 */
569 private void doAddRouteIntent(IpPrefix prefix, Interface egressInterface,
570 MacAddress nextHopMacAddress) {
571 log.debug("Adding intent for prefix {}, next hop mac {}",
572 prefix, nextHopMacAddress);
573
574 MultiPointToSinglePointIntent pushedIntent =
575 pushedRouteIntents.get(prefix);
576
577 // Just for testing.
578 if (pushedIntent != null) {
579 log.error("There should not be a pushed intent: {}", pushedIntent);
580 }
581
582 ConnectPoint egressPort = egressInterface.connectPoint();
583
584 Set<ConnectPoint> ingressPorts = new HashSet<>();
585
586 for (Interface intf : interfaceService.getInterfaces()) {
587 if (!intf.equals(egressInterface)) {
588 ConnectPoint srcPort = intf.connectPoint();
589 ingressPorts.add(srcPort);
590 }
591 }
592
593 // Match the destination IP prefix at the first hop
594 //PacketMatchBuilder builder = new PacketMatchBuilder();
595 //builder.setEtherType(Ethernet.TYPE_IPV4).setDstIpNet(prefix);
596 //PacketMatch packetMatch = builder.build();
597 TrafficSelector selector = DefaultTrafficSelector.builder()
598 .matchEthType(Ethernet.TYPE_IPV4)
599 .matchIPDst(prefix)
600 .build();
601
602 // Rewrite the destination MAC address
603 //ModifyDstMacAction modifyDstMacAction =
604 //new ModifyDstMacAction(nextHopMacAddress);
605 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
606 .setEthDst(nextHopMacAddress)
607 .build();
608
609 MultiPointToSinglePointIntent intent =
610 new MultiPointToSinglePointIntent(nextIntentId(),
611 selector, treatment, ingressPorts, egressPort);
612
613 if (isElectedLeader && isActivatedLeader) {
614 log.debug("Intent installation: adding Intent for prefix: {}",
615 prefix);
616 intentService.submit(intent);
617 }
618
619 // Maintain the Intent
620 pushedRouteIntents.put(prefix, intent);
621 }
622
623 /**
624 * Executes deleting a route entry.
625 * <p/>
626 * Removes prefix from InvertedRadixTree, if success, then try to delete
627 * the relative intent.
628 *
629 * @param routeEntry the route entry to delete
630 */
631 protected void processRouteDelete(RouteEntry routeEntry) {
632 synchronized (this) {
633 log.debug("Processing route delete: {}", routeEntry);
634 IpPrefix prefix = routeEntry.prefix();
635
636 // TODO check the change of logic here - remove doesn't check that
637 // the route entry was what we expected (and we can't do this
638 // concurrently)
639
640 if (bgpRoutes.remove(RouteEntry.createBinaryString(prefix))) {
641 //
642 // Only delete flows if an entry was actually removed from the
643 // tree. If no entry was removed, the <prefix, nexthop> wasn't
644 // there so it's probably already been removed and we don't
645 // need to do anything.
646 //
647 executeRouteDelete(routeEntry);
648 }
649
650 routesWaitingOnArp.remove(routeEntry.nextHop(), routeEntry);
651 // TODO cancel the request in the ARP manager as well
652 }
653 }
654
655 /**
656 * Executed deleting a route entry.
657 *
658 * @param routeEntry the route entry to delete
659 */
660 private void executeRouteDelete(RouteEntry routeEntry) {
661 log.debug("Executing route delete: {}", routeEntry);
662
663 IpPrefix prefix = routeEntry.prefix();
664
665 MultiPointToSinglePointIntent intent =
666 pushedRouteIntents.remove(prefix);
667
668 if (intent == null) {
669 log.debug("There is no intent in pushedRouteIntents to delete " +
670 "for prefix: {}", prefix);
671 } else {
672 if (isElectedLeader && isActivatedLeader) {
673 log.debug("Intent installation: deleting Intent for prefix: {}",
674 prefix);
675 intentService.withdraw(intent);
676 }
677 }
678 }
679
680 /**
681 * This method handles the prefixes which are waiting for ARP replies for
682 * MAC addresses of next hops.
683 *
684 * @param ipAddress next hop router IP address, for which we sent ARP
685 * request out
686 * @param macAddress MAC address which is relative to the ipAddress
687 */
688 //@Override
689 // TODO change name
690 public void arpResponse(IpAddress ipAddress, MacAddress macAddress) {
691 log.debug("Received ARP response: {} => {}", ipAddress, macAddress);
692
693 // We synchronize on this to prevent changes to the InvertedRadixTree
694 // while we're pushing intent. If the InvertedRadixTree changes, the
695 // InvertedRadixTree and intent could get out of sync.
696 synchronized (this) {
697
698 Set<RouteEntry> routesToPush =
699 routesWaitingOnArp.removeAll(ipAddress);
700
701 for (RouteEntry routeEntry : routesToPush) {
702 // These will always be adds
703 IpPrefix prefix = routeEntry.prefix();
704 String binaryString = RouteEntry.createBinaryString(prefix);
705 RouteEntry foundRouteEntry =
706 bgpRoutes.getValueForExactKey(binaryString);
707 if (foundRouteEntry != null &&
708 foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
709 log.debug("Pushing prefix {} next hop {}",
710 routeEntry.prefix(), routeEntry.nextHop());
711 // We only push prefix flows if the prefix is still in the
712 // InvertedRadixTree and the next hop is the same as our
713 // update.
714 // The prefix could have been removed while we were waiting
715 // for the ARP, or the next hop could have changed.
716 addRouteIntentToNextHop(prefix, ipAddress, macAddress);
717 } else {
718 log.debug("Received ARP response, but {}/{} is no longer in"
719 + " InvertedRadixTree", routeEntry.prefix(),
720 routeEntry.nextHop());
721 }
722 }
723 }
724 }
725
726 /**
727 * Gets the SDN-IP routes.
728 *
729 * @return the SDN-IP routes
730 */
731 public Collection<RouteEntry> getRoutes() {
732 Iterator<KeyValuePair<RouteEntry>> it =
733 bgpRoutes.getKeyValuePairsForKeysStartingWith("").iterator();
734
735 List<RouteEntry> routes = new LinkedList<>();
736
737 while (it.hasNext()) {
738 KeyValuePair<RouteEntry> entry = it.next();
739 routes.add(entry.getValue());
740 }
741
742 return routes;
743 }
744
745 /**
746 * Generates a new unique intent ID.
747 *
748 * @return the new intent ID.
749 */
750 private IntentId nextIntentId() {
751 return new IntentId(intentId++);
752 }
753
754 /**
755 * Listener for host events.
756 */
757 class InternalHostListener implements HostListener {
758 @Override
759 public void event(HostEvent event) {
760 if (event.type() == HostEvent.Type.HOST_ADDED ||
761 event.type() == HostEvent.Type.HOST_UPDATED) {
762 Host host = event.subject();
763 for (IpPrefix ip : host.ipAddresses()) {
764 arpResponse(ip.toIpAddress(), host.mac());
765 }
766 }
767 }
768 }
769}