blob: db3580963aaed0b50a3f684a4887622d486e6d03 [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;
Jonathan Hart299d1132014-06-27 09:25:28 -070027import java.util.LinkedList;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080028import java.util.List;
29import java.util.Map;
30import java.util.Map.Entry;
31import java.util.Set;
Jonathan Hart284e70f2014-07-05 12:32:51 -070032import java.util.concurrent.CopyOnWriteArrayList;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080033import java.util.concurrent.ScheduledExecutorService;
34import java.util.concurrent.TimeUnit;
35import java.util.concurrent.locks.ReentrantReadWriteLock;
36
37import net.floodlightcontroller.core.FloodlightContext;
38import net.floodlightcontroller.core.IFloodlightProviderService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080039import net.floodlightcontroller.core.IOFMessageListener;
40import net.floodlightcontroller.core.IOFSwitch;
41import 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
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080060import org.openflow.protocol.OFMessage;
61import org.openflow.protocol.OFPacketIn;
62import org.openflow.protocol.OFPacketOut;
63import org.openflow.protocol.OFPhysicalPort;
64import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
65import org.openflow.protocol.OFPhysicalPort.OFPortState;
66import org.openflow.protocol.OFPort;
67import org.openflow.protocol.OFPortStatus;
68import org.openflow.protocol.OFPortStatus.OFPortReason;
69import org.openflow.protocol.OFType;
70import org.openflow.protocol.action.OFAction;
71import org.openflow.protocol.action.OFActionOutput;
Jonathan Hart299d1132014-06-27 09:25:28 -070072import org.openflow.protocol.action.OFActionType;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080073import org.openflow.util.HexString;
74import org.slf4j.Logger;
75import org.slf4j.LoggerFactory;
76
77/**
Jonathan Hart284e70f2014-07-05 12:32:51 -070078 * Discovers links between OpenFlow switches.
Ray Milkey269ffb92014-04-03 14:43:30 -070079 * <p/>
Jonathan Hart284e70f2014-07-05 12:32:51 -070080 * Discovery is performed by sending probes (LLDP packets) over the links in
81 * the data plane. The LinkDiscoveryManager sends probes periodically on all
82 * ports on all connected switches. The probes contain the sending switch's
83 * DPID and outgoing port number. LLDP packets that are received (via an
84 * OpenFlow packet-in) indicate there is a link between the receiving port and
85 * the sending port, which was encoded in the LLDP. When the
86 * LinkDiscoveryManager observes a new link, a Link object is created and an
87 * event is fired for any event listeners.
Ray Milkey269ffb92014-04-03 14:43:30 -070088 * <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
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800116 private 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;
124 private static final int LINK_TIMEOUT = 35; // original 35 secs, aggressive 5 secs
125 private static final int LLDP_TO_ALL_INTERVAL = 15; //original 15 seconds, aggressive 2 secs.
126 private long lldpClock = 0;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800127 // This value is intentionally kept higher than LLDP_TO_ALL_INTERVAL.
128 // If we want to identify link failures faster, we could decrease this
129 // value to a small number, say 1 or 2 sec.
Jonathan Hart284e70f2014-07-05 12:32:51 -0700130 private static final int LLDP_TO_KNOWN_INTERVAL = 20; // LLDP frequency for known links
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800131
Jonathan Hart284e70f2014-07-05 12:32:51 -0700132 private ReentrantReadWriteLock lock;
HIGUCHI Yutae0515e52013-06-14 13:00:40 -0700133
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800134 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700135 * Map from link to the most recent time it was verified functioning.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800136 */
137 protected Map<Link, LinkInfo> links;
138
139 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700140 * Map from switch id to a set of all links with it as an endpoint.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800141 */
142 protected Map<Long, Set<Link>> switchLinks;
143
144 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700145 * Map from a id:port to the set of links containing it as an endpoint.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800146 */
147 protected Map<NodePortTuple, Set<Link>> portLinks;
148
149 /**
Jonathan Hart284e70f2014-07-05 12:32:51 -0700150 * Listeners are called in the order they were added to the the list.
Ray Milkeyb41100a2014-04-10 10:42:15 -0700151 */
Jonathan Hart284e70f2014-07-05 12:32:51 -0700152 private final List<ILinkDiscoveryListener> linkDiscoveryListeners
153 = new CopyOnWriteArrayList<>();
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800154
Jonathan Hart284e70f2014-07-05 12:32:51 -0700155 /**
156 * List of ports through which LLDPs are not sent.
157 */
158 private Set<NodePortTuple> suppressLinkDiscovery;
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700159
Jonathan Hart284e70f2014-07-05 12:32:51 -0700160 private enum UpdateType {
161 LINK_ADDED,
162 LINK_REMOVED
163 }
164
165 private class LinkUpdate implements IUpdate {
166 private final Link link;
167 private final UpdateType operation;
168
169 public LinkUpdate(Link link, UpdateType operation) {
170 this.link = link;
171 this.operation = operation;
Ray Milkey269ffb92014-04-03 14:43:30 -0700172 }
173
Ray Milkey269ffb92014-04-03 14:43:30 -0700174 @Override
175 public void dispatch() {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700176 if (log.isTraceEnabled()) {
177 log.trace("Dispatching link discovery update {} for {}",
178 operation, link);
179 }
180 for (ILinkDiscoveryListener listener : linkDiscoveryListeners) {
181 switch (operation) {
182 case LINK_ADDED:
183 listener.linkAdded(link);
184 break;
185 case LINK_REMOVED:
186 listener.linkRemoved(link);
187 break;
188 default:
189 log.warn("Unknown link update operation {}", operation);
190 break;
Jonathan Hartb0904bf2013-11-26 14:41:11 -0800191 }
192 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700193 }
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700194 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800195
196 /**
Jonathan Hart284e70f2014-07-05 12:32:51 -0700197 * Gets the LLDP sending period in seconds.
Ray Milkey269ffb92014-04-03 14:43:30 -0700198 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800199 * @return LLDP sending period in seconds.
200 */
201 public int getLldpFrequency() {
202 return LLDP_TO_KNOWN_INTERVAL;
203 }
204
205 /**
Jonathan Hart284e70f2014-07-05 12:32:51 -0700206 * Gets the LLDP timeout value in seconds.
Ray Milkey269ffb92014-04-03 14:43:30 -0700207 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800208 * @return LLDP timeout value in seconds
209 */
210 public int getLldpTimeout() {
211 return LINK_TIMEOUT;
212 }
213
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800214 @Override
Jonathan Hart284e70f2014-07-05 12:32:51 -0700215 public Set<NodePortTuple> getDiscoveryDisabledPorts() {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800216 return suppressLinkDiscovery;
217 }
218
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800219 @Override
Jonathan Hart284e70f2014-07-05 12:32:51 -0700220 public void disableDiscoveryOnPort(long sw, short port) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800221 NodePortTuple npt = new NodePortTuple(sw, port);
222 this.suppressLinkDiscovery.add(npt);
Jonathan Hart284e70f2014-07-05 12:32:51 -0700223 deleteLinksOnPort(npt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800224 }
225
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800226 @Override
Jonathan Hart284e70f2014-07-05 12:32:51 -0700227 public void enableDiscoveryOnPort(long sw, short port) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800228 NodePortTuple npt = new NodePortTuple(sw, port);
229 this.suppressLinkDiscovery.remove(npt);
230 discover(npt);
231 }
232
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800233 private boolean isLinkDiscoverySuppressed(long sw, short portNumber) {
234 return this.suppressLinkDiscovery.contains(new NodePortTuple(sw, portNumber));
235 }
236
Jonathan Hart284e70f2014-07-05 12:32:51 -0700237 private void discoverLinks() {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800238
Jonathan Hart284e70f2014-07-05 12:32:51 -0700239 // time out known links.
240 timeOutLinks();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800241
242 //increment LLDP clock
Ray Milkey269ffb92014-04-03 14:43:30 -0700243 lldpClock = (lldpClock + 1) % LLDP_TO_ALL_INTERVAL;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800244
245 if (lldpClock == 0) {
246 log.debug("Sending LLDP out on all ports.");
247 discoverOnAllPorts();
248 }
249 }
250
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800251 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700252 * Send LLDP on known ports.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800253 */
254 protected void discoverOnKnownLinkPorts() {
255 // Copy the port set.
256 Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>();
257 nptSet.addAll(portLinks.keySet());
258
259 // Send LLDP from each of them.
Ray Milkey269ffb92014-04-03 14:43:30 -0700260 for (NodePortTuple npt : nptSet) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800261 discover(npt);
262 }
263 }
264
Jonathan Hart284e70f2014-07-05 12:32:51 -0700265 private void discover(NodePortTuple npt) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800266 discover(npt.getNodeId(), npt.getPortId());
267 }
268
Jonathan Hart284e70f2014-07-05 12:32:51 -0700269 private void discover(long sw, short port) {
Jonathan Hartba354e02014-06-30 19:18:16 -0700270 sendDiscoveryMessage(sw, port, false);
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800271 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800272
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800273 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800274 * Send link discovery message out of a given switch port.
Jonathan Hart284e70f2014-07-05 12:32:51 -0700275 * The discovery message is a standard LLDP containing ONOS-specific TLVs.
Ray Milkey269ffb92014-04-03 14:43:30 -0700276 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700277 * @param sw the switch to send on
278 * @param port the port to send out
279 * @param isReverse indicates whether the LLDP was sent as a response
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800280 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700281 @LogMessageDoc(level = "ERROR",
282 message = "Failure sending LLDP out port {port} on switch {switch}",
283 explanation = "An I/O error occured while sending LLDP message " +
284 "to the switch.",
285 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800286 protected void sendDiscoveryMessage(long sw, short port,
Ray Milkey269ffb92014-04-03 14:43:30 -0700287 boolean isReverse) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800288
289 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
290 if (iofSwitch == null) {
291 return;
292 }
293
Ray Milkeyb29e6262014-04-09 16:02:14 -0700294 if (port == OFPort.OFPP_LOCAL.getValue()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800295 return;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700296 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800297
298 OFPhysicalPort ofpPort = iofSwitch.getPort(port);
299
300 if (ofpPort == null) {
301 if (log.isTraceEnabled()) {
302 log.trace("Null physical port. sw={}, port={}", sw, port);
303 }
304 return;
305 }
306
307 if (isLinkDiscoverySuppressed(sw, port)) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700308 // Don't send LLDPs out of this port as suppressLLDPs set
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800309 return;
310 }
311
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800312 if (log.isTraceEnabled()) {
313 log.trace("Sending LLDP packet out of swich: {}, port: {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700314 sw, port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800315 }
316
Jonathan Hart299d1132014-06-27 09:25:28 -0700317 OFPacketOut po = createLLDPPacketOut(sw, ofpPort, isReverse);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800318
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800319 try {
320 iofSwitch.write(po, null);
321 iofSwitch.flush();
322 } catch (IOException e) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700323 log.error("Failure sending LLDP out port " + port + " on switch "
324 + iofSwitch.getStringId(), e);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800325 }
326
327 }
328
329 /**
Jonathan Hart299d1132014-06-27 09:25:28 -0700330 * Creates packet_out LLDP for specified output port.
331 *
332 * @param dpid the dpid of the outgoing switch
333 * @param port the outgoing port
334 * @param isReverse whether this is a reverse LLDP or not
335 * @return Packet_out message with LLDP data
336 */
337 private OFPacketOut createLLDPPacketOut(long dpid,
338 final OFPhysicalPort port, boolean isReverse) {
339 // Set up packets
340 // TODO optimize by not creating new packets each time
341 OnosLldp lldpPacket = new OnosLldp();
342
343 Ethernet ethPacket = new Ethernet();
344 ethPacket.setEtherType(Ethernet.TYPE_LLDP);
345 ethPacket.setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING);
346 ethPacket.setPayload(lldpPacket);
347 ethPacket.setPad(true);
348
349 final OFPacketOut packetOut = (OFPacketOut) floodlightProvider.getOFMessageFactory()
350 .getMessage(OFType.PACKET_OUT);
351 packetOut.setBufferId(OFPacketOut.BUFFER_ID_NONE);
352
353 final List<OFAction> actionsList = new LinkedList<OFAction>();
354 final OFActionOutput out = (OFActionOutput) floodlightProvider.getOFMessageFactory()
355 .getAction(OFActionType.OUTPUT);
356 out.setPort(port.getPortNumber());
357 actionsList.add(out);
358 packetOut.setActions(actionsList);
359 final short alen = (short) OFActionOutput.MINIMUM_LENGTH;
360
361 lldpPacket.setSwitch(dpid);
362 lldpPacket.setPort(port.getPortNumber());
363 lldpPacket.setReverse(isReverse);
364 ethPacket.setSourceMACAddress(port.getHardwareAddress());
365
366 final byte[] lldp = ethPacket.serialize();
367 packetOut.setActionsLength(alen);
368 packetOut.setPacketData(lldp);
369 packetOut
370 .setLength((short) (OFPacketOut.MINIMUM_LENGTH + alen + lldp.length));
371 return packetOut;
372 }
373
374 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700375 * Send LLDPs to all switch-ports.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800376 */
377 protected void discoverOnAllPorts() {
378 if (log.isTraceEnabled()) {
Yuta HIGUCHI5302ddf2014-01-06 12:53:35 -0800379 log.trace("Sending LLDP packets out of all the enabled ports on switch");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800380 }
Jonathan Hart284e70f2014-07-05 12:32:51 -0700381
382 for (IOFSwitch sw : floodlightProvider.getSwitches().values()) {
383 if (sw.getEnabledPorts() == null) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700384 continue;
385 }
Jonathan Hart284e70f2014-07-05 12:32:51 -0700386 for (OFPhysicalPort ofp : sw.getEnabledPorts()) {
387 if (isLinkDiscoverySuppressed(sw.getId(), ofp.getPortNumber())) {
388 continue;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800389 }
Jonathan Hart284e70f2014-07-05 12:32:51 -0700390
391 sendDiscoveryMessage(sw.getId(), ofp.getPortNumber(), false);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800392 }
393 }
394 }
395
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800396 @Override
397 public String getName() {
398 return "linkdiscovery";
399 }
400
401 @Override
402 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
403 switch (msg.getType()) {
404 case PACKET_IN:
Pavlin Radoslavov0b88a262014-04-10 15:43:27 -0700405 if (msg instanceof OFPacketIn) {
406 return this.handlePacketIn(sw.getId(), (OFPacketIn) msg,
407 cntx);
408 }
409 break;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800410 case PORT_STATUS:
Pavlin Radoslavov0b88a262014-04-10 15:43:27 -0700411 if (msg instanceof OFPortStatus) {
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700412 return this.handlePortStatus(sw, (OFPortStatus) msg);
Pavlin Radoslavov0b88a262014-04-10 15:43:27 -0700413 }
414 break;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800415 default:
416 break;
417 }
418 return Command.CONTINUE;
419 }
420
Jonathan Hartba354e02014-06-30 19:18:16 -0700421 private Command handleLldp(LLDP lldp, long sw, OFPacketIn pi) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800422 // If LLDP is suppressed on this port, ignore received packet as well
423 IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
424 if (iofSwitch == null) {
425 return Command.STOP;
426 }
427
Ray Milkeyb29e6262014-04-09 16:02:14 -0700428 if (isLinkDiscoverySuppressed(sw, pi.getInPort())) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800429 return Command.STOP;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700430 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800431
432 // If this is a malformed LLDP, or not from us, exit
Ray Milkeyb29e6262014-04-09 16:02:14 -0700433 if (lldp.getPortId() == null || lldp.getPortId().getLength() != 3) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800434 return Command.CONTINUE;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700435 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800436
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800437 // Verify this LLDP packet matches what we're looking for
Jonathan Hart299d1132014-06-27 09:25:28 -0700438 byte[] packetData = pi.getPacketData();
439 if (!OnosLldp.isOnosLldp(packetData)) {
440 log.trace("Dropping LLDP that wasn't sent by ONOS");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800441 return Command.STOP;
442 }
443
Jonathan Hart299d1132014-06-27 09:25:28 -0700444 SwitchPort switchPort = OnosLldp.extractSwitchPort(packetData);
445 long remoteDpid = switchPort.dpid().value();
446 short remotePort = switchPort.port().value();
Jonathan Hart284e70f2014-07-05 12:32:51 -0700447 IOFSwitch remoteSwitch = floodlightProvider.getSwitches().get(
448 switchPort.dpid().value());
Jonathan Hart299d1132014-06-27 09:25:28 -0700449
450
451 OFPhysicalPort physicalPort = null;
452 if (remoteSwitch != null) {
453 physicalPort = remoteSwitch.getPort(remotePort);
454 if (!remoteSwitch.portEnabled(remotePort)) {
455 if (log.isTraceEnabled()) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700456 log.trace("Ignoring link with disabled source port: " +
457 "switch {} port {}", remoteSwitch, remotePort);
Jonathan Hart299d1132014-06-27 09:25:28 -0700458 }
459 return Command.STOP;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800460 }
Jonathan Hart284e70f2014-07-05 12:32:51 -0700461 if (suppressLinkDiscovery.contains(
462 new NodePortTuple(remoteSwitch.getId(), remotePort))) {
Jonathan Hart299d1132014-06-27 09:25:28 -0700463 if (log.isTraceEnabled()) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700464 log.trace("Ignoring link with suppressed src port: " +
465 "switch {} port {}", remoteSwitch, remotePort);
Jonathan Hart299d1132014-06-27 09:25:28 -0700466 }
467 return Command.STOP;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800468 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800469 }
470 if (!iofSwitch.portEnabled(pi.getInPort())) {
471 if (log.isTraceEnabled()) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700472 log.trace("Ignoring link with disabled dest port: " +
473 "switch {} port {}", sw, pi.getInPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800474 }
475 return Command.STOP;
476 }
477
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800478 int srcPortState = (physicalPort != null) ? physicalPort.getState() : 0;
479 physicalPort = iofSwitch.getPort(pi.getInPort());
480 int dstPortState = (physicalPort != null) ? physicalPort.getState() : 0;
481
482 // Store the time of update to this link, and push it out to routingEngine
Jonathan Hart299d1132014-06-27 09:25:28 -0700483 Link lt = new Link(remoteDpid, remotePort, iofSwitch.getId(), pi.getInPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800484
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700485 LinkInfo linkInfo = new LinkInfo(System.currentTimeMillis(),
486 System.currentTimeMillis(), srcPortState, dstPortState);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800487
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700488 addOrUpdateLink(lt, linkInfo);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800489
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800490 // Check if reverse link exists.
491 // If it doesn't exist and if the forward link was seen
492 // first seen within a small interval, send probe on the
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800493 // reverse link.
Jonathan Hart299d1132014-06-27 09:25:28 -0700494 boolean isReverse = OnosLldp.isReverse(lldp);
495
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700496 LinkInfo newLinkInfo = links.get(lt);
Jonathan Hartba354e02014-06-30 19:18:16 -0700497 if (newLinkInfo != null && !isReverse) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800498 Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700499 lt.getSrc(), lt.getSrcPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800500 LinkInfo reverseInfo = links.get(reverseLink);
501 if (reverseInfo == null) {
502 // the reverse link does not exist.
Jonathan Hart284e70f2014-07-05 12:32:51 -0700503 if (newLinkInfo.getFirstSeenTime() >
504 System.currentTimeMillis() - LINK_TIMEOUT) {
Jonathan Hartba354e02014-06-30 19:18:16 -0700505 this.sendDiscoveryMessage(lt.getDst(), lt.getDstPort(), true);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800506 }
507 }
508 }
509
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800510 // Consume this message
511 return Command.STOP;
512 }
513
514 protected Command handlePacketIn(long sw, OFPacketIn pi,
515 FloodlightContext cntx) {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800516 Ethernet eth =
517 IFloodlightProviderService.bcStore.get(cntx,
Ray Milkey269ffb92014-04-03 14:43:30 -0700518 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800519
Jonathan Hartba354e02014-06-30 19:18:16 -0700520 if (eth.getEtherType() == Ethernet.TYPE_LLDP) {
521 return handleLldp((LLDP) eth.getPayload(), sw, pi);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800522 } else if (eth.getEtherType() < 1500) {
523 long destMac = eth.getDestinationMAC().toLong();
Ray Milkey269ffb92014-04-03 14:43:30 -0700524 if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800525 if (log.isTraceEnabled()) {
526 log.trace("Ignoring packet addressed to 802.1D/Q " +
527 "reserved address.");
528 }
529 return Command.STOP;
530 }
531 }
532
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800533 return Command.CONTINUE;
534 }
535
Jonathan Hart284e70f2014-07-05 12:32:51 -0700536 protected void addOrUpdateLink(Link lt, LinkInfo detectedLinkInfo) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800537 lock.writeLock().lock();
538 try {
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700539 LinkInfo existingInfo = links.get(lt);
540
541 LinkInfo newLinkInfo = new LinkInfo(
542 ((existingInfo == null) ? detectedLinkInfo.getFirstSeenTime()
543 : existingInfo.getFirstSeenTime()),
544 detectedLinkInfo.getLastProbeReceivedTime(),
545 detectedLinkInfo.getSrcPortState(),
546 detectedLinkInfo.getDstPortState());
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700547
Jonathan Hart284e70f2014-07-05 12:32:51 -0700548 // Add new LinkInfo or update old LinkInfo
549 links.put(lt, newLinkInfo);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800550
551 if (log.isTraceEnabled()) {
Jonathan Hartba354e02014-06-30 19:18:16 -0700552 log.trace("addOrUpdateLink: {}", lt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800553 }
554
Jonathan Hart284e70f2014-07-05 12:32:51 -0700555 NodePortTuple srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
556 NodePortTuple dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800557
Jonathan Hart284e70f2014-07-05 12:32:51 -0700558 // If this is the first time we've seen the link, add the Link
559 // object to the data structures/indexes as well
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700560 if (existingInfo == null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800561 // index it by switch source
Ray Milkeyb29e6262014-04-09 16:02:14 -0700562 if (!switchLinks.containsKey(lt.getSrc())) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800563 switchLinks.put(lt.getSrc(), new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700564 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800565 switchLinks.get(lt.getSrc()).add(lt);
566
567 // index it by switch dest
Ray Milkeyb29e6262014-04-09 16:02:14 -0700568 if (!switchLinks.containsKey(lt.getDst())) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800569 switchLinks.put(lt.getDst(), new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700570 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800571 switchLinks.get(lt.getDst()).add(lt);
572
573 // index both ends by switch:port
Ray Milkeyb29e6262014-04-09 16:02:14 -0700574 if (!portLinks.containsKey(srcNpt)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800575 portLinks.put(srcNpt, new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700576 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800577 portLinks.get(srcNpt).add(lt);
578
Ray Milkeyb29e6262014-04-09 16:02:14 -0700579 if (!portLinks.containsKey(dstNpt)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800580 portLinks.put(dstNpt, new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700581 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800582 portLinks.get(dstNpt).add(lt);
583
Jonathan Hart284e70f2014-07-05 12:32:51 -0700584 // Publish LINK_ADDED event
585 controller.publishUpdate(new LinkUpdate(lt, UpdateType.LINK_ADDED));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800586 }
587 } finally {
588 lock.writeLock().unlock();
589 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800590 }
591
592 /**
Jonathan Hart284e70f2014-07-05 12:32:51 -0700593 * Removes links from the data structures.
Ray Milkey269ffb92014-04-03 14:43:30 -0700594 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700595 * @param linksToDelete the list of links to delete
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800596 */
Jonathan Hart284e70f2014-07-05 12:32:51 -0700597 protected void deleteLinks(List<Link> linksToDelete) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800598 lock.writeLock().lock();
599 try {
Ray Milkey5df613b2014-04-15 10:50:56 -0700600 for (Link lt : linksToDelete) {
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700601 NodePortTuple srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
602 NodePortTuple dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800603
604 switchLinks.get(lt.getSrc()).remove(lt);
605 switchLinks.get(lt.getDst()).remove(lt);
606 if (switchLinks.containsKey(lt.getSrc()) &&
Ray Milkeyb29e6262014-04-09 16:02:14 -0700607 switchLinks.get(lt.getSrc()).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800608 this.switchLinks.remove(lt.getSrc());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700609 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800610 if (this.switchLinks.containsKey(lt.getDst()) &&
Ray Milkeyb29e6262014-04-09 16:02:14 -0700611 this.switchLinks.get(lt.getDst()).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800612 this.switchLinks.remove(lt.getDst());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700613 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800614
615 if (this.portLinks.get(srcNpt) != null) {
616 this.portLinks.get(srcNpt).remove(lt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700617 if (this.portLinks.get(srcNpt).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800618 this.portLinks.remove(srcNpt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700619 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800620 }
621 if (this.portLinks.get(dstNpt) != null) {
622 this.portLinks.get(dstNpt).remove(lt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700623 if (this.portLinks.get(dstNpt).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800624 this.portLinks.remove(dstNpt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700625 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800626 }
627
Jonathan Hart284e70f2014-07-05 12:32:51 -0700628 this.links.remove(lt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800629
Jonathan Hart284e70f2014-07-05 12:32:51 -0700630 controller.publishUpdate(new LinkUpdate(lt,
631 UpdateType.LINK_REMOVED));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800632
633 if (log.isTraceEnabled()) {
634 log.trace("Deleted link {}", lt);
635 }
636 }
637 } finally {
638 lock.writeLock().unlock();
639 }
640 }
641
642 /**
643 * Handles an OFPortStatus message from a switch. We will add or
644 * delete LinkTupes as well re-compute the topology if needed.
Ray Milkey269ffb92014-04-03 14:43:30 -0700645 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700646 * @param sw The dpid of the switch that sent the port status message
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800647 * @param ps The OFPortStatus message
648 * @return The Command to continue or stop after we process this message
649 */
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700650 protected Command handlePortStatus(IOFSwitch sw, OFPortStatus ps) {
HIGUCHI Yutaa89b2842013-06-17 13:54:57 -0700651
Jonathan Hart284e70f2014-07-05 12:32:51 -0700652 // If we do not control this switch, then we should not process its
653 // port status messages
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700654 if (!registryService.hasControl(sw.getId())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700655 return Command.CONTINUE;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700656 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800657
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800658 if (log.isTraceEnabled()) {
659 log.trace("handlePortStatus: Switch {} port #{} reason {}; " +
660 "config is {} state is {}",
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700661 new Object[]{sw.getStringId(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700662 ps.getDesc().getPortNumber(),
663 ps.getReason(),
664 ps.getDesc().getConfig(),
665 ps.getDesc().getState()});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800666 }
667
668 short port = ps.getDesc().getPortNumber();
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700669 NodePortTuple npt = new NodePortTuple(sw.getId(), port);
Ray Milkey269ffb92014-04-03 14:43:30 -0700670 boolean linkDeleted = false;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800671 boolean linkInfoChanged = false;
672
673 lock.writeLock().lock();
674 try {
675 // if ps is a delete, or a modify where the port is down or
676 // configured down
Ray Milkey269ffb92014-04-03 14:43:30 -0700677 if ((byte) OFPortReason.OFPPR_DELETE.ordinal() == ps.getReason() ||
678 ((byte) OFPortReason.OFPPR_MODIFY.ordinal() ==
679 ps.getReason() && !portEnabled(ps.getDesc()))) {
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700680
Jonathan Hart284e70f2014-07-05 12:32:51 -0700681 deleteLinksOnPort(npt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800682 linkDeleted = true;
Ray Milkey269ffb92014-04-03 14:43:30 -0700683 } else if (ps.getReason() ==
684 (byte) OFPortReason.OFPPR_MODIFY.ordinal()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800685 // If ps is a port modification and the port state has changed
686 // that affects links in the topology
687
688 if (this.portLinks.containsKey(npt)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700689 for (Link lt : this.portLinks.get(npt)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800690 LinkInfo linkInfo = links.get(lt);
Ray Milkey269ffb92014-04-03 14:43:30 -0700691 assert (linkInfo != null);
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700692 LinkInfo newLinkInfo = null;
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800693
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700694 if (lt.isSrcPort(npt) &&
695 linkInfo.getSrcPortState() != ps.getDesc().getState()) {
696 // If this port status is for the src port and the port
697 // state has changed, create a new link info with the new state
698
699 newLinkInfo = new LinkInfo(linkInfo.getFirstSeenTime(),
700 linkInfo.getLastProbeReceivedTime(),
701 ps.getDesc().getState(),
702 linkInfo.getDstPortState());
703 } else if (lt.isDstPort(npt) &&
704 linkInfo.getDstPortState() != ps.getDesc().getState()) {
705 // If this port status is for the dst port and the port
706 // state has changed, create a new link info with the new state
707
708 newLinkInfo = new LinkInfo(linkInfo.getFirstSeenTime(),
709 linkInfo.getLastProbeReceivedTime(),
710 linkInfo.getSrcPortState(),
711 ps.getDesc().getState());
712 }
713
714 if (newLinkInfo != null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800715 linkInfoChanged = true;
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700716 links.put(lt, newLinkInfo);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800717 }
718 }
719 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800720 }
721
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700722
Ray Milkey269ffb92014-04-03 14:43:30 -0700723 if (!linkDeleted && !linkInfoChanged) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800724 if (log.isTraceEnabled()) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700725 log.trace("handlePortStatus: Switch {} port #{} reason {};" +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800726 " no links to update/remove",
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700727 new Object[]{HexString.toHexString(sw.getId()),
Ray Milkey269ffb92014-04-03 14:43:30 -0700728 ps.getDesc().getPortNumber(),
729 ps.getReason()});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800730 }
731 }
732 } finally {
733 lock.writeLock().unlock();
734 }
735
736 if (!linkDeleted) {
737 // Send LLDP right away when port state is changed for faster
738 // cluster-merge. If it is a link delete then there is not need
739 // to send the LLDPs right away and instead we wait for the LLDPs
740 // to be sent on the timer as it is normally done
741 // do it outside the write-lock
742 // sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS);
743 processNewPort(npt.getNodeId(), npt.getPortId());
744 }
745 return Command.CONTINUE;
746 }
747
748 /**
749 * Process a new port.
750 * If link discovery is disabled on the port, then do nothing.
Jonathan Hart284e70f2014-07-05 12:32:51 -0700751 * Otherwise, send LLDP message.
Ray Milkey269ffb92014-04-03 14:43:30 -0700752 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700753 * @param sw the dpid of the switch the port is on
754 * @param p the number of the port
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800755 */
756 private void processNewPort(long sw, short p) {
757 if (isLinkDiscoverySuppressed(sw, p)) {
758 // Do nothing as link discovery is suppressed.
Ray Milkey1aa71f82014-04-08 16:23:24 -0700759 return;
Ray Milkey269ffb92014-04-03 14:43:30 -0700760 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800761 discover(sw, p);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800762 }
763 }
764
765 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700766 * We send out LLDP messages when a switch is added to discover the topology.
Ray Milkey269ffb92014-04-03 14:43:30 -0700767 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800768 * @param sw The IOFSwitch that connected to the controller
769 */
770 @Override
771 public void addedSwitch(IOFSwitch sw) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800772 if (sw.getEnabledPorts() != null) {
773 for (Short p : sw.getEnabledPortNumbers()) {
774 processNewPort(sw.getId(), p);
775 }
776 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800777 }
778
779 /**
780 * When a switch disconnects we remove any links from our map and notify.
Jonathan Hart284e70f2014-07-05 12:32:51 -0700781 *
782 * @param iofSwitch the switch that was removed
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800783 */
784 @Override
785 public void removedSwitch(IOFSwitch iofSwitch) {
786 // Update event history
787 long sw = iofSwitch.getId();
Jonathan Hart6d208022014-06-24 18:56:36 -0700788
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800789 List<Link> eraseList = new ArrayList<Link>();
790 lock.writeLock().lock();
791 try {
792 if (switchLinks.containsKey(sw)) {
793 if (log.isTraceEnabled()) {
794 log.trace("Handle switchRemoved. Switch {}; removing links {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700795 HexString.toHexString(sw), switchLinks.get(sw));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800796 }
797 // add all tuples with an endpoint on this switch to erase list
798 eraseList.addAll(switchLinks.get(sw));
Jonathan Hart284e70f2014-07-05 12:32:51 -0700799 deleteLinks(eraseList);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800800 }
801 } finally {
802 lock.writeLock().unlock();
803 }
804 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800805
Jonathan Hart284e70f2014-07-05 12:32:51 -0700806 /*
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800807 * We don't react the port changed notifications here. we listen for
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800808 * OFPortStatus messages directly. Might consider using this notifier
809 * instead
810 */
811 @Override
812 public void switchPortChanged(Long switchId) {
813 // no-op
814 }
815
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800816 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800817 * Delete links incident on a given switch port.
Ray Milkey269ffb92014-04-03 14:43:30 -0700818 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700819 * @param npt the port to delete links on
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800820 */
Jonathan Hart284e70f2014-07-05 12:32:51 -0700821 protected void deleteLinksOnPort(NodePortTuple npt) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800822 List<Link> eraseList = new ArrayList<Link>();
823 if (this.portLinks.containsKey(npt)) {
824 if (log.isTraceEnabled()) {
825 log.trace("handlePortStatus: Switch {} port #{} " +
826 "removing links {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700827 new Object[]{HexString.toHexString(npt.getNodeId()),
828 npt.getPortId(),
829 this.portLinks.get(npt)});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800830 }
831 eraseList.addAll(this.portLinks.get(npt));
Jonathan Hart284e70f2014-07-05 12:32:51 -0700832 deleteLinks(eraseList);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800833 }
834 }
835
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800836 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800837 * Iterates through the list of links and deletes if the
838 * last discovery message reception time exceeds timeout values.
839 */
Jonathan Hart284e70f2014-07-05 12:32:51 -0700840 protected void timeOutLinks() {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800841 List<Link> eraseList = new ArrayList<Link>();
842 Long curTime = System.currentTimeMillis();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800843
844 // reentrant required here because deleteLink also write locks
845 lock.writeLock().lock();
846 try {
847 Iterator<Entry<Link, LinkInfo>> it =
848 this.links.entrySet().iterator();
849 while (it.hasNext()) {
850 Entry<Link, LinkInfo> entry = it.next();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800851 LinkInfo info = entry.getValue();
852
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700853 if ((info.getLastProbeReceivedTime() + (1000L * LINK_TIMEOUT)
854 < curTime)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800855 eraseList.add(entry.getKey());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800856 }
857 }
858
Jonathan Hart284e70f2014-07-05 12:32:51 -0700859 deleteLinks(eraseList);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800860 } finally {
861 lock.writeLock().unlock();
862 }
863 }
864
865 private boolean portEnabled(OFPhysicalPort port) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700866 if (port == null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800867 return false;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700868 }
869 if ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800870 return false;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700871 }
872 if ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800873 return false;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700874 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800875 return true;
876 }
877
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800878 @Override
879 public Map<Link, LinkInfo> getLinks() {
880 lock.readLock().lock();
881 Map<Link, LinkInfo> result;
882 try {
883 result = new HashMap<Link, LinkInfo>(links);
884 } finally {
885 lock.readLock().unlock();
886 }
887 return result;
888 }
889
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800890 @Override
891 public void addListener(ILinkDiscoveryListener listener) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700892 linkDiscoveryListeners.add(listener);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800893 }
894
Jonathan Hart284e70f2014-07-05 12:32:51 -0700895 @Override
896 public void removeListener(ILinkDiscoveryListener listener) {
897 linkDiscoveryListeners.remove(listener);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800898 }
899
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800900 @Override
901 public boolean isCallbackOrderingPrereq(OFType type, String name) {
902 return false;
903 }
904
905 @Override
906 public boolean isCallbackOrderingPostreq(OFType type, String name) {
907 return false;
908 }
909
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800910 // IFloodlightModule classes
911
912 @Override
913 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800914 Collection<Class<? extends IFloodlightService>> l =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800915 new ArrayList<Class<? extends IFloodlightService>>();
916 l.add(ILinkDiscoveryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800917 return l;
918 }
919
920 @Override
921 public Map<Class<? extends IFloodlightService>, IFloodlightService>
Jonathan Hart284e70f2014-07-05 12:32:51 -0700922 getServiceImpls() {
923 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
924 new HashMap<>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800925 m.put(ILinkDiscoveryService.class, this);
926 return m;
927 }
928
929 @Override
930 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700931 Collection<Class<? extends IFloodlightService>> l = new ArrayList<>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800932 l.add(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800933 l.add(IThreadPoolService.class);
934 l.add(IRestApiService.class);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700935 l.add(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800936 return l;
937 }
938
939 @Override
940 public void init(FloodlightModuleContext context)
941 throws FloodlightModuleException {
942 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800943 threadPool = context.getServiceImpl(IThreadPoolService.class);
944 restApi = context.getServiceImpl(IRestApiService.class);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700945 registryService = context.getServiceImpl(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800946
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800947 this.lock = new ReentrantReadWriteLock();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800948 this.links = new HashMap<Link, LinkInfo>();
949 this.portLinks = new HashMap<NodePortTuple, Set<Link>>();
950 this.suppressLinkDiscovery =
951 Collections.synchronizedSet(new HashSet<NodePortTuple>());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800952 this.switchLinks = new HashMap<Long, Set<Link>>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800953 }
954
955 @Override
956 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700957 @LogMessageDoc(level = "ERROR",
958 message = "No storage source found.",
959 explanation = "Storage source was not initialized; cannot initialize " +
960 "link discovery.",
961 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG),
962 @LogMessageDoc(level = "ERROR",
963 message = "Error in installing listener for " +
964 "switch config table {table}",
965 explanation = "Failed to install storage notification for the " +
966 "switch config table",
967 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG),
968 @LogMessageDoc(level = "ERROR",
969 message = "No storage source found.",
970 explanation = "Storage source was not initialized; cannot initialize " +
971 "link discovery.",
972 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG),
973 @LogMessageDoc(level = "ERROR",
974 message = "Exception in LLDP send timer.",
975 explanation = "An unknown error occured while sending LLDP " +
976 "messages to switches.",
977 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800978 })
979 public void startUp(FloodlightModuleContext context) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800980 ScheduledExecutorService ses = threadPool.getScheduledExecutor();
Jonathan Hart284e70f2014-07-05 12:32:51 -0700981 controller = context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800982
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800983 discoveryTask = new SingletonTask(ses, new Runnable() {
984 @Override
985 public void run() {
986 try {
987 discoverLinks();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800988 } catch (Exception e) {
989 log.error("Exception in LLDP send timer.", e);
990 } finally {
Jonathan Hartba354e02014-06-30 19:18:16 -0700991 log.trace("Rescheduling discovery task");
992 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL,
993 TimeUnit.SECONDS);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800994 }
995 }
996 });
997
Jonathan Hartec4f14e2013-12-12 10:46:38 -0800998 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL, TimeUnit.SECONDS);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800999
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001000 // Register for the OpenFlow messages we want to receive
1001 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
1002 floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this);
1003 // Register for switch updates
1004 floodlightProvider.addOFSwitchListener(this);
Jonathan Hartba354e02014-06-30 19:18:16 -07001005 restApi.addRestletRoutable(new LinkDiscoveryWebRoutable());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001006 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001007}