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