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