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