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