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