blob: bcb8b35f846dca2ff29a951fca08e818f1c71e7e [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
2 * Copyright 2011, Big Switch Networks, Inc.
3 * Originally created by David Erickson, Stanford University
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may
6 * not use this file except in compliance with the License. You may obtain
7 * a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations
15 * under the License.
16 **/
17
18package net.floodlightcontroller.linkdiscovery.internal;
19
20import java.io.IOException;
21import java.net.InetAddress;
22import java.net.InetSocketAddress;
23import java.net.NetworkInterface;
24import java.net.SocketAddress;
25import java.nio.ByteBuffer;
26import java.util.ArrayList;
27import java.util.Collection;
28import java.util.Collections;
29import java.util.HashMap;
30import java.util.HashSet;
31import java.util.Iterator;
32import java.util.List;
33import java.util.Map;
34import java.util.Map.Entry;
35import java.util.Set;
36import java.util.concurrent.BlockingQueue;
37import java.util.concurrent.LinkedBlockingQueue;
38import java.util.concurrent.ScheduledExecutorService;
39import java.util.concurrent.TimeUnit;
40import java.util.concurrent.locks.ReentrantReadWriteLock;
41
42import net.floodlightcontroller.core.FloodlightContext;
43import net.floodlightcontroller.core.IFloodlightProviderService;
44import net.floodlightcontroller.core.IFloodlightProviderService.Role;
45import net.floodlightcontroller.core.IHAListener;
46import net.floodlightcontroller.core.IInfoProvider;
47import net.floodlightcontroller.core.IOFMessageListener;
48import net.floodlightcontroller.core.IOFSwitch;
49import net.floodlightcontroller.core.IOFSwitchListener;
50import net.floodlightcontroller.core.annotations.LogMessageCategory;
51import net.floodlightcontroller.core.annotations.LogMessageDoc;
52import net.floodlightcontroller.core.annotations.LogMessageDocs;
53import net.floodlightcontroller.core.module.FloodlightModuleContext;
54import net.floodlightcontroller.core.module.FloodlightModuleException;
55import net.floodlightcontroller.core.module.IFloodlightModule;
56import net.floodlightcontroller.core.module.IFloodlightService;
57import net.floodlightcontroller.core.util.SingletonTask;
58import net.floodlightcontroller.linkdiscovery.ILinkDiscovery;
59import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LinkType;
60import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.SwitchType;
61import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
62import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.UpdateOperation;
63import net.floodlightcontroller.linkdiscovery.web.LinkDiscoveryWebRoutable;
64import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryListener;
65import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryService;
66import net.floodlightcontroller.linkdiscovery.LinkInfo;
67import net.floodlightcontroller.packet.BSN;
68import net.floodlightcontroller.packet.Ethernet;
69import net.floodlightcontroller.packet.IPv4;
70import net.floodlightcontroller.packet.LLDP;
71import net.floodlightcontroller.packet.LLDPTLV;
72import net.floodlightcontroller.restserver.IRestApiService;
73import net.floodlightcontroller.routing.Link;
74import net.floodlightcontroller.storage.IResultSet;
75import net.floodlightcontroller.storage.IStorageSourceService;
76import net.floodlightcontroller.storage.IStorageSourceListener;
77import net.floodlightcontroller.storage.OperatorPredicate;
78import net.floodlightcontroller.storage.StorageException;
79import net.floodlightcontroller.threadpool.IThreadPoolService;
80import net.floodlightcontroller.topology.NodePortTuple;
81import net.floodlightcontroller.util.EventHistory;
82import net.floodlightcontroller.util.EventHistory.EvAction;
83
84import org.openflow.protocol.OFMessage;
85import org.openflow.protocol.OFPacketIn;
86import org.openflow.protocol.OFPacketOut;
87import org.openflow.protocol.OFPhysicalPort;
88import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
89import org.openflow.protocol.OFPhysicalPort.OFPortState;
90import org.openflow.protocol.OFPort;
91import org.openflow.protocol.OFPortStatus;
92import org.openflow.protocol.OFPortStatus.OFPortReason;
93import org.openflow.protocol.OFType;
94import org.openflow.protocol.action.OFAction;
95import org.openflow.protocol.action.OFActionOutput;
96import org.openflow.util.HexString;
97import org.slf4j.Logger;
98import org.slf4j.LoggerFactory;
99
100/**
101 * This class sends out LLDP messages containing the sending switch's datapath
102 * id as well as the outgoing port number. Received LLrescDP messages that
103 * match a known switch cause a new LinkTuple to be created according to the
104 * invariant rules listed below. This new LinkTuple is also passed to routing
105 * if it exists to trigger updates.
106 *
107 * This class also handles removing links that are associated to switch ports
108 * that go down, and switches that are disconnected.
109 *
110 * Invariants:
111 * -portLinks and switchLinks will not contain empty Sets outside of
112 * critical sections
113 * -portLinks contains LinkTuples where one of the src or dst
114 * SwitchPortTuple matches the map key
115 * -switchLinks contains LinkTuples where one of the src or dst
116 * SwitchPortTuple's id matches the switch id
117 * -Each LinkTuple will be indexed into switchLinks for both
118 * src.id and dst.id, and portLinks for each src and dst
119 * -The updates queue is only added to from within a held write lock
120 */
121@LogMessageCategory("Network Topology")
122public class LinkDiscoveryManager
123implements IOFMessageListener, IOFSwitchListener,
124IStorageSourceListener, ILinkDiscoveryService,
125IFloodlightModule, IInfoProvider, IHAListener {
126 protected static Logger log = LoggerFactory.getLogger(LinkDiscoveryManager.class);
127
128 // Names of table/fields for links in the storage API
129 private static final String LINK_TABLE_NAME = "controller_link";
130 private static final String LINK_ID = "id";
131 private static final String LINK_SRC_SWITCH = "src_switch_id";
132 private static final String LINK_SRC_PORT = "src_port";
133 private static final String LINK_SRC_PORT_STATE = "src_port_state";
134 private static final String LINK_DST_SWITCH = "dst_switch_id";
135 private static final String LINK_DST_PORT = "dst_port";
136 private static final String LINK_DST_PORT_STATE = "dst_port_state";
137 private static final String LINK_VALID_TIME = "valid_time";
138 private static final String LINK_TYPE = "link_type";
139 private static final String SWITCH_CONFIG_TABLE_NAME = "controller_switchconfig";
140 private static final String SWITCH_CONFIG_CORE_SWITCH = "core_switch";
141
142 protected IFloodlightProviderService floodlightProvider;
143 protected IStorageSourceService storageSource;
144 protected IThreadPoolService threadPool;
145 protected IRestApiService restApi;
146
147
148 // LLDP and BDDP fields
149 private static final byte[] LLDP_STANDARD_DST_MAC_STRING =
150 HexString.fromHexString("01:80:c2:00:00:0e");
151 private static final long LINK_LOCAL_MASK = 0xfffffffffff0L;
152 private static final long LINK_LOCAL_VALUE = 0x0180c2000000L;
153
154 // BigSwitch OUI is 5C:16:C7, so 5D:16:C7 is the multicast version
155 // private static final String LLDP_BSN_DST_MAC_STRING = "5d:16:c7:00:00:01";
156 private static final String LLDP_BSN_DST_MAC_STRING = "ff:ff:ff:ff:ff:ff";
157
158
159 // Direction TLVs are used to indicate if the LLDPs were sent
160 // periodically or in response to a recieved LLDP
161 private static final byte TLV_DIRECTION_TYPE = 0x73;
162 private static final short TLV_DIRECTION_LENGTH = 1; // 1 byte
163 private static final byte TLV_DIRECTION_VALUE_FORWARD[] = {0x01};
164 private static final byte TLV_DIRECTION_VALUE_REVERSE[] = {0x02};
165 private static final LLDPTLV forwardTLV
166 = new LLDPTLV().
167 setType((byte)TLV_DIRECTION_TYPE).
168 setLength((short)TLV_DIRECTION_LENGTH).
169 setValue(TLV_DIRECTION_VALUE_FORWARD);
170
171 private static final LLDPTLV reverseTLV
172 = new LLDPTLV().
173 setType((byte)TLV_DIRECTION_TYPE).
174 setLength((short)TLV_DIRECTION_LENGTH).
175 setValue(TLV_DIRECTION_VALUE_REVERSE);
176
177 // Link discovery task details.
178 protected SingletonTask discoveryTask;
179 protected final int DISCOVERY_TASK_INTERVAL = 1;
180 protected final int LINK_TIMEOUT = 35; // timeout as part of LLDP process.
181 protected final int LLDP_TO_ALL_INTERVAL = 15 ; //15 seconds.
182 protected long lldpClock = 0;
183 // This value is intentionally kept higher than LLDP_TO_ALL_INTERVAL.
184 // If we want to identify link failures faster, we could decrease this
185 // value to a small number, say 1 or 2 sec.
186 protected final int LLDP_TO_KNOWN_INTERVAL= 20; // LLDP frequency for known links
187
188 protected LLDPTLV controllerTLV;
189 protected ReentrantReadWriteLock lock;
190 int lldpTimeCount = 0;
191
192 /**
193 * Flag to indicate if automatic port fast is enabled or not.
194 * Default is set to false -- Initialized in the init method as well.
195 */
196 boolean autoPortFastFeature = false;
197
198 /**
199 * Map from link to the most recent time it was verified functioning
200 */
201 protected Map<Link, LinkInfo> links;
202
203 /**
204 * Map from switch id to a set of all links with it as an endpoint
205 */
206 protected Map<Long, Set<Link>> switchLinks;
207
208 /**
209 * Map from a id:port to the set of links containing it as an endpoint
210 */
211 protected Map<NodePortTuple, Set<Link>> portLinks;
212
213 /**
214 * Set of link tuples over which multicast LLDPs are received
215 * and unicast LLDPs are not received.
216 */
217 protected Map<NodePortTuple, Set<Link>> portBroadcastDomainLinks;
218
219 protected volatile boolean shuttingDown = false;
220
221 /* topology aware components are called in the order they were added to the
222 * the array */
223 protected ArrayList<ILinkDiscoveryListener> linkDiscoveryAware;
224 protected BlockingQueue<LDUpdate> updates;
225 protected Thread updatesThread;
226
227 /**
228 * List of ports through which LLDP/BDDPs are not sent.
229 */
230 protected Set<NodePortTuple> suppressLinkDiscovery;
231
232 /** A list of ports that are quarantined for discovering links through
233 * them. Data traffic from these ports are not allowed until the ports
234 * are released from quarantine.
235 */
236 protected LinkedBlockingQueue<NodePortTuple> quarantineQueue;
237 protected LinkedBlockingQueue<NodePortTuple> maintenanceQueue;
238 /**
239 * Quarantine task
240 */
241 protected SingletonTask bddpTask;
242 protected final int BDDP_TASK_INTERVAL = 100; // 100 ms.
243 protected final int BDDP_TASK_SIZE = 5; // # of ports per iteration
244
245 /**
246 * Map of broadcast domain ports and the last time a BDDP was either
247 * sent or received on that port.
248 */
249 protected Map<NodePortTuple, Long> broadcastDomainPortTimeMap;
250
251 /**
252 * Get the LLDP sending period in seconds.
253 * @return LLDP sending period in seconds.
254 */
255 public int getLldpFrequency() {
256 return LLDP_TO_KNOWN_INTERVAL;
257 }
258
259 /**
260 * Get the LLDP timeout value in seconds
261 * @return LLDP timeout value in seconds
262 */
263 public int getLldpTimeout() {
264 return LINK_TIMEOUT;
265 }
266
267 public Map<NodePortTuple, Set<Link>> getPortLinks() {
268 return portLinks;
269 }
270
271 public Set<NodePortTuple> getSuppressLLDPsInfo() {
272 return suppressLinkDiscovery;
273 }
274
275 /**
276 * Add a switch port to the suppressed LLDP list.
277 * Remove any known links on the switch port.
278 */
279 public void AddToSuppressLLDPs(long sw, short port)
280 {
281 NodePortTuple npt = new NodePortTuple(sw, port);
282 this.suppressLinkDiscovery.add(npt);
283 deleteLinksOnPort(npt, "LLDP suppressed.");
284 }
285
286 /**
287 * Remove a switch port from the suppressed LLDP list.
288 * Discover links on that switchport.
289 */
290 public void RemoveFromSuppressLLDPs(long sw, short port)
291 {
292 NodePortTuple npt = new NodePortTuple(sw, port);
293 this.suppressLinkDiscovery.remove(npt);
294 discover(npt);
295 }
296
297 public boolean isShuttingDown() {
298 return shuttingDown;
299 }
300
301 public boolean isFastPort(long sw, short port) {
302 return false;
303 }
304
305 public ILinkDiscovery.LinkType getLinkType(Link lt, LinkInfo info) {
306 if (info.getUnicastValidTime() != null) {
307 return ILinkDiscovery.LinkType.DIRECT_LINK;
308 } else if (info.getMulticastValidTime() != null) {
309 return ILinkDiscovery.LinkType.MULTIHOP_LINK;
310 }
311 return ILinkDiscovery.LinkType.INVALID_LINK;
312 }
313
314 @LogMessageDoc(level="ERROR",
315 message="Error in link discovery updates loop",
316 explanation="An unknown error occured while dispatching " +
317 "link update notifications",
318 recommendation=LogMessageDoc.GENERIC_ACTION)
319 private void doUpdatesThread() throws InterruptedException {
320 do {
321 LDUpdate update = updates.take();
322
323 if (linkDiscoveryAware != null) {
324 if (log.isTraceEnabled()) {
325 log.trace("Dispatching link discovery update {} {} {} {} {} for {}",
326 new Object[]{update.getOperation(),
327 HexString.toHexString(update.getSrc()), update.getSrcPort(),
328 HexString.toHexString(update.getDst()), update.getDstPort(),
329 linkDiscoveryAware});
330 }
331 try {
332 for (ILinkDiscoveryListener lda : linkDiscoveryAware) { // order maintained
333 lda.linkDiscoveryUpdate(update);
334 }
335 }
336 catch (Exception e) {
337 log.error("Error in link discovery updates loop", e);
338 }
339 }
340 } while (updates.peek() != null);
341 }
342 private boolean isLinkDiscoverySuppressed(long sw, short portNumber) {
343 return this.suppressLinkDiscovery.contains(new NodePortTuple(sw, portNumber));
344 }
345
346 protected void discoverLinks() {
347
348 // timeout known links.
349 timeoutLinks();
350
351 //increment LLDP clock
352 lldpClock = (lldpClock + 1)% LLDP_TO_ALL_INTERVAL;
353
354 if (lldpClock == 0) {
355 log.debug("Sending LLDP out on all ports.");
356 discoverOnAllPorts();
357 }
358 }
359
360
361 /**
362 * Quarantine Ports.
363 */
364 protected class QuarantineWorker implements Runnable {
365 @Override
366 public void run() {
367 try {
368 processBDDPLists();
369 }
370 catch (Exception e) {
371 log.error("Error in quarantine worker thread", e);
372 } finally {
373 bddpTask.reschedule(BDDP_TASK_INTERVAL,
374 TimeUnit.MILLISECONDS);
375 }
376 }
377 }
378
379 /**
380 * Add a switch port to the quarantine queue. Schedule the
381 * quarantine task if the quarantine queue was empty before adding
382 * this switch port.
383 * @param npt
384 */
385 protected void addToQuarantineQueue(NodePortTuple npt) {
386 if (quarantineQueue.contains(npt) == false)
387 quarantineQueue.add(npt);
388 }
389
390 /**
391 * Remove a switch port from the quarantine queue.
392 */
393 protected void removeFromQuarantineQueue(NodePortTuple npt) {
394 // Remove all occurrences of the node port tuple from the list.
395 while (quarantineQueue.remove(npt));
396 }
397
398 /**
399 * Add a switch port to maintenance queue.
400 * @param npt
401 */
402 protected void addToMaintenanceQueue(NodePortTuple npt) {
403 // TODO We are not checking if the switch port tuple is already
404 // in the maintenance list or not. This will be an issue for
405 // really large number of switch ports in the network.
406 if (maintenanceQueue.contains(npt) == false)
407 maintenanceQueue.add(npt);
408 }
409
410 /**
411 * Remove a switch port from maintenance queue.
412 * @param npt
413 */
414 protected void removeFromMaintenanceQueue(NodePortTuple npt) {
415 // Remove all occurrences of the node port tuple from the queue.
416 while (maintenanceQueue.remove(npt));
417 }
418
419 /**
420 * This method processes the quarantine list in bursts. The task is
421 * at most once per BDDP_TASK_INTERVAL.
422 * One each call, BDDP_TASK_SIZE number of switch ports are processed.
423 * Once the BDDP packets are sent out through the switch ports, the ports
424 * are removed from the quarantine list.
425 */
426
427 protected void processBDDPLists() {
428 int count = 0;
429 Set<NodePortTuple> nptList = new HashSet<NodePortTuple>();
430
431 while(count < BDDP_TASK_SIZE && quarantineQueue.peek() !=null) {
432 NodePortTuple npt;
433 npt = quarantineQueue.remove();
434 sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false, false);
435 nptList.add(npt);
436 count++;
437 }
438
439 count = 0;
440 while (count < BDDP_TASK_SIZE && maintenanceQueue.peek() != null) {
441 NodePortTuple npt;
442 npt = maintenanceQueue.remove();
443 sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false, false);
444 count++;
445 }
446
447 for(NodePortTuple npt:nptList) {
448 generateSwitchPortStatusUpdate(npt.getNodeId(), npt.getPortId());
449 }
450 }
451
452 public Set<Short> getQuarantinedPorts(long sw) {
453 Set<Short> qPorts = new HashSet<Short>();
454
455 Iterator<NodePortTuple> iter = quarantineQueue.iterator();
456 while (iter.hasNext()) {
457 NodePortTuple npt = iter.next();
458 if (npt.getNodeId() == sw) {
459 qPorts.add(npt.getPortId());
460 }
461 }
462 return qPorts;
463 }
464
465 private void generateSwitchPortStatusUpdate(long sw, short port) {
466 UpdateOperation operation;
467
468 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
469 if (iofSwitch == null) return;
470
471 OFPhysicalPort ofp = iofSwitch.getPort(port);
472 if (ofp == null) return;
473
474 int srcPortState = ofp.getState();
475 boolean portUp = ((srcPortState &
476 OFPortState.OFPPS_STP_MASK.getValue()) !=
477 OFPortState.OFPPS_STP_BLOCK.getValue());
478
479 if (portUp) operation = UpdateOperation.PORT_UP;
480 else operation = UpdateOperation.PORT_DOWN;
481
482 updates.add(new LDUpdate(sw, port, operation));
483 }
484
485 /**
486 * Send LLDP on known ports
487 */
488 protected void discoverOnKnownLinkPorts() {
489 // Copy the port set.
490 Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>();
491 nptSet.addAll(portLinks.keySet());
492
493 // Send LLDP from each of them.
494 for(NodePortTuple npt: nptSet) {
495 discover(npt);
496 }
497 }
498
499 protected void discover(NodePortTuple npt) {
500 discover(npt.getNodeId(), npt.getPortId());
501 }
502
503 protected void discover(long sw, short port) {
504 sendDiscoveryMessage(sw, port, true, false);
505 }
506
507 /**
508 * Send link discovery message out of a given switch port.
509 * The discovery message may be a standard LLDP or a modified
510 * LLDP, where the dst mac address is set to :ff.
511 *
512 * TODO: The modified LLDP will updated in the future and may
513 * use a different eth-type.
514 * @param sw
515 * @param port
516 * @param isStandard indicates standard or modified LLDP
517 * @param isReverse indicates whether the LLDP was sent as a response
518 */
519 @LogMessageDoc(level="ERROR",
520 message="Failure sending LLDP out port {port} on switch {switch}",
521 explanation="An I/O error occured while sending LLDP message " +
522 "to the switch.",
523 recommendation=LogMessageDoc.CHECK_SWITCH)
524 protected void sendDiscoveryMessage(long sw, short port,
525 boolean isStandard,
526 boolean isReverse) {
527
528 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
529 if (iofSwitch == null) {
530 return;
531 }
532
533 if (port == OFPort.OFPP_LOCAL.getValue())
534 return;
535
536 OFPhysicalPort ofpPort = iofSwitch.getPort(port);
537
538 if (ofpPort == null) {
539 if (log.isTraceEnabled()) {
540 log.trace("Null physical port. sw={}, port={}", sw, port);
541 }
542 return;
543 }
544
545 if (isLinkDiscoverySuppressed(sw, port)) {
546 /* Dont send LLDPs out of this port as suppressLLDPs set
547 *
548 */
549 return;
550 }
551
552 // For fast ports, do not send forward LLDPs or BDDPs.
553 if (!isReverse && autoPortFastFeature && isFastPort(sw, port))
554 return;
555
556 if (log.isTraceEnabled()) {
557 log.trace("Sending LLDP packet out of swich: {}, port: {}",
558 sw, port);
559 }
560
561 // using "nearest customer bridge" MAC address for broadest possible propagation
562 // through provider and TPMR bridges (see IEEE 802.1AB-2009 and 802.1Q-2011),
563 // in particular the Linux bridge which behaves mostly like a provider bridge
564 byte[] chassisId = new byte[] {4, 0, 0, 0, 0, 0, 0}; // filled in later
565 byte[] portId = new byte[] {2, 0, 0}; // filled in later
566 byte[] ttlValue = new byte[] {0, 0x78};
567 // OpenFlow OUI - 00-26-E1
568 byte[] dpidTLVValue = new byte[] {0x0, 0x26, (byte) 0xe1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
569 LLDPTLV dpidTLV = new LLDPTLV().setType((byte) 127).setLength((short) dpidTLVValue.length).setValue(dpidTLVValue);
570
571 byte[] dpidArray = new byte[8];
572 ByteBuffer dpidBB = ByteBuffer.wrap(dpidArray);
573 ByteBuffer portBB = ByteBuffer.wrap(portId, 1, 2);
574
575 Long dpid = sw;
576 dpidBB.putLong(dpid);
577 // set the ethernet source mac to last 6 bytes of dpid
578 System.arraycopy(dpidArray, 2, ofpPort.getHardwareAddress(), 0, 6);
579 // set the chassis id's value to last 6 bytes of dpid
580 System.arraycopy(dpidArray, 2, chassisId, 1, 6);
581 // set the optional tlv to the full dpid
582 System.arraycopy(dpidArray, 0, dpidTLVValue, 4, 8);
583
584
585 // set the portId to the outgoing port
586 portBB.putShort(port);
587 if (log.isTraceEnabled()) {
588 log.trace("Sending LLDP out of interface: {}/{}",
589 HexString.toHexString(sw), port);
590 }
591
592 LLDP lldp = new LLDP();
593 lldp.setChassisId(new LLDPTLV().setType((byte) 1).setLength((short) chassisId.length).setValue(chassisId));
594 lldp.setPortId(new LLDPTLV().setType((byte) 2).setLength((short) portId.length).setValue(portId));
595 lldp.setTtl(new LLDPTLV().setType((byte) 3).setLength((short) ttlValue.length).setValue(ttlValue));
596 lldp.getOptionalTLVList().add(dpidTLV);
597
598 // Add the controller identifier to the TLV value.
599 lldp.getOptionalTLVList().add(controllerTLV);
600 if (isReverse) {
601 lldp.getOptionalTLVList().add(reverseTLV);
602 }else {
603 lldp.getOptionalTLVList().add(forwardTLV);
604 }
605
606 Ethernet ethernet;
607 if (isStandard) {
608 ethernet = new Ethernet()
609 .setSourceMACAddress(ofpPort.getHardwareAddress())
610 .setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING)
611 .setEtherType(Ethernet.TYPE_LLDP);
612 ethernet.setPayload(lldp);
613 } else {
614 BSN bsn = new BSN(BSN.BSN_TYPE_BDDP);
615 bsn.setPayload(lldp);
616
617 ethernet = new Ethernet()
618 .setSourceMACAddress(ofpPort.getHardwareAddress())
619 .setDestinationMACAddress(LLDP_BSN_DST_MAC_STRING)
620 .setEtherType(Ethernet.TYPE_BSN);
621 ethernet.setPayload(bsn);
622 }
623
624
625 // serialize and wrap in a packet out
626 byte[] data = ethernet.serialize();
627 OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
628 po.setBufferId(OFPacketOut.BUFFER_ID_NONE);
629 po.setInPort(OFPort.OFPP_NONE);
630
631 // set actions
632 List<OFAction> actions = new ArrayList<OFAction>();
633 actions.add(new OFActionOutput(port, (short) 0));
634 po.setActions(actions);
635 po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
636
637 // set data
638 po.setLengthU(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength() + data.length);
639 po.setPacketData(data);
640
641 // send
642 try {
643 iofSwitch.write(po, null);
644 iofSwitch.flush();
645 } catch (IOException e) {
646 log.error("Failure sending LLDP out port {} on switch {}",
647 new Object[]{ port, iofSwitch.getStringId() }, e);
648 }
649
650 }
651
652 /**
653 * Send LLDPs to all switch-ports
654 */
655 protected void discoverOnAllPorts() {
656 if (log.isTraceEnabled()) {
657 log.trace("Sending LLDP packets out of all the enabled ports on switch {}");
658 }
659 Set<Long> switches = floodlightProvider.getSwitches().keySet();
660 // Send standard LLDPs
661 for (long sw: switches) {
662 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
663 if (iofSwitch == null) continue;
664 if (iofSwitch.getEnabledPorts() != null) {
665 for (OFPhysicalPort ofp: iofSwitch.getEnabledPorts()) {
666 if (isLinkDiscoverySuppressed(sw, ofp.getPortNumber()))
667 continue;
668 if (autoPortFastFeature && isFastPort(sw, ofp.getPortNumber()))
669 continue;
670
671 // sends forward LLDP only non-fastports.
672 sendDiscoveryMessage(sw, ofp.getPortNumber(), true, false);
673
674 // If the switch port is not alreayd in the maintenance
675 // queue, add it.
676 NodePortTuple npt = new NodePortTuple(sw, ofp.getPortNumber());
677 addToMaintenanceQueue(npt);
678 }
679 }
680 }
681 }
682
683 protected void setControllerTLV() {
684 //Setting the controllerTLVValue based on current nano time,
685 //controller's IP address, and the network interface object hash
686 //the corresponding IP address.
687
688 final int prime = 7867;
689 InetAddress localIPAddress = null;
690 NetworkInterface localInterface = null;
691
692 byte[] controllerTLVValue = new byte[] {0, 0, 0, 0, 0, 0, 0, 0}; // 8 byte value.
693 ByteBuffer bb = ByteBuffer.allocate(10);
694
695 try{
696 localIPAddress = java.net.InetAddress.getLocalHost();
697 localInterface = NetworkInterface.getByInetAddress(localIPAddress);
698 } catch (Exception e) {
699 e.printStackTrace();
700 }
701
702 long result = System.nanoTime();
703 if (localIPAddress != null)
704 result = result * prime + IPv4.toIPv4Address(localIPAddress.getHostAddress());
705 if (localInterface != null)
706 result = result * prime + localInterface.hashCode();
707 // set the first 4 bits to 0.
708 result = result & (0x0fffffffffffffffL);
709
710 bb.putLong(result);
711
712 bb.rewind();
713 bb.get(controllerTLVValue, 0, 8);
714
715 this.controllerTLV = new LLDPTLV().setType((byte) 0x0c).setLength((short) controllerTLVValue.length).setValue(controllerTLVValue);
716 }
717
718 @Override
719 public String getName() {
720 return "linkdiscovery";
721 }
722
723 @Override
724 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
725 switch (msg.getType()) {
726 case PACKET_IN:
727 return this.handlePacketIn(sw.getId(), (OFPacketIn) msg, cntx);
728 case PORT_STATUS:
729 return this.handlePortStatus(sw.getId(), (OFPortStatus) msg);
730 default:
731 break;
732 }
733 return Command.CONTINUE;
734 }
735
736 private Command handleLldp(LLDP lldp, long sw, OFPacketIn pi, boolean isStandard, FloodlightContext cntx) {
737 // If LLDP is suppressed on this port, ignore received packet as well
738 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
739 if (iofSwitch == null) {
740 return Command.STOP;
741 }
742
743 if (isLinkDiscoverySuppressed(sw, pi.getInPort()))
744 return Command.STOP;
745
746 // If this is a malformed LLDP, or not from us, exit
747 if (lldp.getPortId() == null || lldp.getPortId().getLength() != 3)
748 return Command.CONTINUE;
749
750 long myId = ByteBuffer.wrap(controllerTLV.getValue()).getLong();
751 long otherId = 0;
752 boolean myLLDP = false;
753 Boolean isReverse = null;
754
755 ByteBuffer portBB = ByteBuffer.wrap(lldp.getPortId().getValue());
756 portBB.position(1);
757
758 Short remotePort = portBB.getShort();
759 IOFSwitch remoteSwitch = null;
760
761 // Verify this LLDP packet matches what we're looking for
762 for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) {
763 if (lldptlv.getType() == 127 && lldptlv.getLength() == 12 &&
764 lldptlv.getValue()[0] == 0x0 && lldptlv.getValue()[1] == 0x26 &&
765 lldptlv.getValue()[2] == (byte)0xe1 && lldptlv.getValue()[3] == 0x0) {
766 ByteBuffer dpidBB = ByteBuffer.wrap(lldptlv.getValue());
767 remoteSwitch = floodlightProvider.getSwitches().get(dpidBB.getLong(4));
768 } else if (lldptlv.getType() == 12 && lldptlv.getLength() == 8){
769 otherId = ByteBuffer.wrap(lldptlv.getValue()).getLong();
770 if (myId == otherId)
771 myLLDP = true;
772 } else if (lldptlv.getType() == TLV_DIRECTION_TYPE &&
773 lldptlv.getLength() == TLV_DIRECTION_LENGTH) {
774 if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_FORWARD[0])
775 isReverse = false;
776 else if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_REVERSE[0])
777 isReverse = true;
778 }
779 }
780
781 if (myLLDP == false) {
782 // This is not the LLDP sent by this controller.
783 // If the LLDP message has multicast bit set, then we need to broadcast
784 // the packet as a regular packet.
785 if (isStandard) {
786 if (log.isTraceEnabled()) {
787 log.trace("Getting standard LLDP from a different controller and quelching it.");
788 }
789 return Command.STOP;
790 }
791 else if (myId < otherId) {
792 if (log.isTraceEnabled()) {
793 log.trace("Getting BDDP packets from a different controller" +
794 "and letting it go through normal processing chain.");
795 }
796 return Command.CONTINUE;
797 }
798 }
799
800
801 if (remoteSwitch == null) {
802 // Ignore LLDPs not generated by Floodlight, or from a switch that has recently
803 // disconnected, or from a switch connected to another Floodlight instance
804 if (log.isTraceEnabled()) {
805 log.trace("Received LLDP from remote switch not connected to the controller");
806 }
807 return Command.STOP;
808 }
809
810 if (!remoteSwitch.portEnabled(remotePort)) {
811 if (log.isTraceEnabled()) {
812 log.trace("Ignoring link with disabled source port: switch {} port {}", remoteSwitch, remotePort);
813 }
814 return Command.STOP;
815 }
816 if (suppressLinkDiscovery.contains(new NodePortTuple(remoteSwitch.getId(),
817 remotePort))) {
818 if (log.isTraceEnabled()) {
819 log.trace("Ignoring link with suppressed src port: switch {} port {}",
820 remoteSwitch, remotePort);
821 }
822 return Command.STOP;
823 }
824 if (!iofSwitch.portEnabled(pi.getInPort())) {
825 if (log.isTraceEnabled()) {
826 log.trace("Ignoring link with disabled dest port: switch {} port {}", sw, pi.getInPort());
827 }
828 return Command.STOP;
829 }
830
831 OFPhysicalPort physicalPort = remoteSwitch.getPort(remotePort);
832 int srcPortState = (physicalPort != null) ? physicalPort.getState() : 0;
833 physicalPort = iofSwitch.getPort(pi.getInPort());
834 int dstPortState = (physicalPort != null) ? physicalPort.getState() : 0;
835
836 // Store the time of update to this link, and push it out to routingEngine
837 Link lt = new Link(remoteSwitch.getId(), remotePort, iofSwitch.getId(), pi.getInPort());
838
839
840 Long lastLldpTime = null;
841 Long lastBddpTime = null;
842
843 Long firstSeenTime = System.currentTimeMillis();
844
845 if (isStandard)
846 lastLldpTime = System.currentTimeMillis();
847 else
848 lastBddpTime = System.currentTimeMillis();
849
850 LinkInfo newLinkInfo =
851 new LinkInfo(firstSeenTime, lastLldpTime, lastBddpTime,
852 srcPortState, dstPortState);
853
854 addOrUpdateLink(lt, newLinkInfo);
855
856 // Check if reverse link exists.
857 // If it doesn't exist and if the forward link was seen
858 // first seen within a small interval, send probe on the
859 // reverse link.
860
861 newLinkInfo = links.get(lt);
862 if (newLinkInfo != null && isStandard && isReverse == false) {
863 Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
864 lt.getSrc(), lt.getSrcPort());
865 LinkInfo reverseInfo = links.get(reverseLink);
866 if (reverseInfo == null) {
867 // the reverse link does not exist.
868 if (newLinkInfo.getFirstSeenTime() > System.currentTimeMillis() - LINK_TIMEOUT) {
869 this.sendDiscoveryMessage(lt.getDst(), lt.getDstPort(), isStandard, true);
870 }
871 }
872 }
873
874 // If the received packet is a BDDP packet, then create a reverse BDDP
875 // link as well.
876 if (!isStandard) {
877 Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
878 lt.getSrc(), lt.getSrcPort());
879
880 // srcPortState and dstPort state are reversed.
881 LinkInfo reverseInfo =
882 new LinkInfo(firstSeenTime, lastLldpTime, lastBddpTime,
883 dstPortState, srcPortState);
884
885 addOrUpdateLink(reverseLink, reverseInfo);
886 }
887
888 // Remove the node ports from the quarantine and maintenance queues.
889 NodePortTuple nptSrc = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
890 NodePortTuple nptDst = new NodePortTuple(lt.getDst(), lt.getDstPort());
891 removeFromQuarantineQueue(nptSrc);
892 removeFromMaintenanceQueue(nptSrc);
893 removeFromQuarantineQueue(nptDst);
894 removeFromMaintenanceQueue(nptDst);
895
896 // Consume this message
897 return Command.STOP;
898 }
899
900 protected Command handlePacketIn(long sw, OFPacketIn pi,
901 FloodlightContext cntx) {
902 Ethernet eth =
903 IFloodlightProviderService.bcStore.get(cntx,
904 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
905
906 if(eth.getEtherType() == Ethernet.TYPE_BSN) {
907 BSN bsn = (BSN) eth.getPayload();
908 if (bsn == null) return Command.STOP;
909 if (bsn.getPayload() == null) return Command.STOP;
910 // It could be a packet other than BSN LLDP, therefore
911 // continue with the regular processing.
912 if (bsn.getPayload() instanceof LLDP == false)
913 return Command.CONTINUE;
914 return handleLldp((LLDP) bsn.getPayload(), sw, pi, false, cntx);
915 } else if (eth.getEtherType() == Ethernet.TYPE_LLDP) {
916 return handleLldp((LLDP) eth.getPayload(), sw, pi, true, cntx);
917 } else if (eth.getEtherType() < 1500) {
918 long destMac = eth.getDestinationMAC().toLong();
919 if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE){
920 if (log.isTraceEnabled()) {
921 log.trace("Ignoring packet addressed to 802.1D/Q " +
922 "reserved address.");
923 }
924 return Command.STOP;
925 }
926 }
927
928 // If packet-in is from a quarantine port, stop processing.
929 NodePortTuple npt = new NodePortTuple(sw, pi.getInPort());
930 if (quarantineQueue.contains(npt)) return Command.STOP;
931
932 return Command.CONTINUE;
933 }
934
935 protected UpdateOperation getUpdateOperation(int srcPortState,
936 int dstPortState) {
937 boolean added =
938 (((srcPortState &
939 OFPortState.OFPPS_STP_MASK.getValue()) !=
940 OFPortState.OFPPS_STP_BLOCK.getValue()) &&
941 ((dstPortState &
942 OFPortState.OFPPS_STP_MASK.getValue()) !=
943 OFPortState.OFPPS_STP_BLOCK.getValue()));
944
945 if (added) return UpdateOperation.LINK_UPDATED;
946 return UpdateOperation.LINK_REMOVED;
947 }
948
949
950
951 protected UpdateOperation getUpdateOperation(int srcPortState) {
952 boolean portUp = ((srcPortState &
953 OFPortState.OFPPS_STP_MASK.getValue()) !=
954 OFPortState.OFPPS_STP_BLOCK.getValue());
955
956 if (portUp) return UpdateOperation.PORT_UP;
957 else return UpdateOperation.PORT_DOWN;
958 }
959
960 protected boolean addOrUpdateLink(Link lt, LinkInfo newInfo) {
961
962 NodePortTuple srcNpt, dstNpt;
963 boolean linkChanged = false;
964
965 lock.writeLock().lock();
966 try {
967 // put the new info. if an old info exists, it will be returned.
968 LinkInfo oldInfo = links.put(lt, newInfo);
969 if (oldInfo != null &&
970 oldInfo.getFirstSeenTime() < newInfo.getFirstSeenTime())
971 newInfo.setFirstSeenTime(oldInfo.getFirstSeenTime());
972
973 if (log.isTraceEnabled()) {
974 log.trace("addOrUpdateLink: {} {}",
975 lt,
976 (newInfo.getMulticastValidTime()!=null) ? "multicast" : "unicast");
977 }
978
979 UpdateOperation updateOperation = null;
980 linkChanged = false;
981
982 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
983 dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
984
985 if (oldInfo == null) {
986 // index it by switch source
987 if (!switchLinks.containsKey(lt.getSrc()))
988 switchLinks.put(lt.getSrc(), new HashSet<Link>());
989 switchLinks.get(lt.getSrc()).add(lt);
990
991 // index it by switch dest
992 if (!switchLinks.containsKey(lt.getDst()))
993 switchLinks.put(lt.getDst(), new HashSet<Link>());
994 switchLinks.get(lt.getDst()).add(lt);
995
996 // index both ends by switch:port
997 if (!portLinks.containsKey(srcNpt))
998 portLinks.put(srcNpt, new HashSet<Link>());
999 portLinks.get(srcNpt).add(lt);
1000
1001 if (!portLinks.containsKey(dstNpt))
1002 portLinks.put(dstNpt, new HashSet<Link>());
1003 portLinks.get(dstNpt).add(lt);
1004
1005 // Add to portNOFLinks if the unicast valid time is null
1006 if (newInfo.getUnicastValidTime() == null)
1007 addLinkToBroadcastDomain(lt);
1008
1009 writeLinkToStorage(lt, newInfo);
1010 updateOperation = UpdateOperation.LINK_UPDATED;
1011 linkChanged = true;
1012
1013 // Add to event history
1014 evHistTopoLink(lt.getSrc(),
1015 lt.getDst(),
1016 lt.getSrcPort(),
1017 lt.getDstPort(),
1018 newInfo.getSrcPortState(), newInfo.getDstPortState(),
1019 getLinkType(lt, newInfo),
1020 EvAction.LINK_ADDED, "LLDP Recvd");
1021 } else {
1022 // Since the link info is already there, we need to
1023 // update the right fields.
1024 if (newInfo.getUnicastValidTime() == null) {
1025 // This is due to a multicast LLDP, so copy the old unicast
1026 // value.
1027 if (oldInfo.getUnicastValidTime() != null) {
1028 newInfo.setUnicastValidTime(oldInfo.getUnicastValidTime());
1029 }
1030 } else if (newInfo.getMulticastValidTime() == null) {
1031 // This is due to a unicast LLDP, so copy the old multicast
1032 // value.
1033 if (oldInfo.getMulticastValidTime() != null) {
1034 newInfo.setMulticastValidTime(oldInfo.getMulticastValidTime());
1035 }
1036 }
1037
1038 Long oldTime = oldInfo.getUnicastValidTime();
1039 Long newTime = newInfo.getUnicastValidTime();
1040 // the link has changed its state between openflow and non-openflow
1041 // if the unicastValidTimes are null or not null
1042 if (oldTime != null & newTime == null) {
1043 // openflow -> non-openflow transition
1044 // we need to add the link tuple to the portNOFLinks
1045 addLinkToBroadcastDomain(lt);
1046 linkChanged = true;
1047 } else if (oldTime == null & newTime != null) {
1048 // non-openflow -> openflow transition
1049 // we need to remove the link from the portNOFLinks
1050 removeLinkFromBroadcastDomain(lt);
1051 linkChanged = true;
1052 }
1053
1054 // Only update the port states if they've changed
1055 if (newInfo.getSrcPortState().intValue() !=
1056 oldInfo.getSrcPortState().intValue() ||
1057 newInfo.getDstPortState().intValue() !=
1058 oldInfo.getDstPortState().intValue())
1059 linkChanged = true;
1060
1061 // Write changes to storage. This will always write the updated
1062 // valid time, plus the port states if they've changed (i.e. if
1063 // they weren't set to null in the previous block of code.
1064 writeLinkToStorage(lt, newInfo);
1065
1066 if (linkChanged) {
1067 updateOperation = getUpdateOperation(newInfo.getSrcPortState(),
1068 newInfo.getDstPortState());
1069 if (log.isTraceEnabled()) {
1070 log.trace("Updated link {}", lt);
1071 }
1072 // Add to event history
1073 evHistTopoLink(lt.getSrc(),
1074 lt.getDst(),
1075 lt.getSrcPort(),
1076 lt.getDstPort(),
1077 newInfo.getSrcPortState(), newInfo.getDstPortState(),
1078 getLinkType(lt, newInfo),
1079 EvAction.LINK_PORT_STATE_UPDATED,
1080 "LLDP Recvd");
1081 }
1082 }
1083
1084 if (linkChanged) {
1085 // find out if the link was added or removed here.
1086 updates.add(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
1087 lt.getDst(), lt.getDstPort(),
1088 getLinkType(lt, newInfo),
1089 updateOperation));
1090 }
1091 } finally {
1092 lock.writeLock().unlock();
1093 }
1094
1095 return linkChanged;
1096 }
1097
1098 public Map<Long, Set<Link>> getSwitchLinks() {
1099 return this.switchLinks;
1100 }
1101
1102 /**
1103 * Removes links from memory and storage.
1104 * @param links The List of @LinkTuple to delete.
1105 */
1106 protected void deleteLinks(List<Link> links, String reason) {
1107 NodePortTuple srcNpt, dstNpt;
1108
1109 lock.writeLock().lock();
1110 try {
1111 for (Link lt : links) {
1112 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1113 dstNpt =new NodePortTuple(lt.getDst(), lt.getDstPort());
1114
1115 switchLinks.get(lt.getSrc()).remove(lt);
1116 switchLinks.get(lt.getDst()).remove(lt);
1117 if (switchLinks.containsKey(lt.getSrc()) &&
1118 switchLinks.get(lt.getSrc()).isEmpty())
1119 this.switchLinks.remove(lt.getSrc());
1120 if (this.switchLinks.containsKey(lt.getDst()) &&
1121 this.switchLinks.get(lt.getDst()).isEmpty())
1122 this.switchLinks.remove(lt.getDst());
1123
1124 if (this.portLinks.get(srcNpt) != null) {
1125 this.portLinks.get(srcNpt).remove(lt);
1126 if (this.portLinks.get(srcNpt).isEmpty())
1127 this.portLinks.remove(srcNpt);
1128 }
1129 if (this.portLinks.get(dstNpt) != null) {
1130 this.portLinks.get(dstNpt).remove(lt);
1131 if (this.portLinks.get(dstNpt).isEmpty())
1132 this.portLinks.remove(dstNpt);
1133 }
1134
1135 LinkInfo info = this.links.remove(lt);
1136 updates.add(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
1137 lt.getDst(), lt.getDstPort(),
1138 getLinkType(lt, info),
1139 UpdateOperation.LINK_REMOVED));
1140
1141 // Update Event History
1142 evHistTopoLink(lt.getSrc(),
1143 lt.getDst(),
1144 lt.getSrcPort(),
1145 lt.getDstPort(),
1146 0, 0, // Port states
1147 ILinkDiscovery.LinkType.INVALID_LINK,
1148 EvAction.LINK_DELETED, reason);
1149
1150 // remove link from storage.
1151 removeLinkFromStorage(lt);
1152
1153 // TODO Whenever link is removed, it has to checked if
1154 // the switchports must be added to quarantine.
1155
1156 if (log.isTraceEnabled()) {
1157 log.trace("Deleted link {}", lt);
1158 }
1159 }
1160 } finally {
1161 lock.writeLock().unlock();
1162 }
1163 }
1164
1165 /**
1166 * Handles an OFPortStatus message from a switch. We will add or
1167 * delete LinkTupes as well re-compute the topology if needed.
1168 * @param sw The IOFSwitch that sent the port status message
1169 * @param ps The OFPortStatus message
1170 * @return The Command to continue or stop after we process this message
1171 */
1172 protected Command handlePortStatus(long sw, OFPortStatus ps) {
1173
1174 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
1175 if (iofSwitch == null) return Command.CONTINUE;
1176
1177 if (log.isTraceEnabled()) {
1178 log.trace("handlePortStatus: Switch {} port #{} reason {}; " +
1179 "config is {} state is {}",
1180 new Object[] {iofSwitch.getStringId(),
1181 ps.getDesc().getPortNumber(),
1182 ps.getReason(),
1183 ps.getDesc().getConfig(),
1184 ps.getDesc().getState()});
1185 }
1186
1187 short port = ps.getDesc().getPortNumber();
1188 NodePortTuple npt = new NodePortTuple(sw, port);
1189 boolean linkDeleted = false;
1190 boolean linkInfoChanged = false;
1191
1192 lock.writeLock().lock();
1193 try {
1194 // if ps is a delete, or a modify where the port is down or
1195 // configured down
1196 if ((byte)OFPortReason.OFPPR_DELETE.ordinal() == ps.getReason() ||
1197 ((byte)OFPortReason.OFPPR_MODIFY.ordinal() ==
1198 ps.getReason() && !portEnabled(ps.getDesc()))) {
1199 deleteLinksOnPort(npt, "Port Status Changed");
1200 LDUpdate update = new LDUpdate(sw, port, UpdateOperation.PORT_DOWN);
1201 updates.add(update);
1202 linkDeleted = true;
1203 }
1204 else if (ps.getReason() ==
1205 (byte)OFPortReason.OFPPR_MODIFY.ordinal()) {
1206 // If ps is a port modification and the port state has changed
1207 // that affects links in the topology
1208
1209 if (this.portLinks.containsKey(npt)) {
1210 for (Link lt: this.portLinks.get(npt)) {
1211 LinkInfo linkInfo = links.get(lt);
1212 assert(linkInfo != null);
1213 Integer updatedSrcPortState = null;
1214 Integer updatedDstPortState = null;
1215 if (lt.getSrc() == npt.getNodeId() &&
1216 lt.getSrcPort() == npt.getPortId() &&
1217 (linkInfo.getSrcPortState() !=
1218 ps.getDesc().getState())) {
1219 updatedSrcPortState = ps.getDesc().getState();
1220 linkInfo.setSrcPortState(updatedSrcPortState);
1221 }
1222 if (lt.getDst() == npt.getNodeId() &&
1223 lt.getDstPort() == npt.getPortId() &&
1224 (linkInfo.getDstPortState() !=
1225 ps.getDesc().getState())) {
1226 updatedDstPortState = ps.getDesc().getState();
1227 linkInfo.setDstPortState(updatedDstPortState);
1228 }
1229 if ((updatedSrcPortState != null) ||
1230 (updatedDstPortState != null)) {
1231 // The link is already known to link discovery
1232 // manager and the status has changed, therefore
1233 // send an LDUpdate.
1234 UpdateOperation operation =
1235 getUpdateOperation(linkInfo.getSrcPortState(),
1236 linkInfo.getDstPortState());
1237 updates.add(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
1238 lt.getDst(), lt.getDstPort(),
1239 getLinkType(lt, linkInfo),
1240 operation));
1241 writeLinkToStorage(lt, linkInfo);
1242 linkInfoChanged = true;
1243 }
1244 }
1245 }
1246
1247 UpdateOperation operation =
1248 getUpdateOperation(ps.getDesc().getState());
1249 updates.add(new LDUpdate(sw, port, operation));
1250 }
1251
1252 if (!linkDeleted && !linkInfoChanged){
1253 if (log.isTraceEnabled()) {
1254 log.trace("handlePortStatus: Switch {} port #{} reason {};"+
1255 " no links to update/remove",
1256 new Object[] {HexString.toHexString(sw),
1257 ps.getDesc().getPortNumber(),
1258 ps.getReason()});
1259 }
1260 }
1261 } finally {
1262 lock.writeLock().unlock();
1263 }
1264
1265 if (!linkDeleted) {
1266 // Send LLDP right away when port state is changed for faster
1267 // cluster-merge. If it is a link delete then there is not need
1268 // to send the LLDPs right away and instead we wait for the LLDPs
1269 // to be sent on the timer as it is normally done
1270 // do it outside the write-lock
1271 // sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS);
1272 processNewPort(npt.getNodeId(), npt.getPortId());
1273 }
1274 return Command.CONTINUE;
1275 }
1276
1277 /**
1278 * Process a new port.
1279 * If link discovery is disabled on the port, then do nothing.
1280 * If autoportfast feature is enabled and the port is a fast port, then
1281 * do nothing.
1282 * Otherwise, send LLDP message. Add the port to quarantine.
1283 * @param sw
1284 * @param p
1285 */
1286 private void processNewPort(long sw, short p) {
1287 if (isLinkDiscoverySuppressed(sw, p)) {
1288 // Do nothing as link discovery is suppressed.
1289 }
1290 else if (autoPortFastFeature && isFastPort(sw, p)) {
1291 // Do nothing as the port is a fast port.
1292 }
1293 else {
1294 NodePortTuple npt = new NodePortTuple(sw, p);
1295 discover(sw, p);
1296 // if it is not a fast port, add it to quarantine.
1297 if (!isFastPort(sw, p)) {
1298 addToQuarantineQueue(npt);
1299 } else {
1300 // Add to maintenance queue to ensure that BDDP packets
1301 // are sent out.
1302 addToMaintenanceQueue(npt);
1303 }
1304 }
1305 }
1306
1307 /**
1308 * We send out LLDP messages when a switch is added to discover the topology
1309 * @param sw The IOFSwitch that connected to the controller
1310 */
1311 @Override
1312 public void addedSwitch(IOFSwitch sw) {
1313
1314 if (sw.getEnabledPorts() != null) {
1315 for (Short p : sw.getEnabledPortNumbers()) {
1316 processNewPort(sw.getId(), p);
1317 }
1318 }
1319 // Update event history
1320 evHistTopoSwitch(sw, EvAction.SWITCH_CONNECTED, "None");
1321 LDUpdate update = new LDUpdate(sw.getId(), null,
1322 UpdateOperation.SWITCH_UPDATED);
1323 updates.add(update);
1324 }
1325
1326 /**
1327 * When a switch disconnects we remove any links from our map and notify.
1328 * @param The id of the switch
1329 */
1330 @Override
1331 public void removedSwitch(IOFSwitch iofSwitch) {
1332 // Update event history
1333 long sw = iofSwitch.getId();
1334 evHistTopoSwitch(iofSwitch, EvAction.SWITCH_DISCONNECTED, "None");
1335 List<Link> eraseList = new ArrayList<Link>();
1336 lock.writeLock().lock();
1337 try {
1338 if (switchLinks.containsKey(sw)) {
1339 if (log.isTraceEnabled()) {
1340 log.trace("Handle switchRemoved. Switch {}; removing links {}",
1341 HexString.toHexString(sw), switchLinks.get(sw));
1342 }
1343 // add all tuples with an endpoint on this switch to erase list
1344 eraseList.addAll(switchLinks.get(sw));
1345 deleteLinks(eraseList, "Switch Removed");
1346
1347 // Send a switch removed update
1348 LDUpdate update = new LDUpdate(sw, null, UpdateOperation.SWITCH_REMOVED);
1349 updates.add(update);
1350 }
1351 } finally {
1352 lock.writeLock().unlock();
1353 }
1354 }
1355
1356 /**
1357 * We don't react the port changed notifications here. we listen for
1358 * OFPortStatus messages directly. Might consider using this notifier
1359 * instead
1360 */
1361 @Override
1362 public void switchPortChanged(Long switchId) {
1363 // no-op
1364 }
1365
1366 /**
1367 * Delete links incident on a given switch port.
1368 * @param npt
1369 * @param reason
1370 */
1371 protected void deleteLinksOnPort(NodePortTuple npt, String reason) {
1372 List<Link> eraseList = new ArrayList<Link>();
1373 if (this.portLinks.containsKey(npt)) {
1374 if (log.isTraceEnabled()) {
1375 log.trace("handlePortStatus: Switch {} port #{} " +
1376 "removing links {}",
1377 new Object[] {HexString.toHexString(npt.getNodeId()),
1378 npt.getPortId(),
1379 this.portLinks.get(npt)});
1380 }
1381 eraseList.addAll(this.portLinks.get(npt));
1382 deleteLinks(eraseList, reason);
1383 }
1384 }
1385
1386 /**
1387 * Iterates through the list of links and deletes if the
1388 * last discovery message reception time exceeds timeout values.
1389 */
1390 protected void timeoutLinks() {
1391 List<Link> eraseList = new ArrayList<Link>();
1392 Long curTime = System.currentTimeMillis();
1393 boolean linkChanged = false;
1394
1395 // reentrant required here because deleteLink also write locks
1396 lock.writeLock().lock();
1397 try {
1398 Iterator<Entry<Link, LinkInfo>> it =
1399 this.links.entrySet().iterator();
1400 while (it.hasNext()) {
1401 Entry<Link, LinkInfo> entry = it.next();
1402 Link lt = entry.getKey();
1403 LinkInfo info = entry.getValue();
1404
1405 // Timeout the unicast and multicast LLDP valid times
1406 // independently.
1407 if ((info.getUnicastValidTime() != null) &&
1408 (info.getUnicastValidTime() + (this.LINK_TIMEOUT * 1000) < curTime)){
1409 info.setUnicastValidTime(null);
1410
1411 if (info.getMulticastValidTime() != null)
1412 addLinkToBroadcastDomain(lt);
1413 // Note that even if mTime becomes null later on,
1414 // the link would be deleted, which would trigger updateClusters().
1415 linkChanged = true;
1416 }
1417 if ((info.getMulticastValidTime()!= null) &&
1418 (info.getMulticastValidTime()+ (this.LINK_TIMEOUT * 1000) < curTime)) {
1419 info.setMulticastValidTime(null);
1420 // if uTime is not null, then link will remain as openflow
1421 // link. If uTime is null, it will be deleted. So, we
1422 // don't care about linkChanged flag here.
1423 removeLinkFromBroadcastDomain(lt);
1424 linkChanged = true;
1425 }
1426 // Add to the erase list only if the unicast
1427 // time is null.
1428 if (info.getUnicastValidTime() == null &&
1429 info.getMulticastValidTime() == null){
1430 eraseList.add(entry.getKey());
1431 } else if (linkChanged) {
1432 UpdateOperation operation;
1433 operation = getUpdateOperation(info.getSrcPortState(),
1434 info.getDstPortState());
1435 updates.add(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
1436 lt.getDst(), lt.getDstPort(),
1437 getLinkType(lt, info),
1438 operation));
1439 }
1440 }
1441
1442 // if any link was deleted or any link was changed.
1443 if ((eraseList.size() > 0) || linkChanged) {
1444 deleteLinks(eraseList, "LLDP timeout");
1445 }
1446 } finally {
1447 lock.writeLock().unlock();
1448 }
1449 }
1450
1451 private boolean portEnabled(OFPhysicalPort port) {
1452 if (port == null)
1453 return false;
1454 if ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0)
1455 return false;
1456 if ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0)
1457 return false;
1458 // Port STP state doesn't work with multiple VLANs, so ignore it for now
1459 // if ((port.getState() & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK.getValue())
1460 // return false;
1461 return true;
1462 }
1463
1464 public Map<NodePortTuple, Set<Link>> getPortBroadcastDomainLinks() {
1465 return portBroadcastDomainLinks;
1466 }
1467
1468 @Override
1469 public Map<Link, LinkInfo> getLinks() {
1470 lock.readLock().lock();
1471 Map<Link, LinkInfo> result;
1472 try {
1473 result = new HashMap<Link, LinkInfo>(links);
1474 } finally {
1475 lock.readLock().unlock();
1476 }
1477 return result;
1478 }
1479
1480 protected void addLinkToBroadcastDomain(Link lt) {
1481
1482 NodePortTuple srcNpt, dstNpt;
1483 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1484 dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
1485
1486 if (!portBroadcastDomainLinks.containsKey(lt.getSrc()))
1487 portBroadcastDomainLinks.put(srcNpt, new HashSet<Link>());
1488 portBroadcastDomainLinks.get(srcNpt).add(lt);
1489
1490 if (!portBroadcastDomainLinks.containsKey(lt.getDst()))
1491 portBroadcastDomainLinks.put(dstNpt, new HashSet<Link>());
1492 portBroadcastDomainLinks.get(dstNpt).add(lt);
1493 }
1494
1495 protected void removeLinkFromBroadcastDomain(Link lt) {
1496
1497 NodePortTuple srcNpt, dstNpt;
1498 srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
1499 dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
1500
1501 if (portBroadcastDomainLinks.containsKey(srcNpt)) {
1502 portBroadcastDomainLinks.get(srcNpt).remove(lt);
1503 if (portBroadcastDomainLinks.get(srcNpt).isEmpty())
1504 portBroadcastDomainLinks.remove(srcNpt);
1505 }
1506
1507 if (portBroadcastDomainLinks.containsKey(dstNpt)) {
1508 portBroadcastDomainLinks.get(dstNpt).remove(lt);
1509 if (portBroadcastDomainLinks.get(dstNpt).isEmpty())
1510 portBroadcastDomainLinks.remove(dstNpt);
1511 }
1512 }
1513
1514 // STORAGE METHODS
1515 /**
1516 * Deletes all links from storage
1517 */
1518 void clearAllLinks() {
1519 storageSource.deleteRowsAsync(LINK_TABLE_NAME, null);
1520 }
1521
1522 /**
1523 * Gets the storage key for a LinkTuple
1524 * @param lt The LinkTuple to get
1525 * @return The storage key as a String
1526 */
1527 private String getLinkId(Link lt) {
1528 return HexString.toHexString(lt.getSrc()) +
1529 "-" + lt.getSrcPort() + "-" +
1530 HexString.toHexString(lt.getDst())+
1531 "-" + lt.getDstPort();
1532 }
1533
1534 /**
1535 * Writes a LinkTuple and corresponding LinkInfo to storage
1536 * @param lt The LinkTuple to write
1537 * @param linkInfo The LinkInfo to write
1538 */
1539 protected void writeLinkToStorage(Link lt, LinkInfo linkInfo) {
1540 LinkType type = getLinkType(lt, linkInfo);
1541
1542 // Write only direct links. Do not write links to external
1543 // L2 network.
1544 // if (type != LinkType.DIRECT_LINK && type != LinkType.TUNNEL) {
1545 // return;
1546 // }
1547
1548 Map<String, Object> rowValues = new HashMap<String, Object>();
1549 String id = getLinkId(lt);
1550 rowValues.put(LINK_ID, id);
1551 rowValues.put(LINK_VALID_TIME, linkInfo.getUnicastValidTime());
1552 String srcDpid = HexString.toHexString(lt.getSrc());
1553 rowValues.put(LINK_SRC_SWITCH, srcDpid);
1554 rowValues.put(LINK_SRC_PORT, lt.getSrcPort());
1555
1556 if (type == LinkType.DIRECT_LINK)
1557 rowValues.put(LINK_TYPE, "internal");
1558 else if (type == LinkType.MULTIHOP_LINK)
1559 rowValues.put(LINK_TYPE, "external");
1560 else if (type == LinkType.TUNNEL)
1561 rowValues.put(LINK_TYPE, "tunnel");
1562 else rowValues.put(LINK_TYPE, "invalid");
1563
1564 if (linkInfo.linkStpBlocked()) {
1565 if (log.isTraceEnabled()) {
1566 log.trace("writeLink, link {}, info {}, srcPortState Blocked",
1567 lt, linkInfo);
1568 }
1569 rowValues.put(LINK_SRC_PORT_STATE,
1570 OFPhysicalPort.OFPortState.OFPPS_STP_BLOCK.getValue());
1571 } else {
1572 if (log.isTraceEnabled()) {
1573 log.trace("writeLink, link {}, info {}, srcPortState {}",
1574 new Object[]{ lt, linkInfo, linkInfo.getSrcPortState() });
1575 }
1576 rowValues.put(LINK_SRC_PORT_STATE, linkInfo.getSrcPortState());
1577 }
1578 String dstDpid = HexString.toHexString(lt.getDst());
1579 rowValues.put(LINK_DST_SWITCH, dstDpid);
1580 rowValues.put(LINK_DST_PORT, lt.getDstPort());
1581 if (linkInfo.linkStpBlocked()) {
1582 if (log.isTraceEnabled()) {
1583 log.trace("writeLink, link {}, info {}, dstPortState Blocked",
1584 lt, linkInfo);
1585 }
1586 rowValues.put(LINK_DST_PORT_STATE,
1587 OFPhysicalPort.OFPortState.OFPPS_STP_BLOCK.getValue());
1588 } else {
1589 if (log.isTraceEnabled()) {
1590 log.trace("writeLink, link {}, info {}, dstPortState {}",
1591 new Object[]{ lt, linkInfo, linkInfo.getDstPortState() });
1592 }
1593 rowValues.put(LINK_DST_PORT_STATE, linkInfo.getDstPortState());
1594 }
1595 storageSource.updateRowAsync(LINK_TABLE_NAME, rowValues);
1596 }
1597
1598 public Long readLinkValidTime(Link lt) {
1599 // FIXME: We're not currently using this right now, but if we start
1600 // to use this again, we probably shouldn't use it in its current
1601 // form, because it's doing synchronous storage calls. Depending
1602 // on the context this may still be OK, but if it's being called
1603 // on the packet in processing thread it should be reworked to
1604 // use asynchronous storage calls.
1605 Long validTime = null;
1606 IResultSet resultSet = null;
1607 try {
1608 String[] columns = { LINK_VALID_TIME };
1609 String id = getLinkId(lt);
1610 resultSet = storageSource.executeQuery(LINK_TABLE_NAME, columns,
1611 new OperatorPredicate(LINK_ID, OperatorPredicate.Operator.EQ, id), null);
1612 if (resultSet.next())
1613 validTime = resultSet.getLong(LINK_VALID_TIME);
1614 }
1615 finally {
1616 if (resultSet != null)
1617 resultSet.close();
1618 }
1619 return validTime;
1620 }
1621
1622 /**
1623 * Removes a link from storage using an asynchronous call.
1624 * @param lt The LinkTuple to delete.
1625 */
1626 protected void removeLinkFromStorage(Link lt) {
1627 String id = getLinkId(lt);
1628 storageSource.deleteRowAsync(LINK_TABLE_NAME, id);
1629 }
1630
1631 @Override
1632 public void addListener(ILinkDiscoveryListener listener) {
1633 linkDiscoveryAware.add(listener);
1634 }
1635
1636 /**
1637 * Register a link discovery aware component
1638 * @param linkDiscoveryAwareComponent
1639 */
1640 public void addLinkDiscoveryAware(ILinkDiscoveryListener linkDiscoveryAwareComponent) {
1641 // TODO make this a copy on write set or lock it somehow
1642 this.linkDiscoveryAware.add(linkDiscoveryAwareComponent);
1643 }
1644
1645 /**
1646 * Deregister a link discovery aware component
1647 * @param linkDiscoveryAwareComponent
1648 */
1649 public void removeLinkDiscoveryAware(ILinkDiscoveryListener linkDiscoveryAwareComponent) {
1650 // TODO make this a copy on write set or lock it somehow
1651 this.linkDiscoveryAware.remove(linkDiscoveryAwareComponent);
1652 }
1653
1654 /**
1655 * Sets the IStorageSource to use for ITology
1656 * @param storageSource the storage source to use
1657 */
1658 public void setStorageSource(IStorageSourceService storageSource) {
1659 this.storageSource = storageSource;
1660 }
1661
1662 /**
1663 * Gets the storage source for this ITopology
1664 * @return The IStorageSource ITopology is writing to
1665 */
1666 public IStorageSourceService getStorageSource() {
1667 return storageSource;
1668 }
1669
1670 @Override
1671 public boolean isCallbackOrderingPrereq(OFType type, String name) {
1672 return false;
1673 }
1674
1675 @Override
1676 public boolean isCallbackOrderingPostreq(OFType type, String name) {
1677 return false;
1678 }
1679
1680 @Override
1681 public void rowsModified(String tableName, Set<Object> rowKeys) {
1682 Map<Long, IOFSwitch> switches = floodlightProvider.getSwitches();
1683 ArrayList<IOFSwitch> updated_switches = new ArrayList<IOFSwitch>();
1684 for(Object key: rowKeys) {
1685 Long swId = new Long(HexString.toLong((String)key));
1686 if (switches.containsKey(swId)) {
1687 IOFSwitch sw = switches.get(swId);
1688 boolean curr_status = sw.hasAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH);
1689 boolean new_status = false;
1690 IResultSet resultSet = null;
1691
1692 try {
1693 resultSet = storageSource.getRow(tableName, key);
1694 for (Iterator<IResultSet> it = resultSet.iterator(); it.hasNext();) {
1695 // In case of multiple rows, use the status in last row?
1696 Map<String, Object> row = it.next().getRow();
1697 if (row.containsKey(SWITCH_CONFIG_CORE_SWITCH)) {
1698 new_status = ((String)row.get(SWITCH_CONFIG_CORE_SWITCH)).equals("true");
1699 }
1700 }
1701 }
1702 finally {
1703 if (resultSet != null)
1704 resultSet.close();
1705 }
1706
1707 if (curr_status != new_status) {
1708 updated_switches.add(sw);
1709 }
1710 } else {
1711 if (log.isTraceEnabled()) {
1712 log.trace("Update for switch which has no entry in switch " +
1713 "list (dpid={}), a delete action.", (String)key);
1714 }
1715 }
1716 }
1717
1718 for (IOFSwitch sw : updated_switches) {
1719 // Set SWITCH_IS_CORE_SWITCH to it's inverse value
1720 if (sw.hasAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH)) {
1721 sw.removeAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH);
1722 if (log.isTraceEnabled()) {
1723 log.trace("SWITCH_IS_CORE_SWITCH set to False for {}", sw);
1724 }
1725 updates.add(new LDUpdate(sw.getId(), SwitchType.BASIC_SWITCH,
1726 UpdateOperation.SWITCH_UPDATED));
1727 }
1728 else {
1729 sw.setAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH, new Boolean(true));
1730 if (log.isTraceEnabled()) {
1731 log.trace("SWITCH_IS_CORE_SWITCH set to True for {}", sw);
1732 }
1733 updates.add(new LDUpdate(sw.getId(), SwitchType.CORE_SWITCH,
1734 UpdateOperation.SWITCH_UPDATED));
1735 }
1736 }
1737 }
1738
1739 @Override
1740 public void rowsDeleted(String tableName, Set<Object> rowKeys) {
1741 // Ignore delete events, the switch delete will do the right thing on it's own
1742 }
1743
1744 // IFloodlightModule classes
1745
1746 @Override
1747 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
1748 Collection<Class<? extends IFloodlightService>> l =
1749 new ArrayList<Class<? extends IFloodlightService>>();
1750 l.add(ILinkDiscoveryService.class);
1751 //l.add(ITopologyService.class);
1752 return l;
1753 }
1754
1755 @Override
1756 public Map<Class<? extends IFloodlightService>, IFloodlightService>
1757 getServiceImpls() {
1758 Map<Class<? extends IFloodlightService>,
1759 IFloodlightService> m =
1760 new HashMap<Class<? extends IFloodlightService>,
1761 IFloodlightService>();
1762 // We are the class that implements the service
1763 m.put(ILinkDiscoveryService.class, this);
1764 return m;
1765 }
1766
1767 @Override
1768 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
1769 Collection<Class<? extends IFloodlightService>> l =
1770 new ArrayList<Class<? extends IFloodlightService>>();
1771 l.add(IFloodlightProviderService.class);
1772 l.add(IStorageSourceService.class);
1773 l.add(IThreadPoolService.class);
1774 l.add(IRestApiService.class);
1775 return l;
1776 }
1777
1778 @Override
1779 public void init(FloodlightModuleContext context)
1780 throws FloodlightModuleException {
1781 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
1782 storageSource = context.getServiceImpl(IStorageSourceService.class);
1783 threadPool = context.getServiceImpl(IThreadPoolService.class);
1784 restApi = context.getServiceImpl(IRestApiService.class);
1785
1786 // Set the autoportfast feature to false.
1787 this.autoPortFastFeature = false;
1788
1789 // We create this here because there is no ordering guarantee
1790 this.linkDiscoveryAware = new ArrayList<ILinkDiscoveryListener>();
1791 this.lock = new ReentrantReadWriteLock();
1792 this.updates = new LinkedBlockingQueue<LDUpdate>();
1793 this.links = new HashMap<Link, LinkInfo>();
1794 this.portLinks = new HashMap<NodePortTuple, Set<Link>>();
1795 this.suppressLinkDiscovery =
1796 Collections.synchronizedSet(new HashSet<NodePortTuple>());
1797 this.portBroadcastDomainLinks = new HashMap<NodePortTuple, Set<Link>>();
1798 this.switchLinks = new HashMap<Long, Set<Link>>();
1799 this.quarantineQueue = new LinkedBlockingQueue<NodePortTuple>();
1800 this.maintenanceQueue = new LinkedBlockingQueue<NodePortTuple>();
1801
1802 this.evHistTopologySwitch =
1803 new EventHistory<EventHistoryTopologySwitch>("Topology: Switch");
1804 this.evHistTopologyLink =
1805 new EventHistory<EventHistoryTopologyLink>("Topology: Link");
1806 this.evHistTopologyCluster =
1807 new EventHistory<EventHistoryTopologyCluster>("Topology: Cluster");
1808 }
1809
1810 @Override
1811 @LogMessageDocs({
1812 @LogMessageDoc(level="ERROR",
1813 message="No storage source found.",
1814 explanation="Storage source was not initialized; cannot initialize " +
1815 "link discovery.",
1816 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
1817 @LogMessageDoc(level="ERROR",
1818 message="Error in installing listener for " +
1819 "switch config table {table}",
1820 explanation="Failed to install storage notification for the " +
1821 "switch config table",
1822 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
1823 @LogMessageDoc(level="ERROR",
1824 message="No storage source found.",
1825 explanation="Storage source was not initialized; cannot initialize " +
1826 "link discovery.",
1827 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
1828 @LogMessageDoc(level="ERROR",
1829 message="Exception in LLDP send timer.",
1830 explanation="An unknown error occured while sending LLDP " +
1831 "messages to switches.",
1832 recommendation=LogMessageDoc.CHECK_SWITCH)
1833 })
1834 public void startUp(FloodlightModuleContext context) {
1835 // Create our storage tables
1836 if (storageSource == null) {
1837 log.error("No storage source found.");
1838 return;
1839 }
1840
1841 storageSource.createTable(LINK_TABLE_NAME, null);
1842 storageSource.setTablePrimaryKeyName(LINK_TABLE_NAME, LINK_ID);
1843 storageSource.deleteMatchingRows(LINK_TABLE_NAME, null);
1844 // Register for storage updates for the switch table
1845 try {
1846 storageSource.addListener(SWITCH_CONFIG_TABLE_NAME, this);
1847 } catch (StorageException ex) {
1848 log.error("Error in installing listener for " +
1849 "switch table {}", SWITCH_CONFIG_TABLE_NAME);
1850 }
1851
1852 ScheduledExecutorService ses = threadPool.getScheduledExecutor();
1853
1854 // To be started by the first switch connection
1855 discoveryTask = new SingletonTask(ses, new Runnable() {
1856 @Override
1857 public void run() {
1858 try {
1859 discoverLinks();
1860 } catch (StorageException e) {
1861 log.error("Storage exception in LLDP send timer; " +
1862 "terminating process", e);
1863 floodlightProvider.terminate();
1864 } catch (Exception e) {
1865 log.error("Exception in LLDP send timer.", e);
1866 } finally {
1867 if (!shuttingDown) {
1868 // null role implies HA mode is not enabled.
1869 Role role = floodlightProvider.getRole();
1870 if (role == null || role == Role.MASTER) {
1871 log.trace("Rescheduling discovery task as role = {}", role);
1872 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL,
1873 TimeUnit.SECONDS);
1874 } else {
1875 log.trace("Stopped LLDP rescheduling due to role = {}.", role);
1876 }
1877 }
1878 }
1879 }
1880 });
1881
1882 // null role implies HA mode is not enabled.
1883 Role role = floodlightProvider.getRole();
1884 if (role == null || role == Role.MASTER) {
1885 log.trace("Setup: Rescheduling discovery task. role = {}", role);
1886 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL, TimeUnit.SECONDS);
1887 } else {
1888 log.trace("Setup: Not scheduling LLDP as role = {}.", role);
1889 }
1890
1891 // Setup the BDDP task. It is invoked whenever switch port tuples
1892 // are added to the quarantine list.
1893 bddpTask = new SingletonTask(ses, new QuarantineWorker());
1894 bddpTask.reschedule(BDDP_TASK_INTERVAL, TimeUnit.MILLISECONDS);
1895
1896 updatesThread = new Thread(new Runnable () {
1897 @Override
1898 public void run() {
1899 while (true) {
1900 try {
1901 doUpdatesThread();
1902 } catch (InterruptedException e) {
1903 return;
1904 }
1905 }
1906 }}, "Topology Updates");
1907 updatesThread.start();
1908
1909
1910
1911 // Register for the OpenFlow messages we want to receive
1912 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
1913 floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this);
1914 // Register for switch updates
1915 floodlightProvider.addOFSwitchListener(this);
1916 floodlightProvider.addHAListener(this);
1917 floodlightProvider.addInfoProvider("summary", this);
1918 if (restApi != null)
1919 restApi.addRestletRoutable(new LinkDiscoveryWebRoutable());
1920 setControllerTLV();
1921 }
1922
1923 // ****************************************************
1924 // Topology Manager's Event History members and methods
1925 // ****************************************************
1926
1927 // Topology Manager event history
1928 public EventHistory<EventHistoryTopologySwitch> evHistTopologySwitch;
1929 public EventHistory<EventHistoryTopologyLink> evHistTopologyLink;
1930 public EventHistory<EventHistoryTopologyCluster> evHistTopologyCluster;
1931 public EventHistoryTopologySwitch evTopoSwitch;
1932 public EventHistoryTopologyLink evTopoLink;
1933 public EventHistoryTopologyCluster evTopoCluster;
1934
1935 // Switch Added/Deleted
1936 private void evHistTopoSwitch(IOFSwitch sw, EvAction actn, String reason) {
1937 if (evTopoSwitch == null) {
1938 evTopoSwitch = new EventHistoryTopologySwitch();
1939 }
1940 evTopoSwitch.dpid = sw.getId();
1941 if ((sw.getChannel() != null) &&
1942 (SocketAddress.class.isInstance(
1943 sw.getChannel().getRemoteAddress()))) {
1944 evTopoSwitch.ipv4Addr =
1945 IPv4.toIPv4Address(((InetSocketAddress)(sw.getChannel().
1946 getRemoteAddress())).getAddress().getAddress());
1947 evTopoSwitch.l4Port =
1948 ((InetSocketAddress)(sw.getChannel().
1949 getRemoteAddress())).getPort();
1950 } else {
1951 evTopoSwitch.ipv4Addr = 0;
1952 evTopoSwitch.l4Port = 0;
1953 }
1954 evTopoSwitch.reason = reason;
1955 evTopoSwitch = evHistTopologySwitch.put(evTopoSwitch, actn);
1956 }
1957
1958 private void evHistTopoLink(long srcDpid, long dstDpid, short srcPort,
1959 short dstPort, int srcPortState, int dstPortState,
1960 ILinkDiscovery.LinkType linkType,
1961 EvAction actn, String reason) {
1962 if (evTopoLink == null) {
1963 evTopoLink = new EventHistoryTopologyLink();
1964 }
1965 evTopoLink.srcSwDpid = srcDpid;
1966 evTopoLink.dstSwDpid = dstDpid;
1967 evTopoLink.srcSwport = srcPort & 0xffff;
1968 evTopoLink.dstSwport = dstPort & 0xffff;
1969 evTopoLink.srcPortState = srcPortState;
1970 evTopoLink.dstPortState = dstPortState;
1971 evTopoLink.reason = reason;
1972 switch (linkType) {
1973 case DIRECT_LINK:
1974 evTopoLink.linkType = "DIRECT_LINK";
1975 break;
1976 case MULTIHOP_LINK:
1977 evTopoLink.linkType = "MULTIHOP_LINK";
1978 break;
1979 case TUNNEL:
1980 evTopoLink.linkType = "TUNNEL";
1981 break;
1982 case INVALID_LINK:
1983 default:
1984 evTopoLink.linkType = "Unknown";
1985 break;
1986 }
1987 evTopoLink = evHistTopologyLink.put(evTopoLink, actn);
1988 }
1989
1990 public void evHistTopoCluster(long dpid, long clusterIdOld,
1991 long clusterIdNew, EvAction action, String reason) {
1992 if (evTopoCluster == null) {
1993 evTopoCluster = new EventHistoryTopologyCluster();
1994 }
1995 evTopoCluster.dpid = dpid;
1996 evTopoCluster.clusterIdOld = clusterIdOld;
1997 evTopoCluster.clusterIdNew = clusterIdNew;
1998 evTopoCluster.reason = reason;
1999 evTopoCluster = evHistTopologyCluster.put(evTopoCluster, action);
2000 }
2001
2002 @Override
2003 public Map<String, Object> getInfo(String type) {
2004 if (!"summary".equals(type)) return null;
2005
2006 Map<String, Object> info = new HashMap<String, Object>();
2007
2008 int num_links = 0;
2009 for (Set<Link> links : switchLinks.values())
2010 num_links += links.size();
2011 info.put("# inter-switch links", num_links / 2);
2012
2013 return info;
2014 }
2015
2016 // IHARoleListener
2017 @Override
2018 public void roleChanged(Role oldRole, Role newRole) {
2019 switch(newRole) {
2020 case MASTER:
2021 if (oldRole == Role.SLAVE) {
2022 if (log.isTraceEnabled()) {
2023 log.trace("Sending LLDPs " +
2024 "to HA change from SLAVE->MASTER");
2025 }
2026 clearAllLinks();
2027 log.debug("Role Change to Master: Rescheduling discovery task.");
2028 discoveryTask.reschedule(1, TimeUnit.MICROSECONDS);
2029 }
2030 break;
2031 case SLAVE:
2032 if (log.isTraceEnabled()) {
2033 log.trace("Clearing links due to " +
2034 "HA change to SLAVE");
2035 }
2036 switchLinks.clear();
2037 links.clear();
2038 portLinks.clear();
2039 portBroadcastDomainLinks.clear();
2040 discoverOnAllPorts();
2041 break;
2042 default:
2043 break;
2044 }
2045 }
2046
2047 @Override
2048 public void controllerNodeIPsChanged(
2049 Map<String, String> curControllerNodeIPs,
2050 Map<String, String> addedControllerNodeIPs,
2051 Map<String, String> removedControllerNodeIPs) {
2052 // ignore
2053 }
2054
2055 public boolean isAutoPortFastFeature() {
2056 return autoPortFastFeature;
2057 }
2058
2059 public void setAutoPortFastFeature(boolean autoPortFastFeature) {
2060 this.autoPortFastFeature = autoPortFastFeature;
2061 }
2062}