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