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