blob: 42c29425c25ab4d550c6b80116e5cb8dc800ae99 [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
Jonathan Hart284e70f2014-07-05 12:32:51 -070018package net.onrc.onos.core.linkdiscovery;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080019
20import java.io.IOException;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080021import java.util.ArrayList;
22import java.util.Collection;
23import java.util.Collections;
24import java.util.HashMap;
25import java.util.HashSet;
26import java.util.Iterator;
27import java.util.List;
28import java.util.Map;
29import java.util.Map.Entry;
30import java.util.Set;
Jonathan Hart284e70f2014-07-05 12:32:51 -070031import java.util.concurrent.CopyOnWriteArrayList;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080032import java.util.concurrent.ScheduledExecutorService;
33import java.util.concurrent.TimeUnit;
34import java.util.concurrent.locks.ReentrantReadWriteLock;
35
36import net.floodlightcontroller.core.FloodlightContext;
37import net.floodlightcontroller.core.IFloodlightProviderService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080038import net.floodlightcontroller.core.IOFMessageListener;
39import net.floodlightcontroller.core.IOFSwitch;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070040import net.floodlightcontroller.core.IOFSwitch.PortChangeType;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080041import net.floodlightcontroller.core.IOFSwitchListener;
Jonathan Hart284e70f2014-07-05 12:32:51 -070042import net.floodlightcontroller.core.IUpdate;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080043import net.floodlightcontroller.core.annotations.LogMessageCategory;
44import net.floodlightcontroller.core.annotations.LogMessageDoc;
45import net.floodlightcontroller.core.annotations.LogMessageDocs;
46import net.floodlightcontroller.core.module.FloodlightModuleContext;
47import net.floodlightcontroller.core.module.FloodlightModuleException;
48import net.floodlightcontroller.core.module.IFloodlightModule;
49import net.floodlightcontroller.core.module.IFloodlightService;
50import net.floodlightcontroller.core.util.SingletonTask;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080051import net.floodlightcontroller.restserver.IRestApiService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080052import net.floodlightcontroller.threadpool.IThreadPoolService;
Jonathan Hart23701d12014-04-03 10:45:48 -070053import net.onrc.onos.core.linkdiscovery.web.LinkDiscoveryWebRoutable;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070054import net.onrc.onos.core.packet.Ethernet;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070055import net.onrc.onos.core.packet.LLDP;
Jonathan Hart299d1132014-06-27 09:25:28 -070056import net.onrc.onos.core.packet.OnosLldp;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070057import net.onrc.onos.core.registry.IControllerRegistryService;
Jonathan Hart299d1132014-06-27 09:25:28 -070058import net.onrc.onos.core.util.SwitchPort;
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -070059
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070060import org.projectfloodlight.openflow.protocol.OFFactory;
61import org.projectfloodlight.openflow.protocol.OFMessage;
62import org.projectfloodlight.openflow.protocol.OFPacketIn;
63import org.projectfloodlight.openflow.protocol.OFPacketOut;
64import org.projectfloodlight.openflow.protocol.OFPortConfig;
65import org.projectfloodlight.openflow.protocol.OFPortDesc;
66import org.projectfloodlight.openflow.protocol.OFPortReason;
67import org.projectfloodlight.openflow.protocol.OFPortState;
68import org.projectfloodlight.openflow.protocol.OFPortStatus;
69import org.projectfloodlight.openflow.protocol.OFType;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070070import org.projectfloodlight.openflow.protocol.action.OFAction;
71import org.projectfloodlight.openflow.types.OFBufferId;
72import org.projectfloodlight.openflow.types.OFPort;
73import org.projectfloodlight.openflow.util.HexString;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080074import org.slf4j.Logger;
75import org.slf4j.LoggerFactory;
76
77/**
Jonathan Hart284e70f2014-07-05 12:32:51 -070078 * Discovers links between OpenFlow switches.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070079 * <p>
80 * Discovery is performed by sending probes (LLDP packets) over the links in the
81 * data plane. The LinkDiscoveryManager sends probes periodically on all ports
82 * on all connected switches. The probes contain the sending switch's DPID and
83 * outgoing port number. LLDP packets that are received (via an OpenFlow
84 * packet-in) indicate there is a link between the receiving port and the
85 * sending port, which was encoded in the LLDP. When the LinkDiscoveryManager
86 * observes a new link, a Link object is created and an event is fired for any
87 * event listeners.
88 * </p>
Jonathan Hart284e70f2014-07-05 12:32:51 -070089 * Links are removed for one of three reasons:
90 * <ul>
91 * <li>A probe has not been received on the link for an interval (the timeout
92 * interval)</li>
93 * <li>The port went down or was disabled (as observed by OpenFlow port-status
94 * messages) or the disconnection of the switch</li>
95 * <li>Link discovery was explicitly disabled on a port with the
96 * {@link #disableDiscoveryOnPort(long, short)} method</li>
97 * </ul>
98 * When the LinkDiscoveryManager removes a link it also fires an event for the
99 * listeners.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800100 */
101@LogMessageCategory("Network Topology")
Jonathan Hart284e70f2014-07-05 12:32:51 -0700102public class LinkDiscoveryManager implements IOFMessageListener, IOFSwitchListener,
Ray Milkey269ffb92014-04-03 14:43:30 -0700103 ILinkDiscoveryService, IFloodlightModule {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800104
Jonathan Hart284e70f2014-07-05 12:32:51 -0700105 private static final Logger log =
106 LoggerFactory.getLogger(LinkDiscoveryManager.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800107
Jonathan Hart284e70f2014-07-05 12:32:51 -0700108 private IFloodlightProviderService controller;
109
110 private IFloodlightProviderService floodlightProvider;
111 private IThreadPoolService threadPool;
112 private IRestApiService restApi;
113 private IControllerRegistryService registryService;
HIGUCHI Yutae0515e52013-06-14 13:00:40 -0700114
Jonathan Hartba354e02014-06-30 19:18:16 -0700115 // LLDP fields
Jonathan Hart0f383542014-07-09 11:38:03 -0700116 static final byte[] LLDP_STANDARD_DST_MAC_STRING =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800117 HexString.fromHexString("01:80:c2:00:00:0e");
Ray Milkey269ffb92014-04-03 14:43:30 -0700118 private static final long LINK_LOCAL_MASK = 0xfffffffffff0L;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800119 private static final long LINK_LOCAL_VALUE = 0x0180c2000000L;
120
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800121 // Link discovery task details.
Jonathan Hart284e70f2014-07-05 12:32:51 -0700122 private SingletonTask discoveryTask;
123 private static final int DISCOVERY_TASK_INTERVAL = 1;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700124 private static final int LINK_TIMEOUT = 35; // original 35 secs, aggressive
125 // 5 secs
126 private static final int LLDP_TO_ALL_INTERVAL = 15; // original 15 seconds,
127 // aggressive 2 secs.
Jonathan Hart284e70f2014-07-05 12:32:51 -0700128 private long lldpClock = 0;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800129 // This value is intentionally kept higher than LLDP_TO_ALL_INTERVAL.
130 // If we want to identify link failures faster, we could decrease this
131 // value to a small number, say 1 or 2 sec.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700132 private static final int LLDP_TO_KNOWN_INTERVAL = 20; // LLDP frequency for
133 // known links
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800134
Jonathan Hart284e70f2014-07-05 12:32:51 -0700135 private ReentrantReadWriteLock lock;
HIGUCHI Yutae0515e52013-06-14 13:00:40 -0700136
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800137 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700138 * Map from link to the most recent time it was verified functioning.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800139 */
140 protected Map<Link, LinkInfo> links;
141
142 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700143 * Map from switch id to a set of all links with it as an endpoint.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800144 */
145 protected Map<Long, Set<Link>> switchLinks;
146
147 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700148 * Map from a id:port to the set of links containing it as an endpoint.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800149 */
150 protected Map<NodePortTuple, Set<Link>> portLinks;
151
152 /**
Jonathan Hart284e70f2014-07-05 12:32:51 -0700153 * Listeners are called in the order they were added to the the list.
Ray Milkeyb41100a2014-04-10 10:42:15 -0700154 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700155 private final List<ILinkDiscoveryListener> linkDiscoveryListeners =
156 new CopyOnWriteArrayList<>();
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800157
Jonathan Hart284e70f2014-07-05 12:32:51 -0700158 /**
159 * List of ports through which LLDPs are not sent.
160 */
161 private Set<NodePortTuple> suppressLinkDiscovery;
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700162
Jonathan Hart284e70f2014-07-05 12:32:51 -0700163 private enum UpdateType {
164 LINK_ADDED,
165 LINK_REMOVED
166 }
167
168 private class LinkUpdate implements IUpdate {
169 private final Link link;
170 private final UpdateType operation;
171
172 public LinkUpdate(Link link, UpdateType operation) {
173 this.link = link;
174 this.operation = operation;
Ray Milkey269ffb92014-04-03 14:43:30 -0700175 }
176
Ray Milkey269ffb92014-04-03 14:43:30 -0700177 @Override
178 public void dispatch() {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700179 if (log.isTraceEnabled()) {
180 log.trace("Dispatching link discovery update {} for {}",
181 operation, link);
182 }
183 for (ILinkDiscoveryListener listener : linkDiscoveryListeners) {
184 switch (operation) {
185 case LINK_ADDED:
186 listener.linkAdded(link);
187 break;
188 case LINK_REMOVED:
189 listener.linkRemoved(link);
190 break;
191 default:
192 log.warn("Unknown link update operation {}", operation);
193 break;
Jonathan Hartb0904bf2013-11-26 14:41:11 -0800194 }
195 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700196 }
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700197 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800198
199 /**
Jonathan Hart284e70f2014-07-05 12:32:51 -0700200 * Gets the LLDP sending period in seconds.
Ray Milkey269ffb92014-04-03 14:43:30 -0700201 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800202 * @return LLDP sending period in seconds.
203 */
204 public int getLldpFrequency() {
205 return LLDP_TO_KNOWN_INTERVAL;
206 }
207
208 /**
Jonathan Hart284e70f2014-07-05 12:32:51 -0700209 * Gets the LLDP timeout value in seconds.
Ray Milkey269ffb92014-04-03 14:43:30 -0700210 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800211 * @return LLDP timeout value in seconds
212 */
213 public int getLldpTimeout() {
214 return LINK_TIMEOUT;
215 }
216
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800217 @Override
Jonathan Hart284e70f2014-07-05 12:32:51 -0700218 public Set<NodePortTuple> getDiscoveryDisabledPorts() {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800219 return suppressLinkDiscovery;
220 }
221
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800222 @Override
Jonathan Hart284e70f2014-07-05 12:32:51 -0700223 public void disableDiscoveryOnPort(long sw, short port) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800224 NodePortTuple npt = new NodePortTuple(sw, port);
225 this.suppressLinkDiscovery.add(npt);
Jonathan Hart284e70f2014-07-05 12:32:51 -0700226 deleteLinksOnPort(npt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800227 }
228
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800229 @Override
Jonathan Hart284e70f2014-07-05 12:32:51 -0700230 public void enableDiscoveryOnPort(long sw, short port) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800231 NodePortTuple npt = new NodePortTuple(sw, port);
232 this.suppressLinkDiscovery.remove(npt);
233 discover(npt);
234 }
235
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700236 private boolean isLinkDiscoverySuppressed(long sw, short p) {
237 return this.suppressLinkDiscovery.contains(new NodePortTuple(sw, p));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800238 }
239
Jonathan Hart284e70f2014-07-05 12:32:51 -0700240 private void discoverLinks() {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800241
Jonathan Hart284e70f2014-07-05 12:32:51 -0700242 // time out known links.
243 timeOutLinks();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800244
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700245 // increment LLDP clock
Ray Milkey269ffb92014-04-03 14:43:30 -0700246 lldpClock = (lldpClock + 1) % LLDP_TO_ALL_INTERVAL;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800247
248 if (lldpClock == 0) {
249 log.debug("Sending LLDP out on all ports.");
250 discoverOnAllPorts();
251 }
252 }
253
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800254 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700255 * Send LLDP on known ports.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800256 */
257 protected void discoverOnKnownLinkPorts() {
258 // Copy the port set.
259 Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>();
260 nptSet.addAll(portLinks.keySet());
261
262 // Send LLDP from each of them.
Ray Milkey269ffb92014-04-03 14:43:30 -0700263 for (NodePortTuple npt : nptSet) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800264 discover(npt);
265 }
266 }
267
Jonathan Hart284e70f2014-07-05 12:32:51 -0700268 private void discover(NodePortTuple npt) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800269 discover(npt.getNodeId(), npt.getPortId());
270 }
271
Jonathan Hart284e70f2014-07-05 12:32:51 -0700272 private void discover(long sw, short port) {
Jonathan Hartba354e02014-06-30 19:18:16 -0700273 sendDiscoveryMessage(sw, port, false);
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800274 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800275
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800276 /**
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700277 * Send link discovery message out of a given switch port. The discovery
278 * message is a standard LLDP containing ONOS-specific TLVs.
Ray Milkey269ffb92014-04-03 14:43:30 -0700279 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700280 * @param sw the switch to send on
281 * @param port the port to send out
282 * @param isReverse indicates whether the LLDP was sent as a response
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800283 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700284 @LogMessageDoc(level = "ERROR",
285 message = "Failure sending LLDP out port {port} on switch {switch}",
286 explanation = "An I/O error occured while sending LLDP message " +
287 "to the switch.",
288 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800289 protected void sendDiscoveryMessage(long sw, short port,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700290 boolean isReverse) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800291
292 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
293 if (iofSwitch == null) {
294 return;
295 }
296
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700297 if (port == OFPort.LOCAL.getShortPortNumber()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800298 return;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700299 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800300
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700301 OFPortDesc ofpPort = iofSwitch.getPort(port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800302
303 if (ofpPort == null) {
304 if (log.isTraceEnabled()) {
305 log.trace("Null physical port. sw={}, port={}", sw, port);
306 }
307 return;
308 }
309
310 if (isLinkDiscoverySuppressed(sw, port)) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700311 // Don't send LLDPs out of this port as suppressLLDPs set
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800312 return;
313 }
314
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800315 if (log.isTraceEnabled()) {
316 log.trace("Sending LLDP packet out of swich: {}, port: {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700317 sw, port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800318 }
319
Jonathan Harta213bce2014-08-11 15:44:07 -0700320 OFFactory factory = iofSwitch.getFactory();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700321 OFPacketOut po = createLLDPPacketOut(sw, ofpPort, isReverse, factory);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800322
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800323 try {
324 iofSwitch.write(po, null);
325 iofSwitch.flush();
326 } catch (IOException e) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700327 log.error("Failure sending LLDP out port " + port + " on switch "
328 + iofSwitch.getStringId(), e);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800329 }
330
331 }
332
333 /**
Jonathan Hart299d1132014-06-27 09:25:28 -0700334 * Creates packet_out LLDP for specified output port.
335 *
336 * @param dpid the dpid of the outgoing switch
337 * @param port the outgoing port
338 * @param isReverse whether this is a reverse LLDP or not
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700339 * @param factory the factory to use to create the message
Jonathan Hart299d1132014-06-27 09:25:28 -0700340 * @return Packet_out message with LLDP data
341 */
342 private OFPacketOut createLLDPPacketOut(long dpid,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700343 final OFPortDesc port, boolean isReverse, OFFactory factory) {
Jonathan Hart299d1132014-06-27 09:25:28 -0700344 // Set up packets
345 // TODO optimize by not creating new packets each time
346 OnosLldp lldpPacket = new OnosLldp();
347
348 Ethernet ethPacket = new Ethernet();
349 ethPacket.setEtherType(Ethernet.TYPE_LLDP);
350 ethPacket.setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING);
351 ethPacket.setPayload(lldpPacket);
352 ethPacket.setPad(true);
353
Jonathan Hart299d1132014-06-27 09:25:28 -0700354 lldpPacket.setSwitch(dpid);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700355 lldpPacket.setPort(port.getPortNo().getShortPortNumber());
Jonathan Hart299d1132014-06-27 09:25:28 -0700356 lldpPacket.setReverse(isReverse);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700357 ethPacket.setSourceMACAddress(port.getHwAddr().getBytes());
Jonathan Hart299d1132014-06-27 09:25:28 -0700358 final byte[] lldp = ethPacket.serialize();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700359
360 List<OFAction> actions = new ArrayList<OFAction>();
361 actions.add(factory.actions()
362 .buildOutput()
363 .setPort(OFPort.ofShort(port.getPortNo().getShortPortNumber()))
364 .build());
365 OFPacketOut po = factory.buildPacketOut()
366 .setData(lldp)
367 .setBufferId(OFBufferId.NO_BUFFER)
368 .setInPort(OFPort.CONTROLLER)
369 .setActions(actions)
370 .build();
371
372 return po;
Jonathan Hart299d1132014-06-27 09:25:28 -0700373 }
374
375 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700376 * Send LLDPs to all switch-ports.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800377 */
378 protected void discoverOnAllPorts() {
379 if (log.isTraceEnabled()) {
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800380 log.trace("Sending LLDP packets out of all the enabled ports on switch");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800381 }
Jonathan Hart284e70f2014-07-05 12:32:51 -0700382
383 for (IOFSwitch sw : floodlightProvider.getSwitches().values()) {
384 if (sw.getEnabledPorts() == null) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700385 continue;
386 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700387 for (OFPortDesc ofp : sw.getEnabledPorts()) {
388 if (isLinkDiscoverySuppressed(sw.getId(),
389 ofp.getPortNo().getShortPortNumber())) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700390 continue;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800391 }
Jonathan Hart284e70f2014-07-05 12:32:51 -0700392
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700393 sendDiscoveryMessage(sw.getId(),
394 ofp.getPortNo().getShortPortNumber(), false);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800395 }
396 }
397 }
398
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800399 @Override
400 public String getName() {
401 return "linkdiscovery";
402 }
403
404 @Override
405 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
406 switch (msg.getType()) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700407 case PACKET_IN:
408 if (msg instanceof OFPacketIn) {
409 return this.handlePacketIn(sw.getId(), (OFPacketIn) msg,
410 cntx);
411 }
412 break;
413 case PORT_STATUS:
414 if (msg instanceof OFPortStatus) {
415 return this.handlePortStatus(sw, (OFPortStatus) msg);
416 }
417 break;
418 default:
419 break;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800420 }
421 return Command.CONTINUE;
422 }
423
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700424 protected Command handleLldp(LLDP lldp, long sw, OFPacketIn pi, short inport) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800425 // If LLDP is suppressed on this port, ignore received packet as well
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700426 IOFSwitch iofSwitch = floodlightProvider.getSwitch(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800427 if (iofSwitch == null) {
428 return Command.STOP;
429 }
430
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700431 if (isLinkDiscoverySuppressed(sw, inport)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800432 return Command.STOP;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700433 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800434
435 // If this is a malformed LLDP, or not from us, exit
Ray Milkeyb29e6262014-04-09 16:02:14 -0700436 if (lldp.getPortId() == null || lldp.getPortId().getLength() != 3) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800437 return Command.CONTINUE;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700438 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800439
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800440 // Verify this LLDP packet matches what we're looking for
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700441 byte[] packetData = pi.getData();
Jonathan Hart299d1132014-06-27 09:25:28 -0700442 if (!OnosLldp.isOnosLldp(packetData)) {
443 log.trace("Dropping LLDP that wasn't sent by ONOS");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800444 return Command.STOP;
445 }
446
Jonathan Hart299d1132014-06-27 09:25:28 -0700447 SwitchPort switchPort = OnosLldp.extractSwitchPort(packetData);
Pavlin Radoslavov3d322f42014-08-18 14:58:55 -0700448 long remoteDpid = switchPort.getDpid().value();
449 short remotePort = switchPort.getPortNumber().shortValue();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700450 IOFSwitch remoteSwitch = floodlightProvider.getSwitches().get(
Pavlin Radoslavov3d322f42014-08-18 14:58:55 -0700451 switchPort.getDpid().value());
Jonathan Hart299d1132014-06-27 09:25:28 -0700452
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700453 OFPortDesc physicalPort = null;
Jonathan Hart299d1132014-06-27 09:25:28 -0700454 if (remoteSwitch != null) {
455 physicalPort = remoteSwitch.getPort(remotePort);
456 if (!remoteSwitch.portEnabled(remotePort)) {
457 if (log.isTraceEnabled()) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700458 log.trace("Ignoring link with disabled source port: " +
459 "switch {} port {}", remoteSwitch, remotePort);
Jonathan Hart299d1132014-06-27 09:25:28 -0700460 }
461 return Command.STOP;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800462 }
Jonathan Hart284e70f2014-07-05 12:32:51 -0700463 if (suppressLinkDiscovery.contains(
464 new NodePortTuple(remoteSwitch.getId(), remotePort))) {
Jonathan Hart299d1132014-06-27 09:25:28 -0700465 if (log.isTraceEnabled()) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700466 log.trace("Ignoring link with suppressed src port: " +
467 "switch {} port {}", remoteSwitch, remotePort);
Jonathan Hart299d1132014-06-27 09:25:28 -0700468 }
469 return Command.STOP;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800470 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800471 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700472 if (!iofSwitch.portEnabled(inport)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800473 if (log.isTraceEnabled()) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700474 log.trace("Ignoring link with disabled dest port: " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700475 "switch {} port {}", sw, inport);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800476 }
477 return Command.STOP;
478 }
479
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700480 // TODO It probably should be empty Set instead of null. Confirm and fix.
481 Set<OFPortState> srcPortState = (physicalPort != null)
482 ? physicalPort.getState() : null;
483 physicalPort = iofSwitch.getPort(inport);
484 Set<OFPortState> dstPortState = (physicalPort != null)
485 ? physicalPort.getState() : null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800486
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700487 // Store the time of update to this link, and push it out to
488 // routingEngine
489 Link lt = new Link(remoteDpid, remotePort, iofSwitch.getId(), inport);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800490
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700491 LinkInfo linkInfo = new LinkInfo(System.currentTimeMillis(),
492 System.currentTimeMillis(), srcPortState, dstPortState);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800493
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700494 addOrUpdateLink(lt, linkInfo);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800495
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800496 // Check if reverse link exists.
497 // If it doesn't exist and if the forward link was seen
498 // first seen within a small interval, send probe on the
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800499 // reverse link.
Jonathan Hart299d1132014-06-27 09:25:28 -0700500 boolean isReverse = OnosLldp.isReverse(lldp);
501
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700502 LinkInfo newLinkInfo = links.get(lt);
Jonathan Hartba354e02014-06-30 19:18:16 -0700503 if (newLinkInfo != null && !isReverse) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800504 Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700505 lt.getSrc(), lt.getSrcPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800506 LinkInfo reverseInfo = links.get(reverseLink);
507 if (reverseInfo == null) {
508 // the reverse link does not exist.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700509 if (newLinkInfo.getFirstSeenTime() > System.currentTimeMillis()
510 - LINK_TIMEOUT) {
Jonathan Hartba354e02014-06-30 19:18:16 -0700511 this.sendDiscoveryMessage(lt.getDst(), lt.getDstPort(), true);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800512 }
513 }
514 }
515
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800516 // Consume this message
517 return Command.STOP;
518 }
519
520 protected Command handlePacketIn(long sw, OFPacketIn pi,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700521 FloodlightContext cntx) {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800522 Ethernet eth =
523 IFloodlightProviderService.bcStore.get(cntx,
Ray Milkey269ffb92014-04-03 14:43:30 -0700524 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700525 short inport = (short) cntx.getStorage()
526 .get(IFloodlightProviderService.CONTEXT_PI_INPORT);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800527
Jonathan Hartba354e02014-06-30 19:18:16 -0700528 if (eth.getEtherType() == Ethernet.TYPE_LLDP) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700529 return handleLldp((LLDP) eth.getPayload(), sw, pi, inport);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800530 } else if (eth.getEtherType() < 1500) {
531 long destMac = eth.getDestinationMAC().toLong();
Ray Milkey269ffb92014-04-03 14:43:30 -0700532 if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800533 if (log.isTraceEnabled()) {
534 log.trace("Ignoring packet addressed to 802.1D/Q " +
535 "reserved address.");
536 }
537 return Command.STOP;
538 }
539 }
540
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800541 return Command.CONTINUE;
542 }
543
Jonathan Hart284e70f2014-07-05 12:32:51 -0700544 protected void addOrUpdateLink(Link lt, LinkInfo detectedLinkInfo) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800545 lock.writeLock().lock();
546 try {
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700547 LinkInfo existingInfo = links.get(lt);
548
549 LinkInfo newLinkInfo = new LinkInfo(
550 ((existingInfo == null) ? detectedLinkInfo.getFirstSeenTime()
551 : existingInfo.getFirstSeenTime()),
552 detectedLinkInfo.getLastProbeReceivedTime(),
553 detectedLinkInfo.getSrcPortState(),
554 detectedLinkInfo.getDstPortState());
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700555
Jonathan Hart284e70f2014-07-05 12:32:51 -0700556 // Add new LinkInfo or update old LinkInfo
557 links.put(lt, newLinkInfo);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800558
559 if (log.isTraceEnabled()) {
Jonathan Hartba354e02014-06-30 19:18:16 -0700560 log.trace("addOrUpdateLink: {}", lt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800561 }
562
Jonathan Hart284e70f2014-07-05 12:32:51 -0700563 NodePortTuple srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
564 NodePortTuple dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800565
Jonathan Hart284e70f2014-07-05 12:32:51 -0700566 // If this is the first time we've seen the link, add the Link
567 // object to the data structures/indexes as well
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700568 if (existingInfo == null) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700569 log.trace("Creating new Link: {}", lt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800570 // index it by switch source
Ray Milkeyb29e6262014-04-09 16:02:14 -0700571 if (!switchLinks.containsKey(lt.getSrc())) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800572 switchLinks.put(lt.getSrc(), new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700573 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800574 switchLinks.get(lt.getSrc()).add(lt);
575
576 // index it by switch dest
Ray Milkeyb29e6262014-04-09 16:02:14 -0700577 if (!switchLinks.containsKey(lt.getDst())) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800578 switchLinks.put(lt.getDst(), new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700579 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800580 switchLinks.get(lt.getDst()).add(lt);
581
582 // index both ends by switch:port
Ray Milkeyb29e6262014-04-09 16:02:14 -0700583 if (!portLinks.containsKey(srcNpt)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800584 portLinks.put(srcNpt, new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700585 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800586 portLinks.get(srcNpt).add(lt);
587
Ray Milkeyb29e6262014-04-09 16:02:14 -0700588 if (!portLinks.containsKey(dstNpt)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800589 portLinks.put(dstNpt, new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700590 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800591 portLinks.get(dstNpt).add(lt);
592
Jonathan Hart284e70f2014-07-05 12:32:51 -0700593 // Publish LINK_ADDED event
594 controller.publishUpdate(new LinkUpdate(lt, UpdateType.LINK_ADDED));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800595 }
596 } finally {
597 lock.writeLock().unlock();
598 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800599 }
600
601 /**
Jonathan Hart284e70f2014-07-05 12:32:51 -0700602 * Removes links from the data structures.
Ray Milkey269ffb92014-04-03 14:43:30 -0700603 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700604 * @param linksToDelete the list of links to delete
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800605 */
Jonathan Hart284e70f2014-07-05 12:32:51 -0700606 protected void deleteLinks(List<Link> linksToDelete) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800607 lock.writeLock().lock();
608 try {
Ray Milkey5df613b2014-04-15 10:50:56 -0700609 for (Link lt : linksToDelete) {
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700610 NodePortTuple srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
611 NodePortTuple dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800612
613 switchLinks.get(lt.getSrc()).remove(lt);
614 switchLinks.get(lt.getDst()).remove(lt);
615 if (switchLinks.containsKey(lt.getSrc()) &&
Ray Milkeyb29e6262014-04-09 16:02:14 -0700616 switchLinks.get(lt.getSrc()).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800617 this.switchLinks.remove(lt.getSrc());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700618 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800619 if (this.switchLinks.containsKey(lt.getDst()) &&
Ray Milkeyb29e6262014-04-09 16:02:14 -0700620 this.switchLinks.get(lt.getDst()).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800621 this.switchLinks.remove(lt.getDst());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700622 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800623
624 if (this.portLinks.get(srcNpt) != null) {
625 this.portLinks.get(srcNpt).remove(lt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700626 if (this.portLinks.get(srcNpt).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800627 this.portLinks.remove(srcNpt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700628 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800629 }
630 if (this.portLinks.get(dstNpt) != null) {
631 this.portLinks.get(dstNpt).remove(lt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700632 if (this.portLinks.get(dstNpt).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800633 this.portLinks.remove(dstNpt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700634 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800635 }
636
Jonathan Hart284e70f2014-07-05 12:32:51 -0700637 this.links.remove(lt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800638
Jonathan Hart284e70f2014-07-05 12:32:51 -0700639 controller.publishUpdate(new LinkUpdate(lt,
640 UpdateType.LINK_REMOVED));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800641
642 if (log.isTraceEnabled()) {
643 log.trace("Deleted link {}", lt);
644 }
645 }
646 } finally {
647 lock.writeLock().unlock();
648 }
649 }
650
651 /**
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700652 * Handles an OFPortStatus message from a switch. We will add or delete
653 * LinkTupes as well re-compute the topology if needed.
Ray Milkey269ffb92014-04-03 14:43:30 -0700654 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700655 * @param sw The dpid of the switch that sent the port status message
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800656 * @param ps The OFPortStatus message
657 * @return The Command to continue or stop after we process this message
658 */
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700659 protected Command handlePortStatus(IOFSwitch sw, OFPortStatus ps) {
HIGUCHI Yutaa89b2842013-06-17 13:54:57 -0700660
Jonathan Hart284e70f2014-07-05 12:32:51 -0700661 // If we do not control this switch, then we should not process its
662 // port status messages
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700663 if (!registryService.hasControl(sw.getId())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700664 return Command.CONTINUE;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700665 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800666
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800667 if (log.isTraceEnabled()) {
668 log.trace("handlePortStatus: Switch {} port #{} reason {}; " +
669 "config is {} state is {}",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700670 new Object[] {sw.getStringId(),
671 ps.getDesc().getPortNo(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700672 ps.getReason(),
673 ps.getDesc().getConfig(),
674 ps.getDesc().getState()});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800675 }
676
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700677 short port = ps.getDesc().getPortNo().getShortPortNumber();
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700678 NodePortTuple npt = new NodePortTuple(sw.getId(), port);
Ray Milkey269ffb92014-04-03 14:43:30 -0700679 boolean linkDeleted = false;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800680 boolean linkInfoChanged = false;
681
682 lock.writeLock().lock();
683 try {
684 // if ps is a delete, or a modify where the port is down or
685 // configured down
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700686 if (OFPortReason.DELETE == ps.getReason() ||
687 (OFPortReason.MODIFY == ps.getReason() &&
688 !portEnabled(ps.getDesc()))) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700689 deleteLinksOnPort(npt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800690 linkDeleted = true;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700691 } else if (ps.getReason() == OFPortReason.MODIFY) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800692 // If ps is a port modification and the port state has changed
693 // that affects links in the topology
694
695 if (this.portLinks.containsKey(npt)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700696 for (Link lt : this.portLinks.get(npt)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800697 LinkInfo linkInfo = links.get(lt);
Ray Milkey269ffb92014-04-03 14:43:30 -0700698 assert (linkInfo != null);
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700699 LinkInfo newLinkInfo = null;
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800700
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700701 if (lt.isSrcPort(npt) &&
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700702 !linkInfo.getSrcPortState().equals(
703 ps.getDesc().getState())) {
704 // If this port status is for the src port and the
705 // port state has changed, create a new link info
706 // with the new state
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700707
708 newLinkInfo = new LinkInfo(linkInfo.getFirstSeenTime(),
709 linkInfo.getLastProbeReceivedTime(),
710 ps.getDesc().getState(),
711 linkInfo.getDstPortState());
712 } else if (lt.isDstPort(npt) &&
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700713 !linkInfo.getDstPortState().equals(
714 ps.getDesc().getState())) {
715 // If this port status is for the dst port and the
716 // port state has changed, create a new link info
717 // with the new state
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700718
719 newLinkInfo = new LinkInfo(linkInfo.getFirstSeenTime(),
720 linkInfo.getLastProbeReceivedTime(),
721 linkInfo.getSrcPortState(),
722 ps.getDesc().getState());
723 }
724
725 if (newLinkInfo != null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800726 linkInfoChanged = true;
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700727 links.put(lt, newLinkInfo);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800728 }
729 }
730 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800731 }
732
Ray Milkey269ffb92014-04-03 14:43:30 -0700733 if (!linkDeleted && !linkInfoChanged) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800734 if (log.isTraceEnabled()) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700735 log.trace("handlePortStatus: Switch {} port #{} reason {};" +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800736 " no links to update/remove",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700737 new Object[] {HexString.toHexString(sw.getId()),
738 ps.getDesc().getPortNo(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700739 ps.getReason()});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800740 }
741 }
742 } finally {
743 lock.writeLock().unlock();
744 }
745
746 if (!linkDeleted) {
747 // Send LLDP right away when port state is changed for faster
748 // cluster-merge. If it is a link delete then there is not need
749 // to send the LLDPs right away and instead we wait for the LLDPs
750 // to be sent on the timer as it is normally done
751 // do it outside the write-lock
752 // sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS);
753 processNewPort(npt.getNodeId(), npt.getPortId());
754 }
755 return Command.CONTINUE;
756 }
757
758 /**
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700759 * Process a new port. If link discovery is disabled on the port, then do
760 * nothing. Otherwise, send LLDP message.
Ray Milkey269ffb92014-04-03 14:43:30 -0700761 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700762 * @param sw the dpid of the switch the port is on
763 * @param p the number of the port
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800764 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700765 private void processNewPort(long sw, int p) {
766 if (isLinkDiscoverySuppressed(sw, (short) p)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800767 // Do nothing as link discovery is suppressed.
Ray Milkey1aa71f82014-04-08 16:23:24 -0700768 return;
Ray Milkey269ffb92014-04-03 14:43:30 -0700769 } else {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700770 discover(sw, (short) p);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800771 }
772 }
773
774 /**
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700775 * We send out LLDP messages when a switch is added to discover the
776 * topology.
Ray Milkey269ffb92014-04-03 14:43:30 -0700777 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700778 * @param swId the datapath Id of the new switch
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800779 */
780 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700781 public void switchActivatedMaster(long swId) {
782 IOFSwitch sw = floodlightProvider.getSwitch(swId);
783 if (sw == null) {
784 log.warn("Added switch not available {} ", swId);
785 return;
786 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800787 if (sw.getEnabledPorts() != null) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700788 for (Integer p : sw.getEnabledPortNumbers()) {
789 processNewPort(swId, p);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800790 }
791 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800792 }
793
794 /**
795 * When a switch disconnects we remove any links from our map and notify.
Jonathan Hart284e70f2014-07-05 12:32:51 -0700796 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700797 * @param swId the datapath Id of the switch that was removed
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800798 */
799 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700800 public void switchDisconnected(long swId) {
Pavlin Radoslavov4bc13b72014-08-28 17:40:48 -0700801 // Cleanup link state
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800802 List<Link> eraseList = new ArrayList<Link>();
803 lock.writeLock().lock();
804 try {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700805 if (switchLinks.containsKey(swId)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800806 if (log.isTraceEnabled()) {
807 log.trace("Handle switchRemoved. Switch {}; removing links {}",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700808 HexString.toHexString(swId), switchLinks.get(swId));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800809 }
810 // add all tuples with an endpoint on this switch to erase list
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700811 eraseList.addAll(switchLinks.get(swId));
Jonathan Hart284e70f2014-07-05 12:32:51 -0700812 deleteLinks(eraseList);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800813 }
814 } finally {
815 lock.writeLock().unlock();
816 }
817 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800818
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700819 @Override
820 public void switchActivatedEqual(long swId) {
821 // TODO Auto-generated method stub
822
823 }
824
825 @Override
826 public void switchMasterToEqual(long swId) {
827 // TODO Auto-generated method stub
828
829 }
830
831 @Override
832 public void switchEqualToMaster(long swId) {
833 // for now treat as switchActivatedMaster
834 switchActivatedMaster(swId);
835 }
836
Jonathan Hart284e70f2014-07-05 12:32:51 -0700837 /*
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700838 * We don't react to port changed notifications here. we listen for
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800839 * OFPortStatus messages directly. Might consider using this notifier
840 * instead
841 */
842 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700843 public void switchPortChanged(long swId, OFPortDesc port,
844 PortChangeType changeType) {
845 // TODO Auto-generated method stub
846
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800847 }
848
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800849 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800850 * Delete links incident on a given switch port.
Ray Milkey269ffb92014-04-03 14:43:30 -0700851 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700852 * @param npt the port to delete links on
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800853 */
Jonathan Hart284e70f2014-07-05 12:32:51 -0700854 protected void deleteLinksOnPort(NodePortTuple npt) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800855 List<Link> eraseList = new ArrayList<Link>();
856 if (this.portLinks.containsKey(npt)) {
857 if (log.isTraceEnabled()) {
858 log.trace("handlePortStatus: Switch {} port #{} " +
859 "removing links {}",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700860 new Object[] {HexString.toHexString(npt.getNodeId()),
Ray Milkey269ffb92014-04-03 14:43:30 -0700861 npt.getPortId(),
862 this.portLinks.get(npt)});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800863 }
864 eraseList.addAll(this.portLinks.get(npt));
Jonathan Hart284e70f2014-07-05 12:32:51 -0700865 deleteLinks(eraseList);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800866 }
867 }
868
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800869 /**
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700870 * Iterates through the list of links and deletes if the last discovery
871 * message reception time exceeds timeout values.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800872 */
Jonathan Hart284e70f2014-07-05 12:32:51 -0700873 protected void timeOutLinks() {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800874 List<Link> eraseList = new ArrayList<Link>();
875 Long curTime = System.currentTimeMillis();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800876
877 // reentrant required here because deleteLink also write locks
878 lock.writeLock().lock();
879 try {
880 Iterator<Entry<Link, LinkInfo>> it =
881 this.links.entrySet().iterator();
882 while (it.hasNext()) {
883 Entry<Link, LinkInfo> entry = it.next();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800884 LinkInfo info = entry.getValue();
885
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700886 if ((info.getLastProbeReceivedTime() + (1000L * LINK_TIMEOUT)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700887 < curTime)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800888 eraseList.add(entry.getKey());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800889 }
890 }
891
Jonathan Hart284e70f2014-07-05 12:32:51 -0700892 deleteLinks(eraseList);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800893 } finally {
894 lock.writeLock().unlock();
895 }
896 }
897
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700898 private boolean portEnabled(OFPortDesc port) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700899 if (port == null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800900 return false;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700901 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700902 if (port.getConfig().contains(OFPortConfig.PORT_DOWN)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800903 return false;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700904 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700905 if (port.getState().contains(OFPortState.LINK_DOWN)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800906 return false;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700907 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800908 return true;
909 }
910
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800911 @Override
912 public Map<Link, LinkInfo> getLinks() {
913 lock.readLock().lock();
914 Map<Link, LinkInfo> result;
915 try {
916 result = new HashMap<Link, LinkInfo>(links);
917 } finally {
918 lock.readLock().unlock();
919 }
920 return result;
921 }
922
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800923 @Override
924 public void addListener(ILinkDiscoveryListener listener) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700925 linkDiscoveryListeners.add(listener);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800926 }
927
Jonathan Hart284e70f2014-07-05 12:32:51 -0700928 @Override
929 public void removeListener(ILinkDiscoveryListener listener) {
930 linkDiscoveryListeners.remove(listener);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800931 }
932
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800933 @Override
934 public boolean isCallbackOrderingPrereq(OFType type, String name) {
935 return false;
936 }
937
938 @Override
939 public boolean isCallbackOrderingPostreq(OFType type, String name) {
940 return false;
941 }
942
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800943 // IFloodlightModule classes
944
945 @Override
946 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800947 Collection<Class<? extends IFloodlightService>> l =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800948 new ArrayList<Class<? extends IFloodlightService>>();
949 l.add(ILinkDiscoveryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800950 return l;
951 }
952
953 @Override
954 public Map<Class<? extends IFloodlightService>, IFloodlightService>
Jonathan Hart284e70f2014-07-05 12:32:51 -0700955 getServiceImpls() {
956 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
957 new HashMap<>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800958 m.put(ILinkDiscoveryService.class, this);
959 return m;
960 }
961
962 @Override
963 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700964 Collection<Class<? extends IFloodlightService>> l = new ArrayList<>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800965 l.add(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800966 l.add(IThreadPoolService.class);
967 l.add(IRestApiService.class);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700968 l.add(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800969 return l;
970 }
971
972 @Override
973 public void init(FloodlightModuleContext context)
974 throws FloodlightModuleException {
975 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800976 threadPool = context.getServiceImpl(IThreadPoolService.class);
977 restApi = context.getServiceImpl(IRestApiService.class);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700978 registryService = context.getServiceImpl(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800979
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800980 this.lock = new ReentrantReadWriteLock();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800981 this.links = new HashMap<Link, LinkInfo>();
982 this.portLinks = new HashMap<NodePortTuple, Set<Link>>();
983 this.suppressLinkDiscovery =
984 Collections.synchronizedSet(new HashSet<NodePortTuple>());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800985 this.switchLinks = new HashMap<Long, Set<Link>>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800986 }
987
988 @Override
989 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700990 @LogMessageDoc(level = "ERROR",
991 message = "No storage source found.",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700992 explanation = "Storage source was not initialized; cannot initialize "
993 +
Ray Milkey269ffb92014-04-03 14:43:30 -0700994 "link discovery.",
995 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG),
996 @LogMessageDoc(level = "ERROR",
997 message = "Error in installing listener for " +
998 "switch config table {table}",
999 explanation = "Failed to install storage notification for the " +
1000 "switch config table",
1001 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG),
1002 @LogMessageDoc(level = "ERROR",
1003 message = "No storage source found.",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001004 explanation = "Storage source was not initialized; cannot initialize "
1005 +
Ray Milkey269ffb92014-04-03 14:43:30 -07001006 "link discovery.",
1007 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG),
1008 @LogMessageDoc(level = "ERROR",
1009 message = "Exception in LLDP send timer.",
1010 explanation = "An unknown error occured while sending LLDP " +
1011 "messages to switches.",
1012 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001013 })
1014 public void startUp(FloodlightModuleContext context) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001015 ScheduledExecutorService ses = threadPool.getScheduledExecutor();
Jonathan Hart284e70f2014-07-05 12:32:51 -07001016 controller = context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001017
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001018 discoveryTask = new SingletonTask(ses, new Runnable() {
1019 @Override
1020 public void run() {
1021 try {
1022 discoverLinks();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001023 } catch (Exception e) {
1024 log.error("Exception in LLDP send timer.", e);
1025 } finally {
Jonathan Hartba354e02014-06-30 19:18:16 -07001026 log.trace("Rescheduling discovery task");
1027 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL,
1028 TimeUnit.SECONDS);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001029 }
1030 }
1031 });
1032
Jonathan Hartec4f14e2013-12-12 10:46:38 -08001033 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL, TimeUnit.SECONDS);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001034
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001035 // Register for the OpenFlow messages we want to receive
1036 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
1037 floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this);
1038 // Register for switch updates
1039 floodlightProvider.addOFSwitchListener(this);
Jonathan Hartba354e02014-06-30 19:18:16 -07001040 restApi.addRestletRoutable(new LinkDiscoveryWebRoutable());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001041 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001042}