blob: ba17483ab0c0101d1ac36f9c75c30aacf20021b8 [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001package net.floodlightcontroller.topology;
2
3import java.io.IOException;
4import java.util.ArrayList;
5import java.util.Collection;
6import java.util.Date;
7import java.util.HashMap;
8import java.util.HashSet;
9import java.util.List;
10import java.util.Map;
11import java.util.Set;
12import java.util.concurrent.BlockingQueue;
13import java.util.concurrent.LinkedBlockingQueue;
14import java.util.concurrent.ScheduledExecutorService;
15import java.util.concurrent.TimeUnit;
16
17import net.floodlightcontroller.core.FloodlightContext;
18import net.floodlightcontroller.core.IFloodlightProviderService;
19import net.floodlightcontroller.core.IFloodlightProviderService.Role;
20import net.floodlightcontroller.core.IOFMessageListener;
21import net.floodlightcontroller.core.IOFSwitch;
22import net.floodlightcontroller.core.IHAListener;
23import net.floodlightcontroller.core.annotations.LogMessageCategory;
24import net.floodlightcontroller.core.annotations.LogMessageDoc;
25import net.floodlightcontroller.core.module.FloodlightModuleContext;
26import net.floodlightcontroller.core.module.FloodlightModuleException;
27import net.floodlightcontroller.core.module.IFloodlightModule;
28import net.floodlightcontroller.core.module.IFloodlightService;
29import net.floodlightcontroller.core.util.SingletonTask;
30import net.floodlightcontroller.counter.ICounterStoreService;
31import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryListener;
32import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryService;
33import net.floodlightcontroller.packet.BSN;
34import net.floodlightcontroller.packet.Ethernet;
35import net.floodlightcontroller.packet.LLDP;
36import net.floodlightcontroller.restserver.IRestApiService;
37import net.floodlightcontroller.routing.IRoutingService;
38import net.floodlightcontroller.routing.Link;
39import net.floodlightcontroller.routing.Route;
40import net.floodlightcontroller.threadpool.IThreadPoolService;
41import net.floodlightcontroller.topology.web.TopologyWebRoutable;
42
43import org.openflow.protocol.OFMessage;
44import org.openflow.protocol.OFPacketIn;
45import org.openflow.protocol.OFPacketOut;
46import org.openflow.protocol.OFPort;
47import org.openflow.protocol.action.OFAction;
48import org.openflow.protocol.action.OFActionOutput;
49import org.openflow.protocol.OFType;
50import org.slf4j.Logger;
51import org.slf4j.LoggerFactory;
52
53/**
54 * Topology manager is responsible for maintaining the controller's notion
55 * of the network graph, as well as implementing tools for finding routes
56 * through the topology.
57 */
58@LogMessageCategory("Network Topology")
59public class TopologyManager implements
60 IFloodlightModule, ITopologyService,
61 IRoutingService, ILinkDiscoveryListener,
62 IOFMessageListener, IHAListener {
63
64 protected static Logger log = LoggerFactory.getLogger(TopologyManager.class);
65
66 public static final String CONTEXT_TUNNEL_ENABLED =
67 "com.bigswitch.floodlight.topologymanager.tunnelEnabled";
68
69 /**
70 * Set of ports for each switch
71 */
72 protected Map<Long, Set<Short>> switchPorts;
73
74 /**
75 * Set of links organized by node port tuple
76 */
77 protected Map<NodePortTuple, Set<Link>> switchPortLinks;
78
79 /**
80 * Set of direct links
81 */
82 protected Map<NodePortTuple, Set<Link>> directLinks;
83
84 /**
85 * set of links that are broadcast domain links.
86 */
87 protected Map<NodePortTuple, Set<Link>> portBroadcastDomainLinks;
88
89 /**
90 * set of tunnel links
91 */
92 protected Map<NodePortTuple, Set<Link>> tunnelLinks;
93
94 protected ILinkDiscoveryService linkDiscovery;
95 protected IThreadPoolService threadPool;
96 protected IFloodlightProviderService floodlightProvider;
97 protected IRestApiService restApi;
98
99 // Modules that listen to our updates
100 protected ArrayList<ITopologyListener> topologyAware;
101
102 protected BlockingQueue<LDUpdate> ldUpdates;
103 protected List<LDUpdate> appliedUpdates;
104
105 // These must be accessed using getCurrentInstance(), not directly
106 protected TopologyInstance currentInstance;
107 protected TopologyInstance currentInstanceWithoutTunnels;
108
109 protected SingletonTask newInstanceTask;
110 private Date lastUpdateTime;
111
112 /**
113 * Flag that indicates if links (direct/tunnel/multihop links) were
114 * updated as part of LDUpdate.
115 */
116 protected boolean linksUpdated;
117 /**
118 * Flag that indicates if direct or tunnel links were updated as
119 * part of LDUpdate.
120 */
121 protected boolean dtLinksUpdated;
122
123 /**
124 * Thread for recomputing topology. The thread is always running,
125 * however the function applyUpdates() has a blocking call.
126 */
127 @LogMessageDoc(level="ERROR",
128 message="Error in topology instance task thread",
129 explanation="An unknown error occured in the topology " +
130 "discovery module.",
131 recommendation=LogMessageDoc.CHECK_CONTROLLER)
132 protected class UpdateTopologyWorker implements Runnable {
133 @Override
134 public void run() {
135 try {
136 updateTopology();
137 }
138 catch (Exception e) {
139 log.error("Error in topology instance task thread", e);
140 }
141 }
142 }
143
144 public boolean updateTopology() {
145 boolean newInstanceFlag;
146 linksUpdated = false;
147 dtLinksUpdated = false;
148 applyUpdates();
149 newInstanceFlag = createNewInstance();
150 lastUpdateTime = new Date();
151 informListeners();
152 return newInstanceFlag;
153 }
154
155 // **********************
156 // ILinkDiscoveryListener
157 // **********************
158
159 @Override
160 public void linkDiscoveryUpdate(LDUpdate update) {
161 boolean scheduleFlag = false;
162 // if there's no udpates in the queue, then
163 // we need to schedule an update.
164 if (ldUpdates.peek() == null)
165 scheduleFlag = true;
166
167 if (log.isTraceEnabled()) {
168 log.trace("Queuing update: {}", update);
169 }
170 ldUpdates.add(update);
171
172 if (scheduleFlag) {
173 newInstanceTask.reschedule(1, TimeUnit.MICROSECONDS);
174 }
175 }
176
177 // ****************
178 // ITopologyService
179 // ****************
180
181 //
182 // ITopologyService interface methods
183 //
184 @Override
185 public Date getLastUpdateTime() {
186 return lastUpdateTime;
187 }
188
189 @Override
190 public void addListener(ITopologyListener listener) {
191 topologyAware.add(listener);
192 }
193
194 @Override
195 public boolean isAttachmentPointPort(long switchid, short port) {
196 return isAttachmentPointPort(switchid, port, true);
197 }
198
199 @Override
200 public boolean isAttachmentPointPort(long switchid, short port,
201 boolean tunnelEnabled) {
202 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
203
204 // if the port is not attachment point port according to
205 // topology instance, then return false
206 if (ti.isAttachmentPointPort(switchid, port) == false)
207 return false;
208
209 // Check whether the port is a physical port. We should not learn
210 // attachment points on "special" ports.
211 if ((port & 0xff00) == 0xff00 && port != (short)0xfffe) return false;
212
213 // Make sure that the port is enabled.
214 IOFSwitch sw = floodlightProvider.getSwitches().get(switchid);
215 if (sw == null) return false;
216 return (sw.portEnabled(port));
217 }
218
219 public long getOpenflowDomainId(long switchId) {
220 return getOpenflowDomainId(switchId, true);
221 }
222
223 public long getOpenflowDomainId(long switchId, boolean tunnelEnabled) {
224 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
225 return ti.getOpenflowDomainId(switchId);
226 }
227
228 @Override
229 public long getL2DomainId(long switchId) {
230 return getL2DomainId(switchId, true);
231 }
232
233 @Override
234 public long getL2DomainId(long switchId, boolean tunnelEnabled) {
235 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
236 return ti.getL2DomainId(switchId);
237 }
238
239 @Override
240 public boolean inSameOpenflowDomain(long switch1, long switch2) {
241 return inSameOpenflowDomain(switch1, switch2, true);
242 }
243
244 @Override
245 public boolean inSameOpenflowDomain(long switch1, long switch2,
246 boolean tunnelEnabled) {
247 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
248 return ti.inSameOpenflowDomain(switch1, switch2);
249 }
250
251 @Override
252 public boolean isAllowed(long sw, short portId) {
253 return isAllowed(sw, portId, true);
254 }
255
256 @Override
257 public boolean isAllowed(long sw, short portId, boolean tunnelEnabled) {
258 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
259 return ti.isAllowed(sw, portId);
260 }
261
262 ////////////////////////////////////////////////////////////////////////
263 ////////////////////////////////////////////////////////////////////////
264 @Override
265 public boolean isIncomingBroadcastAllowed(long sw, short portId) {
266 return isIncomingBroadcastAllowed(sw, portId, true);
267 }
268
269 public boolean isIncomingBroadcastAllowed(long sw, short portId,
270 boolean tunnelEnabled) {
271 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
272 return ti.isIncomingBroadcastAllowedOnSwitchPort(sw, portId);
273 }
274
275 ////////////////////////////////////////////////////////////////////////
276 ////////////////////////////////////////////////////////////////////////
277 /** Get all the ports connected to the switch */
278 @Override
279 public Set<Short> getPortsWithLinks(long sw) {
280 return getPortsWithLinks(sw, true);
281 }
282
283 /** Get all the ports connected to the switch */
284 @Override
285 public Set<Short> getPortsWithLinks(long sw, boolean tunnelEnabled) {
286 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
287 return ti.getPortsWithLinks(sw);
288 }
289
290 ////////////////////////////////////////////////////////////////////////
291 ////////////////////////////////////////////////////////////////////////
292 /** Get all the ports on the target switch (targetSw) on which a
293 * broadcast packet must be sent from a host whose attachment point
294 * is on switch port (src, srcPort).
295 */
296 public Set<Short> getBroadcastPorts(long targetSw,
297 long src, short srcPort) {
298 return getBroadcastPorts(targetSw, src, srcPort, true);
299 }
300
301 /** Get all the ports on the target switch (targetSw) on which a
302 * broadcast packet must be sent from a host whose attachment point
303 * is on switch port (src, srcPort).
304 */
305 public Set<Short> getBroadcastPorts(long targetSw,
306 long src, short srcPort,
307 boolean tunnelEnabled) {
308 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
309 return ti.getBroadcastPorts(targetSw, src, srcPort);
310 }
311
312 ////////////////////////////////////////////////////////////////////////
313 ////////////////////////////////////////////////////////////////////////
314 @Override
315 public NodePortTuple getOutgoingSwitchPort(long src, short srcPort,
316 long dst, short dstPort) {
317 // Use this function to redirect traffic if needed.
318 return getOutgoingSwitchPort(src, srcPort, dst, dstPort, true);
319 }
320
321 @Override
322 public NodePortTuple getOutgoingSwitchPort(long src, short srcPort,
323 long dst, short dstPort,
324 boolean tunnelEnabled) {
325 // Use this function to redirect traffic if needed.
326 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
327 return ti.getOutgoingSwitchPort(src, srcPort,
328 dst, dstPort);
329 }
330
331 ////////////////////////////////////////////////////////////////////////
332 ////////////////////////////////////////////////////////////////////////
333 @Override
334 public NodePortTuple getIncomingSwitchPort(long src, short srcPort,
335 long dst, short dstPort) {
336 return getIncomingSwitchPort(src, srcPort, dst, dstPort, true);
337 }
338
339 @Override
340 public NodePortTuple getIncomingSwitchPort(long src, short srcPort,
341 long dst, short dstPort,
342 boolean tunnelEnabled) {
343 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
344 return ti.getIncomingSwitchPort(src, srcPort,
345 dst, dstPort);
346 }
347
348 ////////////////////////////////////////////////////////////////////////
349 ////////////////////////////////////////////////////////////////////////
350 /**
351 * Checks if the two switchports belong to the same broadcast domain.
352 */
353 @Override
354 public boolean isInSameBroadcastDomain(long s1, short p1, long s2,
355 short p2) {
356 return isInSameBroadcastDomain(s1, p1, s2, p2, true);
357
358 }
359
360 @Override
361 public boolean isInSameBroadcastDomain(long s1, short p1,
362 long s2, short p2,
363 boolean tunnelEnabled) {
364 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
365 return ti.inSameBroadcastDomain(s1, p1, s2, p2);
366
367 }
368
369
370 /**
371 * Checks if the switchport is a broadcast domain port or not.
372 */
373 @Override
374 public boolean isBroadcastDomainPort(long sw, short port) {
375 return isBroadcastDomainPort(sw, port, true);
376 }
377
378 @Override
379 public boolean isBroadcastDomainPort(long sw, short port,
380 boolean tunnelEnabled) {
381 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
382 return ti.isBroadcastDomainPort(new NodePortTuple(sw, port));
383 }
384
385
386 /**
387 * Checks if the new attachment point port is consistent with the
388 * old attachment point port.
389 */
390 @Override
391 public boolean isConsistent(long oldSw, short oldPort,
392 long newSw, short newPort) {
393 return isConsistent(oldSw, oldPort,
394 newSw, newPort, true);
395 }
396
397 @Override
398 public boolean isConsistent(long oldSw, short oldPort,
399 long newSw, short newPort,
400 boolean tunnelEnabled) {
401 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
402 return ti.isConsistent(oldSw, oldPort, newSw, newPort);
403 }
404
405 ////////////////////////////////////////////////////////////////////////
406 ////////////////////////////////////////////////////////////////////////
407 /**
408 * Checks if the two switches are in the same Layer 2 domain.
409 */
410 @Override
411 public boolean inSameL2Domain(long switch1, long switch2) {
412 return inSameL2Domain(switch1, switch2, true);
413 }
414
415 @Override
416 public boolean inSameL2Domain(long switch1, long switch2,
417 boolean tunnelEnabled) {
418 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
419 return ti.inSameL2Domain(switch1, switch2);
420 }
421
422 ////////////////////////////////////////////////////////////////////////
423 ////////////////////////////////////////////////////////////////////////
424 @Override
425 public NodePortTuple getAllowedOutgoingBroadcastPort(long src,
426 short srcPort,
427 long dst,
428 short dstPort) {
429 return getAllowedOutgoingBroadcastPort(src, srcPort,
430 dst, dstPort, true);
431 }
432
433 @Override
434 public NodePortTuple getAllowedOutgoingBroadcastPort(long src,
435 short srcPort,
436 long dst,
437 short dstPort,
438 boolean tunnelEnabled){
439 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
440 return ti.getAllowedOutgoingBroadcastPort(src, srcPort,
441 dst, dstPort);
442 }
443 ////////////////////////////////////////////////////////////////////////
444 ////////////////////////////////////////////////////////////////////////
445 @Override
446 public NodePortTuple
447 getAllowedIncomingBroadcastPort(long src, short srcPort) {
448 return getAllowedIncomingBroadcastPort(src,srcPort, true);
449 }
450
451 @Override
452 public NodePortTuple
453 getAllowedIncomingBroadcastPort(long src, short srcPort,
454 boolean tunnelEnabled) {
455 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
456 return ti.getAllowedIncomingBroadcastPort(src,srcPort);
457 }
458
459 @Override
460 public Set<NodePortTuple> getBroadcastDomainPorts() {
461 return portBroadcastDomainLinks.keySet();
462 }
463
464 @Override
465 public Set<NodePortTuple> getTunnelPorts() {
466 return tunnelLinks.keySet();
467 }
468
469 @Override
470 public Set<NodePortTuple> getBlockedPorts() {
471 Set<NodePortTuple> bp;
472 Set<NodePortTuple> blockedPorts =
473 new HashSet<NodePortTuple>();
474
475 // As we might have two topologies, simply get the union of
476 // both of them and send it.
477 bp = getCurrentInstance(true).getBlockedPorts();
478 if (bp != null)
479 blockedPorts.addAll(bp);
480
481 bp = getCurrentInstance(false).getBlockedPorts();
482 if (bp != null)
483 blockedPorts.addAll(bp);
484
485 return blockedPorts;
486 }
487
488 @Override
489 public List<LDUpdate> getLastLinkUpdates() {
490 return appliedUpdates;
491 }
492 ////////////////////////////////////////////////////////////////////////
493 ////////////////////////////////////////////////////////////////////////
494
495 // ***************
496 // IRoutingService
497 // ***************
498
499 @Override
500 public Route getRoute(long src, long dst) {
501 return getRoute(src, dst, true);
502 }
503
504 @Override
505 public Route getRoute(long src, long dst, boolean tunnelEnabled) {
506 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
507 return ti.getRoute(src, dst);
508 }
509
510 @Override
511 public Route getRoute(long src, short srcPort, long dst, short dstPort) {
512 return getRoute(src, srcPort, dst, dstPort, true);
513 }
514
515 @Override
516 public Route getRoute(long src, short srcPort, long dst, short dstPort,
517 boolean tunnelEnabled) {
518 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
519 return ti.getRoute(src, srcPort, dst, dstPort);
520 }
521
522 @Override
523 public boolean routeExists(long src, long dst) {
524 return routeExists(src, dst, true);
525 }
526
527 @Override
528 public boolean routeExists(long src, long dst, boolean tunnelEnabled) {
529 TopologyInstance ti = getCurrentInstance(tunnelEnabled);
530 return ti.routeExists(src, dst);
531 }
532
533
534 // ******************
535 // IOFMessageListener
536 // ******************
537
538 @Override
539 public String getName() {
540 return "topology";
541 }
542
543 @Override
544 public boolean isCallbackOrderingPrereq(OFType type, String name) {
545 return "linkdiscovery".equals(name);
546 }
547
548 @Override
549 public boolean isCallbackOrderingPostreq(OFType type, String name) {
550 return false;
551 }
552
553 @Override
554 public Command receive(IOFSwitch sw, OFMessage msg,
555 FloodlightContext cntx) {
556 switch (msg.getType()) {
557 case PACKET_IN:
558 return this.processPacketInMessage(sw,
559 (OFPacketIn) msg, cntx);
560 default:
561 break;
562 }
563
564 return Command.CONTINUE;
565 }
566
567 // ***************
568 // IHAListener
569 // ***************
570
571 @Override
572 public void roleChanged(Role oldRole, Role newRole) {
573 switch(newRole) {
574 case MASTER:
575 if (oldRole == Role.SLAVE) {
576 log.debug("Re-computing topology due " +
577 "to HA change from SLAVE->MASTER");
578 newInstanceTask.reschedule(1, TimeUnit.MILLISECONDS);
579 }
580 break;
581 case SLAVE:
582 log.debug("Clearing topology due to " +
583 "HA change to SLAVE");
584 clearCurrentTopology();
585 break;
586 default:
587 break;
588 }
589 }
590
591 @Override
592 public void controllerNodeIPsChanged(
593 Map<String, String> curControllerNodeIPs,
594 Map<String, String> addedControllerNodeIPs,
595 Map<String, String> removedControllerNodeIPs) {
596 // no-op
597 }
598
599 // *****************
600 // IFloodlightModule
601 // *****************
602
603 @Override
604 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
605 Collection<Class<? extends IFloodlightService>> l =
606 new ArrayList<Class<? extends IFloodlightService>>();
607 l.add(ITopologyService.class);
608 l.add(IRoutingService.class);
609 return l;
610 }
611
612 @Override
613 public Map<Class<? extends IFloodlightService>, IFloodlightService>
614 getServiceImpls() {
615 Map<Class<? extends IFloodlightService>,
616 IFloodlightService> m =
617 new HashMap<Class<? extends IFloodlightService>,
618 IFloodlightService>();
619 // We are the class that implements the service
620 m.put(ITopologyService.class, this);
621 m.put(IRoutingService.class, this);
622 return m;
623 }
624
625 @Override
626 public Collection<Class<? extends IFloodlightService>>
627 getModuleDependencies() {
628 Collection<Class<? extends IFloodlightService>> l =
629 new ArrayList<Class<? extends IFloodlightService>>();
630 l.add(ILinkDiscoveryService.class);
631 l.add(IThreadPoolService.class);
632 l.add(IFloodlightProviderService.class);
633 l.add(ICounterStoreService.class);
634 l.add(IRestApiService.class);
635 return l;
636 }
637
638 @Override
639 public void init(FloodlightModuleContext context)
640 throws FloodlightModuleException {
641 linkDiscovery = context.getServiceImpl(ILinkDiscoveryService.class);
642 threadPool = context.getServiceImpl(IThreadPoolService.class);
643 floodlightProvider =
644 context.getServiceImpl(IFloodlightProviderService.class);
645 restApi = context.getServiceImpl(IRestApiService.class);
646
647 switchPorts = new HashMap<Long,Set<Short>>();
648 switchPortLinks = new HashMap<NodePortTuple, Set<Link>>();
649 directLinks = new HashMap<NodePortTuple, Set<Link>>();
650 portBroadcastDomainLinks = new HashMap<NodePortTuple, Set<Link>>();
651 tunnelLinks = new HashMap<NodePortTuple, Set<Link>>();
652 topologyAware = new ArrayList<ITopologyListener>();
653 ldUpdates = new LinkedBlockingQueue<LDUpdate>();
654 appliedUpdates = new ArrayList<LDUpdate>();
655 clearCurrentTopology();
656 }
657
658 @Override
659 public void startUp(FloodlightModuleContext context) {
660 ScheduledExecutorService ses = threadPool.getScheduledExecutor();
661 newInstanceTask = new SingletonTask(ses, new UpdateTopologyWorker());
662 linkDiscovery.addListener(this);
663 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
664 floodlightProvider.addHAListener(this);
665 addRestletRoutable();
666 }
667
668 protected void addRestletRoutable() {
669 restApi.addRestletRoutable(new TopologyWebRoutable());
670 }
671
672 // ****************
673 // Internal methods
674 // ****************
675 /**
676 * If the packet-in switch port is disabled for all data traffic, then
677 * the packet will be dropped. Otherwise, the packet will follow the
678 * normal processing chain.
679 * @param sw
680 * @param pi
681 * @param cntx
682 * @return
683 */
684 protected Command dropFilter(long sw, OFPacketIn pi,
685 FloodlightContext cntx) {
686 Command result = Command.CONTINUE;
687 short port = pi.getInPort();
688
689 // If the input port is not allowed for data traffic, drop everything.
690 // BDDP packets will not reach this stage.
691 if (isAllowed(sw, port) == false) {
692 if (log.isTraceEnabled()) {
693 log.trace("Ignoring packet because of topology " +
694 "restriction on switch={}, port={}", sw, port);
695 result = Command.STOP;
696 }
697 }
698
699 // if sufficient information is available, then drop broadcast
700 // packets here as well.
701 return result;
702 }
703
704 /**
705 * TODO This method must be moved to a layer below forwarding
706 * so that anyone can use it.
707 * @param packetData
708 * @param sw
709 * @param ports
710 * @param cntx
711 */
712 @LogMessageDoc(level="ERROR",
713 message="Failed to clear all flows on switch {switch}",
714 explanation="An I/O error occured while trying send " +
715 "topology discovery packet",
716 recommendation=LogMessageDoc.CHECK_SWITCH)
717 public void doMultiActionPacketOut(byte[] packetData, IOFSwitch sw,
718 Set<Short> ports,
719 FloodlightContext cntx) {
720
721 if (ports == null) return;
722 if (packetData == null || packetData.length <= 0) return;
723
724 OFPacketOut po =
725 (OFPacketOut) floodlightProvider.getOFMessageFactory().
726 getMessage(OFType.PACKET_OUT);
727
728 List<OFAction> actions = new ArrayList<OFAction>();
729 for(short p: ports) {
730 actions.add(new OFActionOutput(p, (short) 0));
731 }
732
733 // set actions
734 po.setActions(actions);
735 // set action length
736 po.setActionsLength((short) (OFActionOutput.MINIMUM_LENGTH *
737 ports.size()));
738 // set buffer-id to BUFFER_ID_NONE
739 po.setBufferId(OFPacketOut.BUFFER_ID_NONE);
740 // set in-port to OFPP_NONE
741 po.setInPort(OFPort.OFPP_NONE.getValue());
742
743 // set packet data
744 po.setPacketData(packetData);
745
746 // compute and set packet length.
747 short poLength = (short)(OFPacketOut.MINIMUM_LENGTH +
748 po.getActionsLength() +
749 packetData.length);
750
751 po.setLength(poLength);
752
753 try {
754 //counterStore.updatePktOutFMCounterStore(sw, po);
755 if (log.isTraceEnabled()) {
756 log.trace("write broadcast packet on switch-id={} " +
757 "interaces={} packet-data={} packet-out={}",
758 new Object[] {sw.getId(), ports, packetData, po});
759 }
760 sw.write(po, cntx);
761
762 } catch (IOException e) {
763 log.error("Failure writing packet out", e);
764 }
765 }
766
767
768 /**
769 * The BDDP packets are forwarded out of all the ports out of an
770 * openflowdomain. Get all the switches in the same openflow
771 * domain as the sw (disabling tunnels). Then get all the
772 * external switch ports and send these packets out.
773 * @param sw
774 * @param pi
775 * @param cntx
776 */
777 protected void doFloodBDDP(long pinSwitch, OFPacketIn pi,
778 FloodlightContext cntx) {
779
780 TopologyInstance ti = getCurrentInstance(false);
781
782 Set<Long> switches = ti.getSwitchesInOpenflowDomain(pinSwitch);
783
784 if (switches == null)
785 {
786 // indicates no links are connected to the switches
787 switches = new HashSet<Long>();
788 switches.add(pinSwitch);
789 }
790
791 for(long sid: switches) {
792 IOFSwitch sw = floodlightProvider.getSwitches().get(sid);
793 if (sw == null) continue;
794 Collection<Short> enabledPorts = sw.getEnabledPortNumbers();
795 if (enabledPorts == null)
796 continue;
797 Set<Short> ports = new HashSet<Short>();
798 ports.addAll(enabledPorts);
799
800 // all the ports known to topology // without tunnels.
801 // out of these, we need to choose only those that are
802 // broadcast port, otherwise, we should eliminate.
803 Set<Short> portsKnownToTopo = ti.getPortsWithLinks(sid);
804
805 if (portsKnownToTopo != null) {
806 for(short p: portsKnownToTopo) {
807 NodePortTuple npt =
808 new NodePortTuple(sid, p);
809 if (ti.isBroadcastDomainPort(npt) == false) {
810 ports.remove(p);
811 }
812 }
813 }
814
815 // remove the incoming switch port
816 if (pinSwitch == sid) {
817 ports.remove(pi.getInPort());
818 }
819
820 // we have all the switch ports to which we need to broadcast.
821 doMultiActionPacketOut(pi.getPacketData(), sw, ports, cntx);
822 }
823
824 }
825
826 protected Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi,
827 FloodlightContext cntx) {
828
829 // get the packet-in switch.
830 Ethernet eth =
831 IFloodlightProviderService.bcStore.
832 get(cntx,IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
833
834 if (eth.getEtherType() == Ethernet.TYPE_BSN) {
835 BSN bsn = (BSN) eth.getPayload();
836 if (bsn == null) return Command.STOP;
837 if (bsn.getPayload() == null) return Command.STOP;
838
839 // It could be a packet other than BSN LLDP, therefore
840 // continue with the regular processing.
841 if (bsn.getPayload() instanceof LLDP == false)
842 return Command.CONTINUE;
843
844 doFloodBDDP(sw.getId(), pi, cntx);
845 } else {
846 return dropFilter(sw.getId(), pi, cntx);
847 }
848 return Command.STOP;
849 }
850
851
852 /**
853 * Updates concerning switch disconnect and port down are not processed.
854 * LinkDiscoveryManager is expected to process those messages and send
855 * multiple link removed messages. However, all the updates from
856 * LinkDiscoveryManager would be propagated to the listeners of topology.
857 */
858 @LogMessageDoc(level="ERROR",
859 message="Error reading link discovery update.",
860 explanation="Unable to process link discovery update",
861 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG)
862 public void applyUpdates() {
863 appliedUpdates.clear();
864 LDUpdate update = null;
865 while (ldUpdates.peek() != null) {
866 try {
867 update = ldUpdates.take();
868 } catch (Exception e) {
869 log.error("Error reading link discovery update.", e);
870 }
871 if (log.isTraceEnabled()) {
872 log.trace("Applying update: {}", update);
873 }
874 if (update.getOperation() == UpdateOperation.LINK_UPDATED) {
875 addOrUpdateLink(update.getSrc(), update.getSrcPort(),
876 update.getDst(), update.getDstPort(),
877 update.getType());
878 } else if (update.getOperation() == UpdateOperation.LINK_REMOVED){
879 removeLink(update.getSrc(), update.getSrcPort(),
880 update.getDst(), update.getDstPort());
881 }
882 // Add to the list of applied updates.
883 appliedUpdates.add(update);
884 }
885 }
886
887 /**
888 * This function computes a new topology.
889 */
890 /**
891 * This function computes a new topology instance.
892 * It ignores links connected to all broadcast domain ports
893 * and tunnel ports. The method returns if a new instance of
894 * topology was created or not.
895 */
896 protected boolean createNewInstance() {
897 Set<NodePortTuple> blockedPorts = new HashSet<NodePortTuple>();
898
899 if (!linksUpdated) return false;
900
901 Map<NodePortTuple, Set<Link>> openflowLinks;
902 openflowLinks =
903 new HashMap<NodePortTuple, Set<Link>>(switchPortLinks);
904
905 // Remove all tunnel links.
906 for(NodePortTuple npt: tunnelLinks.keySet()) {
907 if (openflowLinks.get(npt) != null)
908 openflowLinks.remove(npt);
909 }
910
911 // Remove all broadcast domain links.
912 for(NodePortTuple npt: portBroadcastDomainLinks.keySet()) {
913 if (openflowLinks.get(npt) != null)
914 openflowLinks.remove(npt);
915 }
916
917 TopologyInstance nt = new TopologyInstance(switchPorts,
918 blockedPorts,
919 openflowLinks,
920 portBroadcastDomainLinks.keySet(),
921 tunnelLinks.keySet());
922 nt.compute();
923 // We set the instances with and without tunnels to be identical.
924 // If needed, we may compute them differently.
925 currentInstance = nt;
926 currentInstanceWithoutTunnels = nt;
927 return true;
928 }
929
930
931 public void informListeners() {
932 for(int i=0; i<topologyAware.size(); ++i) {
933 ITopologyListener listener = topologyAware.get(i);
934 listener.topologyChanged();
935 }
936 }
937
938 public void addSwitch(long sid) {
939 if (switchPorts.containsKey(sid) == false) {
940 switchPorts.put(sid, new HashSet<Short>());
941 }
942 }
943
944 private void addPortToSwitch(long s, short p) {
945 addSwitch(s);
946 switchPorts.get(s).add(p);
947 }
948
949 public boolean removeSwitchPort(long sw, short port) {
950
951 Set<Link> linksToRemove = new HashSet<Link>();
952 NodePortTuple npt = new NodePortTuple(sw, port);
953 if (switchPortLinks.containsKey(npt) == false) return false;
954
955 linksToRemove.addAll(switchPortLinks.get(npt));
956 for(Link link: linksToRemove) {
957 removeLink(link);
958 }
959 return true;
960 }
961
962 public boolean removeSwitch(long sid) {
963 // Delete all the links in the switch, switch and all
964 // associated data should be deleted.
965 if (switchPorts.containsKey(sid) == false) return false;
966
967 Set<Link> linksToRemove = new HashSet<Link>();
968 for(Short p: switchPorts.get(sid)) {
969 NodePortTuple n1 = new NodePortTuple(sid, p);
970 linksToRemove.addAll(switchPortLinks.get(n1));
971 }
972
973 if (linksToRemove.isEmpty()) return false;
974
975 for(Link link: linksToRemove) {
976 removeLink(link);
977 }
978 return true;
979 }
980
981 /**
982 * Add the given link to the data structure. Returns true if a link was
983 * added.
984 * @param s
985 * @param l
986 * @return
987 */
988 private boolean addLinkToStructure(Map<NodePortTuple,
989 Set<Link>> s, Link l) {
990 boolean result1 = false, result2 = false;
991
992 NodePortTuple n1 = new NodePortTuple(l.getSrc(), l.getSrcPort());
993 NodePortTuple n2 = new NodePortTuple(l.getDst(), l.getDstPort());
994
995 if (s.get(n1) == null) {
996 s.put(n1, new HashSet<Link>());
997 }
998 if (s.get(n2) == null) {
999 s.put(n2, new HashSet<Link>());
1000 }
1001 result1 = s.get(n1).add(l);
1002 result2 = s.get(n2).add(l);
1003
1004 return (result1 || result2);
1005 }
1006
1007 /**
1008 * Delete the given link from the data strucure. Returns true if the
1009 * link was deleted.
1010 * @param s
1011 * @param l
1012 * @return
1013 */
1014 private boolean removeLinkFromStructure(Map<NodePortTuple,
1015 Set<Link>> s, Link l) {
1016
1017 boolean result1 = false, result2 = false;
1018 NodePortTuple n1 = new NodePortTuple(l.getSrc(), l.getSrcPort());
1019 NodePortTuple n2 = new NodePortTuple(l.getDst(), l.getDstPort());
1020
1021 if (s.get(n1) != null) {
1022 result1 = s.get(n1).remove(l);
1023 if (s.get(n1).isEmpty()) s.remove(n1);
1024 }
1025 if (s.get(n2) != null) {
1026 result2 = s.get(n2).remove(l);
1027 if (s.get(n2).isEmpty()) s.remove(n2);
1028 }
1029 return result1 || result2;
1030 }
1031
1032 public void addOrUpdateLink(long srcId, short srcPort, long dstId,
1033 short dstPort, LinkType type) {
1034 boolean flag1 = false, flag2 = false;
1035
1036 Link link = new Link(srcId, srcPort, dstId, dstPort);
1037 addPortToSwitch(srcId, srcPort);
1038 addPortToSwitch(dstId, dstPort);
1039
1040 addLinkToStructure(switchPortLinks, link);
1041
1042 if (type.equals(LinkType.MULTIHOP_LINK)) {
1043 addLinkToStructure(portBroadcastDomainLinks, link);
1044 flag1 = removeLinkFromStructure(tunnelLinks, link);
1045 flag2 = removeLinkFromStructure(directLinks, link);
1046 dtLinksUpdated = flag1 || flag2;
1047 } else if (type.equals(LinkType.TUNNEL)) {
1048 addLinkToStructure(tunnelLinks, link);
1049 removeLinkFromStructure(portBroadcastDomainLinks, link);
1050 removeLinkFromStructure(directLinks, link);
1051 dtLinksUpdated = true;
1052 } else if (type.equals(LinkType.DIRECT_LINK)) {
1053 addLinkToStructure(directLinks, link);
1054 removeLinkFromStructure(tunnelLinks, link);
1055 removeLinkFromStructure(portBroadcastDomainLinks, link);
1056 dtLinksUpdated = true;
1057 }
1058 linksUpdated = true;
1059 }
1060
1061 public void removeLink(Link link) {
1062 boolean flag1 = false, flag2 = false;
1063
1064 flag1 = removeLinkFromStructure(directLinks, link);
1065 flag2 = removeLinkFromStructure(tunnelLinks, link);
1066
1067 linksUpdated = true;
1068 dtLinksUpdated = flag1 || flag2;
1069
1070 removeLinkFromStructure(portBroadcastDomainLinks, link);
1071 removeLinkFromStructure(switchPortLinks, link);
1072
1073 NodePortTuple srcNpt =
1074 new NodePortTuple(link.getSrc(), link.getSrcPort());
1075 NodePortTuple dstNpt =
1076 new NodePortTuple(link.getDst(), link.getDstPort());
1077
1078 // Remove switch ports if there are no links through those switch ports
1079 if (switchPortLinks.get(srcNpt) == null) {
1080 if (switchPorts.get(srcNpt.getNodeId()) != null)
1081 switchPorts.get(srcNpt.getNodeId()).remove(srcNpt.getPortId());
1082 }
1083 if (switchPortLinks.get(dstNpt) == null) {
1084 if (switchPorts.get(dstNpt.getNodeId()) != null)
1085 switchPorts.get(dstNpt.getNodeId()).remove(dstNpt.getPortId());
1086 }
1087
1088 // Remove the node if no ports are present
1089 if (switchPorts.get(srcNpt.getNodeId())!=null &&
1090 switchPorts.get(srcNpt.getNodeId()).isEmpty()) {
1091 switchPorts.remove(srcNpt.getNodeId());
1092 }
1093 if (switchPorts.get(dstNpt.getNodeId())!=null &&
1094 switchPorts.get(dstNpt.getNodeId()).isEmpty()) {
1095 switchPorts.remove(dstNpt.getNodeId());
1096 }
1097 }
1098
1099 public void removeLink(long srcId, short srcPort,
1100 long dstId, short dstPort) {
1101 Link link = new Link(srcId, srcPort, dstId, dstPort);
1102 removeLink(link);
1103 }
1104
1105 public void clear() {
1106 switchPorts.clear();
1107 switchPortLinks.clear();
1108 portBroadcastDomainLinks.clear();
1109 tunnelLinks.clear();
1110 directLinks.clear();
1111 appliedUpdates.clear();
1112 }
1113
1114 /**
1115 * Clears the current topology. Note that this does NOT
1116 * send out updates.
1117 */
1118 public void clearCurrentTopology() {
1119 this.clear();
1120 linksUpdated = true;
1121 dtLinksUpdated = true;
1122 createNewInstance();
1123 lastUpdateTime = new Date();
1124 }
1125
1126 /**
1127 * Getters. No Setters.
1128 */
1129 public Map<Long, Set<Short>> getSwitchPorts() {
1130 return switchPorts;
1131 }
1132
1133 public Map<NodePortTuple, Set<Link>> getSwitchPortLinks() {
1134 return switchPortLinks;
1135 }
1136
1137 public Map<NodePortTuple, Set<Link>> getPortBroadcastDomainLinks() {
1138 return portBroadcastDomainLinks;
1139 }
1140
1141 public TopologyInstance getCurrentInstance(boolean tunnelEnabled) {
1142 if (tunnelEnabled)
1143 return currentInstance;
1144 else return this.currentInstanceWithoutTunnels;
1145 }
1146
1147 public TopologyInstance getCurrentInstance() {
1148 return this.getCurrentInstance(true);
1149 }
1150
1151 /**
1152 * Switch methods
1153 */
1154 public Set<Short> getPorts(long sw) {
1155 Set<Short> ports = new HashSet<Short>();
1156 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
1157 if (iofSwitch == null) return null;
1158
1159 Collection<Short> ofpList = iofSwitch.getEnabledPortNumbers();
1160 if (ofpList == null) return null;
1161
1162 Set<Short> qPorts = linkDiscovery.getQuarantinedPorts(sw);
1163 if (qPorts != null)
1164 ofpList.removeAll(qPorts);
1165
1166 ports.addAll(ofpList);
1167 return ports;
1168 }
1169}