blob: 2d8cf9ebf1a6e7d3fee177a119b81e805343697f [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 }
Pankaj Berde27c3a952013-06-10 21:26:25 -0700874 if (update.getOperation() == UpdateOperation.LINK_UPDATED ||
875 update.getOperation() == UpdateOperation.LINK_ADDED ) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800876 addOrUpdateLink(update.getSrc(), update.getSrcPort(),
877 update.getDst(), update.getDstPort(),
878 update.getType());
879 } else if (update.getOperation() == UpdateOperation.LINK_REMOVED){
880 removeLink(update.getSrc(), update.getSrcPort(),
881 update.getDst(), update.getDstPort());
882 }
883 // Add to the list of applied updates.
884 appliedUpdates.add(update);
885 }
886 }
887
888 /**
889 * This function computes a new topology.
890 */
891 /**
892 * This function computes a new topology instance.
893 * It ignores links connected to all broadcast domain ports
894 * and tunnel ports. The method returns if a new instance of
895 * topology was created or not.
896 */
897 protected boolean createNewInstance() {
898 Set<NodePortTuple> blockedPorts = new HashSet<NodePortTuple>();
899
900 if (!linksUpdated) return false;
901
902 Map<NodePortTuple, Set<Link>> openflowLinks;
903 openflowLinks =
904 new HashMap<NodePortTuple, Set<Link>>(switchPortLinks);
905
906 // Remove all tunnel links.
907 for(NodePortTuple npt: tunnelLinks.keySet()) {
908 if (openflowLinks.get(npt) != null)
909 openflowLinks.remove(npt);
910 }
911
912 // Remove all broadcast domain links.
913 for(NodePortTuple npt: portBroadcastDomainLinks.keySet()) {
914 if (openflowLinks.get(npt) != null)
915 openflowLinks.remove(npt);
916 }
917
918 TopologyInstance nt = new TopologyInstance(switchPorts,
919 blockedPorts,
920 openflowLinks,
921 portBroadcastDomainLinks.keySet(),
922 tunnelLinks.keySet());
923 nt.compute();
924 // We set the instances with and without tunnels to be identical.
925 // If needed, we may compute them differently.
926 currentInstance = nt;
927 currentInstanceWithoutTunnels = nt;
928 return true;
929 }
930
931
932 public void informListeners() {
933 for(int i=0; i<topologyAware.size(); ++i) {
934 ITopologyListener listener = topologyAware.get(i);
935 listener.topologyChanged();
936 }
937 }
938
939 public void addSwitch(long sid) {
940 if (switchPorts.containsKey(sid) == false) {
941 switchPorts.put(sid, new HashSet<Short>());
942 }
943 }
944
945 private void addPortToSwitch(long s, short p) {
946 addSwitch(s);
947 switchPorts.get(s).add(p);
948 }
949
950 public boolean removeSwitchPort(long sw, short port) {
951
952 Set<Link> linksToRemove = new HashSet<Link>();
953 NodePortTuple npt = new NodePortTuple(sw, port);
954 if (switchPortLinks.containsKey(npt) == false) return false;
955
956 linksToRemove.addAll(switchPortLinks.get(npt));
957 for(Link link: linksToRemove) {
958 removeLink(link);
959 }
960 return true;
961 }
962
963 public boolean removeSwitch(long sid) {
964 // Delete all the links in the switch, switch and all
965 // associated data should be deleted.
966 if (switchPorts.containsKey(sid) == false) return false;
967
968 Set<Link> linksToRemove = new HashSet<Link>();
969 for(Short p: switchPorts.get(sid)) {
970 NodePortTuple n1 = new NodePortTuple(sid, p);
971 linksToRemove.addAll(switchPortLinks.get(n1));
972 }
973
974 if (linksToRemove.isEmpty()) return false;
975
976 for(Link link: linksToRemove) {
977 removeLink(link);
978 }
979 return true;
980 }
981
982 /**
983 * Add the given link to the data structure. Returns true if a link was
984 * added.
985 * @param s
986 * @param l
987 * @return
988 */
989 private boolean addLinkToStructure(Map<NodePortTuple,
990 Set<Link>> s, Link l) {
991 boolean result1 = false, result2 = false;
992
993 NodePortTuple n1 = new NodePortTuple(l.getSrc(), l.getSrcPort());
994 NodePortTuple n2 = new NodePortTuple(l.getDst(), l.getDstPort());
995
996 if (s.get(n1) == null) {
997 s.put(n1, new HashSet<Link>());
998 }
999 if (s.get(n2) == null) {
1000 s.put(n2, new HashSet<Link>());
1001 }
1002 result1 = s.get(n1).add(l);
1003 result2 = s.get(n2).add(l);
1004
1005 return (result1 || result2);
1006 }
1007
1008 /**
1009 * Delete the given link from the data strucure. Returns true if the
1010 * link was deleted.
1011 * @param s
1012 * @param l
1013 * @return
1014 */
1015 private boolean removeLinkFromStructure(Map<NodePortTuple,
1016 Set<Link>> s, Link l) {
1017
1018 boolean result1 = false, result2 = false;
1019 NodePortTuple n1 = new NodePortTuple(l.getSrc(), l.getSrcPort());
1020 NodePortTuple n2 = new NodePortTuple(l.getDst(), l.getDstPort());
1021
1022 if (s.get(n1) != null) {
1023 result1 = s.get(n1).remove(l);
1024 if (s.get(n1).isEmpty()) s.remove(n1);
1025 }
1026 if (s.get(n2) != null) {
1027 result2 = s.get(n2).remove(l);
1028 if (s.get(n2).isEmpty()) s.remove(n2);
1029 }
1030 return result1 || result2;
1031 }
1032
1033 public void addOrUpdateLink(long srcId, short srcPort, long dstId,
1034 short dstPort, LinkType type) {
1035 boolean flag1 = false, flag2 = false;
1036
1037 Link link = new Link(srcId, srcPort, dstId, dstPort);
1038 addPortToSwitch(srcId, srcPort);
1039 addPortToSwitch(dstId, dstPort);
1040
1041 addLinkToStructure(switchPortLinks, link);
1042
1043 if (type.equals(LinkType.MULTIHOP_LINK)) {
1044 addLinkToStructure(portBroadcastDomainLinks, link);
1045 flag1 = removeLinkFromStructure(tunnelLinks, link);
1046 flag2 = removeLinkFromStructure(directLinks, link);
1047 dtLinksUpdated = flag1 || flag2;
1048 } else if (type.equals(LinkType.TUNNEL)) {
1049 addLinkToStructure(tunnelLinks, link);
1050 removeLinkFromStructure(portBroadcastDomainLinks, link);
1051 removeLinkFromStructure(directLinks, link);
1052 dtLinksUpdated = true;
1053 } else if (type.equals(LinkType.DIRECT_LINK)) {
1054 addLinkToStructure(directLinks, link);
1055 removeLinkFromStructure(tunnelLinks, link);
1056 removeLinkFromStructure(portBroadcastDomainLinks, link);
1057 dtLinksUpdated = true;
1058 }
1059 linksUpdated = true;
1060 }
1061
1062 public void removeLink(Link link) {
1063 boolean flag1 = false, flag2 = false;
1064
1065 flag1 = removeLinkFromStructure(directLinks, link);
1066 flag2 = removeLinkFromStructure(tunnelLinks, link);
1067
1068 linksUpdated = true;
1069 dtLinksUpdated = flag1 || flag2;
1070
1071 removeLinkFromStructure(portBroadcastDomainLinks, link);
1072 removeLinkFromStructure(switchPortLinks, link);
1073
1074 NodePortTuple srcNpt =
1075 new NodePortTuple(link.getSrc(), link.getSrcPort());
1076 NodePortTuple dstNpt =
1077 new NodePortTuple(link.getDst(), link.getDstPort());
1078
1079 // Remove switch ports if there are no links through those switch ports
1080 if (switchPortLinks.get(srcNpt) == null) {
1081 if (switchPorts.get(srcNpt.getNodeId()) != null)
1082 switchPorts.get(srcNpt.getNodeId()).remove(srcNpt.getPortId());
1083 }
1084 if (switchPortLinks.get(dstNpt) == null) {
1085 if (switchPorts.get(dstNpt.getNodeId()) != null)
1086 switchPorts.get(dstNpt.getNodeId()).remove(dstNpt.getPortId());
1087 }
1088
1089 // Remove the node if no ports are present
1090 if (switchPorts.get(srcNpt.getNodeId())!=null &&
1091 switchPorts.get(srcNpt.getNodeId()).isEmpty()) {
1092 switchPorts.remove(srcNpt.getNodeId());
1093 }
1094 if (switchPorts.get(dstNpt.getNodeId())!=null &&
1095 switchPorts.get(dstNpt.getNodeId()).isEmpty()) {
1096 switchPorts.remove(dstNpt.getNodeId());
1097 }
1098 }
1099
1100 public void removeLink(long srcId, short srcPort,
1101 long dstId, short dstPort) {
1102 Link link = new Link(srcId, srcPort, dstId, dstPort);
1103 removeLink(link);
1104 }
1105
1106 public void clear() {
1107 switchPorts.clear();
1108 switchPortLinks.clear();
1109 portBroadcastDomainLinks.clear();
1110 tunnelLinks.clear();
1111 directLinks.clear();
1112 appliedUpdates.clear();
1113 }
1114
1115 /**
1116 * Clears the current topology. Note that this does NOT
1117 * send out updates.
1118 */
1119 public void clearCurrentTopology() {
1120 this.clear();
1121 linksUpdated = true;
1122 dtLinksUpdated = true;
1123 createNewInstance();
1124 lastUpdateTime = new Date();
1125 }
1126
1127 /**
1128 * Getters. No Setters.
1129 */
1130 public Map<Long, Set<Short>> getSwitchPorts() {
1131 return switchPorts;
1132 }
1133
1134 public Map<NodePortTuple, Set<Link>> getSwitchPortLinks() {
1135 return switchPortLinks;
1136 }
1137
1138 public Map<NodePortTuple, Set<Link>> getPortBroadcastDomainLinks() {
1139 return portBroadcastDomainLinks;
1140 }
1141
1142 public TopologyInstance getCurrentInstance(boolean tunnelEnabled) {
1143 if (tunnelEnabled)
1144 return currentInstance;
1145 else return this.currentInstanceWithoutTunnels;
1146 }
1147
1148 public TopologyInstance getCurrentInstance() {
1149 return this.getCurrentInstance(true);
1150 }
1151
1152 /**
1153 * Switch methods
1154 */
1155 public Set<Short> getPorts(long sw) {
1156 Set<Short> ports = new HashSet<Short>();
1157 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
1158 if (iofSwitch == null) return null;
1159
1160 Collection<Short> ofpList = iofSwitch.getEnabledPortNumbers();
1161 if (ofpList == null) return null;
1162
1163 Set<Short> qPorts = linkDiscovery.getQuarantinedPorts(sw);
1164 if (qPorts != null)
1165 ofpList.removeAll(qPorts);
1166
1167 ports.addAll(ofpList);
1168 return ports;
1169 }
1170}