blob: a924f00cfaf954e0b7aa0a1ad1fba8aeb67f2d56 [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
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;
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 Hart0f383542014-07-09 11:38:03 -0700421 protected 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();
Yuta HIGUCHI9da3a6e2014-06-10 22:11:58 -0700446 short remotePort = switchPort.port().shortValue();
447 IOFSwitch remoteSwitch = floodlightProvider.getSwitches().get(switchPort.dpid().value());
Jonathan Hart299d1132014-06-27 09:25:28 -0700448
449
450 OFPhysicalPort physicalPort = null;
451 if (remoteSwitch != null) {
452 physicalPort = remoteSwitch.getPort(remotePort);
453 if (!remoteSwitch.portEnabled(remotePort)) {
454 if (log.isTraceEnabled()) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700455 log.trace("Ignoring link with disabled source port: " +
456 "switch {} port {}", remoteSwitch, remotePort);
Jonathan Hart299d1132014-06-27 09:25:28 -0700457 }
458 return Command.STOP;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800459 }
Jonathan Hart284e70f2014-07-05 12:32:51 -0700460 if (suppressLinkDiscovery.contains(
461 new NodePortTuple(remoteSwitch.getId(), remotePort))) {
Jonathan Hart299d1132014-06-27 09:25:28 -0700462 if (log.isTraceEnabled()) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700463 log.trace("Ignoring link with suppressed src port: " +
464 "switch {} port {}", remoteSwitch, remotePort);
Jonathan Hart299d1132014-06-27 09:25:28 -0700465 }
466 return Command.STOP;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800467 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800468 }
469 if (!iofSwitch.portEnabled(pi.getInPort())) {
470 if (log.isTraceEnabled()) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700471 log.trace("Ignoring link with disabled dest port: " +
472 "switch {} port {}", sw, pi.getInPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800473 }
474 return Command.STOP;
475 }
476
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800477 int srcPortState = (physicalPort != null) ? physicalPort.getState() : 0;
478 physicalPort = iofSwitch.getPort(pi.getInPort());
479 int dstPortState = (physicalPort != null) ? physicalPort.getState() : 0;
480
481 // Store the time of update to this link, and push it out to routingEngine
Jonathan Hart299d1132014-06-27 09:25:28 -0700482 Link lt = new Link(remoteDpid, remotePort, iofSwitch.getId(), pi.getInPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800483
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700484 LinkInfo linkInfo = new LinkInfo(System.currentTimeMillis(),
485 System.currentTimeMillis(), srcPortState, dstPortState);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800486
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700487 addOrUpdateLink(lt, linkInfo);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800488
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800489 // Check if reverse link exists.
490 // If it doesn't exist and if the forward link was seen
491 // first seen within a small interval, send probe on the
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800492 // reverse link.
Jonathan Hart299d1132014-06-27 09:25:28 -0700493 boolean isReverse = OnosLldp.isReverse(lldp);
494
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700495 LinkInfo newLinkInfo = links.get(lt);
Jonathan Hartba354e02014-06-30 19:18:16 -0700496 if (newLinkInfo != null && !isReverse) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800497 Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700498 lt.getSrc(), lt.getSrcPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800499 LinkInfo reverseInfo = links.get(reverseLink);
500 if (reverseInfo == null) {
501 // the reverse link does not exist.
Jonathan Hart284e70f2014-07-05 12:32:51 -0700502 if (newLinkInfo.getFirstSeenTime() >
503 System.currentTimeMillis() - LINK_TIMEOUT) {
Jonathan Hartba354e02014-06-30 19:18:16 -0700504 this.sendDiscoveryMessage(lt.getDst(), lt.getDstPort(), true);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800505 }
506 }
507 }
508
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800509 // Consume this message
510 return Command.STOP;
511 }
512
513 protected Command handlePacketIn(long sw, OFPacketIn pi,
514 FloodlightContext cntx) {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800515 Ethernet eth =
516 IFloodlightProviderService.bcStore.get(cntx,
Ray Milkey269ffb92014-04-03 14:43:30 -0700517 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800518
Jonathan Hartba354e02014-06-30 19:18:16 -0700519 if (eth.getEtherType() == Ethernet.TYPE_LLDP) {
520 return handleLldp((LLDP) eth.getPayload(), sw, pi);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800521 } else if (eth.getEtherType() < 1500) {
522 long destMac = eth.getDestinationMAC().toLong();
Ray Milkey269ffb92014-04-03 14:43:30 -0700523 if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800524 if (log.isTraceEnabled()) {
525 log.trace("Ignoring packet addressed to 802.1D/Q " +
526 "reserved address.");
527 }
528 return Command.STOP;
529 }
530 }
531
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800532 return Command.CONTINUE;
533 }
534
Jonathan Hart284e70f2014-07-05 12:32:51 -0700535 protected void addOrUpdateLink(Link lt, LinkInfo detectedLinkInfo) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800536 lock.writeLock().lock();
537 try {
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700538 LinkInfo existingInfo = links.get(lt);
539
540 LinkInfo newLinkInfo = new LinkInfo(
541 ((existingInfo == null) ? detectedLinkInfo.getFirstSeenTime()
542 : existingInfo.getFirstSeenTime()),
543 detectedLinkInfo.getLastProbeReceivedTime(),
544 detectedLinkInfo.getSrcPortState(),
545 detectedLinkInfo.getDstPortState());
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700546
Jonathan Hart284e70f2014-07-05 12:32:51 -0700547 // Add new LinkInfo or update old LinkInfo
548 links.put(lt, newLinkInfo);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800549
550 if (log.isTraceEnabled()) {
Jonathan Hartba354e02014-06-30 19:18:16 -0700551 log.trace("addOrUpdateLink: {}", lt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800552 }
553
Jonathan Hart284e70f2014-07-05 12:32:51 -0700554 NodePortTuple srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
555 NodePortTuple dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800556
Jonathan Hart284e70f2014-07-05 12:32:51 -0700557 // If this is the first time we've seen the link, add the Link
558 // object to the data structures/indexes as well
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700559 if (existingInfo == null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800560 // index it by switch source
Ray Milkeyb29e6262014-04-09 16:02:14 -0700561 if (!switchLinks.containsKey(lt.getSrc())) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800562 switchLinks.put(lt.getSrc(), new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700563 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800564 switchLinks.get(lt.getSrc()).add(lt);
565
566 // index it by switch dest
Ray Milkeyb29e6262014-04-09 16:02:14 -0700567 if (!switchLinks.containsKey(lt.getDst())) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800568 switchLinks.put(lt.getDst(), new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700569 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800570 switchLinks.get(lt.getDst()).add(lt);
571
572 // index both ends by switch:port
Ray Milkeyb29e6262014-04-09 16:02:14 -0700573 if (!portLinks.containsKey(srcNpt)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800574 portLinks.put(srcNpt, new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700575 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800576 portLinks.get(srcNpt).add(lt);
577
Ray Milkeyb29e6262014-04-09 16:02:14 -0700578 if (!portLinks.containsKey(dstNpt)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800579 portLinks.put(dstNpt, new HashSet<Link>());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700580 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800581 portLinks.get(dstNpt).add(lt);
582
Jonathan Hart284e70f2014-07-05 12:32:51 -0700583 // Publish LINK_ADDED event
584 controller.publishUpdate(new LinkUpdate(lt, UpdateType.LINK_ADDED));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800585 }
586 } finally {
587 lock.writeLock().unlock();
588 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800589 }
590
591 /**
Jonathan Hart284e70f2014-07-05 12:32:51 -0700592 * Removes links from the data structures.
Ray Milkey269ffb92014-04-03 14:43:30 -0700593 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700594 * @param linksToDelete the list of links to delete
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800595 */
Jonathan Hart284e70f2014-07-05 12:32:51 -0700596 protected void deleteLinks(List<Link> linksToDelete) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800597 lock.writeLock().lock();
598 try {
Ray Milkey5df613b2014-04-15 10:50:56 -0700599 for (Link lt : linksToDelete) {
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700600 NodePortTuple srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
601 NodePortTuple dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800602
603 switchLinks.get(lt.getSrc()).remove(lt);
604 switchLinks.get(lt.getDst()).remove(lt);
605 if (switchLinks.containsKey(lt.getSrc()) &&
Ray Milkeyb29e6262014-04-09 16:02:14 -0700606 switchLinks.get(lt.getSrc()).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800607 this.switchLinks.remove(lt.getSrc());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700608 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800609 if (this.switchLinks.containsKey(lt.getDst()) &&
Ray Milkeyb29e6262014-04-09 16:02:14 -0700610 this.switchLinks.get(lt.getDst()).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800611 this.switchLinks.remove(lt.getDst());
Ray Milkeyb29e6262014-04-09 16:02:14 -0700612 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800613
614 if (this.portLinks.get(srcNpt) != null) {
615 this.portLinks.get(srcNpt).remove(lt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700616 if (this.portLinks.get(srcNpt).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800617 this.portLinks.remove(srcNpt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700618 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800619 }
620 if (this.portLinks.get(dstNpt) != null) {
621 this.portLinks.get(dstNpt).remove(lt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700622 if (this.portLinks.get(dstNpt).isEmpty()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800623 this.portLinks.remove(dstNpt);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700624 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800625 }
626
Jonathan Hart284e70f2014-07-05 12:32:51 -0700627 this.links.remove(lt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800628
Jonathan Hart284e70f2014-07-05 12:32:51 -0700629 controller.publishUpdate(new LinkUpdate(lt,
630 UpdateType.LINK_REMOVED));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800631
632 if (log.isTraceEnabled()) {
633 log.trace("Deleted link {}", lt);
634 }
635 }
636 } finally {
637 lock.writeLock().unlock();
638 }
639 }
640
641 /**
642 * Handles an OFPortStatus message from a switch. We will add or
643 * delete LinkTupes as well re-compute the topology if needed.
Ray Milkey269ffb92014-04-03 14:43:30 -0700644 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700645 * @param sw The dpid of the switch that sent the port status message
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800646 * @param ps The OFPortStatus message
647 * @return The Command to continue or stop after we process this message
648 */
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700649 protected Command handlePortStatus(IOFSwitch sw, OFPortStatus ps) {
HIGUCHI Yutaa89b2842013-06-17 13:54:57 -0700650
Jonathan Hart284e70f2014-07-05 12:32:51 -0700651 // If we do not control this switch, then we should not process its
652 // port status messages
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700653 if (!registryService.hasControl(sw.getId())) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700654 return Command.CONTINUE;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700655 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800656
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800657 if (log.isTraceEnabled()) {
658 log.trace("handlePortStatus: Switch {} port #{} reason {}; " +
659 "config is {} state is {}",
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700660 new Object[]{sw.getStringId(),
Ray Milkey269ffb92014-04-03 14:43:30 -0700661 ps.getDesc().getPortNumber(),
662 ps.getReason(),
663 ps.getDesc().getConfig(),
664 ps.getDesc().getState()});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800665 }
666
667 short port = ps.getDesc().getPortNumber();
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700668 NodePortTuple npt = new NodePortTuple(sw.getId(), port);
Ray Milkey269ffb92014-04-03 14:43:30 -0700669 boolean linkDeleted = false;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800670 boolean linkInfoChanged = false;
671
672 lock.writeLock().lock();
673 try {
674 // if ps is a delete, or a modify where the port is down or
675 // configured down
Ray Milkey269ffb92014-04-03 14:43:30 -0700676 if ((byte) OFPortReason.OFPPR_DELETE.ordinal() == ps.getReason() ||
677 ((byte) OFPortReason.OFPPR_MODIFY.ordinal() ==
678 ps.getReason() && !portEnabled(ps.getDesc()))) {
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700679
Jonathan Hart284e70f2014-07-05 12:32:51 -0700680 deleteLinksOnPort(npt);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800681 linkDeleted = true;
Ray Milkey269ffb92014-04-03 14:43:30 -0700682 } else if (ps.getReason() ==
683 (byte) OFPortReason.OFPPR_MODIFY.ordinal()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800684 // If ps is a port modification and the port state has changed
685 // that affects links in the topology
686
687 if (this.portLinks.containsKey(npt)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700688 for (Link lt : this.portLinks.get(npt)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800689 LinkInfo linkInfo = links.get(lt);
Ray Milkey269ffb92014-04-03 14:43:30 -0700690 assert (linkInfo != null);
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700691 LinkInfo newLinkInfo = null;
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800692
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700693 if (lt.isSrcPort(npt) &&
694 linkInfo.getSrcPortState() != ps.getDesc().getState()) {
695 // If this port status is for the src port and the port
696 // state has changed, create a new link info with the new state
697
698 newLinkInfo = new LinkInfo(linkInfo.getFirstSeenTime(),
699 linkInfo.getLastProbeReceivedTime(),
700 ps.getDesc().getState(),
701 linkInfo.getDstPortState());
702 } else if (lt.isDstPort(npt) &&
703 linkInfo.getDstPortState() != ps.getDesc().getState()) {
704 // If this port status is for the dst port and the port
705 // state has changed, create a new link info with the new state
706
707 newLinkInfo = new LinkInfo(linkInfo.getFirstSeenTime(),
708 linkInfo.getLastProbeReceivedTime(),
709 linkInfo.getSrcPortState(),
710 ps.getDesc().getState());
711 }
712
713 if (newLinkInfo != null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800714 linkInfoChanged = true;
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700715 links.put(lt, newLinkInfo);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800716 }
717 }
718 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800719 }
720
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700721
Ray Milkey269ffb92014-04-03 14:43:30 -0700722 if (!linkDeleted && !linkInfoChanged) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800723 if (log.isTraceEnabled()) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700724 log.trace("handlePortStatus: Switch {} port #{} reason {};" +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800725 " no links to update/remove",
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700726 new Object[]{HexString.toHexString(sw.getId()),
Ray Milkey269ffb92014-04-03 14:43:30 -0700727 ps.getDesc().getPortNumber(),
728 ps.getReason()});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800729 }
730 }
731 } finally {
732 lock.writeLock().unlock();
733 }
734
735 if (!linkDeleted) {
736 // Send LLDP right away when port state is changed for faster
737 // cluster-merge. If it is a link delete then there is not need
738 // to send the LLDPs right away and instead we wait for the LLDPs
739 // to be sent on the timer as it is normally done
740 // do it outside the write-lock
741 // sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS);
742 processNewPort(npt.getNodeId(), npt.getPortId());
743 }
744 return Command.CONTINUE;
745 }
746
747 /**
748 * Process a new port.
749 * If link discovery is disabled on the port, then do nothing.
Jonathan Hart284e70f2014-07-05 12:32:51 -0700750 * Otherwise, send LLDP message.
Ray Milkey269ffb92014-04-03 14:43:30 -0700751 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700752 * @param sw the dpid of the switch the port is on
753 * @param p the number of the port
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800754 */
755 private void processNewPort(long sw, short p) {
756 if (isLinkDiscoverySuppressed(sw, p)) {
757 // Do nothing as link discovery is suppressed.
Ray Milkey1aa71f82014-04-08 16:23:24 -0700758 return;
Ray Milkey269ffb92014-04-03 14:43:30 -0700759 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800760 discover(sw, p);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800761 }
762 }
763
764 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700765 * We send out LLDP messages when a switch is added to discover the topology.
Ray Milkey269ffb92014-04-03 14:43:30 -0700766 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800767 * @param sw The IOFSwitch that connected to the controller
768 */
769 @Override
770 public void addedSwitch(IOFSwitch sw) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800771 if (sw.getEnabledPorts() != null) {
772 for (Short p : sw.getEnabledPortNumbers()) {
773 processNewPort(sw.getId(), p);
774 }
775 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800776 }
777
778 /**
779 * When a switch disconnects we remove any links from our map and notify.
Jonathan Hart284e70f2014-07-05 12:32:51 -0700780 *
781 * @param iofSwitch the switch that was removed
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800782 */
783 @Override
784 public void removedSwitch(IOFSwitch iofSwitch) {
785 // Update event history
786 long sw = iofSwitch.getId();
Jonathan Hart6d208022014-06-24 18:56:36 -0700787
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800788 List<Link> eraseList = new ArrayList<Link>();
789 lock.writeLock().lock();
790 try {
791 if (switchLinks.containsKey(sw)) {
792 if (log.isTraceEnabled()) {
793 log.trace("Handle switchRemoved. Switch {}; removing links {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700794 HexString.toHexString(sw), switchLinks.get(sw));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800795 }
796 // add all tuples with an endpoint on this switch to erase list
797 eraseList.addAll(switchLinks.get(sw));
Jonathan Hart284e70f2014-07-05 12:32:51 -0700798 deleteLinks(eraseList);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800799 }
800 } finally {
801 lock.writeLock().unlock();
802 }
803 }
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800804
Jonathan Hart284e70f2014-07-05 12:32:51 -0700805 /*
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800806 * We don't react the port changed notifications here. we listen for
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800807 * OFPortStatus messages directly. Might consider using this notifier
808 * instead
809 */
810 @Override
811 public void switchPortChanged(Long switchId) {
812 // no-op
813 }
814
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800815 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800816 * Delete links incident on a given switch port.
Ray Milkey269ffb92014-04-03 14:43:30 -0700817 *
Jonathan Hart284e70f2014-07-05 12:32:51 -0700818 * @param npt the port to delete links on
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800819 */
Jonathan Hart284e70f2014-07-05 12:32:51 -0700820 protected void deleteLinksOnPort(NodePortTuple npt) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800821 List<Link> eraseList = new ArrayList<Link>();
822 if (this.portLinks.containsKey(npt)) {
823 if (log.isTraceEnabled()) {
824 log.trace("handlePortStatus: Switch {} port #{} " +
825 "removing links {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700826 new Object[]{HexString.toHexString(npt.getNodeId()),
827 npt.getPortId(),
828 this.portLinks.get(npt)});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800829 }
830 eraseList.addAll(this.portLinks.get(npt));
Jonathan Hart284e70f2014-07-05 12:32:51 -0700831 deleteLinks(eraseList);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800832 }
833 }
834
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800835 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800836 * Iterates through the list of links and deletes if the
837 * last discovery message reception time exceeds timeout values.
838 */
Jonathan Hart284e70f2014-07-05 12:32:51 -0700839 protected void timeOutLinks() {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800840 List<Link> eraseList = new ArrayList<Link>();
841 Long curTime = System.currentTimeMillis();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800842
843 // reentrant required here because deleteLink also write locks
844 lock.writeLock().lock();
845 try {
846 Iterator<Entry<Link, LinkInfo>> it =
847 this.links.entrySet().iterator();
848 while (it.hasNext()) {
849 Entry<Link, LinkInfo> entry = it.next();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800850 LinkInfo info = entry.getValue();
851
Jonathan Hartcd1ab172014-07-03 14:59:48 -0700852 if ((info.getLastProbeReceivedTime() + (1000L * LINK_TIMEOUT)
853 < curTime)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800854 eraseList.add(entry.getKey());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800855 }
856 }
857
Jonathan Hart284e70f2014-07-05 12:32:51 -0700858 deleteLinks(eraseList);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800859 } finally {
860 lock.writeLock().unlock();
861 }
862 }
863
864 private boolean portEnabled(OFPhysicalPort port) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700865 if (port == null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800866 return false;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700867 }
868 if ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800869 return false;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700870 }
871 if ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800872 return false;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700873 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800874 return true;
875 }
876
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800877 @Override
878 public Map<Link, LinkInfo> getLinks() {
879 lock.readLock().lock();
880 Map<Link, LinkInfo> result;
881 try {
882 result = new HashMap<Link, LinkInfo>(links);
883 } finally {
884 lock.readLock().unlock();
885 }
886 return result;
887 }
888
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800889 @Override
890 public void addListener(ILinkDiscoveryListener listener) {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700891 linkDiscoveryListeners.add(listener);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800892 }
893
Jonathan Hart284e70f2014-07-05 12:32:51 -0700894 @Override
895 public void removeListener(ILinkDiscoveryListener listener) {
896 linkDiscoveryListeners.remove(listener);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800897 }
898
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800899 @Override
900 public boolean isCallbackOrderingPrereq(OFType type, String name) {
901 return false;
902 }
903
904 @Override
905 public boolean isCallbackOrderingPostreq(OFType type, String name) {
906 return false;
907 }
908
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800909 // IFloodlightModule classes
910
911 @Override
912 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Yuta HIGUCHIe8813402014-01-08 13:36:50 -0800913 Collection<Class<? extends IFloodlightService>> l =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800914 new ArrayList<Class<? extends IFloodlightService>>();
915 l.add(ILinkDiscoveryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800916 return l;
917 }
918
919 @Override
920 public Map<Class<? extends IFloodlightService>, IFloodlightService>
Jonathan Hart284e70f2014-07-05 12:32:51 -0700921 getServiceImpls() {
922 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
923 new HashMap<>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800924 m.put(ILinkDiscoveryService.class, this);
925 return m;
926 }
927
928 @Override
929 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Jonathan Hart284e70f2014-07-05 12:32:51 -0700930 Collection<Class<? extends IFloodlightService>> l = new ArrayList<>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800931 l.add(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800932 l.add(IThreadPoolService.class);
933 l.add(IRestApiService.class);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700934 l.add(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800935 return l;
936 }
937
938 @Override
939 public void init(FloodlightModuleContext context)
940 throws FloodlightModuleException {
941 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800942 threadPool = context.getServiceImpl(IThreadPoolService.class);
943 restApi = context.getServiceImpl(IRestApiService.class);
Umesh Krishnaswamy57a32a92013-03-21 14:21:15 -0700944 registryService = context.getServiceImpl(IControllerRegistryService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800945
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800946 this.lock = new ReentrantReadWriteLock();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800947 this.links = new HashMap<Link, LinkInfo>();
948 this.portLinks = new HashMap<NodePortTuple, Set<Link>>();
949 this.suppressLinkDiscovery =
950 Collections.synchronizedSet(new HashSet<NodePortTuple>());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800951 this.switchLinks = new HashMap<Long, Set<Link>>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800952 }
953
954 @Override
955 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700956 @LogMessageDoc(level = "ERROR",
957 message = "No storage source found.",
958 explanation = "Storage source was not initialized; cannot initialize " +
959 "link discovery.",
960 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG),
961 @LogMessageDoc(level = "ERROR",
962 message = "Error in installing listener for " +
963 "switch config table {table}",
964 explanation = "Failed to install storage notification for the " +
965 "switch config table",
966 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG),
967 @LogMessageDoc(level = "ERROR",
968 message = "No storage source found.",
969 explanation = "Storage source was not initialized; cannot initialize " +
970 "link discovery.",
971 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG),
972 @LogMessageDoc(level = "ERROR",
973 message = "Exception in LLDP send timer.",
974 explanation = "An unknown error occured while sending LLDP " +
975 "messages to switches.",
976 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800977 })
978 public void startUp(FloodlightModuleContext context) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800979 ScheduledExecutorService ses = threadPool.getScheduledExecutor();
Jonathan Hart284e70f2014-07-05 12:32:51 -0700980 controller = context.getServiceImpl(IFloodlightProviderService.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800981
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800982 discoveryTask = new SingletonTask(ses, new Runnable() {
983 @Override
984 public void run() {
985 try {
986 discoverLinks();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800987 } catch (Exception e) {
988 log.error("Exception in LLDP send timer.", e);
989 } finally {
Jonathan Hartba354e02014-06-30 19:18:16 -0700990 log.trace("Rescheduling discovery task");
991 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL,
992 TimeUnit.SECONDS);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800993 }
994 }
995 });
996
Jonathan Hartec4f14e2013-12-12 10:46:38 -0800997 discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL, TimeUnit.SECONDS);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800998
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800999 // Register for the OpenFlow messages we want to receive
1000 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
1001 floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this);
1002 // Register for switch updates
1003 floodlightProvider.addOFSwitchListener(this);
Jonathan Hartba354e02014-06-30 19:18:16 -07001004 restApi.addRestletRoutable(new LinkDiscoveryWebRoutable());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001005 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001006}