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